2010-01-11 39 views
8

Hola a todos,Android ListView Selección Problema

Me disculpo por la siguiente pregunta larga ...

Tengo un LinearLayout que contiene un ListView y algunos otros artículos. En cuanto a ListView, cada una de sus filas es LinearLayout que contiene 3 vistas: Checkbox, ImageView y TextView (de izquierda a derecha, horizontal). Como quería que se seleccionara toda la fila cuando usaba la bola de seguimiento (para resaltar con un color de fondo), configuré las tres vistas dentro de la fila LinearLayout como no enfocables, y funcionó.

Ahora estoy teniendo 2 problemas con respecto a este ListView. Primero, quiero que cada vez que toco una fila en el ListView (con mi dedo), para obtener el mismo comportamiento que cuando uso la bola de seguimiento: signifique que quiero que se seleccione la fila (resaltada). Lo que está ocurriendo ahora es que cuando toco la fila realmente se selecciona, pero cuando suelto mi dedo, la selección se va (al igual que sucede en la lista de contactos del dispositivo).

Segundo: desde un menú, puedo mostrar un LinearLayout nuevo en lugar del que contiene el ListView (pantalla de la aplicación diferente). Cuando esto sucede, todavía almacena el objeto de LinearLayout que contiene el ListView, porque quiero poder volver a visualizarlo más tarde sin crearlo desde cero. El problema es que cuando vuelvo a decepcionar el LinearLayout con el ListView, no se selecciona ninguna de las filas de ListView, incluso si se seleccionó una fila de ceratin cuando el LinearLayout con el ListView "dejó" la pantalla.

Lo sentimos de nuevo por la larga publicación.

Gracias!

Respuesta

3
  1. Éste es el comportamiento predeterminado y esperado. Alterar esto es fuertemente sugerido en contra.
  2. Esto se sigue de 1. hasta cierto punto. El estado seleccionado no es persistente. Más bien, asigne un OnItemClickListener y haga que guarde el id del elemento seleccionado en alguna variable. Si necesita volver a seleccionar el elemento cuando vuelvas, puede utilizar setSelection()
+0

Ya intenté usar setSelection() cuando volví, pero no sirve-ante todo, la fila no está resaltada en absoluto (sin color de fondo), y la segunda fila se encuentra en la parte superior de la pantalla, aunque estaba ubicado en algún lugar en el medio cuando me fui. Gracias. – WhiteTigerK

+0

Otra cosa: en la lista de contactos del dispositivo al usar la bola de seguimiento para seleccionar un contacto y luego presionar la bola de seguimiento para ver sus detalles, se guarda el estado de la lista de contactos, por lo que al volver a la lista de contactos el contacto sigue seleccionado y su la ubicación en la pantalla es la misma. Por lo tanto, supongo que este comportamiento es posible de lograr. Gracias. – WhiteTigerK

+0

sí, se guardará el estado de selección, pero si en algún momento toca la pantalla, se activa * modo táctil * y cuando está en modo táctil, como ya ha notado, no hay comentarios visuales para un elemento seleccionado (solo breves comentarios cuando se toca el elemento, lo que no ocurre cuando regresa a la vista de lista). Este es el comportamiento predeterminado. no lo cambies –

0

Esto debería ayudar:

private void setListviewSelection(final ListView list, final int pos) { 
    list.post(new Runnable() { 
     @Override 
     public void run() { 
      list.setSelection(pos); 
      View v = list.getChildAt(pos); 
      if (v != null) { 
       v.requestFocus(); 
      } 
     } 
    }); 
} 
+1

no funciona. ingresa el método pero no ocurre nada –

1

he resuelto que el envío de un mensaje después de notifyDataSetChanged() ; y llamando setSelection(0) en cuestión mi función handleMessage. Es Parece feo, pero me ayudó varias veces cuando el SDK se comporta de manera extraña.

5
private void setListviewSelection(final ListView list, final int pos) { 
list.post(new Runnable() 
    { 
    @Override 
    public void run() 
     { 
     list.setSelection(pos); 
     View v = list.getChildAt(pos); 
     if (v != null) 
     { 
      v.requestFocus(); 
     } 
    } 
}); 
} 

Above me ayuda a establecer una fila enfocada en la lista.

+2

Hmm esto no es correcto. 'setSelection' seleccionará el i-ésimo elemento de la lista, pero' getChildAt' devuelve * visible * child, es decir, depende del desplazamiento actual. Entonces, si la lista se desplaza, esto no funcionará. – smok

+0

Estoy de acuerdo con @smok – Divers

19

Sí, Desde la perspectiva de un desarrollador de iOS, me parece que es muy difícil de aplicar características como "selección por defecto al iniciar conjunto" y "recordar el estado de selección después de usuario ha hecho clic fila" a ListView.

Así que vamos a empezar con "recordar la selección" problema first.The es que incluso si usted sabe que puede utilizar XML de selección para definir más destacado/presionado/enfoque style.but que el estilo no se mantuvo después de usuario hizo clic en esa fila.Por ejemplo, tengo un selector de XML resaltado (list_selector.xml según Resol carpeta/estirable) así (pero puede tener otros campos tienen que poner de relieve como el color de texto de Vista de Texto en fila):

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:drawable="@drawable/list_selector_pressed" android:state_pressed="true" /> 
    <item android:drawable="@drawable/list_selector_pressed" android:state_selected="true" /> 
</selector>  

y list_selector_pressed. XML, que define el estilo de resaltado - establecer el color de fondo a un color gris:

<?xml version="1.0" encoding="utf-8"?> 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item> 
     <shape xmlns:android="http://schemas.android.com/apk/res/android"> 
      <solid android:color="@color/dark_gray" /> 
     </shape> 
    </item> 
</layer-list> 

Así como @ David Hedlund sugirió:

Más bien, asignar una OnItem ClickListener, y haz que guarde la identificación del ítem seleccionado en alguna variable.

es necesario crear una variable de instancia en la parte superior de su clase:

private View currentSelectedView; 

luego ir a

@Override 
public void onListItemClick(ListView l, View v, int position, long id) { 
    if (currentSelectedView != null && currentSelectedView != v) { 
     unhighlightCurrentRow(currentSelectedView); 
    } 

    currentSelectedView = v; 
    highlightCurrentRow(currentSelectedView); 
    //other codes 
    } 

Muy simple: comprobamos si currentSelectedView es null o corriente clic o no. primero debemos descolgar cualquier estilo llamando al método unhighlightCurrentRow (currentSelectedView) --- usted puede preguntarse por qué pasamos la variable instantánea currentSelectedView como parámetro, lo explicaré más adelante. Luego asignamos view a currentSelectedView y resaltamos la fila actual; para que el estilo persista después de que el usuario hace clic.

private void unhighlightCurrentRow(View rowView) { 
    rowView.setBackgroundColor(Color.TRANSPARENT); 
    TextView textView = (TextView) rowView.findViewById(R.id.menuTitle); 
    textView.setTextColor(getResources().getColor(R.color.white)); 
} 

private void highlightCurrentRow(View rowView) { 
    rowView.setBackgroundColor(getResources().getColor(
      R.color.dark_gray)); 
    TextView textView = (TextView) rowView.findViewById(R.id.menuTitle); 
    textView.setTextColor(getResources().getColor(R.color.yellow)); 

} 

Aja, eso es todo. Así es como implementamos "recordar selección" para la vista de lista. Como se ve, tenemos que duplicar los códigos para peinar, tanto en XML y el código de Java - bastante estúpido :(

siguiente acerca de "selección por defecto ajuste" Usted puede pensar que usted puede hacer esto

.
listView.setAdapter(adatper) 
listView.setSelection(0); 
currentSelectedView = listView.getChildAt(0); 
highlightCurrentRow(currentSelectedView); 

en onCreate() en la actividad o onActivityCreated() en el fragmento.
Pero si lo ejecuta, obtendrá una excepción NullPointer y por qué? porque en este momento, la vista de lista no se representa sin embargo no lo hace y Android como iOS, que tiene viewWillAppear. Así que tienes que crear una variable instantánea para recordar si es primera vez para rendir celular vista de lista y en onListItemClick ha decidido eliminar esa variable:

Así que bajo la declaración currentSelectedView:

private Boolean firstTimeStartup = true; 

a continuación, añadir métodos: supongamos que queremos resaltar la primera fila en la vista de lista:

public class HomeAdapter extends ArrayAdapter<String> { 
    int layoutResourceId; 

    public HomeAdapter(Context context, int textViewResourceId, 
      ArrayList<String> objects) { 
     super(context, textViewResourceId, objects); 
     layoutResourceId = textViewResourceId; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     if (convertView == null) { 
      convertView = LayoutInflater.from(getContext()).inflate(
        layoutResourceId, null); 
     } 

     if (firstTimeStartup && postion == 0) { 
      highlightCurrentRow(convertView); 
     } else { 
      unhighlightCurrentRow(convertView); 
     } 

     TextView title = (TextView) convertView 
       .findViewById(R.id.menuTitle); 
     title.setText(getItem(position)); 
     return convertView; 
    } 
} 

Bastante simple. Pero hay que hacer algunos cambios en el método de onListItemClick:

@Override 
public void onListItemClick(ListView l, View v, int position, long id) { 

    if (firstTimeStartup) {// first time highlight first row 
     currentSelectedView = l.getChildAt(0); 
    } 
    firstTimeStartup = false; 
    if (currentSelectedView != null && currentSelectedView != v) { 
     unhighlightCurrentRow(currentSelectedView); 
    } 

    currentSelectedView = v; 
    highlightCurrentRow(currentSelectedView); 

    //other codes 
} 

hay que ir! Disfruta de Android :)

+0

mi experiencia aquí con respecto a la parte recordar/resaltar del código fue que no es necesario duplicar las configuraciones xml y programáticamente. Es más bien que los xml no funcionaron y los programáticos sí. En ese sentido, uno podría saltear la creación de 'list_selector.xml' y' list_selector_pressed.xml' y simplemente escribir el código que describe – Thomas

0

Estoy usando un adaptador y no quería establecer colores de fondo personalizados, pero usa el android: state_selected en drawable xml. SetSelection no funcionó para mí, pero tal vez sea también porque necesitaba SetNotifyDataChanged, que muestra que el Estado seleccionado no es persistente.

También encontré que el estado Seleccionado para un elemento en un ListView no es persistente, ya que SetNotifyDataChanged da como resultado la actualización del diseño de ListView que los borra a todos. Establecer el elemento en Seleccionado en GetView del Adaptador también es demasiado pronto.

Eventualmente configuré el estado Seleccionado para la vista del elemento seleccionado después de que se modificó el diseño de la vista de lista, que es cuando se activa el evento LayoutChange (en Java, probablemente está adjuntando a OnLayoutChangeListener de ListView).

Para hacerlo realmente fácil, almaceno la vista del artículo seleccionado como SelectedItemView del Adaptador. En el controlador de eventos LayViewChange de ListView, establecí el SelectedItemView.Selected del adaptador en verdadero.

Aquí está el código de mi actividad en la que me puse el adaptador para el ListView y también suscribirse a LayoutChange (o en Java adjuntar un OnLayoutChangeListener)

 ringTonesListView.Adapter = ringTonesListAdapter; 
     ringTonesListView.LayoutChange += (s, layoutChangeArgs) => { 
      //At this stage the layout has been updated and the Selected can be set to true for the view of the selected item. This will result in android:state_selected logic to be applied as desired and styling can be completely done per layout in Resources. 
      ringTonesListAdapter.SelectedItemView.Selected = true; 
     }; 

Aquí está mi código para el adaptador:

public class RingTonesListAdapter : BaseAdapter<RingToneItem> 
{ 
    List<RingTone> Items { get; set; } 

    public override View GetView(int position, View convertView, ViewGroup parent) 
    { 
     View view = convertView; 

     // re-use an existing view, if one is available 
     // otherwise create a new one 
     if (view == null) 
     { 
      view = Context.LayoutInflater.Inflate(Resource.Layout.AlertSoundItem, parent, false); 
      view.Click += SelectRingTone; 
     } 

     RingTone ringTone = this[position]; 
     if (ringTone.Selected) 
     { 
      //==> Important 
      //Store this view since it's the view for the Selected Item 
      SelectedItemView = view; 
      //Setting view.Selected to true here doesn't help either, since Selected will be cleared after. 
     } 

     return view; 
    } 

    private void SelectRingTone(object sender, EventArgs args) 
    { 
     View view = (View)sender; 
     string title = view.FindViewById<TextView>(Resource.Id.ringToneTitle).Text; 
     RingToneItem ringToneItem = Items.First(rt => rt.Title == title); 
     if (!ringToneItem.Selected) 
     { 
      //The RingTone was not selected and is selected now 
      //Deselect Old and Select new 
      foreach (RingToneItem oldItem in Items.Where(rt => rt.Selected)) 
      { 
       oldItem.Selected = false; 
      } 

      // Select New RingTone 
      ringToneItem.Selected = true; 
      //Update the ListView. 
      //This will result in removal of Selected state for all Items when the ListView updates it's layout 
      NotifyDataSetChanged(); 
     } 

     //Now play the test sound 
     NotifierService.TestSound(Context, ringToneItem); 
    } 

    public View SelectedItemView { get; set; } 
} 
1

Una solución mucho más limpia para mí se centró en el hecho de que si configura android:choiceMode="singleChoice" o android:choiceMode="multipleChoice" en su ListView, entonces el ListView intentará mantener el estado comprobado para cualquiera de ingle o selección múltiple ListView. El Gotcha es que depende de la celda de lista implementar Checkable

Así, se inicia mediante la implementación de un CheckableLinearLayout (o FrameLayout podría ser mejor) para que el androide mantendrá el estado de activación para usted y usted puede mantener el Disponibles de sincronizados. La clave aquí es refreshDrawableState y onCreateDrawableState Actualizan Drawable para que el fondo se dibuje resaltado o no.

public class CheckableLinearLayout extends LinearLayout implements Checkable { 
    boolean checked; 

    public CheckableLinearLayout(Context context) { 
     super(context); 
    } 

    public CheckableLinearLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    @Override 
    public void setChecked(boolean checked) { 
     this.checked = checked; 
     refreshDrawableState(); 
    } 

    @Override 
    public boolean isChecked() { 
     return checked; 
    } 

    @Override 
    public void toggle() { 
     setChecked(!isChecked()); 
    } 

    private static final int[] CheckedStateSet = { android.R.attr.state_checked }; 

    @Override 
    protected int[] onCreateDrawableState(int extraSpace) { 
     final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 
     if (isChecked()) { 
      mergeDrawableStates(drawableState, CheckedStateSet); 
     } 
     return drawableState; 
    } 
} 

A continuación, un simple selector para el dibujable fondo que mostrará el estado de activación:

<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:drawable="#ccc" android:state_checked="true" /> 
    <item android:drawable="@android:color/transparent" /> 
</selector> 

ahora puedo usar CheckableLinearLayout en cualquier lista que yo quiero mostrar estado de selección persistente (única o múltiple):

<CheckableLinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:background="@drawable/checkable_cell" 
    > 

    <TextView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:id="@+id/text" 
     android:layout_margin="10dp" 
     android:maxLines="8" 
     /> 
</CheckableLinearLayout> 

no necesito ningún código especial en el Fragment para actualizar el estado de activación, y puedo mantener el seleccionado posición alrededor de la rotación con:

state.putInt("selection", listView.getCheckedItemPosition()); 

y

listView.setItemChecked(state.getInt("selection"), true); 

mismo modo, puede utilizar setItemChecked para fijar un elemento seleccionado inicialmente, o utilizar getCheckedItemPosition (o getCheckedItemPositions() para selección múltiple) para obtener la selección actual.

+0

Funcionó en mi caso de uso. Gracias. – MCLLC