ListFragment+CheckedTextView和ListFragment+CheckBox两种多选效果

来源:互联网 发布:熊出没知夏日连连看 编辑:程序博客网 时间:2024/05/20 17:08

本来不想写这篇博客,原因是不想被几个月以后的自己鄙视,其实很容易的,可是又有几个细节要注意,还遇上了几个不那么难的bug,但是不好描述,也解释不清楚,也不好咨询别人的问题。所以我决心还是记录一下。(注意一下所有出现Qrom,qrom词的都可以把qrom去掉,因为qrom是我引入的第三方控件库,对原生的控件进行了重新封装,效果更好而已,大家也不要问我要,这个是公司产品,我也不能给你们,但这并不影响你们理解博客)


先看效果(请忽略我是在ViewPager中添加ListFragment事情):


 1.               2.  


CheckedTextView基本版如图1,实现步骤如下:

item的布局:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:orientation="horizontal" >    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/ic_launcher" />    <CheckedTextView        android:id="@+id/checktv_title"        android:layout_width="match_parent"        android:layout_height="?android:attr/listPreferredItemHeight"        android:checkMark="?android:attr/listChoiceIndicatorMultiple"        android:gravity="center_vertical"        android:paddingLeft="6dip"        android:paddingRight="6dip"        android:textAppearance="?android:attr/textAppearanceLarge" /></LinearLayout>

代码:

package com.marttinli.qromstudy1_1;import android.annotation.SuppressLint;import android.os.Bundle;import android.util.SparseArray;import android.view.View;import android.view.ViewGroup;import android.widget.ArrayAdapter;import android.widget.CheckedTextView;import com.tencent.qrom.support.v4.app.ListFragment;import com.tencent.qrom.widget.AdapterView;import com.tencent.qrom.widget.AdapterView.OnItemClickListener;import com.tencent.qrom.widget.ListView;public class ListFragmentCheckMode extends ListFragment {String[] datas;ListView listView;@SuppressLint("UseSparseArrays")private SparseArray<Boolean> checkedMap = new SparseArray<Boolean>();@Overridepublic void onActivityCreated(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onActivityCreated(savedInstanceState);datas = getResources().getStringArray(R.array.date);setListAdapter(new MyAdapter());listView = getListView();listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);listView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubCheckedTextView cheView = (CheckedTextView) view.findViewById(R.id.checktv_title);cheView.setChecked(!cheView.isChecked());checkedMap.put(position, checView.isChecked());}});}private class MyAdapter extends ArrayAdapter<String> {public MyAdapter() {// TODO Auto-generated constructor stubsuper(getActivity(), 0, datas);}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubViewHolder holder = null;if (convertView == null) {convertView = getActivity().getLayoutInflater().inflate(R.layout.item1_listview, null);holder = new ViewHolder();holder.tView = (CheckedTextView) convertView.findViewById(R.id.checktv_title);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.tView.setText(datas[position]);holder.tView.setChecked(checkedMap.get(position) == null ? false: checkedMap.get(position));return convertView;}class ViewHolder {CheckedTextView tView;}}}


这里必须注意:

1,getListView()放在onActivityCreated()中获取,反正不能在onCreated()获取,因为在onCreated的时候listview还未初始化完成,其他地方暂时没有尝试过。

2,item.xml中CheckedTextView设置

 android:checkMark="?android:attr/listChoiceIndicatorMultiple"
不过字面翻译应该很好理解,如果没有的话,复选框就不会出现

3,在onItemClick()中获取CheckedTextView使用view.findViewById(R.id.checktv_title);,网上有很多种其他方法,但是终究没有让我找到一种正确的。他们的问题主要是在于,获取到的CheckedTextView只是对应屏幕上的item,一旦listview有位置滑动,那么则将不能准确对焦。

还是举个例子吧:

CheckedTextView cheView = (CheckedTextView) parent.getChildAt(position).findViewById(R.id.checktv_title);
这种获取方法就是错误的,理由如上,不信的可以自己尝试下。

4,

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

这里也可以设置成单选框,至于是不是只要修改了这里就可以了呢,我也不知道,但是我感觉第2步的CheckedTextView的checkMark也要设置成单选,你可以试一下。

5,在获取选中状态时,建议使用CheckedTextView.isChecked()

因为之前我还使用过另外一个方法listview.isItemChecked(position)也能获取到选中状态,但是她不太稳定,有时候选中了也会返回false,原因暂不明确,总之不建议使用。


本代码中其他装逼地方:

1,SparseArray的使用

使用操作基本同HashMap,功能同HashMap,那为什么不使用大家熟悉的HashMap呢?

原因一,SparseArray采用二分法查找,在效率上优于HashMap(注明:我也不知道HashMap用什么方法查找,也没有比较过他们的效率,google官方推荐使用SparseArray并说她效率高,不管你信不信,我是信了)。

有人想了解细节的,我推荐一篇文章:Android应用性能优化之使用SparseArray替代HashMap

原因二,SparseArray相对HashMap更加不熟悉,程序员应该要有坚持学习新东西的心态,另外,相对而言不熟悉的东西才能提高逼格。

2,有两句精简的代码

cheView.setChecked(!cheView.isChecked());
holder.tView.setChecked(checkedMap.get(position) == null ? false: checkedMap.get(position));
这两段之所以这么写:

原因一:代码更加简洁

原因二:逼格



CheckedTextView Plus版如图2

这里集成了在ActionMode下的全选,取消全选,删除操作。

代码如下:

package com.marttinli.qromstudy1_1;import java.util.ArrayList;import android.annotation.SuppressLint;import android.os.Bundle;import android.util.SparseArray;import android.view.ActionMode;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.CheckedTextView;import com.tencent.qrom.app.ActionBar;import com.tencent.qrom.support.v4.app.ListFragment;import com.tencent.qrom.widget.AdapterView;import com.tencent.qrom.widget.AdapterView.OnItemClickListener;import com.tencent.qrom.widget.AdapterView.OnItemLongClickListener;import com.tencent.qrom.widget.ListView;import com.tencent.qrom.widget.ToggleButton;public class ListFragmentCheckMode extends ListFragment {ArrayList<String> datas;ListView listView;ActionMode mActionMode;MyAdapter myAdapter;@SuppressLint("UseSparseArrays")private SparseArray<Boolean> checkedMap;@Overridepublic void onActivityCreated(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onActivityCreated(savedInstanceState);datas = listFromStrings(getResources().getStringArray(R.array.date));checkedMap = mapsFromDatas(datas);setListAdapter(myAdapter = new MyAdapter());listView = getListView();listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);listView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubCheckedTextView cheView = (CheckedTextView) view.findViewById(R.id.checktv_title);cheView.setChecked(!cheView.isChecked());// 这里证明了cheView.isChecked()比listView.isItemChecked(position)更稳定,原因尚不明确System.out.println("listView.isItemChecked(position):"+ listView.isItemChecked(position)+ " cheView.isChecked():" + cheView.isChecked());checkedMap.put(position, cheView.isChecked());}});listView.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stub// Start the CAB using the ActionMode.Callback defined abovemActionMode = getActivity().startActionMode(mCallback);initActionBarButton();return true;}});}private void initActionBarButton() {final ActionBar qromActionBar = getActivity().getQromActionBar();final ToggleButton selectAll = (ToggleButton) qromActionBar.getMultiChoiceView(true);if (selectAll != null) {selectAll.setText(getString(R.string.selectedAll));selectAll.setTextOff(getString(R.string.selectedAll));selectAll.setTextOn(getString(R.string.unSelectedAll));selectAll.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (selectAll.isChecked()) {selectedAll();} else {unSelectedAll();}}});}final Button cancelButton = (Button) qromActionBar.getCloseView(true);if (cancelButton != null) {cancelButton.setText(R.string.cancel);cancelButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {if (mActionMode != null) {mActionMode.finish();}}});}}private SparseArray<Boolean> mapsFromDatas(ArrayList<String> strings) {SparseArray<Boolean> maps = new SparseArray<>();for (int i = 0; i < strings.size(); i++) {maps.put(i, false);}return maps;}private void selectedAll() {for (int i = 0; i < datas.size(); i++) {checkedMap.put(i, true);}myAdapter.notifyDataSetChanged();}private void unSelectedAll() {for (int i = 0; i < datas.size(); i++) {checkedMap.put(i, false);}myAdapter.notifyDataSetChanged();}/** * 这里for循环从大往小,是很有必要的。保证remove后面的数据不会对面前数据的位置有影响 *  * @param map */private void removeItems(SparseArray<Boolean> map) {for (int i = datas.size() - 1; i >= 0; i--) {System.out.println("i:" + i);if (map.get(i)) {datas.remove(i);map.remove(i);System.out.println("remove i:" + i);}}myAdapter.notifyDataSetChanged();listView.refreshDrawableState();}private ArrayList<String> listFromStrings(String[] strings) {ArrayList<String> arrayList = new ArrayList<String>();for (int i = 0; i < strings.length; i++) {arrayList.add(strings[i]);}return arrayList;}private class MyAdapter extends ArrayAdapter<String> {public MyAdapter() {// TODO Auto-generated constructor stubsuper(getActivity(), 0, datas);}/** * getView表示只显示在屏幕的items会初始化。 */@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubViewHolder holder = null;if (convertView == null) {convertView = getActivity().getLayoutInflater().inflate(R.layout.item1_listview, null);holder = new ViewHolder();holder.tView = (CheckedTextView) convertView.findViewById(R.id.checktv_title);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.tView.setText(datas.get(position));holder.tView.setChecked(checkedMap.get(position) == null ? false: checkedMap.get(position));return convertView;}class ViewHolder {CheckedTextView tView;}}private ActionMode.Callback mCallback = new ActionMode.Callback() {@Overridepublic boolean onPrepareActionMode(ActionMode mode, Menu menu) {return false;}@Overridepublic void onDestroyActionMode(ActionMode mode) {// TODO Auto-generated method stub}@Overridepublic boolean onCreateActionMode(ActionMode mode, Menu menu) {MenuInflater inflater = mode.getMenuInflater();inflater.inflate(R.menu.main2, menu);return true;}@Overridepublic boolean onActionItemClicked(ActionMode mode, MenuItem item) {boolean ret = false;if (item.getItemId() == R.id.delete) {removeItems(checkedMap);checkedMap = mapsFromDatas(datas);mode.finish();ret = true;}return ret;}};}

注意:

1,我在removeItems()方法里对datas循环删除是从大往小遍历,个人觉得使用得恰到好处。如此可以保证remove之后,在removed对象位置之前的对象的位置都不会有变化。

2,在removeItems()之后要对checkMap的数据重新初始化,保证对应的position和listview同步刷新。

3,ActionMode虽然会上下弹出一个Bar,但是上面那个Bar其实还是ActionBar。我猜有可能原来的ActionBar会暂时缓存起来,并且在Activity中释放掉了,等到ActionMode模式结束,然后重新绘制原来那个ActionBar。


CheckedTextView Plus++版也能图2一样,不过是由正常模式在ActionMode下切换到CheckBox模式

先看效果图吧,有图更比说很多话就管用

  在ActionMode下切换到    


先分析下思路(这里换成了CheckBox,跟CheckedTextView使用基本一样,请忽略细节)。

1,item.xml中同时设置Button和CheckBox为alignRight,在ActionMode出现和释放阶段通过刷新adapter设置切换Button和CheckBox一个visibility,另一个gone。这么一分析就很简单的。

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="match_parent"    android:descendantFocusability="blocksDescendants"    android:orientation="horizontal" >    <ImageView        android:id="@+id/imageView1"        android:layout_width="48dp"        android:layout_height="48dp"        android:contentDescription="@string/app_name"        android:src="@drawable/ic_launcher" />    <TextView        android:layout_marginLeft="5dp"        android:id="@+id/textView1"        android:layout_width="200dp"        android:layout_height="48dp"        android:gravity="center" />    <Button        android:id="@+id/button1"        android:layout_width="96dp"        android:layout_height="42dp"        android:layout_alignParentRight="true"        android:visibility="gone" />    <com.tencent.qrom.widget.CheckBox        android:id="@+id/select_check"        android:layout_width="wrap_content"        android:layout_height="42dp"        android:layout_alignParentRight="true"        android:layout_centerVertical="true"        android:layout_marginLeft="42dp"        android:clickable="false"        android:focusable="false"        android:visibility="visible" >    </com.tencent.qrom.widget.CheckBox></RelativeLayout>

代码:

package com.marttinli.qromstudy1_1;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.content.Intent;import android.content.pm.ApplicationInfo;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.os.Bundle;import android.util.SparseArray;import android.view.ActionMode;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.ImageView;import android.widget.RelativeLayout;import android.widget.TextView;import com.tencent.qrom.app.ActionBar;import com.tencent.qrom.support.v4.app.Fragment;import com.tencent.qrom.widget.AdapterView;import com.tencent.qrom.widget.AdapterView.OnItemClickListener;import com.tencent.qrom.widget.AdapterView.OnItemLongClickListener;import com.tencent.qrom.widget.CheckBox;import com.tencent.qrom.widget.ListView;import com.tencent.qrom.widget.ToggleButton;enum CheckStatus {NoCheckStatus, CheckStaus}public class MyListFragment extends Fragment {List<PackageInfo> list = new ArrayList<>();;onItemSelectedListener mListener;PackageManager packageManager;private SparseArray<Boolean> checkedMap;ActionMode mActionMode;ListView listView;MyAdapter myAdapter;public static final int FILTER_ALL_APP = 0; // 所有应用程序public static final int FILTER_SYSTEM_APP = 1; // 系统程序public static final int FILTER_THIRD_APP = 2; // 第三方应用程序public static final int FILTER_SDCARD_APP = 3; // 安装在SDCard的应用程序CheckStatus checkStaus = CheckStatus.NoCheckStatus;@Overridepublic void onAttach(Activity activity) {super.onAttach(activity);try {mListener = (onItemSelectedListener) activity;} catch (ClassCastException e) {throw new ClassCastException(activity.toString()+ " must implement onItemSelectedListener");}}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View rootView = inflater.inflate(R.layout.fragment_section_dummy,container, false);packageManager = getActivity().getPackageManager();List<PackageInfo> mlist = packageManager.getInstalledPackages(0);list.addAll(getApplications(mlist, FILTER_THIRD_APP));checkedMap = mapsFromDatas(list);listView = (ListView) rootView.findViewById(R.id.listview);listView.setAdapter(myAdapter = new MyAdapter());listView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubCheckBox cheView = (CheckBox) view.findViewById(R.id.select_check);cheView.setChecked(!cheView.isChecked());// 这里证明了cheView.isChecked()比listView.isItemChecked(position)更稳定,原因尚不明确System.out.println("listView.isItemChecked(position):"+ listView.isItemChecked(position)+ " cheView.isChecked():" + cheView.isChecked());checkedMap.put(position, cheView.isChecked());}});listView.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stub// Start the CAB using the ActionMode.Callback defined abovemActionMode = getActivity().startActionMode(mCallback);initActionBarButton();return true;}});return rootView;}private ActionMode.Callback mCallback = new ActionMode.Callback() {@Overridepublic boolean onPrepareActionMode(ActionMode mode, Menu menu) {return false;}@Overridepublic void onDestroyActionMode(ActionMode mode) {// TODO Auto-generated method stubcheckStaus = CheckStatus.NoCheckStatus;myAdapter.notifyDataSetChanged();}@Overridepublic boolean onCreateActionMode(ActionMode mode, Menu menu) {MenuInflater inflater = mode.getMenuInflater();inflater.inflate(R.menu.main2, menu);checkStaus = CheckStatus.CheckStaus;myAdapter.notifyDataSetChanged();return true;}@Overridepublic boolean onActionItemClicked(ActionMode mode, MenuItem item) {boolean ret = false;if (item.getItemId() == R.id.delete) {removeItems(checkedMap);checkedMap = mapsFromDatas(list);ret = true;}return ret;}};private void initActionBarButton() {final ActionBar qromActionBar = getActivity().getQromActionBar();final ToggleButton selectAll = (ToggleButton) qromActionBar.getMultiChoiceView(true);if (selectAll != null) {selectAll.setText(getString(R.string.selectedAll));selectAll.setTextOff(getString(R.string.selectedAll));selectAll.setTextOn(getString(R.string.unSelectedAll));selectAll.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (selectAll.isChecked()) {selectedAll();} else {unSelectedAll();}}});}final Button cancelButton = (Button) qromActionBar.getCloseView(true);if (cancelButton != null) {cancelButton.setText(R.string.cancel);cancelButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {if (mActionMode != null) {mActionMode.finish();}}});}}private void selectedAll() {for (int i = 0; i < list.size(); i++) {checkedMap.put(i, true);}myAdapter.notifyDataSetChanged();}private void unSelectedAll() {for (int i = 0; i < list.size(); i++) {checkedMap.put(i, false);}myAdapter.notifyDataSetChanged();}/** * 这里for循环从大往小,是很有必要的。保证remove后面的数据不会对面前数据的位置有影响 *  * @param map */private void removeItems(SparseArray<Boolean> map) {for (int i = list.size() - 1; i >= 0; i--) {System.out.println("i:" + i);if (map.get(i)) {list.remove(i);map.remove(i);System.out.println("remove i:" + i);}}}private SparseArray<Boolean> mapsFromDatas(List<PackageInfo> list2) {SparseArray<Boolean> maps = new SparseArray<>();for (int i = 0; i < list2.size(); i++) {maps.put(i, false);}return maps;}private List<PackageInfo> getApplications(List<PackageInfo> mlist, int flag) {List<PackageInfo> l = new ArrayList<PackageInfo>();switch (flag) {case FILTER_ALL_APP:l.addAll(mlist);break;case FILTER_SYSTEM_APP:for (PackageInfo packageInfo : mlist) {if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {l.add(packageInfo);}}break;case FILTER_THIRD_APP:for (PackageInfo packageInfo : mlist) {if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {l.add(packageInfo);}// 本来是系统程序,被用户手动更新后,该系统程序也成为第三方应用程序了else if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {l.add(packageInfo);}}break;case FILTER_SDCARD_APP:for (PackageInfo packageInfo : mlist) {if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {l.add(packageInfo);}}break;default:break;}return l;}public interface onItemSelectedListener {public void onItemSelected(int position);}public void onListItemClick(ListView l, View v, int position, long id) {// TODO Auto-generated method stubmListener.onItemSelected(position);}class MyAdapter extends BaseAdapter {@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn list.get(arg0);}@Overridepublic long getItemId(int arg0) {// TODO Auto-generated method stubreturn arg0;}@Overridepublic View getView(int arg0, View convertView, ViewGroup arg2) {Holder holder;if (null == convertView) {holder = new Holder();convertView = (RelativeLayout) LayoutInflater.from(getActivity()).inflate(R.layout.item2_listview, null);holder.iconView = (ImageView) convertView.findViewById(R.id.imageView1);holder.textView = (TextView) convertView.findViewById(R.id.textView1);holder.button = (Button) convertView.findViewById(R.id.button1);holder.box = (CheckBox) convertView.findViewById(R.id.select_check);convertView.setTag(holder);} else {holder = (Holder) convertView.getTag();}final PackageInfo app = list.get(arg0);holder.iconView.setImageDrawable(packageManager.getApplicationIcon(app.applicationInfo));holder.textView.setText(packageManager.getApplicationLabel(app.applicationInfo).toString());holder.button.setText("打开");holder.button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View view) {Intent intent = packageManager.getLaunchIntentForPackage(app.packageName);getActivity().startActivity(intent);}});holder.box.setChecked(checkedMap.get(arg0) == null ? false: checkedMap.get(arg0));if (checkStaus == CheckStatus.NoCheckStatus) {holder.button.setVisibility(View.VISIBLE);holder.box.setVisibility(View.GONE);} else {holder.button.setVisibility(View.GONE);holder.box.setVisibility(View.VISIBLE);}return convertView;}class Holder {public ImageView iconView;public TextView textView;public Button button;public CheckBox box;}}}

这里一切都很简单,并没有什么要注意的地方。只是我把之前做的一个功能:关于获取本机app的知识点整合进来了。如果对这个知识点有兴趣点击:PackageManager获取指定类别应用程序






0 0
原创粉丝点击