Как получить селектор элементов списка android ListView для использования state_checked
Разрывая мои волосы, пытаясь заставить Android ListView делать то, что я хочу.
Я хочу иметь ListView в режиме одиночного выбора с настраиваемой строкой строки, которая имеет другой цвет фона для выбранных, нажатых и отмеченных (т.е. выбор отображается цветом, а не галочкой - вот что я хотел бы обычно вызывайте "выбор", но выбор в андроиде кажется строкой, которую я собираюсь выбрать, прежде чем нажимать на нее)
Я подумал о попытке выбора фонового селектора с тремя состояниями в нем. Он отлично работает для state_selected и state_pressed, но не state_checked. Поэтому я создал CheckableRelativeLayout, который расширяет RelativeLayout и реализует Checkable и используется для представления каждой строки.
Здесь показана упрощенная версия:
<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 выглядит как
<?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>
Цвета указаны в другом месте.
Это все еще не сработало. Поэтому в пользовательском ListAdapter я отслеживал строку "checked" и пытался (в getView)
if (position == checkedPosition) . Ret.getBackground() SetState (CHECKED_STATE_SET);
И это STILL не работает. Как я могу заставить его делать то, что я хочу?
Ответы
Ответ 1
Вам нужно переопределить onCreateDrawableState в вашем CheckableRelativeLayout и установить для него Clickable = "true".
Мой код для 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();
}
Ответ 2
Лучше этого, вместо установки clickable = true и переопределения CheckableLinearLayout performClick(), держите предложение Pavel для переопределения onCreateDrawableState и замените CheckableLinearLayout setChecked() следующим:
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();
}
Это позволит избежать проблем при обращении к клику и длинному клику.
Ответ 3
IMHO, проще использовать пользовательский адаптер:
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;
}
}
custom_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>
Как использовать:
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
}
});
}