ListView實現下拉刷新-2-將頂部佈局加載到ListView中

上一篇實現了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;

 548662a50001dac505000399.jpg

這兩種方法:方法一雖然比較簡單,但是隻能添加一個。方法二比較複雜,但是可以添加多個。所以推薦使用方法二;

實現了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(00, 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中了,並被隱藏;

下一篇我們將主要討論狀態的改變,和下拉刷新的實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章