2012-01-07 23 views
12

Tengo un elemento de lista con EditText, no sé cuántos elementos habrá. Tengo un problema cuando ingreso un texto en EditText, y luego me desplazo hacia abajo en un ListView, después de volver a desplazarme hacia arriba no hay texto en mi primer EditText, o hay texto de EditText en ListView.EditText pierde contenido en desplazamiento en ListView

He intentado con TextWatcher y guardo datos en una matriz, pero hay problemas porque la posición de vista devuelta en ListView no siempre es correcta, así que perdí algunos datos de la matriz. -.-

Cómo detectar la posición correcta de la vista en ListView?

Por ejemplo:

Si tengo 10 elementos en ListView, y sólo 5 de ellos son actualmente visibles. Posición de retorno del adaptador de 0 a 4 ... eso está bien. Cuando me desplazo hacia abajo, la posición del elemento 6 es 0 ... wtf? y pierdo datos de la matriz en la posición 0 :)

Estoy usando ArrayAdapter.

Por favor ayuda.

Aquí hay algo de código:

public View getView(int position, View convertView, ViewGroup parent) { 

    tmp_position = position; 

    if (convertView == null) { 

     holder = new ViewHolder(); 

     LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     convertView = vi.inflate(R.layout.element_in_game, null); 

     holder.scoreToUpdate = (EditText) convertView 
       .findViewById(R.id.elementUpdateScore); 

     holder.scoreToUpdate.addTextChangedListener(new TextWatcher() { 

      @Override 
      public void onTextChanged(CharSequence s, int start, 
        int before, int count) { 
       scoresToUpdate[tmp_position] = s.toString(); 
      } 

      @Override 
      public void beforeTextChanged(CharSequence s, int start, 
        int count, int after) { 

      } 

      @Override 
      public void afterTextChanged(Editable s) { 

      } 
     }); 

     initScoresToUpdateEditTexts(holder.scoreToUpdate, hint); 

     convertView.setTag(holder); 

    } else { 

     holder = (ViewHolder) convertView.getTag(); 

     holder.scoreToUpdate.setText(scoresToUpdate[tmp_position]); 

    } 

    return convertView; 
} 

Respuesta

14

usted debe tratar de esta forma de la siguiente manera:

if (convertView == null) { 

     holder = new ViewHolder(); 

     LayoutInflater vi = (LayoutInflater)  
     getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     convertView = vi.inflate(R.layout.element_in_game, null); 

     holder.scoreToUpdate = (EditText) convertView 
       .findViewById(R.id.elementUpdateScore); 


     convertView.setTag(holder); 

    } else { 

     holder = (ViewHolder) convertView.getTag(); 

    } 
    //binding data from array list 
    holder.scoreToUpdate.setText(scoresToUpdate[position]); 
    holder.scoreToUpdate.addTextChangedListener(new TextWatcher() { 

      @Override 
      public void onTextChanged(CharSequence s, int start, 
        int before, int count) { 
       //setting data to array, when changed 
       scoresToUpdate[position] = s.toString(); 
      } 

      @Override 
      public void beforeTextChanged(CharSequence s, int start, 
        int count, int after) { 

      } 

      @Override 
      public void afterTextChanged(Editable s) { 

      } 
     }); 

    return convertView; 
} 

Explicación: Comportamiento Reciclaje de ListView, en realidad borrar todos los datos se unen con su línea de artículo, cuando pasan de la visión. Por lo tanto, cada nueva fila entrará en la vista desde cualquier dirección que necesite para vincular datos nuevamente. Además, cuando se modifica el texto de EditText, también se deben mantener los valores en alguna estructura de datos, por lo que más adelante se puede vincular el enlace de datos para extraer la posición. Puede usar ArrayList para mantener datos de texto de edición, y también puede usar para enlazar datos.

Comportamiento similar en Recyclerview Adapter, detrás de la lógica debe conocer el enlace de datos de fila de los adaptadores.

+8

no funciona para mí? – DPM

+0

Anand, hola, estoy teniendo el mismo problema ahora, probé la solución en tu código pero aparentemente no puedo hacer que funcione, ¿cómo puedo actualizar datos específicos en el arraylist dependiendo del texto de edición –

+0

@PankajNimgade, para mí esto funciona: http://stackoverflow.com/a/21404201/2914140. Dentro de 'holder.scoreToUpdate.setOnFocusChangeListener (...' write: if (! HasFocus) {scoresToUpdate [position] = holder.scoreToUpdate.getText(). ToString();} – CoolMind

5

Si sólo tendrá ~ 10 filas, no se moleste con el ListView. Simplemente colóquelos en una posición vertical LinearLayout y envuélvalo en un ScrollView, y le ahorrará algo de dolor de cabeza.

Si va a tener docenas o cientos de filas, le sugiero que busque un mejor paradigma UX que EditText widgets en ListView filas.

Dicho todo esto, parece que no está manejando su reciclaje de hileras correctamente, o no sabe que las hileras se reciclan. Si tiene 10 elementos en su ListAdapter, y solo tiene espacio para mostrar 5 filas con widgets EditText, no debería terminar con 10 EditText widgets cuando el usuario se desplaza hacia la parte inferior. Deberías terminar con 5-7: los de la pantalla, y quizás uno o dos más para reciclar cuando el usuario se desplaza a continuación.

This free excerpt de one of my books pasa por el proceso de crear subclases personalizadas de ArrayAdapter y hacer que funcione el reciclaje. También cubre tener una fila interactiva, usando RatingBar para la entrada del usuario. Eso es mucho más fácil que EditText, ya que todo lo que tiene que preocuparse es hacer clic en eventos. Le invitamos a que intente ampliar esa técnica con los widgets EditText y los oyentes TextWatcher, pero no soy seguidor.

+0

tnx para una respuesta rápida! Echaré un vistazo a su libro ... espero que encuentre algo :) – Veljko

+0

He intentado utilizar su concepto, pero sucede lo mismo. – Veljko

0

Una mejor manera es la creación de EditarTexto en tiempo de ejecución y el establecimiento de su id como el argumentoposición

de getView() método. por lo que ahora cuando se añade el texto, guárdelo en un vector variable (clase

variable), y en el método getView basado en posición variable de ajuste el texto (que se puede

obtiene con elementAt() método del vector) de EditText correspondiente.

y no olvide agregar este EditText a la Vista inflada.

+0

lo he intentado antes ... pero el problema es que getView solo devuelve la posición en el rango de elementos visibles. creo que lo menciono en mi pregunta. – Veljko

+0

@Veljko ... ¿cuál es el problema en eso? De cualquier forma que desee mostrar texto de EditText visible solo. Es por eso que te dije que mantuvieras un Vector que almacena el Texto. o una Hashtable es una mejor opción ya que puede dar posición como clave convirtiéndola en String. – ngesh

1

A partir de una breve revisión de su código, parece que se está enfrentando al reciclador. Cuando se desplaza de la posición 1 a la posición 20 en una vista de lista, no crea 20 instancias diferentes de la vista del elemento de vista de lista. En cambio, tiene 7 u 8: cuando uno se desplaza fuera de la pantalla, se agrega a la recicladora y luego se envía al método getView como parámetro convertView, de modo que puede volver a llenarlo con nuevos datos en lugar de crear una nueva vista .

Tradicionalmente, uno usa el patrón ViewHolder para guardar las subvistas de un elemento de la lista (textview, imageview, etc.), no data (establecer y obtener el puntaje desde el titular) - Lo que sucede es que estableces el valor en cero en la posición cero, desplácese hacia abajo hasta el 6º elemento en una pantalla de 5 elementos, el 0º elemento se reutiliza para la posición 6, y extrae el valor existente del titular adjunto a ese elemento de lista (en este caso, un 0)

Respuesta simple: ¡No almacene datos específicos de posición en el soporte! Stash en una matriz externa o hashtable en alguna parte.

0

Este Código le ayudará a

private class EfficientAdapter extends BaseAdapter { 
     private LayoutInflater mInflater; 
     private String[] attitude_names; 
     public String[] attitude_values; 
     private String name; 
public static HashMap<Integer,String> myList=new HashMap<Integer,String>(); 

     public EfficientAdapter(Context context) { 
      mInflater = LayoutInflater.from(context); 
      attitude_names = context.getResources().getStringArray(R.array.COMP_ATTITUDE_NAME); 
      attitude_values = new String[attitude_names.length]; 
     } 
// initialize myList 
for(int i=0;i<attitude_names.length;i++) 
{ 
    myList.put(i,""); 
} 


     public Object getItem(int position) { 
      return position; 
     } 

     public long getItemId(int position) { 
      return position; 
     } 

     public View getView(int position, View convertView, ViewGroup parent) { 
      final ViewHolder holder; 

      if (convertView == null) { 
       convertView = mInflater.inflate(R.layout.addcomp_attitude_row, null); 

       holder = new ViewHolder(); 
       holder.Attitude_Name = (TextView) convertView.findViewById(R.id.addcomp_att_name); 
       holder.Attitude_Value = (EditText) convertView.findViewById(R.id.addcomp_att_value); 
       holder.Attitude_Value.addTextChangedListener(new TextWatcher() 
        { 
         public void afterTextChanged(Editable edt) 
         { 
          myList.put(pos,s.toString.trim()); 
          attitude_values[holder.ref] = edt.toString(); 
         } 

         public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {} 

         public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { 
          //attitude_values[ref] = Attitude_Value.getText().toString(); 
         } 
        }); 

       convertView.setTag(holder); 
      } else { 
       holder = (ViewHolder) convertView.getTag(); 
      } 


      holder.ref=position; 
      holder.Attitude_Name.setText(attitude_names[position]); 
      holder.Attitude_Value.setHint(attitude_names[position]); 
      holder.Attitude_Value.setText(myList.get(position)); 




      return convertView; 
     } 

     class ViewHolder { 
      TextView Attitude_Name; 
      EditText Attitude_Value; 
      int ref; 



     } 

     @Override 
     public int getCount() { 
      return attitude_names.length; 
     } 
    } 

Aquí he incluido un objeto HashMap que mantener el ojo de lo que contiene EditarTexto value.And cuando se desplaza la vista de lista, que se representará de nuevo llamando a su getView método.

En este código, cuando cargue listview por primera vez, todo el texto de edición estará sin texto. Cuando ingrese texto, se anotará en myList.So cuando vuelva a mostrar la lista, se evitará el texto.

0

Obtener el paseo del cheque convertView == null y es otra declaración. Va a obtener un peor rendimiento pero deshabilitará el reciclaje.

2

Hace poco estaba buscando una solución similar y descubrí que el uso del textChangeListener NO era el mejor método. Le contesté una pregunta relacionada aquí:

https://stackoverflow.com/a/13312282/1812518

Es mi primer post, pero espero que sea lo suficientemente servicial.

12

puede usar setOnFocusChangeListener en su lugar. en mi caso tengo lista ampliable y parece bueno :) así:

holder.count.setOnFocusChangeListener(new View.OnFocusChangeListener() { 
     public void onFocusChange(View v, boolean hasFocus) { 
      if (!hasFocus) { 
       EditText et =(EditText)v.findViewById(R.id.txtCount); 
       myList.get(parentPos).put(childPos, 
         et.getText().toString().trim()); 
      } 
     } 
    }); 
+1

Perfecto. Intenté con miles de fragmentos de código disponibles en SO. Solo los tuyos parecen funcionar. Gracias una vez más –

+0

por favor defina qué es childPos y parentPos en su código Estoy atrapado con el mismo problema – jyomin

+0

su bienvenida @SyamantakBasu y rahul, – amin10043

4

que tenían un tema relacionado que he resuelto. Cada fila de mi ListView tiene una vista diferente (contiene diferentes controles: EditView, ImageView, TextView). Quiero que los datos ingresados ​​o seleccionados en esos controles persistan incluso cuando el control se desplaza fuera de la pantalla. La solución que utilicé fue para implementar los métodos siguientes en ArrayAdapter así:

public int getViewTypeCount() { 
    return getCount(); 
} 

public int getItemViewType(int position) { 
    return position; 
} 

Esto se asegurará de que cuando se llama a getView() pasa lo mismo View instancia que se volvió de getView() en la primera llamada.

public View getView(int position, View convertView, ViewGroup parent) { 

    if (convertView != null) 
    return convertView; 
    else 
    // create new view 
} 
+1

Este es un truco muy malo. Al establecer ** getViewTypeCount ** en ** getCount **, crea una nueva fila para cada elemento en su ListView y no se vuelve a realizar el recierre, lo que puede ser un problema si tiene muchos elementos o un teléfono anterior. – Marko

0

Mi adaptador extiende el BaseAdapter y tuve una situación similar a bickster tenía donde tenía la vista de lista que tenía diferentes puntos de vista en ellas (una forma dinámica como la situación) y necesaria para conservar los datos. Mi vista de lista no iba a ser demasiado grande, por lo que el reciclaje de la vista no iba a ser un gran recurso consume en mi caso y por lo tanto un simple

if (convertView != null) return convertView;

en el inicio de la getView era suficiente para mí . No estoy seguro de qué tan eficiente es una solución, pero era lo que estaba buscando.

Editar Importante: Esto está bien si y sólo si tiene muy pocas filas (sin desplazamiento), de lo contrario este truco es horrible uno. En su lugar, debe encargarse de los datos manualmente

Cuestiones relacionadas