带有复选框的类似 Gmail 的 ListView(并使用 ActionBar)
Posted
技术标签:
【中文标题】带有复选框的类似 Gmail 的 ListView(并使用 ActionBar)【英文标题】:Gmail-like ListView with checkboxes (and using the ActionBar) 【发布时间】:2012-02-09 02:35:51 【问题描述】:我正在尝试重现 Google 对 Gmail 应用中的 ListView 所做的操作。特别是,我希望每个列表项都包含一个 CheckBox 和两个 TextView(一个在另一个之上)。当 CheckBox 被选中(或单击)以及单击列表项上的其他任何位置时,我需要侦听器。最后,我希望 ActionBar 反映已选择的项目并提供全选、全选等选项(请参阅this screenshot)。
到目前为止,这是我想出的布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="horizontal" >
<CheckBox android:id="@+id/checkBox"
android:layout_
android:layout_
android:layout_gravity="center_vertical|center_horizontal" />
<LinearLayout android:id="@+id/linearLayout1"
android:layout_
android:layout_
android:orientation="vertical"
android:padding="6dp"
android:focusable="true"
android:clickable="true" >
<TextView android:id="@+id/titleTextView"
android:layout_
android:layout_
android:textSize="16sp" />
<TextView android:id="@+id/dateTextView"
android:layout_
android:layout_
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
这会正确显示所有内容,但我需要有关如何为两个视图(@+id/checkBox 和 @+id/linearLayout1)设置侦听器的指针.我查看了List16 API demo,但他们使用的是 simple_list_item_activated_1 布局,我不确定 XML 的样子。正如他们的代码所示,我创建了一个实现 ListView.MultiChoiceModeListener 的 ModeCallback 类,并将 ListView 的选择模式设置为 CHOICE_MODE_MULTIPLE_MODAL,但我不知道如何获取 CheckBox在我的布局中使用它。
是否有人成功复制了 Gmail 应用的 ListView 行为?我进行了相当多的搜索,但无法提出任何建议(尽管其他几个人提出了类似的问题,like this one - 大多数答案都指向同一个 API 演示)。
另外,就上下文而言,我正在将 SQLite 数据库中的数据加载到列表中,并且我创建了自己的光标适配器(工作正常)。我感觉我需要在 newView() 和 bindView() 方法中在这个类中设置监听器,但是我尝试过的所有方法都没有奏效。
有什么想法吗?
【问题讨论】:
我需要更好地解释我的问题,还是我问的太多了? :// 【参考方案1】:这适用于 SDK 7 (android 2.1) 及更高版本。 (与 SherlockActionBar 一起)
完全模仿 gmail 列表框体验。
我覆盖了 onTouchEvent,所以按下左角将激活选择模式; 比尝试点击小复选框更好。
我覆盖了 performItemClick,所以在左侧按 not 将作为常规按动作。
我重写了 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;
;
不要忘记在您的 list_item_layout.xml 上使用 android:background="?attr/activatedBackgroundIndicator"
【讨论】:
我在滚动列表视图时使用复选框实现上下文操作栏,然后不维护操作栏上设置的复选框计数。有什么帮助吗?【参考方案2】:图中的模式称为动作模式。如果您使用自定义行视图和自定义适配器,则不必使用 CHOICE_MODE_MULTIPLE_MODAL 来启动操作模式。我试了一次,失败了,怀疑是用于内置适配器的。
为了在您的代码中自己调用动作模式,请在您的任何视图的任何侦听器方法中调用方法 startActionMode,让它是复选框、文本视图或类似的东西。然后您将 ModeCallback 作为参数传递给它,并在 ModeCallback 类中执行您想要执行的任何操作。
我不认为这个可以在 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;
【讨论】:
谢谢!今晚我会试试这个,看看我能不能让它工作。在此之前将其标记为答案。【参考方案3】:除了cnbuff410的回答,使用以下代码更新检查项目计数
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 上的复选框不会触发动作模式。仅内置 ListView,例如扩展 ListActivity 会自动做到这一点。
【讨论】:
以上是关于带有复选框的类似 Gmail 的 ListView(并使用 ActionBar)的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Google Keep 中添加复选框 ListView?