Gmail-подобный ListView с флажками (и с помощью ActionBar)
Я пытаюсь воссоздать то, что Google сделал с ListView в приложении Gmail. В частности, я хотел бы, чтобы каждый элемент списка включал CheckBox и два TextViews (один поверх другого). Мне нужны слушатели, когда CheckBox отмечен (или щелкнут), и когда в другом месте элемента списка будет нажата кнопка. Наконец, я бы хотел, чтобы ActionBar отражал, что элементы выбраны и предоставляют такие параметры, как "Выбрать все", "Выбрать" Нет "и т.д. (См. этот скриншот),
![enter image description here]()
До сих пор здесь был макет, который я придумал.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<CheckBox android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal" />
<LinearLayout android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dp"
android:focusable="true"
android:clickable="true" >
<TextView android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp" />
<TextView android:id="@+id/dateTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
Это отображает все правильно, но мне нужно указать, как настроить слушателей для двух представлений (@+ id/checkBox и @+ id/linearLayout1)., Я просмотрел List16 API demo, но они используют макет simple_list_item_activated_1, и я не уверен, что XML для этого выглядит, Как показывает их код, я создал класс ModeCallback, который реализует ListView.MultiChoiceModeListener, и я установил режим выбора ListView на CHOICE_MODE_MULTIPLE_MODAL, но я не знаю, как получить CheckBox в моем макете для работы с этим.
Кто-нибудь успешно скопировал поведение ListView в приложении Gmail? Я искал совсем немного и ничего не мог придумать (несмотря на то, что некоторые другие задавали похожие вопросы, как этот - большинство ответов просто указывают на эту же демонстрационную версию API).
Кроме того, для контекста я загружаю данные из базы данных SQLite в список, и я создал свой собственный адаптер курсора (который отлично работает). У меня есть ощущение, что мне нужно настроить слушателей в этом классе в методах newView() и bindView(), но все, что я пробовал, не сработало.
Любые идеи?
Ответы
Ответ 1
Режим на рисунке называется режимом действия. Если вы используете свой собственный просмотр строки и пользовательский адаптер, вам не нужно использовать CHOICE_MODE_MULTIPLE_MODAL для запуска режима действия. Я попробовал один раз и не смог, и я подозреваю, что он используется для встроенного адаптера.
Чтобы вызвать режим действия самостоятельно в вашем коде, вызовите метод startActionMode в любом методе прослушивания любого из ваших представлений, пусть это будет checkbox, textview или что-то в этом роде. Затем вы передаете в него параметры ModeCallback и выполняете любую операцию, которую хотите выполнять в классе ModeCallback.
Я не думаю, что этот был бы работать на pre-3.0 Android, хотя.
Вот краткий пример. У меня есть расширяемая активность в списке и вызывается меню режима действия, когда пользователь проверяет/снимает флажок, и вот мой метод getChildView в моем классе адаптера:
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.childrow, null);
}
CheckBox cb = (CheckBox)convertView.findViewById(R.id.row_check);
cb.setChecked(false); // Initialize
cb.setTag(groupPosition + "," + childPosition);
cb.setFocusable(false); // To make the whole row selectable
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String tag = (String)buttonView.getTag();
String[] pos = tag.split(",");
if (isChecked) {
if (mActionMode == null) mActionMode = startActionMode(mMultipleCallback);
}
else {
if (mActionMode != null) { // Only operate when mActionMode is available
mActionMode.finish();
mActionMode = null;
}
}
}
});
TextView tvTop = (TextView)convertView.findViewById(R.id.row_text_top);
TextView tvBottom = (TextView)convertView.findViewById(R.id.row_text_bottom);
tvTop.setText(mChildren.get(groupPosition).get(childPosition));
tvBottom.setText(mChildrenSize.get(groupPosition).get(childPosition));
return convertView;
}
Ответ 2
Это будет работать в SDK 7 (Android 2.1) и выше. (вместе с SherlockActionBar)
Totaly имитирует работу с почтовым ящиком gmail.
Я переопределяю onTouchEvent, поэтому нажмите на левом углу, активирует режим выбора;
лучше, чем пытаться щелкнуть по крошечной коробке.
Я переопределяю performItemClick, поэтому нажатие не влево будет действовать как регулярное нажатие.
Я переопределяю setItemChecked, чтобы он обновлял mActionMode по мере необходимости.
public class SelectListView extends ListView {
private SherlockFragmentActivity mActivity;
ActionMode mActionMode;
public SelectListView(Context context) {
this( context, null, 0);
}
public SelectListView(Context context, AttributeSet attrs) {
this( context, attrs, 0);
}
public SelectListView(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mActivity = (SherlockFragmentActivity) context;
}
@Override
public boolean performItemClick(View view, int position, long id) {
OnItemClickListener mOnItemClickListener = getOnItemClickListener();
if (mOnItemClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null)
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
mOnItemClickListener.onItemClick(this, view, position, id);
return true;
}
return false;
}
boolean mSelectionMode = false;
int mStartPosition;
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int x = (int) ev.getX();
final int y = (int) ev.getY();
if (action == MotionEvent.ACTION_DOWN && x < getWidth() / 7) {
mSelectionMode = true;
mStartPosition = pointToPosition(x, y);
}
if (!mSelectionMode)
return super.onTouchEvent(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if (pointToPosition(x, y) != mStartPosition)
mSelectionMode = false;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
default:
mSelectionMode = false;
int mItemPosition = pointToPosition(x, y);
if (mStartPosition != ListView.INVALID_POSITION)
setItemChecked(mItemPosition, !isItemChecked(mItemPosition));
}
return true;
}
@Override
public void setItemChecked(int position, boolean value) {
super.setItemChecked(position, value);
// boolean r = getAdapter().hasStableIds();
int checkedCount = getCheckItemIds().length;
if (checkedCount == 0) {
if (mActionMode != null)
mActionMode.finish();
return;
}
if (mActionMode == null)
mActionMode = mActivity.startActionMode(new ModeCallback());
mActionMode.setTitle(checkedCount + " selected");
}
class ModeCallback implements ActionMode.Callback {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.add(getResources().getString(R.string.aBar_remove)).setIcon(R.drawable.ic_action_trash)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Toast.makeText(mActivity, "Delted items", Toast.LENGTH_SHORT).show();
mode.finish();
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
clearChecked();
}
}
public void clearChecked() {
SparseBooleanArray CItem = getCheckedItemPositions();
for (int i = 0; i < CItem.size(); i++)
if (CItem.valueAt(i))
super.setItemChecked(CItem.keyAt(i), false);
}
}
вы можете использовать любой адаптер списка, который вам нужен.
если у вас есть флажок в вашем list_item_layout, вам необходимо расширить ваш адаптер следующим образом:
ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(this, R.layout.simple_list_item_multiple_choice,
R.id.text1, Cheeses.sCheeseStrings) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
CheckBox checkBox = (CheckBox) v.findViewById(R.id.CheckBox);
checkBox.setChecked(((ListView)parent).isItemChecked(position));
return v;
}
};
Не забудьте использовать android: background = "? attr/activatedBackgroundIndicator" в вашем list_item_layout.xml
Ответ 3
В дополнение tocnbuff410 ответ используйте следующий код для обновления отмеченных элементов count
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
++checkBoxCount;
} else {
--checkBoxCount;
}
...
if (mActionMode != null) {
mActionMode.setSubtitle(checkBoxCount + " item(s) selected.");
}
Я могу подтвердить, что следующий код не будет работать с флажками пользовательских списков просмотров. Однако долгое нажатие будет работать:
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new ModeCallback());
Выбор флажка в пользовательском ListView не приведет к срабатыванию actionmode. Только встроенные ListViews, например. расширение ListActivity автоматически сделает это.