自定义listView单选功能 android:focusable和android:focusableInTouchMode的区别

上篇我讲了ListView多选,这边我将讲到ListView单选:

listview里面的item每次只能选择一个。

.XML

<?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="wrap_content"
    android:background="@color/white"
    android:gravity="center_vertical"
    android:descendantFocusability="blocksDescendants"
    android:orientation="horizontal"
    android:padding="11dp">


    <CheckBox
        android:id="@+id/custser_check"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginRight="11dp"
        android:background="@drawable/check_orange"
        android:button="@null" />

    <TextView
        android:id="@+id/custser_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="11dp"
        android:layout_weight="1"
        android:drawableLeft="@mipmap/user"
        android:drawablePadding="11dp"
        android:gravity="center_vertical"
        android:text="18296127347"
        android:textColor="@color/black"
        android:textSize="18sp" />

</RelativeLayout>

.java

public class AuthorizeActivity extends BaseActivity implements View.OnClickListener {
    private ListView lv;
    private List<String> listStr;
    private Myadapter adapter;
    private String tag = "AuthorizeActivity";
    private int selectPosition = -1;//用于记录用户选择的变量
    private String choice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        baseapp.TemaddActivity(this);
        setContentView(R.layout.activity_authorize);
        initView();
    }

    private void initView() {
        lv = (ListView) findViewById(R.id.authorize_lv);
        findViewById(R.id.authorize_tv).setOnClickListener(this);
        findViewById(R.id.authorize).setOnClickListener(this);
        findViewById(R.id.authorize_next).setOnClickListener(this);
        listStr = new ArrayList<>();
        lv.setOnItemClickListener(itemlistener);
    }

    /*item监听*/// 屏蔽子控件focus
    AdapterView.OnItemClickListener itemlistener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            //获取选中的参数
            selectPosition = position;
            adapter.notifyDataSetChanged();
            choice = listStr.get(position);
            ToastUtil.showLong("你选择的用户是: " + choice);
        }
    };


    @Override
    protected void onResume() {
        super.onResume();
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            selectPosition = 0;
            String[] arrays = extras.getStringArray(CustSerAddActivity.list);
            listStr = Arrays.asList(arrays);
            choice = listStr.get(selectPosition);
            Logs.v(tag + " 49  添加的数据 " + Arrays.toString(arrays));
            initLv(lv, listStr);
        }
    }


    /*初始化listView*/
    private void initLv(ListView lv, List<String> list) {
        adapter = (Myadapter) lv.getAdapter();
        if (adapter == null) {
            adapter = new Myadapter(this, list);
            lv.setAdapter(adapter);
        } else {
            adapter.setData(list);
            adapter.notifyDataSetChanged();
        }
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.authorize:
                baseapp.TemfinishActivity(this);
                break;
            case R.id.authorize_tv:
                Bundle bundle = new Bundle();
                bundle.putString(CustSerAddActivity.titlestr, "添加售后人员");
                bundle.putString(CustSerAddActivity.activity, "AuthorizeActivity");
                bundle.putStringArray(CustSerAddActivity.list, listStr.toArray(new String[listStr.size()]));
                Logs.v(tag + " 110 " + listStr.size());
                SmallUtil.getActivity(AuthorizeActivity.this, CustSerAddActivity.class, bundle);
                baseapp.TemfinishActivity(this);
                break;
            case R.id.authorize_next:
                Intent a = new Intent(AuthorizeActivity.this, AuthorizeChoiceActivity.class);
                a.putExtra(AuthorizeChoiceActivity.UserStr, choice);
                startActivity(a);
                break;
        }
    }


    /*自定义授权的适配器*/
    private class Myadapter extends BaseAdapter {
        private Context context;
        private List<String> brandsList;

        public Myadapter(Context context, List<String> brandsList) {
            this.context = context;
            this.brandsList = brandsList;
        }

        @Override
        public int getCount() {
            return listStr.size();
        }

        @Override
        public Object getItem(int position) {
            return listStr.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = LayoutInflater.from(context).inflate(
                        R.layout.item_authorizecheck, null);
            }
            TextView name = ViewHolderUtil.get(convertView, R.id.custser_name);
            name.setText(brandsList.get(position));
            CheckBox checkBox = ViewHolderUtil.get(convertView, R.id.custser_check);
            if (position == selectPosition) {
                checkBox.setChecked(true);
            } else {
                checkBox.setChecked(false);
            }
            return convertView;
        }

        public void setData(List<String> scanResults) {
            brandsList = scanResults;
        }
    }


}

重点:父控件抢占焦点问题

有时候,当ListView中的每一个item是自定义的View时,有可能会导致ListView的OnItemClickListener的listener无法调用,请看如下情况:

可能原因一:

如果你的自定义ListViewItem中有Button或者Checkable的子类控件的话,那么默认focus是交给了子控件,而 ListView的Item能被选中的基础是它能获取Focus,也就是说我们可以通过将ListView中Item中包含的所有控件的 focusable属性设置为false,这样的话ListView的Item自动获得了Focus的权限,也就可以被选中了。

我们可以通过对Item Layout的根控件设置其android:descendantFocusability="blocksDescendants"即可,这样Item Layout就屏蔽了所有子控件获取Focus的权限,不需要针对Item Layout中的每一个控件重新设置focusable属性了,如此就可以顺利的响应onItemClickListener中的onItemClick()方法了。

原因总结:

ListView中的Item内部的View获得了焦点,如Button, Checkbox等。 

解决办法: 

不要让ListView中的Item内部的View获得焦点就OK了,这样做:android:descendantFocusability="blocksDescendants"

Constant Value Description
beforeDescendants 0 The ViewGroup will get focus before any of its descendants.
afterDescendants 1 The ViewGroup will get focus only if none of its descendants want it.
blocksDescendants 2 The ViewGroup will block its descendants from receiving focus.

该属性是当一个为view获取焦点时,定义viewGroup和其子控件两者之间的关系。

属性的值有三种:

        beforeDescendants:viewgroup会优先其子类控件而获取到焦点

        afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点

        blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点



效果图:





发现了一个bug

checkbox光写

android:focusable="false"

无法屏蔽checkBox勾选的属性,还是会抢掉它父控件item的焦点,导致onItemClickListener起不了作用。

应该如下写:



android:clickable:好像是控制按钮是否可以被点击和点击之后触发监听器事件。
android:focusable:控制键盘是否可以获得这个按钮的焦点。(我按实体键盘上方向键,button被选中)

android:focusable和android:focusableInTouchMode的区别


前者针对在键盘下操作的情况,如果设置为true,则键盘上下左右选中,焦点会随之移动。

而后者,显然是针对触屏情况下的,也就是我们点击屏幕的上的某个控件时,不要立即执行相应的点击逻辑,而是先显示焦点(即控件被选中),再点击才执行逻辑。

android:focusable=“true”不会改变android:focusableInTouchMode,因此只在键盘状态下显示焦点,在TouchMode状态下,依旧无法显示焦点。

android:focusable=“false”,一定会使android:focusableInTouchMode=“false”。

相对的

android:focusableInTouchMode=“false”,不会影响android:focusable。

android:focusableInTouchMode=”true”,一定会是android:focusable=“true”

 

说来说去有点绕。

但请记住一点,就是对于现在触屏时代的手机而言,如果要获取焦点,我们只需要设置

android:foucusableInTouchMode=“true”就可以了。

 

所有的获取焦点,都要有一个前提,那就是该控件必须设置android:clickable=”true”,如果都点击不了,设置焦点应该没什么意义了吧。



猜你喜欢

转载自blog.csdn.net/xxdw1992/article/details/79924037