2010-09-18 8 views
22

Strappare i capelli cercando di ottenere un ListView Android per fare ciò che voglio.Come ottenere un selettore di oggetti ListView di Android per utilizzare lo stato_checked

Voglio avere un ListView in modalità scelta singola con un layout di riga personalizzato che ha un colore di sfondo diverso per selezionato, premuto e controllato (cioè la scelta è mostrata da un colore piuttosto che un segno di spunta - questo è quello che io Normalmente chiamerei la "selezione" ma la selezione in Android sembra la linea che sto per scegliere prima di premere)

Ho pensato di provare un selettore di sfondo con i tre stati in esso. Funziona bene per state_selected e state_pressed, ma non state_checked. Così ho creato un CheckableRelativeLayout che estende RelativeLayout e implementa Checkable e utilizzato per la visualizzazione di ogni riga.

Una versione semplificata è mostrato qui:

<my.package.CheckableRelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:background="@drawable/bkg_selector"> 
    > 

    <ImageView android:id="@+id/animage" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentLeft="true" 
    /> 
</my.package.CheckableRelativeLayout> 

bkg_selector sembra

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

I colori sono definiti altrove.

Questo ancora non ha funzionato. Così nel ListAdapter personalizzato ho rintracciato la "controllata" di fila e ha cercato (in GetView)

se (posizione == checkedPosition) ret.getBackground() setState (CHECKED_STATE_SET).;

E ANCORA non funziona. Come posso farlo fare ciò che voglio?

risposta

45

È necessario eseguire l'override di onCreateDrawableState nel CheckableRelativeLayout e impostare Clickable = "true" per esso. Il mio codice per LinearLayout:

public class CheckableLinearLayout extends LinearLayout implements Checkable { 
private boolean checked = false; 

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

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

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

public void setChecked(boolean b) { 
    checked = b; 
} 

public boolean isChecked() { 
    return checked; 
} 

public void toggle() { 
    checked = !checked; 
} 

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

@Override 
public boolean performClick() { 
    toggle(); 
    return super.performClick(); 
} 
+0

Grazie - Cercherò questo fuori e vedi se funziona (devo scavare il mio codice per adattarlo!) – MrPurpleStreak

+1

Avrei più voti se potessi! – Gallal

+2

Meglio di così, invece di impostare cliccabile = true e ignorando di CheckableLinearLayout performClick(), ignorare questo: @Override \t public void setChecked (booleano selezionata) { \t \t mChecked = controllato; \t \t per (Checkable c: mCheckableViews) { \t \t \t c.setChecked (selezionata); \t \t} \t \t refreshDrawableState(); \t} –

10

Meglio di così, invece di impostare cliccabile = true e ignorando di CheckableLinearLayout performClick(), tenere il suggerimento di Pavel per superiori onCreateDrawableState e sostituire di CheckableLinearLayout setChecked() dal seguente:

private final List<Checkable> mCheckableViews = new ArrayList<Checkable>(); 

@Override 
protected void onFinishInflate() { 
    super.onFinishInflate(); 
    final int childCount = getChildCount(); 
    findCheckableChildren(this); 
} 

private void findCheckableChildren(View v) { 
    if (v instanceof Checkable && v instanceof ViewGroup) { 
     mCheckableViews.add((Checkable) v); 
    } 
    if (v instanceof ViewGroup) { 
     final ViewGroup vg = (ViewGroup) v; 
     final int childCount = vg.getChildCount(); 
     for (int i = 0; i < childCount; ++i) { 
      findCheckableChildren(vg.getChildAt(i)); 
     } 
    } 
} 

    @Override 
    public void setChecked(boolean checked) { 
     mChecked = checked; 
     for (Checkable c : mCheckableViews) { 
      c.setChecked(checked); 
     } 
     refreshDrawableState(); 
    } 

Eviterà problemi di clic e richiami di clic lunghi.

+0

perfetto .. :) grazie .. –

+1

Da dove prendi mCheckableViews? –

+0

Ho aggiornato la mia risposta. –

0

IMHO, è più facile da usare un adattatore personalizzato:

class CustomAdapter extends ArrayAdapter<CustomRowItem> { 
    Context context; 

    public CustomAdapter(Context context, int resourceId, List<CustomRowItem> items) { 
     super(context, resourceId, items); 
     this.context = context; 
    } 

    private class ViewHolder { 
     TextView txt; 
     View layout; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
     ViewHolder holder = null; 
     CustomRowItem rowItem = getItem(position); 

     LayoutInflater mInflater = (LayoutInflater) context 
      .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.сustom_list, null); 
      holder = new ViewHolder(); 
      holder.txt = (TextView) convertView.findViewById(R.id.сustom_list_txt); 
      holder.layout = convertView.findViewById(R.id.сustom_list_layout); 
      convertView.setTag(holder); 
     } else 
      holder = (ViewHolder) convertView.getTag(); 

     holder.txt.setText(rowItem.getText()); 
     if(rowItem.isChecked()) 
      holder.layout.setBackgroundColor(-16720999); //color for checked 
     else 
      holder.layout.setBackgroundColor(0); //color for unchecked 

     return convertView; 
    } 

} 

class CustomRowItem { 
    private boolean value; 
    private String text; 

    public CustomRowItem(String text, boolean value) { 
     this.text = text; 
     this.value = value; 
    } 
    public boolean isChecked() { 
     return value; 
    } 
    public void setChecked(boolean checked) { 
     value = checked; 
    } 
    public String getText() { 
     return text; 
    } 
    void setText(String text) { 
     this.text = text; 
    } 
} 

сustom_list.xml:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:padding="10dp" 
    android:id="@+id/сustom_list_layout" 
    android:orientation="vertical" > 
    <TextView 
     android:id="@+id/сustom_list_txt"   
     android:textSize="20sp" 
     android:layout_width="fill_parent"   
     android:layout_height="fill_parent"/> 
</LinearLayout> 

Come uso:

public class ExampleAct extends Activity { 

    final List<CustomRowItem> list = new ArrayList<CustomRowItem>(); 
    CustomAdapter adapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.example); 

     ListView listView=(ListView)findViewById(R.id.listView); 

     adapter = new CustomAdapter(this, R.layout.сustom_list, list); 
     listView.setAdapter(adapter); 

     list.add(new CustomRowItem("unchecked item",false)); 
     list.add(new CustomRowItem("checked item",true)); 

     adapter.notifyDataSetChanged(); 

     listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { //unchecked on click 
      @Override 
      public void onItemClick(AdapterView<?> parent, View itemClicked, int index, long id) { 
       if(list.get(index).isChecked()) { 
        list.get(index).setChecked(false); //uncheck 
        adapter.notifyDataSetChanged(); 
       } else { 
        // other actions 
       } 

     }); 

     listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { //checked on long click 
      @Override 
      public boolean onItemLongClick(AdapterView<?> parent, View itemClicked, int index, long id) { 
       list.get(index).setChecked(true); //check 
       adapter.notifyDataSetChanged(); 
       return true; // or false for calling context menu 
      } 
     }); 

    } 
Problemi correlati