http://www.open-open.com/lib/view/open1324995841233.html
顶部指示器?
这是什么?
好吧,我承认这是我自己想出来的词,因为我不知道它有什么学名,究竟是什么呢?看下这个图就知道了。

这是我们的美工MM画的,偶的神呐,这虽然很漂亮,不过也让人头疼,这个箭头应该在滚到顶部的时候消失,滚下来的时候(即有条目隐藏的时候)才显示,类似的底部指示器也要有这样的效果。事实上默认的ListView和ScrollView都已经有了类似的效果,在顶部或底部还有更多内容时,会有部分渐变虚化的效果,不过美工已经设计了这样的效果,那么我们就来做吧。
出于省事的目的,本教程中的例子会基于上一篇教程来修改,主要是添加1个继承自ListView的类,以及修改布局定义文件。
ArrowListView控件的编写
001 | package net.learningandroid.lib.view; |
003 | import net.learningandroid.lib.R; |
004 | import android.content.Context; |
005 | import android.graphics.Bitmap; |
006 | import android.graphics.Canvas; |
007 | import android.graphics.Paint; |
008 | import android.graphics.Rect; |
009 | import android.graphics.drawable.BitmapDrawable; |
010 | import android.util.AttributeSet; |
011 | import android.util.Log; |
012 | import android.view.View; |
013 | import android.widget.ListView; |
018 | * <a class="referer" href="http://my.oschina.net/arthor" target="_blank">@author</a> Mr. Lu |
020 | public class ArrowListView extends ListView { |
022 | private final float scale = getContext().getResources().getDisplayMetrics().density; |
023 | private float topArrowPadding; |
024 | private float bottomArrowPadding; |
026 | private static float DEFAULT_TOP_PADDING_DP = 2 .0f; |
027 | private static float DEFAULT_BOTTOM_PADDING_DP = 2 .0f; |
029 | public ArrowListView(Context context, AttributeSet attrs) { |
030 | super (context, attrs); |
032 | String strTopArrowPadding = attrs.getAttributeValue( null , |
034 | String strBottomArrowPadding = attrs.getAttributeValue( null , |
035 | "bottomArrowPadding" ); |
037 | topArrowPadding = convertDisplayUom(strTopArrowPadding, |
038 | DEFAULT_TOP_PADDING_DP); |
039 | bottomArrowPadding = convertDisplayUom(strBottomArrowPadding, |
040 | DEFAULT_BOTTOM_PADDING_DP); |
042 | Log.v( "ArrowListView" , String.valueOf(topArrowPadding)); |
048 | private float convertDisplayUom(String sour, float defaultValue) { |
050 | if (sour.toLowerCase().endsWith( "px" )) { |
051 | return Float.parseFloat(sour.toLowerCase().replace( "px" , "" )); |
052 | } else if (sour.toLowerCase().endsWith( "dp" )) { |
053 | return Integer.parseInt(sour.toLowerCase().replace( "dp" , |
057 | } catch (Exception e) { |
060 | return (defaultValue * scale + 0 .5f); |
064 | * onDraw方法,根据ListView滚动位置绘出箭头. |
067 | protected void onDraw(Canvas canvas) { |
068 | super .onDraw(canvas); |
069 | Paint paint = new Paint(); |
072 | Bitmap topPic = ((BitmapDrawable) getResources().getDrawable( |
073 | R.drawable.arrow_up)).getBitmap(); |
074 | Bitmap bottomPic = ((BitmapDrawable) getResources().getDrawable( |
075 | R.drawable.arrow_down)).getBitmap(); |
079 | this .getDrawingRect(r); |
082 | float top = r.top + topArrowPadding; |
083 | float bottom = r.bottom - bottomArrowPadding - bottomPic.getHeight(); |
084 | float left = r.left + (r.right - r.left - topPic.getWidth()) / 2 ; |
087 | boolean drawTop = false ; |
088 | boolean drawBottom = false ; |
090 | if ( this .getChildCount() > 0 ) { |
091 | Rect rTop = new Rect(); |
092 | this .getChildAt( 0 ).getLocalVisibleRect(rTop); |
093 | Rect rBottom = new Rect(); |
094 | View lastChild = this .getChildAt( this .getChildCount() - 1 ); |
095 | lastChild.getLocalVisibleRect(rBottom); |
097 | drawTop = ( this .getFirstVisiblePosition() > 0 || this |
098 | .getFirstVisiblePosition() == 0 |
100 | drawBottom = ( this .getLastVisiblePosition() < this .getAdapter() |
101 | .getCount() - 1 || this .getLastVisiblePosition() == this |
102 | .getAdapter().getCount() - 1 |
103 | && rBottom.bottom < lastChild.getHeight()); |
107 | canvas.drawBitmap(topPic, left, top, paint); |
111 | canvas.drawBitmap(bottomPic, left, bottom, paint); |
就要点解释一下上面这段代码:
注意构造方法,我们必须继承public ArrowListView(Context context, AttributeSet attrs),这样才可以让这个类在xml定义文件中使用。
还要注意到,这里用了attrs.getAttributeValue来读取XML定义文件中的属性,其实有更好的方法,容我下次再讲解,这里先偷个懒。
convertDisplayUom方法是用来将dp转换成px的,可以看到由于我们用了getAttributeValue的方式,所以需要手动将String转成Float,很麻烦。
最后就是onDraw啦,计算出画箭头的位置,画出来就行了。
接下来就是布局文件的编写了
ArrowListView在XML文件中的使用
01 | <? xml version = "1.0" encoding = "utf-8" ?> |
03 | xmlns:android = "http://schemas.android.com/apk/res/android" |
04 | android:layout_width = "fill_parent" android:layout_height = "fill_parent" |
06 | android:orientation = "vertical" |
09 | android:text = "Arrow List View Sample" |
10 | android:layout_width = "fill_parent" android:layout_height = "wrap_content" |
13 | < net.learningandroid.lib.view.ArrowListView |
14 | android:id = "@+id/arrowListView" |
15 | android:layout_width = "fill_parent" android:layout_height = "wrap_content" |
16 | android:paddingTop = "15dp" android:paddingBottom = "20dp" |
18 | android:layout_margin = "10dp" |
19 | android:background = "@drawable/float_panel" |
20 | android:layout_weight = "1" |
21 | android:cacheColorHint = "#FFEDEDED" android:divider = "#00EDEDED" |
23 | topArrowPadding = "5dp" bottomArrowPadding = "10dp" |
这里需要注意的是自定义控件和其中的属性的写法,不再是ListView了,而是你自己编写的控件类的类名。其它的内容就是定义padding,background,以及取消了分隔线的显示。
用这个布局文件替代上一篇教程中的布局文件,但Adapter的定义不变,因为ArrowListView是继承自ListView的,所以原先的Adapter的使用是一样的。
最后我们来看下效果:

如何?只需要小心的调整ListView的Padding和ArrowPadding的值就可以控制箭头出现的位置,如果需要控制更多,比如更换图片,或者当顶部无内容时让箭头变暗、有内容时变亮,都可以用同样的方法。
并且,如果修改了Attribute的读取方法之后,还可以通过xml文件来指定箭头的图片。