上一篇实现了Adapter类的创建,和getView函数的分析;
这一篇主要讲第二部分,即将顶部布局加载到ListView中;重点是ReFlashListView的实现上,这一篇中我会谈一谈在阅读源代码的过程中所遇到的困难和采取的方法;
首先看ReFlashListView类:
public class ReFlashListView extends ListView implements OnScrollListener |
表明ReFlashListView是继承自ListView的,并且 实现了OnScrollListener接口:
继承自ListView:如何进行构造方法的添加呢?我在包下又创建了一个MyListView类,进行了以下两种方法的实现:
方法1:将ListView的包导入后,鼠标移动到MyListView下,之后就会出现三种构造方法,可以点击添加;
方法2:右击->Source->Generate Constructors from Superclass,在窗口中选择Select All,点击ok;
这两种方法:方法一虽然比较简单,但是只能添加一个。方法二比较复杂,但是可以添加多个。所以推荐使用方法二;
实现了OnScrollListener接口:按住ctrl+右键,进入OnScrollListener接口的定义位置,发现他是定义在AbsListView$OnScrolListener.java文件中,在里面声明的常量和函数有:
public interface OnScrollListener { public static int SCROLL_STATE_IDLE = 0 public static int SCROLL_STATE_TOUCH_SCROLL = 1; public static int SCROLL_STATE_FLING = 2; public void onScrollStateChanged(AbsListView view, int scrollState); public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount); } |
在导入OnScrollListener的包后,鼠标移动到我们的实验类MyListView下,之后点击Add unimplemented methods;就为类添加了onScrollStateChanged和onScroll这两者函数了;
有关于ReFlashListView的继承关系与接口实现讨论结束之后,接下来我们将讨论各个成员函数的作用,并在这个过程中,获得ReFlashListView类的成员变量;最后再将成员变量和其所起的作用,总结给出(毕竟我们并不是一开始就知道需要什么成员变量,只有在写函数的时候才在开始的位置补充上的)。
首先给出各个函数的声明:
private void initView(Context context) private void measureView(View view) private void topPadding( int topPadding) public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) public void onScrollStateChanged(AbsListView view, int scrollState) public boolean onTouchEvent(MotionEvent ev) private void onMove(MotionEvent ev) private void reflashViewByState() public void reflashComplete() public void setInterface(IReflashListener iReflashListener) public interface IReflashListener |
各个函数的作用:
initView:初始化界面,添加顶部布局文件到 listview
measureView:通知父布局,占用的宽,高;
topPadding:设置header布局上边距;
onScroll:获取到当前可见的第一个的编号;
onScrollStateChanged:获得listview 当前滚动状态;
onTouchEvent:获得手势;
onMove:根据手势,来改变view的显示;
reflashViewByState:根据当前状态,改变界面显示;
reflashComplete:获取完数据;
setInterface:设置监听器,主要是接口回调机制中起作用;
在initView函数中:
private void initView(Context context) { LayoutInflater inflater = LayoutInflater.from(context); ///获得context的Inflater; header = inflater.inflate(R.layout.header_layout, null); ///为xml文件创建一个View对象; measureView(header); ///获得高度 headerHeight = header.getMeasuredHeight(); ///测量高度 Log.i( "tag" , "headerHeight = " + headerHeight); topPadding(-headerHeight); this .addHeaderView(header); ///添加顶部view this .setOnScrollListener( this ); } |
Inflater类的作用主要是连接xml与VIew对象的,具体的话可以看我的博客:常用但忽略的android知识1-Inflate
heade是View类型,属于类的成员变量,用来记录顶部布局;
为什么要用measureView函数来获得高度的原因是:在onCreate()里面获取控件的高度是0,ReFlashListView实例化的时候正好是在onCreate函数中的,具体的话可以看我的博客:常用但忽略的anroid知识5-获得一个view的宽和高
之后调用topPadding函数来设置高度,从而达到隐藏的目的;
ListView的成员函数addHeaderView,可以用来为ListView加载顶部布局;
最后setOnScrollListener设置监听器,自动调用onScroll,onScrollStateChanged这两个函数。
在measureView函数中:
private void measureView(View view) { ViewGroup.LayoutParams p = view.getLayoutParams(); if (p == null ) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int width = ViewGroup.getChildMeasureSpec( 0 , 0 , p.width); int height; int tempHeight = p.height; if (tempHeight > 0 ) { height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); } else { height = MeasureSpec.makeMeasureSpec( 0 , MeasureSpec.UNSPECIFIED); } view.measure(width, height); } |
ViewGroup.LayoutParams这个在代码中的解释是:LayoutParams are used by views to tell their parents how they want to be laid out.也就是获得view的大小;
MeasureSpec这个在代码中的解释是:A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode.
注意到在课程的回复区:有大神是这样说的"测量子 view 的时候根本不用这么麻烦,既然没有按照 android view 系统的标准流程走,在 onMeasure 的时候测量,而提前在构造函数的时候就测量,其实只需要高,所以只需要
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
height = MeasureSpec.makeMeasureSpec(tempHeight,MeasureSpec.AT_MOST);
view.measure(width, height);
就行啦,宽随便设置一个值就行嘛,反正也不使用的。"
在topPadding函数中:
private void topPadding( int topPadding) { header.setPadding(header.getPaddingLeft(), topPadding, header.getPaddingRight(), header.getPaddingBottom()); header.invalidate(); } |
需要注意invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面;
通过上面的这几个函数,大致的Head顶部布局就被加载到ListView中了,并被隐藏;
下一篇我们将主要讨论状态的改变,和下拉刷新的实现。ListView实现下拉刷新-2-将顶部布局加载到ListView中
原文地址:http://blog.csdn.net/softtrilobite/article/details/41869519