我正在尝试重新制作Google在Gmail应用中使用ListView所做的事情。特别是,我想让每个列表项包括一个CheckBox和两个TextView(一个在另一个之上)。检查(或单击)CheckBox时以及单击列表项上的任何其他位置时,我需要侦听器。最后,我希望ActionBar能够反映出选中的项目,并提供Select All,Select None等选项(参见this screenshot)。
到目前为止,这是我想出的布局。
<?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是什么样的。正如他们的代码所示,我创建了一个实现 ListView.MultiChoiceModeListener 的 ModeCallback 类,并将ListView的选择模式设置为CHOICE_MODE_MULTIPLE_MODAL,但我不知道如何获取CheckBox在我的布局中使用它。
是否有人成功复制了Gmail应用的ListView行为?我已经搜索了很多并且无法提出任何问题(尽管其他几个人提出了类似的问题,like this one - 大多数答案只是回到同样的API演示中。)
另外,对于上下文,我正在将SQLite数据库中的数据加载到列表中,并且我已经创建了自己的Cursor适配器(工作正常)。我有一种感觉,我需要在newView()和bindView()方法中设置这个类中的监听器,但我尝试过的所有东西都没有用。
有什么想法吗?
答案 0 :(得分:11)
这适用于SDK 7(android 2.1)及更高版本。 (与SherlockActionBar一起)
Totaly模仿gmail列表框体验。
我覆盖了onTouchEvent,所以按左下角将激活选择模式; mutch更好然后试图点击微小的cheackbox。
我覆盖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;
}
};
不要忘记在list_item_layout.xml上使用android:background =“?attr / activatedBackgroundIndicator”
答案 1 :(得分:7)
图中的模式称为动作模式。如果您使用自定义行视图和自定义适配器,则不必使用CHOICE_MODE_MULTIPLE_MODAL来启动操作模式。我试了一次然后失败了,我怀疑它用于内置适配器。
为了在你的代码中自己调用动作模式,在任何视图的任何侦听器方法中调用方法startActionMode,让它成为checkbox,textview或类似的东西。然后将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;
}
答案 2 :(得分:2)
另外tocnbuff410的答案使用以下代码来更新已检查的项目计数
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会自动执行此操作。