一、ListView圆角:重写ListView的onInterceptTouchEvent方法,通过pointToPosition(x,y)方法判断当前点击位置所对应的项,有三种情况:分别是第一项、最后一项、中间项;其实第一种情况又分为两种情况:列表总共只有一项和列表不止一项;参照下边的代码即可理解;
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: int x = (int) ev.getX(); int y = (int) ev.getY(); int itemnum = pointToPosition(x, y); if (itemnum == AdapterView.INVALID_POSITION) break; else{ if(itemnum==0){ // 选择项为1 if(itemnum==(getAdapter().getCount()-1)){// 列表只有一项 setSelector(R.drawable.app_list_corner_round); }else{ // 列表不止一项 setSelector(R.drawable.app_list_corner_round_top); } }else if(itemnum==(getAdapter().getCount()-1)) // 选择项为最后一项 setSelector(R.drawable.app_list_corner_round_bottom); else{ setSelector(R.drawable.app_list_corner_shape); } } break; case MotionEvent.ACTION_UP: break; }
二、选中项高亮显示,实现ListView选中项高亮显示有两种方法;
1、查看ListView的方法setSelection(int index),看样子好像是,但实际试了之后以现不行,既然不行,那就用一个比较笨的方法:自定义adapter,在adapter的getView方法里面设置背景;然后在ListView的onItemClick回调中向adapter传入点击的项,即可设置选中项高亮状态;
public void setSelectedposition(int selectedposition) { this.selectedposition = selectedposition; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = inflater.inflate(R.layout.item, null); if (selectedposition == position) { v.setBackgroundColor(Color.parseColor("#36adcf")); } else { v.setBackgroundColor(Color.TRANSPARENT); } return v; }
2、有没有更简单的方法?第一种方法看着觉得有点怪怪的,有点歪门邪道的感觉;另一种的方法是通过selector方法实现;定义一个drawable文件,里面包含一个选择状态:state_activated,然后将这个drawable指定为ListView的item的背景;关于这个状态具体含意不再多说,参见官方api;
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/adcf" android:state_activated="true"></item> <item android:drawable="@android:color/transparent"/> </selector>
下面是在代码中的实现(部分代码):
listview.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
@Override protected void onListItemClick(ListView l, View v, int position, long id) { getListView().setItemChecked(position, true); }
注:上面的方法是ListActivity的方法,此方法对应OnItemClickListener接口中的方法;
三、ListView列表项延迟加载
我们都知道当ListView上面滚动时,会执行adapter的getView方法,但问题出来了,有时候为了节约资源及解决效率问题,并不需要滚动到某一项就开始加载数据,而是希望滚动停止,或者说滚动速度低于某一个值时才获取数据,下面讨论的就是这种情况下的处理:
1、为ListView添加OnScrollListener滚动监听器,此监听器可以获取当前滚动状态及可视条目;
public int first = 0; // 第一条可视项目 public int visi = 0; // 可视条目总数 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { first = firstVisibleItem; visi = visibleItemCount; } public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: // 滚动完成 mBusy = false; int first = view.getFirstVisiblePosition(); for (int i = 0; i < visi; i++) { // 设置所有可视条目Tag为null,条目将会自动加载数据 TextView t = (TextView) view.getChildAt(i); if (t.getTag() != null) { t.setText(mStrings[first + i]); t.setTag(null); } }break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: // 正在滚动(手指未离开屏幕) mBusy = true; break; case OnScrollListener.SCROLL_STATE_FLING: // 正在滚动(手指已离开屏幕) mBusy = true; break; } }
在Adapter中根据mBusy状态,判断是否立即加载数据,重写getView方法;
public View getView(int position, View convertView, ViewGroup parent) { TextView text; if (convertView == null) { text = (TextView) mInflater.inflate(android.R.layout.simple_list_item_1, parent, false); } else { text = (TextView) convertView; } if (!mBusy) { text.setText(mStrings[position]); // Tag为空代表当前View的数据已经加载完成 text.setTag(null); } else { text.setText("Loading..."); // 非空的View代表当前View仍然需要加载更多的数据 text.setTag("cc"); } return text; } }
四、类似聊天记录,自动将新添加的项从底部添加并显示到可见区域,主要是两个属性的设置,如下:
<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:stackFromBottom="true" 自动显示到列表最底部 android:transcriptMode="normal" /> 设置当显示大量的条目时,希望将最新的item显示到可见区域
顺便记录一下输入框点击Enter事件,OnKeyListener(参照下面的代码,可实现输入新消息按下Enter并将新消息添加到列表底部);
public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: // TODO return true; } } return false; }
五、设置部分条目不可用:通常情况下,一个列表中的所有条目都可以使用,也即可以响应点击事件和有点击的效果,但有的情况下,并不希望所有的条目都这样,就可以自定义Adapter,通过条目的相关属性判断是否可用,(disEnabled状态下,默认的分割条不显示)
@Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { // TODO 通过一定的判断条件设置position条目可用 }
六、ListView点击条目展开条目显示更多信息:这种情况也是比较常见的,最多见的应该是评论,一个评论列表,不同的人,评论的内容长度不同,如果全部显示完,就会显示得参差不齐,但又不能只显示了部分,此时就得添加点击条目查看更多的功能了;
简单说下思路:自定义一个View,继承自FrameLayout,里面有两个TextView,一个是单行的(singleLine),另外一个是多行的(wrap_content);公开一个方法设置多行的TextView的可见状态为VISIBLE或者GONE,在ListView的ItemClick回调中设置当列表条目未展开时,点击条目展开条目,也即显示更多信息;当列表是展开的,点击收起条目;并且通知数据源改变:notifyDataSetChanged;
七、在二中说了选中单项的高亮显示,现在来看看多选的高亮显示,同样,多选我这里也知道两种方法;
1、同二中第2种方法,定义一个selector,里面包含一个选择状态state_activited,将此drawable设置为ListView的selector或者是直接设置为条目的背景;此种模式类似于4.0之后部分手机短信列表的长按之后进行选择条目然后进行操作删除的效果;
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); // 设置选择模式 lv.setMultiChoiceModeListener(new ModeCallback());
private class ModeCallback implements ListView.MultiChoiceModeListener { public boolean onCreateActionMode(ActionMode mode, Menu menu) { // 在ActionBar上创建按钮 MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.list_select_menu, menu); mode.setTitle("Select Items"); return true; } public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return true; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { // TODO 点击某一个按钮的回调 } return true; } public void onDestroyActionMode(ActionMode mode) { } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { // 选择项改变回调事件,通过此方法获取选择了哪些项 final int checkedCount = getListView().getCheckedItemCount(); } } }
2、第二种方法比较简单些,适用范围更加广泛,上一种方法在2.x的版本上运行会出错,因为MultiChoiceMode是高版本上的新的一个接口;对于低版本的手机,下面这种方法更加适用(Item的背景或者selector同上);
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
如果需要获取此种方法的选择项,就需要在OnItemClickListener的回调方法中进行判断,即每点击一项,改变当前checked的值,最后再获取结果;
未完待续。。。