Gmail-like ListView with checkboxes (and using the ActionBar)

后端 未结 3 1994
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-04 13:07

I\'m trying to recreate what Google did with the ListView in the Gmail app. In particular, I would like to have each list item include a CheckBox and two TextViews (one on t

相关标签:
3条回答
  • 2020-12-04 13:21

    In addition tocnbuff410's answer use the following code to update checked items count

    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if (isChecked) {
        ++checkBoxCount;
     } else {
        --checkBoxCount;
     }
    ...
    
    if (mActionMode != null) {
        mActionMode.setSubtitle(checkBoxCount + " item(s) selected.");
    }
    

    I can confirm that following code would not work on checkboxes of custom listviews. However, long press would still work:

    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
    listView.setMultiChoiceModeListener(new ModeCallback());
    

    Selecting checkbox on custom ListView would not trigger actionmode. Only inbuilt ListViews e.g. extending ListActivity would automatically do that.

    0 讨论(0)
  • 2020-12-04 13:27

    The mode in the figure is called action mode. If you are using your custom row view and custom adapter, you don't have to use CHOICE_MODE_MULTIPLE_MODAL to start action mode. I tried it once and failed, and I suspect it is used for built-in adapter.

    In order to call the action mode by yourself in your code, call method startActionMode in any listener method of any of your view, let it be checkbox, textview or something like that. You then pass the ModeCallback as parameters into it, and do whatever operation you want to do in ModeCallback class.

    I don't think this one would work on pre-3.0 Android though.

    Here is a brief example. I have a expandable list activity and would like to call out the action mode menu when user check/uncheck the checkbox, and here is my getChildView method in my custom adapter class:

    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;
        }
    
    0 讨论(0)
  • 2020-12-04 13:32

    This will work in SDK 7 (android 2.1) and higher. (together with SherlockActionBar)

    Totaly mimic gmail listbox experience.

    I override onTouchEvent so press in the left corner side will activate selection mode; mutch better then trying to click on the tiny cheackbox.

    I override performItemClick so pressing not in the left will act as a Regular press action.

    I override setItemChecked so it will update mActionMode as needed.

    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);
        }
    
    }
    

    you can use any listbox adapter you need.

    if you have a checkbox on your list_item_layout tou need to extent your adapter like this:

    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;
            }
    
        };
    

    Don't forget to use android:background="?attr/activatedBackgroundIndicator" on your list_item_layout.xml

    0 讨论(0)
提交回复
热议问题