collapsing - Android ListView Refresh Single Row
layout collapsemode parallax (6)
Después de obtener los datos para una sola fila de un ListView
, quiero actualizar esa única fila.
Actualmente estoy usando notifyDataSetChanged();
pero eso hace que la View
reaccione muy lentamente. ¿Hay alguna otra solución?
Como Romain Guy explicó hace un tiempo durante la sesión de Google I / O , la forma más eficiente de actualizar solo una vista en una vista de lista es algo como lo siguiente (esta información actualiza toda la vista):
ListView list = getListView();
int start = list.getFirstVisiblePosition();
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++)
if(target==list.getItemAtPosition(i)){
View view = list.getChildAt(i-start);
list.getAdapter().getView(i, view, list);
break;
}
Suponiendo que el target
es un elemento del adaptador.
Este código recupera el ListView
, luego examina las vistas mostradas actualmente, compara el elemento de target
que estás buscando con cada elemento de vista que se muestra, y si tu objetivo está entre esos, obtén la vista getView
y ejecuta el adaptador getView
en esa vista para actualizar el monitor.
Como una nota al margen invalidate
no funciona como algunas personas esperan y no actualizará la vista como lo hace getView, notifyDataSetChanged
reconstruirá toda la lista y terminará llamando a getview
para cada elemento mostrado y invalidateViews
también afectará a un montón.
Una última cosa, uno también puede obtener un rendimiento adicional si solo necesita cambiar un elemento secundario de una vista de fila y no toda la fila como getView
hace getView
. En ese caso, el siguiente código puede reemplazar list.getAdapter().getView(i, view, list);
(ejemplo para cambiar un texto de TextView
):
((TextView)view.findViewById(R.id.myid)).setText("some new text");
En el código confiamos.
El siguiente código funcionó para mí. Tenga en cuenta que al llamar a GetChild () tiene que compensar con el primer elemento de la lista, ya que es relativo a eso.
int iFirst = getFirstVisiblePosition();
int iLast = getLastVisiblePosition();
if ( indexToChange >= numberOfRowsInSection() ) {
Log.i( "MyApp", "Invalid index. Row Count = " + numberOfRowsInSection() );
}
else {
if ( ( index >= iFirst ) && ( index <= iLast ) ) {
// get the view at the position being updated - need to adjust index based on first in the list
View vw = getChildAt( sysvar_index - iFirst );
if ( null != vw ) {
// get the text view for the view
TextView tv = (TextView) vw.findViewById(com.android.myapp.R.id.scrollingListRowTextView );
if ( tv != null ) {
// update the text, invalidation seems to be automatic
tv.setText( "Item = " + myAppGetItem( index ) + ". Index = " + index + ". First = " + iFirst + ". Last = " + iLast );
}
}
}
}
Este método más simple me funciona bien, y solo necesita conocer el índice de posición para obtener la vista:
// mListView is an instance variable
private void updateItemAtPosition(int position) {
int visiblePosition = mListView.getFirstVisiblePosition();
View view = mListView.getChildAt(position - visiblePosition);
mListView.getAdapter().getView(position, view, mListView);
}
Hay otra cosa mucho más eficiente que puede hacer, si es que se ajusta a su caso de uso.
Si está cambiando el estado y de alguna manera puede llamar al correcto (conociendo la posición) mListView.getAdapter().getView()
será el más eficiente de todos.
Puedo demostrar una manera realmente fácil de hacerlo, creando una clase interna anónima en mi clase ListAdapter.getView()
. En este ejemplo, tengo un TextView
muestra un texto "nuevo" y esa vista se establece en GONE
cuando se hace clic en el elemento de la lista:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// assign the view we are converting to a local variable
View view = convertView;
Object quotation = getItem(position);
// first check to see if the view is null. if so, we have to inflate it.
if (view == null)
view = mInflater.inflate(R.layout.list_item_quotation, parent, false);
final TextView newTextView = (TextView) view.findViewById(R.id.newTextView);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCallbacks != null)
mCallbacks.onItemSelected(quotation.id);
if (!quotation.isRead()) {
servicesSingleton.setQuotationStatusReadRequest(quotation.id);
quotation.setStatusRead();
newTextView.setVisibility(View.GONE);
}
}
});
if(quotation.isRead())
newTextView.setVisibility(View.GONE);
else
newTextView.setVisibility(View.VISIBLE);
return view;
}
El marco utiliza automáticamente la position
correcta y debe preocuparse por recuperarlo antes de llamar a getView
.
Solo para el registro, ¿alguien consideró la ''Vista vista'' en el método de anulación?
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Update the selected item
((TextView)view.findViewById(R.id.cardText2)).setText("done!");
}
Una opción es manipular ListView
directamente. Primero compruebe si el índice de la fila actualizada está entre getFirstVisiblePosition()
y getLastVisiblePosition()
, estas dos le dan la primera y la última posición en el adaptador que son visibles en la pantalla. Luego puede obtener la View
fila con getChildAt(int index)
y cambiarla.