在ListView中实现顶部和底部内容指示器

jopen 12年前
     <p>顶部指示器?<br /> 这是什么?<br /> 好吧,我承认这是我自己想出来的词,因为我不知道它有什么学名,究竟是什么呢?看下这个图就知道了。<br /> <img title="顶部指示器示例" border="0" alt="顶部指示器示例" src="https://simg.open-open.com/show/90503409d74244fb3a001bfcc34a9fb9.jpg" width="273" height="22" /><br /> 这是我们的美工MM画的,偶的神呐,这虽然很漂亮,不过也让人头疼,这个<span>箭头</span>应该在滚到顶部的时候消失,滚下来的时候(即有条目隐藏的时候)才显示,类似的底部指示器也要有这样的效果。事实上默认的<span>ListView</span>和ScrollView都已经有了类似的效果,在顶部或底部还有更多内容时,会有部分渐变虚化的效果,不过美工已经设计了这样的效果,那么我们就来做吧。<br /> 出于省事的目的,本教程中的例子会基于上一篇教程来修改,主要是添加1个继承自<span>ListView</span>的类,以及修改布局定义文件。</p>    <h3>Arrow<span>ListView</span>控件的编写</h3>    <pre class="brush:java; toolbar: true; auto-links: false;">package net.learningandroid.lib.view;   import net.learningandroid.lib.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.ListView;   /**  * 支持上下箭头的ListView  *   * <a class="referer" href="/misc/goto?guid=4959499969295633705" target="_blank">@author</a> Mr. Lu  */ public class ArrowListView extends ListView {    private final float scale = getContext().getResources().getDisplayMetrics().density;  private float topArrowPadding;  private float bottomArrowPadding;    private static float DEFAULT_TOP_PADDING_DP = 2.0f;  private static float DEFAULT_BOTTOM_PADDING_DP = 2.0f;    public ArrowListView(Context context, AttributeSet attrs) {   super(context, attrs);     String strTopArrowPadding = attrs.getAttributeValue(null,     "topArrowPadding");   String strBottomArrowPadding = attrs.getAttributeValue(null,     "bottomArrowPadding");     topArrowPadding = convertDisplayUom(strTopArrowPadding,     DEFAULT_TOP_PADDING_DP);   bottomArrowPadding = convertDisplayUom(strBottomArrowPadding,     DEFAULT_BOTTOM_PADDING_DP);     Log.v("ArrowListView", String.valueOf(topArrowPadding));  }    /**   * 单位转换   */  private float convertDisplayUom(String sour, float defaultValue) {   try {    if (sour.toLowerCase().endsWith("px")) {     return Float.parseFloat(sour.toLowerCase().replace("px", ""));    } else if (sour.toLowerCase().endsWith("dp")) {     return Integer.parseInt(sour.toLowerCase().replace("dp",       ""))       * scale + 0.5f;    }   } catch (Exception e) {   }     return (defaultValue * scale + 0.5f);  }    /**   * onDraw方法,根据ListView滚动位置绘出箭头.   */  @Override  protected void onDraw(Canvas canvas) {   super.onDraw(canvas);   Paint paint = new Paint();     // 取得箭头的图片,此处是固定图片,其实上可以做成配置方式   Bitmap topPic = ((BitmapDrawable) getResources().getDrawable(     R.drawable.arrow_up)).getBitmap();   Bitmap bottomPic = ((BitmapDrawable) getResources().getDrawable(     R.drawable.arrow_down)).getBitmap();     // 取得ListView的绘制区域大小   Rect r = new Rect();   this.getDrawingRect(r);     // 计算箭头的绘制位置   float top = r.top + topArrowPadding;   float bottom = r.bottom - bottomArrowPadding - bottomPic.getHeight();   float left = r.left + (r.right - r.left - topPic.getWidth()) / 2;     // 计算是否需要绘制   boolean drawTop = false;   boolean drawBottom = false;     if (this.getChildCount() > 0) {    Rect rTop = new Rect();    this.getChildAt(0).getLocalVisibleRect(rTop);    Rect rBottom = new Rect();    View lastChild = this.getChildAt(this.getChildCount() - 1);    lastChild.getLocalVisibleRect(rBottom);      drawTop = (this.getFirstVisiblePosition() > 0 || this        .getFirstVisiblePosition() == 0        && rTop.top > 0);    drawBottom = (this.getLastVisiblePosition() < this.getAdapter()        .getCount() - 1 || this.getLastVisiblePosition() == this        .getAdapter().getCount() - 1        && rBottom.bottom < lastChild.getHeight());   }   // 绘出箭头   if (drawTop) {    canvas.drawBitmap(topPic, left, top, paint);   }     if (drawBottom) {    canvas.drawBitmap(bottomPic, left, bottom, paint);   }  } }</pre>    <p></p>    <p>就要点解释一下上面这段代码:<br /> 注意构造方法,我们必须继承public Arrow<span>ListView</span>(Context context, AttributeSet attrs),这样才可以让这个类在xml定义文件中使用。<br /> 还要注意到,这里用了attrs.getAttributeValue来读取XML定义文件中的属性,其实有更好的方法,容我下次再讲解,这里先偷个懒。<br /> convertDisplayUom方法是用来将dp转换成px的,可以看到由于我们用了getAttributeValue的方式,所以需要手动将String转成Float,很麻烦。<br /> 最后就是onDraw啦,计算出画<span>箭头</span>的位置,画出来就行了。<br /> 接下来就是布局文件的编写了</p>    <h3>ArrowListView在XML文件中的使用</h3>    <pre class="brush:xml; toolbar: true; auto-links: false;"><?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="vertical"  >  <TextView   android:text="Arrow List View Sample"   android:layout_width="fill_parent" android:layout_height="wrap_content"   />  <net.learningandroid.lib.view.ArrowListView   android:id="@+id/arrowListView"   android:layout_width="fill_parent" android:layout_height="wrap_content"   android:paddingTop="15dp" android:paddingBottom="20dp"    android:layout_margin="10dp"   android:background="@drawable/float_panel"   android:layout_weight="1"   android:cacheColorHint="#FFEDEDED" android:divider="#00EDEDED"    topArrowPadding="5dp" bottomArrowPadding="10dp"  /> </LinearLayout></pre>    <p></p>    <p>这里需要注意的是<span>自定义控件</span>和其中的属性的写法,不再是ListView了,而是你自己编写的控件类的类名。其它的内容就是定义padding,background,以及取消了分隔线的显示。<br /> 用这个布局文件替代上一篇教程中的布局文件,但Adapter的定义不变,因为ArrowListView是继承自ListView的,所以原先的Adapter的使用是一样的。<br /> <br /> 最后我们来看下效果:<br /> <img title="带内容指示器的ListView最终效果图" border="0" alt="带内容指示器的ListView最终效果图" src="https://simg.open-open.com/show/2c7cf5d7e96043ae3bd9b4ca577d8735.jpg" width="320" height="455" /><br /> 如何?只需要小心的调整ListView的Padding和ArrowPadding的值就可以控制<span>箭头</span>出现的位置,如果需要控制更多,比如更换图片,或者当顶部无内容时让<span>箭头</span>变暗、有内容时变亮,都可以用同样的方法。<br /> 并且,如果修改了Attribute的读取方法之后,还可以通过xml文件来指定<span>箭头</span>的图片。</p>    <p>文章出处:<a href="/misc/goto?guid=4959499969384465131" rel="nofollow">http://www.learningandroid.net/blog/advance/listview-top-bottom-content-indicator/</a></p>