类似Gmail的ListView带复选框(并使用ActionBar)

时间:2012-01-12 19:43:38

标签: android listview checkbox android-actionbar

我正在尝试重新制作Google在Gmail应用中使用ListView所做的事情。特别是,我想让每个列表项包括一个CheckBox和两个TextView(一个在另一个之上)。检查(或单击)CheckBox时以及单击列表项上的任何其他位置时,我需要侦听器。最后,我希望ActionBar能够反映出选中的项目,并提供Select All,Select None等选项(参见this screenshot)。

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是什么样的。正如他们的代码所示,我创建了一个实现 ListView.MultiChoiceModeListener ModeCallback 类,并将ListView的选择模式设置为CHOICE_MODE_MULTIPLE_MODAL,但我不知道如何获取CheckBox在我的布局中使用它。

是否有人成功复制了Gmail应用的ListView行为?我已经搜索了很多并且无法提出任何问题(尽管其他几个人提出了类似的问题,like this one - 大多数答案只是回到同样的API演示中。)

另外,对于上下文,我正在将SQLite数据库中的数据加载到列表中,并且我已经创建了自己的Cursor适配器(工作正常)。我有一种感觉,我需要在newView()和bindView()方法中设置这个类中的监听器,但我尝试过的所有东西都没有用。

有什么想法吗?

3 个答案:

答案 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会自动执行此操作。