上篇我讲了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”,如果都点击不了,设置焦点应该没什么意义了吧。