本文为原创博客,出自http://blog.csdn.net/minimicall
到今天为止,搜芽的卖家版本应该来说已经基本完成,攻坚克难的一路过来。速度也控制的比较好。
项目过程进度
从任务分配量上来看,基本还是我个人英雄主义。接下来这样不行。但暂时也没办法,师弟还需要一个学习的过程。智质不错,而且态度端正。相信搜芽买家,他就可以承担更多的开发任务了。
接下来进入正题,说我们的PullToRefresh的点击事件。其实,我是想做长按进入删除的。
见效果图。当然这个是我做出来之后的了,但做出来不容易。
效果图
先上效果图
这个时候,用户长按某个item,进入删除选中模式。
这个时候ActionBar上的菜单发生了变化,变成了一个全选和删除。
我们还可以点击ActionBar全选来选择所有Item
当然,你可以取消全选这个时候,你也可以单机某个Item取消该项。
选中了,然后按删除菜单,就是那个垃圾桶图标即可。就会删除,删除同步至服务器。
问题出先了。发现点击无效。。!
问题排查
网上很多方法。
第一个比较有用的是:http://www.tuicool.com/articles/ria6Zf
记录下自己所犯的错误,在写ListView的点击事件时OnItemClickListener,onItemClick方法没有执行,导致ListView条目点击事件失效,检查发现百度上有很多不同的答案,但究其本质都是ListView的Item抢占焦点或者Item没有获取焦点甚至没有绑定上OnItemClickListener监听事件,而我所犯的错误是在ListView的Item布局中引入了一个Style,在Style中有一项<item name="android:clickable">true</item>,正是这一项导致所有Item都要抢占焦点,所以ListView的点击事件失效,在我去掉这一项之后ListView确实正常工作了。需要引以为戒的是,在androidl应用开发中,焦点没有获取或者其他组件抢占焦点的事情经常发生,我们可以在代码中,xml布局中,甚至Style中定义时候抢占焦点,在一般情况下,这个设置并不会造成什么异常,但我需要注意重要的组件在合适的时机必须拿到焦点,否则会产生意想不到的后果,比如我的ListView。一般组件获取焦点可以使用一下方法:
- View.setFocusable( true ),对应xml : android:focusable= "true" .
- View.setFocusableInTouchMode( true ),对应xml : android:focusableInTouchMode= "true" .
注意:这两个属性要同时使用。
两者的意思是让组件可以获得焦点。不过有些区别,前者执行false条件后,在执行true,还是不能获取焦点。后者执行上述过程,还是能获取焦点。当你加入上述代码后,在创建activity时,调用对应view的requestFocus(),(requestFocus()需要在setContentView之后执行 )这样就可以获得焦点了。当editText失去焦点了,也就不会有软键盘了
但针对ListView还可以使用 android:descendantFocusability 属性, 下面我们来看一下 android:descendantFocusability用法简析
以下摘自: http://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html
开发中很常见的一个问题,项目中的listview不仅仅是简单的文字,常常需要自己定义listview,自己的Adapter去继承BaseAdapter,在adapter中按照需求进行编写,问题就出现了,可能会发生点击每一个item的时候没有反应,无法获取的焦点。原因多半是由于在你自己定义的Item中存在诸如ImageButton,Button,CheckBox等子控件(也可以说是Button或者Checkable的子类控件),此时这些子控件会将焦点获取到,所以常常当点击item时变化的是子控件,item本身的点击没有响应。
这时候就可以使用descendantFocusability来解决啦,API描述如下:
android:descendantFocusability
Defines the relationship between the ViewGroup and its descendants when looking for a View to take focus.
Must be one of the following constant values.
该属性是当一个为view获取焦点时,定义viewGroup和其子控件两者之间的关系。
属性的值有三种:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
通常我们用到的是第三种,即在Item布局的根布局加上android:descendantFocusability=”blocksDescendants”的属性就好了,至此listview点击的灵异事件告一段落。心得:遇到不会不懂的地方除了网上查询资料之外,也可以多多去尝试每种属性的作用,多阅读官方文档(我始终觉得还是读原文的比翻译的理解的会更好)
第二个比较有意的是:http://blog.csdn.net/kankankankan2222/article/details/7693190
如果ListView中的单个Item的view中存在checkbox,button等view,会导致ListView.setOnItemClickListener无效,
事件会被子View捕获到,ListView无法捕获处理该事件.
解决方法:
在checkbox、button对应的view处加android:focusable="false"
android:clickable="false"android:focusableInTouchMode="false"
其中focusable是关键
从OnClickListener调用getSelectedItemPosition(),Click 和selection 是不相关的,Selection是通过D-pad or trackball 来操作的,Click通常是点击操作的。
我犯得错是自己在getView里面不小心设置了对Click的监听导致的。已经被我注释掉了
// private class ClickListner implements OnClickListener { // private int position; // // private ClickListner(int position) { // this.position = position; // } // // @Override // public void onClick(View v) { // Log.d(TAG,"xxxxxxxxxxx"); // MCloth cloth = mCloths.get(position); // if (!isEnabled(position)) { // return; // } // Context context = v.getContext(); // //DetailActivity.launch(context, movie, Referer.NEW_ARRIVIAL); // } // }接下来我讲一下,如何实现PullToRefresh来实现长按进入删除选中模式。
长按进入删除模式
首先,在需要初始化View各种监听的地方,我们开一个函数。如下,相信大家都熟悉,
private void setupViews(View rootView) { mListView = (PullToRefreshListView) rootView .findViewById(R.id.cloths_lv); mListView.setMode(PullToRefreshBase.Mode.BOTH); ((ViewGroup) mListView.getParent()).addView(mErrorView); mListView.setEmptyView(mErrorView); mClothManageAdapter = new ClothManageAdapter(getActivity(), getImageFetcher()); mListView.setAdapter(mClothManageAdapter); ((MainActivity) getActivity()).getIndicator().setOnTabSelectedListener( mTabSelectdListener); mProgressBar = (ProgressBar) rootView.findViewById(R.id.pb_progress); setListViewRefresh(); setLongClickListener(); }
我们把注意力放到setLongClickListener上,这个是我封的一个private方法,如下:
private void setLongClickListener() { Log.d(TAG, "setLongClick"); mListView.getRefreshableView().setOnItemClickListener( mOnItemClickListener);//单击事件的监听 mListView.getRefreshableView().setChoiceMode( ListView.CHOICE_MODE_MULTIPLE_MODAL);//选择模式选为可多选 mListView.getRefreshableView().setMultiChoiceModeListener( mMutilChoiceListener);//多选监听 }好,我们再进一步看单击时间的监听
private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mActionMode == null) {//还没有按长按的时候,走这里。 Log.d(TAG, "onItemClick,mActionMode is null"); } else {//已经有了一次长按之后,进入了选择模式,走这里 Log.d(TAG, "onItemClick,mActionMode is not null, select item " + position + " to select"); mListView.getRefreshableView().setItemChecked(position, true); } } };好,我们看我们很关键的多选监听。直接上代码
private final MultiChoiceModeListener mMutilChoiceListener = new MultiChoiceModeListener() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { Log.d(TAG, "onCreateActionMode, mode:" + mode + ",menu:" + menu); mActionMode = mode; getActivity().getMenuInflater().inflate( R.menu.menu_favorite_delete, menu);//加载菜单到ActionBar中 return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public void onDestroyActionMode(ActionMode mode) { mActionMode = null; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) {//菜单点击事件 case R.id.menu_delete: // 删除 Log.d(TAG,"delete menu"); List<MCloth> deleted = new ArrayList<MCloth>(); SparseBooleanArray checked = mListView.getRefreshableView() .getCheckedItemPositions(); for (int i = 0; i < checked.size(); i++) { Log.d(TAG, "get from Adapter("+checked.keyAt(i)+","+checked.valueAt(i)+")"); if (checked.valueAt(i)) { deleted.add(mClothManageAdapter.getItem(checked .keyAt(i))); } } // for (MCloth cloth : deleted) { // if(cloth != null){ // //Log.d(TAG, "deleting the cloth id="+cloth.id+" in remote server now"); // WEBInterface1.DelCloth(cloth.id); // } // } new DeleteClothTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, deleted); mClothManageAdapter.remove(deleted); mode.finish(); loadData(true); break; case R.id.menu_selectall: // 全选 if (mIsSelectAll) { item.setTitle("取消全选"); mIsSelectAll = false; } else { item.setTitle("全选"); mIsSelectAll = true; } for (int i = 0; i < mListView.getRefreshableView().getCount(); i++) { mListView.getRefreshableView().setItemChecked(i, !mIsSelectAll); } break; } return true; } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { setActionModeTitle(mode, mListView.getRefreshableView() .getCheckedItemCount()); } }; private void setActionModeTitle(ActionMode mode, int count) { Log.d(TAG, "setActionModeTitile,mode:" + mode + ",count:" + count); mActionMode.setTitle("选中" + count + "个布料"); }这样
核心的代码就是上面了,实现了长按删除的,效果不错吧。不懂的可以联系我。希望我们的搜芽产品能够一天天的长成大树。 晚安。有想加入我们团队的,也可以联系我。我会给你力所能及的帮助,也期待大家的愉快合作。