android中ListView使用最为频繁,但是同时出现问题也是最多的,下面就总结一下我在开发中遇到的问题,
问题一:在自定义添加adapter中添加button,或是checkBox时无法同时响应setOnItemClickListener事件中的item click 事件和控件本身点击事件
item 原布局文件;
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" > <Button android:id="@+id/array_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="一个按钮" /> <ImageView android:id="@+id/array_image" android:layout_toRightOf="@+id/array_button" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentTop="true" android:layout_alignParentBottom="true" android:adjustViewBounds="true" android:padding="2dip" /> <TextView android:id="@+id/array_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/array_image" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:singleLine="true" android:ellipsize="marquee" android:textSize="15dip" /> <TextView android:id="@+id/array_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/array_image" android:layout_below="@+id/array_title" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:singleLine="true" android:ellipsize="marquee" android:textSize="20dip" /> </RelativeLayout>
重写的adapter:
public View getView(final int position, View convertView, ViewGroup parent) { ImageView iamge = null; TextView title = null; TextView text = null; Button button = null; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate( mTextViewResourceID, null); iamge = (ImageView) convertView.findViewById(R.id.array_image); title = (TextView) convertView.findViewById(R.id.array_title); text = (TextView) convertView.findViewById(R.id.array_text); button = (Button)convertView.findViewById(R.id.array_button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Toast.makeText(arrayList,"您点击的第"+position +"个按钮", Toast.LENGTH_LONG).show(); } }); } }
解决方法:只要在item布局文件中添加 android:descendantFocusability="blocksDescendants" 属性,
在添加的控件添加属性
android:clickable="true"
android:focusable="false"
更改后的item布局文件内容:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:descendantFocusability="blocksDescendants"> <Button android:id="@+id/array_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="一个按钮" android:clickable="true" android:focusable="false" /> <ImageView android:id="@+id/array_image" android:layout_toRightOf="@+id/array_button" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentTop="true" android:layout_alignParentBottom="true" android:adjustViewBounds="true" android:padding="2dip" /> <TextView android:id="@+id/array_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/array_image" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:singleLine="true" android:ellipsize="marquee" android:textSize="15dip" /> <TextView android:id="@+id/array_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/array_image" android:layout_below="@+id/array_title" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:singleLine="true" android:ellipsize="marquee" android:textSize="20dip" /> </RelativeLayout>
例如单击item选项后改变这条item背景色
解决方法:使用adapterView.getChildAt(index)方法
mListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { View v=adapterView.getChildAt(position); v.setBackgroundColor(Color.RED); } });
当数据量很大时,即开始滑动是再点击会发生adapterView.getChildAt(index)获取view为空
这时就需要另一个方法adapterView.getFirstVisiblePosition();获取第一个可见item位置,使用当前position-这个位置
具体实现View v=adapterView.getChildAt(pos-adapterView.getFirstVisiblePosition());
判断adapter内数据第一次出现加载:
根据getView中的position判断是否大于mLastAnimatedPosition,如果大于mLastAnimatedPosition则为第一次
private int mLastAnimatedPosition= -1; boolean firstLoadData(int pos){ boolean isFirst=false; if(pos>mLastAnimatedPosition){ isFirst=true; this.mLastAnimatedPosition=pos; } return isFirst; }
ListView mListView; int backIndex,backTop; //保存item view index,偏移量 void saveListViewPositionView(){ backIndex=mListView.getFirstVisiblePosition(); View v=mListView.getChildAt(0); backTop=(v==null) ? 0:v.getTop(); } //根据上次保存index,偏移量恢复视图 void restoreListViewPositionView(){ mListView.setSelectionFromTop(backIndex, backTop); }
其实还可以使用setSelection也可以定位,只是setSelectionFromTop要比setSelection更精准。因为通过getFirstVisiblePosition得到的第一个item可能已经有一部分是不可见的了,如果用setSelection无法反映出这不可见的部分。
还可以使用带有滚动过程带有动画效果方式:
mListView.smoothScrollBy("distance",duration); mListView.smoothScrollToPosition(position);
Listview 添加HeaderView时,设置指定大小值:
ImageView imageHeaderView = new ImageView(this); imageHeaderView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.deckblatt)); imageHeaderView.setScaleType(ScaleType.CENTER_CROP); imageHeaderView.setLayoutParams(new AbsListView.LayoutParams(100, 100)); myList.addHeaderView(imageHeaderView);
HeaderView设置隐藏时Gone还会有占位,需要remove或者增加了一个多余的Layout(item_container)来囊括item内部控件。此时,如果在item_container .setVisibility(View.GONE)则可以完美实现隐藏HeaderView的目的!
ListView设置Selector为空
mListView.setSelector(null);//空指针
mListView.setSelector(newColorDrawable(Color.TRANSPARENT));
ListView及item焦点问题:
mListView.requestFocusFromTouch();
這時你需要在你的xml文件中对些控件添加android:focusable=false;
ListView的右边滚动滑块启用:
使用XML布局只需要在 ListView节点中加入 android:fastScrollEnabled="true" 这个属性即可,
而对于Java代码可以通过myListView.setFastScrollEnabled(true); 来控制启用,参数false为隐藏。
还有一点就是当你的滚动内容较小,不到当前ListView的3个屏幕高度时则不会出现这个
listview 中几个实用的属性:
去掉ListView Selector选种时黄色底纹一闪的效果
<?xmlversion="1.0" encoding="utf-8"?>
<shapexmlns:android="http://schemas.android.com/apk/res/android">
<solidandroid:color="@android:color/transparent"/>
<cornersandroid:radius="0dip" />
</shape>
//listview.setSelector(R.drawable.thisShape);
Listview右边滚动条始终显示
xml文件中做如下修改 android:fadeScrollbars="false"
stackFromBottom属性,这只该属性之后你做好的列表就会显示你列表的最下面,值为true和false
android:stackFromBottom="true"
transciptMode属性,需要用ListView或者其它显示大量Items的控件实时跟踪或者查看信息,并且希望最新的条目可以自动滚动到可视范围内。通过设置的控件transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。
android:transcriptMode="alwaysScroll"
cacheColorHint属性,很多人希望能够改变一下它的背景,使他能够符合整体的UI设计,改变背景背很简单只需要准备一张图片然后指定属性 android:background="@drawable/bg",不过不要高兴地太早,当你这么做以后,发现背景是变了,但是当你拖动,或者点击list空白位置的时候发现ListItem都变成黑色的了,破坏了整体效果。
如果你只是换背景的颜色的话,可以直接指定android:cacheColorHint为你所要的颜色,如果你是用图片做背景的话,那也只要将android:cacheColorHint指定为透明(#00000000)就可以了
divider属性,该属性作用是每一项之间需要设置一个图片做为间隔,或是去掉item之间的分割线
android:divider="@drawable/list_driver" 其中 @drawable/list_driver 是一个图片资源,如果不想显示分割线则只要设置为android:divider="@drawable/@null" 就可以了
fadingEdge属性,上边和下边有黑色的阴影
android:fadingEdge="none" 设置后没有阴影了~
scrollbars属性,作用是隐藏listView的滚动条,
android:scrollbars="none"与setVerticalScrollBarEnabled(true);的效果是一样的,不活动的时候隐藏,活动的时候也隐藏
fadeScrollbars属性,android:fadeScrollbars="true" 配置ListView布局的时候,设置这个属性为true就可以实现滚动条的自动隐藏和显示。
LIstView局部刷新View处理:(How can I update a single row in a ListView?)
private void updateItemView(int index,boolean selected){ View vitem = mListView.getChildAt(index - mListView.getFirstVisiblePosition()); CheckBox checkStore=(CheckBox)vitem.findViewById(R.id.shop_title_checkbox); checkStore.setChecked(selected); vitem.invalidate(); }
控制listview滚动速度
// scroll speed decreases as friction increases. a value of 2 worked
getListView().setFriction(ViewConfiguration.getScrollFriction() * 2);
// 因子越大滚动越慢 ,只有API>11时可以使用(Verified that this works really nicely for API >= 11:)
headerView or footview问题
在添加和删除HeaderView之前,先执行mListView.setAdapter()不然报错,这个问题在低于4.0以前版本会有这个问题,4.0之后在哪里add都可以
headerView or footview 在设置为gone时还会有展位情况,解决方法:反复执行remove,add,这样在一定程度上影响效率,可以在嵌套一层可以解决
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <!-- @color/shop_cart_order_pref_bg_color --> <RelativeLayout android:id="@+id/rl" android:layout_width="match_parent" android:layout_height="40dip" android:gravity="center_vertical" android:paddingLeft="10dp" android:paddingRight="10dp" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:drawablePadding="5dp" android:drawableRight="@drawable/jiantou" android:text="@string/nologin_tipe" android:textColor="#999999" android:textSize="12sp" /> </RelativeLayout> </LinearLayout>操作里面的id=rl布局gone即可占位消除(此问题也可用于解决listview 中的item部分内容隐藏占位问题)
速度计算处理方式 (1,GestureDetector.SimpleOnGestureListener onFling处理,也可以使用速度计算VelocityTracker实现)
VelocityTrackingTouchListener mTouchListener; GestureDetector mGestureDetector; void initTouchLis(View v){ // Instantiate a gesture listener to consume scroll and fling events FlingDetector flingDetector = new FlingDetector(); // Pass the FlingDetector to mGestureDetector to receive the appropriate callbacks mGestureDetector = new GestureDetector(this, flingDetector); mTouchListener = new VelocityTrackingTouchListener(); v.setOnTouchListener(mTouchListener); } class VelocityTrackingTouchListener implements View.OnTouchListener { private VelocityTracker mVelocityTracker; private int mPointerId; private float xVelocity,yVelocity; @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: if (mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(motionEvent); //求第一个触点的id, 此时可能有多个触点,但至少一个 mPointerId = motionEvent.getPointerId(0); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(motionEvent); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second xVelocity = mVelocityTracker.getXVelocity(mPointerId); yVelocity = mVelocityTracker.getYVelocity(mPointerId); break; case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } mGestureDetector.onTouchEvent(motionEvent); return true; } } class FlingDetector extends GestureDetector.SimpleOnGestureListener { public FlingDetector() { super(); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) { // updateText("in onFling"); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // updateText(String.format("onScroll velocity = (%f, %f)", mTouchListener.xVelocity, mTouchListener.yVelocity)); return false; } }
持续更新中。。。