ListView的Choice模式

ListView的Choice模式

/** 
 * Normal list that does not indicate choices 
 */  
public static final int CHOICE_MODE_NONE = 0;  
/** 
 * The list allows up to one choice 
 */  
public static final int CHOICE_MODE_SINGLE = 1;  
/** 
 * The list allows multiple choices 
 */  
public static final int CHOICE_MODE_MULTIPLE = 2;  
/** 
 * The list allows multiple choices in a modal selection mode 
 */  
public static final int CHOICE_MODE_MULTIPLE_MODAL = 3; 

如何设置ListView的选择模式?

  1. 在ListView的xml文件中添加如下代码

    <ListView
           android:choiceMode="multipleChoice"
           android:id="@+id/list"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           style="?android:attr/listViewWhiteStyle"
           />
  2. 在java代码中设置

    targetLsitView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
  3. 默认情况下设置为NONE

ListView的单选模式

这个模式不常用,当我们进行某一个item的选择的时候,会消除掉其他的item的选择状态,应用场景不太多!

ListView的MULTIPLE模式

这种多选模式,可用于进行多项选择的时候使用,但是当我们开始的时候就进行多选可能不太符合我们的想法,所以我比较建议使用下一种模式!

ListView的MULTIPLE_MODEL模式

上边的模式选中一个item的时候,listView的onItemClick也同时触发,而一个ListView点击item的后续操作一般是切换到另外一个界面,所以实际应用中,我们还需要设置一个标志位,用来区别当前是多选状态还是普通状态 ,如果是多选状态,调用ListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 如果是普通状态调用mListView.setChoiceMode(ListView.CHOICE_MODE_NONE); CHOICE_MODE_MULTIPLE模式的特点在于他本身没有排斥性,在能选择item的情况下,也可以响应普通点击事件。为了解决这个问题 ,在android3.0之后增加了CHOICE_MODE_MULTIPLE_MODAL模式。

CHOICE_MODE_MULTIPLE_MODAL和CHOICE_MODE_MULTIPLE恰恰相反,他是对普通点击操作和多选操作是排斥的,一旦有一个item被选中,即进入到多选状态,item的onclick事件被屏蔽。这种排斥性也是他比CHOICE_MODE_MULTIPLE多了个MODAL的原因。此外CHOICE_MODE_MULTIPLE_MODAL还结合了android3.0的actionmode,当进入多选状态,actionbar的位置会显示新的菜单。

我们来看看CHOICE_MODE_MULTIPLE_MODAL模式的实现原理:

如何实现两种状态的互斥:当点击一个item的时候absListView中会调用performItemClick,如果是CHOICE_MODE_MULTIPLE,则该item点击一次,mCheckStates中相应位置的状态变更一次,但是CHOICE_MODE_MULTIPLE_MODAL模式不同,必须要mChoiceActionMode!= null

的情况下,才会去变更mCheckStates中相应位置的状态;不光如此,如果mChoiceActionMode!= null

,他还会阻挡ItemClick事件的继续传播,从而屏蔽了ListView OnItemClickListener的onItemClick方法。

如何启用actionmode:一般我们使用actionmode都是在activity中调用startActionMode,但是如果你要使用ListView的CHOICE_MODE_MULTIPLE_MODAL,请不要这么做, 在absListView中有一个变量mChoiceActionMode,定义如下:

ActionMode mChoiceActionMode;  

当长按item 或者是调用主动调用setItemChecked方法mChoiceActionMode将被实例化,而如果你是在activity中调用startActionMode,那么虽然actionbar上的菜单变化了,ListView 中的mChoiceActionMode却没有实例化,刚刚我们谈到mChoiceActionMode==null 表示未进入到多选状态,所以这时你点击一个item其实还是普通的点击行为。

因此在CHOICE_MODE_MULTIPLE_MODAL模式下要启用多选操作,只有两种办法:

(1) 长按当长按item ;

(2) 主动调用ListView的setItemChecked(int position, boolean value)方法选中一个item。

但是这两种进入多选状态的方法都有一个弊端,那就是进入多选状态之后,总是有一个item是被选中的, 方法(1)中长按item,被按的item被选中,这种结果是合理的可以接受的,但是如果你想主动进入多选状态(比如我在点击actionbar的某个菜单的时候想进入多选状态),就必须采用方法(2):调用setItemChecked,这就出现个问题,你该让哪个item被选中呢?貌似最合理的该是一个都不选中吧,我只是进入到这个状态,还没有开始选呢。幸运的是,我们可以使用一些技巧,实现能主动进入多选状态,且没有一个item被选中。

PS:

CHOICE_MODE_MULTIPLE_MODAL模式中,一旦有一个item被选中,即进入到多选状态,而他还有个相反的特性,一旦所有的Item被主动的设置为未选中,则退出多选状态,mChoiceActionMode会调用自己的finish()方法。为什么呢?在MultiChoiceModeWrapper类中:

@Override  
public void onItemCheckedStateChanged(ActionMode mode,  
        int position, long id, boolean checked) {  
    mWrapped.onItemCheckedStateChanged(mode, position, id, checked);  
    // If there are no items selected we no longer need the selection mode.  
    if (getCheckedItemCount() == 0) {  
        mode.finish();  
    }  
}  

使用选择模式,item布局需要为Checkable

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="50dp">
    <ImageView
        android:id="@+id/checkbox"
        android:layout_centerVertical="true"
        android:layout_marginLeft="@dimen/checkedButtonMarginHorizontal"
        android:layout_marginRight="@dimen/checkedButtonMarginHorizontal"
        android:layout_gravity="center_vertical"
        android:src="@drawable/checked_blue"
        android:layout_width="@dimen/checkedButtonSize"
        android:layout_height="@dimen/checkedButtonSize"/>

    <TextView
        android:id="@+id/time"
        android:layout_marginLeft="75dp"
        android:gravity="center_vertical|left"
        android:textSize="@dimen/text_title_larger"
        android:text="24:34"
        android:layout_width="100dp"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/chooseDoseLayout"
        android:orientation="horizontal"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:layout_marginRight="@dimen/checkedButtonMarginHorizontal"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="match_parent">
        <ImageView
            android:focusable="false"
            android:id="@+id/incButton"
            android:src="@drawable/add_blue"
            android:layout_marginRight="3dp"
            android:layout_gravity="center_vertical"
            android:layout_width="@dimen/checkedButtonSize"
            android:layout_height="@dimen/checkedButtonSize"/>
        <EditText
            android:id="@+id/dose"
            android:inputType="numberDecimal"
            android:gravity="center_vertical|center"
            android:textSize="@dimen/text_title_larger"
            android:text="15"
            android:layout_width="50dp"
            android:layout_height="match_parent"/>
        <ImageView
            android:focusable="false"
            android:id="@+id/decButton"
            android:src="@drawable/less_blue"
            android:layout_marginLeft="3dp"
            android:layout_gravity="center_vertical"
            android:layout_width="@dimen/checkedButtonSize"
            android:layout_height="@dimen/checkedButtonSize"/>
    </LinearLayout>
</RelativeLayout>

View.java

package noclay.demos.Demos.Demo1;

import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Checkable;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.no_clay.toolsapp.R;


/**
 * Created by noclay on 2017/5/15.
 */

public class CheckableChooseDoseView extends RelativeLayout implements Checkable, View.OnClickListener {

    Context mContext;
    ImageView mCheckbox;
    TextView mTime;
    ImageView mIncButton;
    EditText mDose;
    ImageView mDecButton;
    LinearLayout mChooseDoseLayout;
    private boolean isChecked = false;
    OnChildClickListener onChildListener;
    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
    private static final String TAG = "CheckableChooseDoseView";
    public interface OnChildClickListener{
        void onClickIncButton();
        void onClickDecButton();
    }

    public CheckableChooseDoseView(Context context) {
        super(context);
        mContext = context;
        initView();
    }

    public CheckableChooseDoseView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        initView();
    }

    public CheckableChooseDoseView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initView();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CheckableChooseDoseView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mContext = context;
        initView();
    }

    private void initView() {
        LayoutInflater.from(mContext).inflate(R.layout.demo1_checkable_choose_time_and_dose,
                this, true);
        mCheckbox = (ImageView) findViewById(R.id.checkbox);
        mTime = (TextView) findViewById(R.id.time);
        mIncButton = (ImageView) findViewById(R.id.incButton);
        mDose = (EditText) findViewById(R.id.dose);
        mDecButton = (ImageView) findViewById(R.id.decButton);
        mChooseDoseLayout = (LinearLayout) findViewById(R.id.chooseDoseLayout);
        clearCheck(isChecked);
    }


    public void clearCheck(boolean checked) {
        mCheckbox.setVisibility(GONE);
        mChooseDoseLayout.setVisibility(GONE);
        if (checked) {
            mChooseDoseLayout.setVisibility(VISIBLE);
            mCheckbox.setVisibility(VISIBLE);
        }
    }

    @Override
    public void setChecked(boolean checked) {
        if (checked != isChecked){
            isChecked = checked;
            refreshDrawableState();
        }
    }

    @Override
    public boolean isChecked() {
        return isChecked;
    }

    @Override
    public void toggle() {
        setChecked(!isChecked);
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        // 在原有状态中添加一个空间space用于保存checked状态
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            // 将checked状态合并到原有的状态数组中
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        clearCheck(isChecked);
        return drawableState;
    }



    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "onInterceptTouchEvent: ");
        return true;
    }

    @Override
    public void refreshDrawableState() {
        super.refreshDrawableState();
        clearCheck(isChecked);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.incButton:{
                onChildListener.onClickIncButton();
                break;
            }
            case R.id.decButton:{
                onChildListener.onClickDecButton();
                break;
            }
        }
    }

    public void setTime(String time) {
        mTime.setText(time);
    }

    public void setFloat(Float value){
        mDose.setText(value.toString());
    }

    public void setOnChildListener(OnChildClickListener onChildListener) {
        this.onChildListener = onChildListener;
    }
}

item.xml

<?xml version="1.0" encoding="utf-8"?>
<noclay.demos.Demos.Demo1.CheckableChooseDoseView
    android:descendantFocusability="blocksDescendants"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_item"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="vertical">
</noclay.demos.Demos.Demo1.CheckableChooseDoseView>

PS

需要在getView中这样写才能够实现,但不知道为什么?(应该是ListView在设置itemchecked的时候是这样使用的)

    public View getView(int position, View convertView, ViewGroup parent) {
        CheckableChooseDoseView view;
        final ViewHolder holder;
        if (convertView == null) {
            view = (CheckableChooseDoseView) LayoutInflater.from(mContext).inflate(
                    R.layout.demo1_item_checkable_choose_time, parent, false)
                    .findViewById(R.id.list_item);
        }else{
            view = (CheckableChooseDoseView) convertView;
        }
        view.setTime(mStrings[position]);
        view.setOnChildListener(new CheckableChooseDoseView.OnChildClickListener() {
            @Override
            public void onClickIncButton() {
                Log.d("toggle", "onClickIncButton: ");
            }

            @Override
            public void onClickDecButton() {
                Log.d("toggle", "onClickDecButton: ");
            }
        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值