2010-09-18 13 views
22

Rozerwanie włosów na strzępy, starając się, aby Android ListView zrobił to, co chcę.Jak uzyskać selektor elementów ListView z Androidem do użycia state_checked

Chcę mieć tryb ListView w trybie pojedynczego wyboru z niestandardowym układem wierszy, który ma inny kolor tła dla zaznaczonych, wciśniętych i zaznaczonych (tzn. Wybór jest zaznaczony kolorem, a nie znacznikiem wyboru - to właśnie normalnie nazywam "selekcja", ale wybór w Androidzie wydaje się być linią, którą wybieram zanim ją nacisnę)

Pomyślałem o wypróbowaniu selektora tła z trzema stanami w nim. Działa dobrze dla state_selected i state_pressed, ale nie state_checked. Stworzyłem więc CheckableRelativeLayout, który rozszerza RelativeLayout i implementuje Checkable i służy do widoku każdego wiersza.

Uproszczona wersja znajduje się tutaj:

<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 wygląda

<?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> 

kolory są zdefiniowane w innym miejscu.

To nadal nie działa. Tak więc w niestandardowym narzędziu ListAdapter śledziłem wiersz "sprawdzany" i wypróbowywano (w getView)

, jeśli (pozycja == zaznaczona pozycja) ret.getBackground() .setState (CHECKED_STATE_SET);

I nadal nie działa. Jak mogę zrobić to, co chcę?

Odpowiedz

45

Musisz zastąpić onCreateDrawableState w swoim CheckableRelativeLayout i ustawić Clickable = "true" dla niego. Mój kod 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

Dzięki - Spróbuję to uwagę i zobacz, czy to działa (musisz wykopać mój kod, aby go dopasować!) – MrPurpleStreak

+1

Chciałbym awansować, gdybym mógł! – Gallal

+2

Lepsze niż to, że zamiast ustalania klikalne = true i przesłanianie CheckableLinearLayout za performClick(), to zmienić: @Override (zaznaczone logiczna) \t public void setChecked { \t \t mChecked = sprawdzane; \t za (Cienkowite c: mCheckableViews) { \t \t \t c.setChecked (sprawdzone); \t \t} \t \t refreshDrawableState(); \t} –

10

Lepsze niż to, że zamiast ustalania klikalne = true i przesłanianie CheckableLinearLayout za performClick(), zachować sugestię Pavel za nadrzędne onCreateDrawableState i zastąpić CheckableLinearLayout za setChecked() brzmienie:

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(); 
    } 

Pozwoli to uniknąć problemów związanych z kliknięciami i długimi kliknięciami.

+0

doskonały .. :) dzięki .. –

+1

Skąd masz mCheckableViews z? –

+0

Zaktualizowałem moją odpowiedź. –

0

IMHO, jest łatwiejszy w użyciu niestandardowego adaptera:

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> 

Jak zastosowanie:

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 
      } 
     }); 

    } 
Powiązane problemy