欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

ListView实现顶部和底部内容指示器的方法

程序员文章站 2022-10-09 11:19:08
顶部指示器? 这是什么? 好吧,我承认这是我自己想出来的词,因为我不知道它有什么学名,究竟是什么呢?看下这个图就知道了。 这是我们的美工mm画的,...

顶部指示器?
这是什么?
好吧,我承认这是我自己想出来的词,因为我不知道它有什么学名,究竟是什么呢?看下这个图就知道了。

ListView实现顶部和底部内容指示器的方法

这是我们的美工mm画的,偶的神呐,这虽然很漂亮,不过也让人头疼,这个箭头应该在滚到顶部的时候消失,滚下来的时候(即有条目隐藏的时候)才显示,类似的底部指示器也要有这样的效果。事实上默认的listview和scrollview都已经有了类似的效果,在顶部或底部还有更多内容时,会有部分渐变虚化的效果,不过美工已经设计了这样的效果,那么我们就来做吧。
出于省事的目的,本教程中的例子会基于上一篇教程来修改,主要是添加1个继承自listview的类,以及修改布局定义文件。
arrowlistview控件的编写

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="http://my.oschina.net/arthor" 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);
    }
  }
}

就要点解释一下上面这段代码:
注意构造方法,我们必须继承public arrowlistview(context context, attributeset attrs),这样才可以让这个类在xml定义文件中使用。
还要注意到,这里用了attrs.getattributevalue来读取xml定义文件中的属性,其实有更好的方法,容我下次再讲解,这里先偷个懒。
convertdisplayuom方法是用来将dp转换成px的,可以看到由于我们用了getattributevalue的方式,所以需要手动将string转成float,很麻烦。
最后就是ondraw啦,计算出画箭头的位置,画出来就行了。
接下来就是布局文件的编写了

arrowlistview在xml文件中的使用

<?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>

这里需要注意的是自定义控件和其中的属性的写法,不再是listview了,而是你自己编写的控件类的类名。其它的内容就是定义padding,background,以及取消了分隔线的显示。
用这个布局文件替代上一篇教程中的布局文件,但adapter的定义不变,因为arrowlistview是继承自listview的,所以原先的adapter的使用是一样的。

最后我们来看下效果:

ListView实现顶部和底部内容指示器的方法

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