亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android筆記之:App列表之下拉刷新的使用

2020-04-11 12:26:32
字體:
來源:轉載
供稿:網友
Android的ListView是應用最廣的一個組件,功能強大,擴展性靈活(不局限于ListView本身一個類),前面的文章有介紹分組,拖拽,3D立體,游標,圓角,而今天我們要介紹的是另外一個擴展ListView:下拉刷新的ListView。
    下拉刷新界面最初流行于iphone應用界面,如圖:


    然后在Android中也逐漸被應用,比如微博,資訊類。
    所以,今天要實現的結果應該也是類似的,先貼出最終完成效果,如下圖,接下來我們一步一步實現。

 

1. 流程分析
    下拉刷新最主要的流程是:
    (1). 下拉,顯示提示頭部界面(HeaderView),這個過程提示用戶"下拉刷新"
    (2). 下拉到一定程度,超出了刷新最基本的下拉界限,我們認為達到了刷新的條件,提示用戶可以"松手刷新"了,效果上允許用戶繼續下拉
    (3). 用戶松手,可能用戶下拉遠遠不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然后提示用戶"正在加載"。
    (4). 加載完成后,隱藏提示頭部界面。
    示意圖如下:

->->

2. 實現分析
    當前我們要實現上述流程,是基于ListView的,所以對應ListView本身的功能我們來分析一下實現原理:
    (1). 下拉,顯示提示頭部界面,這個過程提示用戶"下拉刷新"
        a. 下拉的操作,首先是監聽滾動,ListView提供了onScroll()方法
        b. 與下拉類似一個動作向下飛滑,所以ListView的scrollState有3種值:SCROLL_STATE_IDLE, SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING,意思容易理解,而我們要下拉的觸發條件是SCROLL_STATE_TOUCH_SCROLL。判斷當前的下拉操作狀態,ListView提供了public void onScrollStateChanged(AbsListView view, int scrollState) {}。
    c. 下拉的過程中,我們可能還需要下拉到多少的邊界值處理,重寫onTouchEvent(MotionEvent ev){}方法,可依據ACTION_DOWN,ACTION_MOVE,ACTION_UP實現更精細的判斷。
    (2). 下拉到一定程度,超出了刷新最基本的下拉界限,我們認為達到了刷新的條件,提示用戶可以"松手刷新"了,效果上允許用戶繼續下拉
        a. 達到下拉刷新界限,一般指達到header的高度的,所以有兩步,第一,獲取header的高度,第二,當header.getBottom()>=header的高度時,我們認為就達到了刷新界限值
        b. 繼續允許用戶下拉,當header完全下拉后,默認無法繼續下拉,但是可以增加header的PaddingTop實現這種效果
    (3). 用戶松手,可能用戶下拉遠遠不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然后提示用戶"正在加載"。
        a. 松手后反彈,這個不能一下子彈回去,看上去太突然,需要一步一步柔性的彈回去,像彈簧一樣,我們可以new一個Thread循環計算減少PaddingTop,直到PaddingTop為0,反彈結束。
        b. 正在加載,在子線程里處理后臺任務
    (4). 加載完成后,隱藏提示頭部界面。
        a. 后臺任務完成后,我們需要隱藏header,setSelection(1)即實現了從第2項開始顯示,間接隱藏了header。
上面我們分析了實現過程的輪廓,接下來,通過細節說明和代碼具體實現。

3. 初始化
    一切狀態顯示都是用HeaderView顯示的,所以我們需要一個HeaderView的layout,使用addHeaderView方法添加到ListView中。
    同時,默認狀態下,HeaderView是不顯示的,只是在下拉后才顯示,所以我們需要隱藏HeaderView且不影響后續的下拉顯示,用setSelection(1)。
    refresh_list_header.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="wrap_content"
    android:gravity="center">
    <ProgressBar android:id="@+id/refresh_list_header_progressbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        style="?android:attr/progressBarStyleSmall"
        android:visibility="gone">
    </ProgressBar>
    <ImageView android:id="@+id/refresh_list_header_pull_down"
        android:layout_width="9dip"
        android:layout_height="25dip"
        android:layout_gravity="center"
        android:src="@drawable/refresh_list_pull_down" />
    <ImageView android:id="@+id/refresh_list_header_release_up"
        android:layout_width="9dip"
        android:layout_height="25dip"
        android:layout_gravity="center"
        android:src="@drawable/refresh_list_release_up"
        android:visibility="gone" />
    <RelativeLayout android:layout_width="180dip"
        android:layout_height="wrap_content">
        <TextView android:id="@+id/refresh_list_header_text"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_alignParentTop="true"
            android:textSize="12dip"
            android:textColor="#192F06"
            android:paddingTop="8dip"
            android:text="@string/app_list_header_refresh_down"/>
        <TextView android:id="@+id/refresh_list_header_last_update"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_below="@id/refresh_list_header_text"
            android:textSize="12dip"
            android:textColor="#192F06"
            android:paddingBottom="8dip"
            android:text="@string/app_list_header_refresh_last_update"/>
    </RelativeLayout>
</LinearLayout>   

代碼中在構造函數中添加init()方法加載如下:
復制代碼 代碼如下:

private LinearLayout mHeaderLinearLayout = null;
private TextView mHeaderTextView = null;
private TextView mHeaderUpdateText = null;
private ImageView mHeaderPullDownImageView = null;
private ImageView mHeaderReleaseDownImageView = null;
private ProgressBar mHeaderProgressBar = null;

public RefreshListView(Context context) {
    this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

void init(final Context context) {
    mHeaderLinearLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.refresh_list_header, null);
    addHeaderView(mHeaderLinearLayout);
    mHeaderTextView = (TextView) findViewById(R.id.refresh_list_header_text);
    mHeaderUpdateText = (TextView) findViewById(R.id.refresh_list_header_last_update);
    mHeaderPullDownImageView = (ImageView) findViewById(R.id.refresh_list_header_pull_down);
    mHeaderReleaseDownImageView = (ImageView) findViewById(R.id.refresh_list_header_release_up);
    mHeaderProgressBar = (ProgressBar) findViewById(R.id.refresh_list_header_progressbar);

    setSelection(1);
}

   默認就顯示完成了。

4. HeaderView的默認高度測量
    因為下拉到HeaderView全部顯示出來,就由提示"下拉刷新"變為"松手刷新",全部顯示的出來的測量標準就是header.getBottom()>=header的高度。
    所以,首先我們需要測量HeaderView的默認高度。

復制代碼 代碼如下:

//因為是在構造函數里測量高度,應該先measure一下
private void measureView(View child) {
    ViewGroup.LayoutParams p = child.getLayoutParams();
    if (p == null) {
        p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
    int lpHeight = p.height;
    int childHeightSpec;
    if (lpHeight > 0) {
        childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                MeasureSpec.EXACTLY);
    } else {
        childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                MeasureSpec.UNSPECIFIED);
    }
    child.measure(childWidthSpec, childHeightSpec);
}

然后在init的上述代碼后面加上調用measureView后,使用getMeasureHeight()方法獲取header的高度:
復制代碼 代碼如下:

private int mHeaderHeight;
void init(final Context context) {
    ... ...
    measureView(mHeaderLinearLayout);
    mHeaderHeight = mHeaderLinearLayout.getMeasuredHeight();
}

  后面我們就會用到這個mHeaderHeight.

5. scrollState監聽記錄
    scrollState有3種,使用onScrollStateChanged()方法監聽記錄。

復制代碼 代碼如下:

private int mCurrentScrollState;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    mCurrentScrollState = scrollState;
}

    然后即可使用mCurrentScrollState作為后面判斷的條件了。

6. 刷新狀態分析
    因為一些地方需要知道我們處在正常狀態下還是進入下拉刷新狀態還是松手反彈狀態,比如,
    (1). 在非正常的狀態下,我們不小心飛滑了一下(松手的瞬間容易出現這種情況),我們不能setSelection(1)的,否則總是松手后header跳的一下消失掉了。
    (2). 下拉后要做一個下拉效果的特殊處理,需要用到OVER_PULL_REFRESH(松手刷新狀態下)
    (3). 松手反彈后要做一個反彈效果的特殊處理,需要用到OVER_PULL_REFRESH和ENTER_PULL_REFRESH。

復制代碼 代碼如下:

private final static int NONE_PULL_REFRESH = 0;   //正常狀態
private final static int ENTER_PULL_REFRESH = 1;  //進入下拉刷新狀態
private final static int OVER_PULL_REFRESH = 2;   //進入松手刷新狀態
private final static int EXIT_PULL_REFRESH = 3;     //松手后反彈后加載狀態
private int mPullRefreshState = 0;                         //記錄刷新狀態
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (mCurrentScrollState ==SCROLL_STATE_TOUCH_SCROLL
            && firstVisibleItem == 0
            && (mHeaderLinearLayout.getBottom() >= 0 && mHeaderLinearLayout.getBottom() < mHeaderHeight)) {
        //進入且僅進入下拉刷新狀態
        if (mPullRefreshState == NONE_PULL_REFRESH) {
            mPullRefreshState = ENTER_PULL_REFRESH;
        }
    } else if (mCurrentScrollState ==SCROLL_STATE_TOUCH_SCROLL
            && firstVisibleItem == 0
            && (mHeaderLinearLayout.getBottom() >= mHeaderHeight)) {
        //下拉達到界限,進入松手刷新狀態
        if (mPullRefreshState == ENTER_PULL_REFRESH || mPullRefreshState == NONE_PULL_REFRESH) {
            mPullRefreshState = OVER_PULL_REFRESH;
            //下面是進入松手刷新狀態需要做的一個顯示改變
            mDownY = mMoveY;//用于后面的下拉特殊效果
            mHeaderTextView.setText("松手刷新");
            mHeaderPullDownImageView.setVisibility(View.GONE);
            mHeaderReleaseDownImageView.setVisibility(View.VISIBLE);
        }
    } else if (mCurrentScrollState ==SCROLL_STATE_TOUCH_SCROLL && firstVisibleItem != 0) {
        //不刷新了
        if (mPullRefreshState == ENTER_PULL_REFRESH) {
            mPullRefreshState = NONE_PULL_REFRESH;
        }
    } else if (mCurrentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0) {
        //飛滑狀態,不能顯示出header,也不能影響正常的飛滑
        //只在正常情況下才糾正位置
        if (mPullRefreshState == NONE_PULL_REFRESH) {
            setSelection(1);
        }
    }
}

  mPullRefreshState將是后面我們處理邊界的重要變量。

6. 下拉效果的特殊處理
    所謂的特殊處理,當header完全顯示后,下拉只按下拉1/3的距離下拉,給用戶一種艱難下拉,該松手的彈簧感覺。
    這個在onTouchEvent里處理比較方便:

復制代碼 代碼如下:

private float mDownY;
private float mMoveY;
@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //記下按下位置
            //改變
            mDownY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            //移動時手指的位置
            mMoveY = ev.getY();
            if (mPullRefreshState == OVER_PULL_REFRESH) {
                //注意下面的mDownY在onScroll的第二個else中被改變了
                mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                        (int)((mMoveY - mDownY)/3), //1/3距離折扣
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
            }
            break;
        case MotionEvent.ACTION_UP:
            ... ...
            break;
    }
    return super.onTouchEvent(ev);
}

//重復貼出下面這段需要注意的代碼
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    ... ...
    else if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
            && firstVisibleItem == 0
            && (mHeaderLinearLayout.getBottom() >= mHeaderHeight)) {
        //下拉達到界限,進入松手刷新狀態
        if (mPullRefreshState == ENTER_PULL_REFRESH || mPullRefreshState == NONE_PULL_REFRESH) {
            mPullRefreshState = OVER_PULL_REFRESH;
            mDownY = mMoveY; //為下拉1/3折扣效果記錄開始位置
            mHeaderTextView.setText("松手刷新");//顯示松手刷新
            mHeaderPullDownImageView.setVisibility(View.GONE);//隱藏"下拉刷新"
            mHeaderReleaseDownImageView.setVisibility(View.VISIBLE);//顯示向上的箭頭
        }
    }
    ... ...
}

  onScroll里監聽到了進入松手刷新狀態,onTouchEvent就開始在ACTION_MOVE中處理1/3折扣問題。

7. 反彈效果的特殊處理
    松手后我們需要一個柔性的反彈效果,意味著我們彈回去的過程需要分一步步走,我的解決方案是:
    在子線程里計算PaddingTop,并減少到原來的3/4,循環通知主線程,直到PaddingTop小于1(這個值取一個小值,合適即可)。
    松手后,當然是在onTouchEvent的ACTION_UP條件下處理比較方便:

復制代碼 代碼如下:

//因為涉及到handler數據處理,為方便我們定義如下常量
private final static int REFRESH_BACKING = 0;      //反彈中
private final static int REFRESH_BACED = 1;        //達到刷新界限,反彈結束后
private final static int REFRESH_RETURN = 2;       //沒有達到刷新界限,返回
private final static int REFRESH_DONE = 3;         //加載數據結束

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        ... ...
        case MotionEvent.ACTION_UP:
            //when you action up, it will do these:
            //1. roll back util header topPadding is 0
            //2. hide the header by setSelection(1)
            if (mPullRefreshState == OVER_PULL_REFRESH || mPullRefreshState == ENTER_PULL_REFRESH) {
                new Thread() {
                    public void run() {
                        Message msg;
                        while(mHeaderLinearLayout.getPaddingTop() > 1) {
                            msg = mHandler.obtainMessage();
                            msg.what = REFRESH_BACKING;
                            mHandler.sendMessage(msg);
                            try {
                                sleep(5);//慢一點反彈,別一下子就彈回去了
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        msg = mHandler.obtainMessage();
                        if (mPullRefreshState == OVER_PULL_REFRESH) {
                            msg.what = REFRESH_BACED;//加載數據完成,結束返回
                        } else {
                            msg.what = REFRESH_RETURN;//未達到刷新界限,直接返回
                        }
                        mHandler.sendMessage(msg);
                    };
                }.start();
            }
            break;
    }
    return super.onTouchEvent(ev);
}

private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case REFRESH_BACKING:
            mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                    (int) (mHeaderLinearLayout.getPaddingTop()*0.75f),
                    mHeaderLinearLayout.getPaddingRight(),
                    mHeaderLinearLayout.getPaddingBottom());
            break;
        case REFRESH_BACED:
            mHeaderTextView.setText("正在加載...");
            mHeaderProgressBar.setVisibility(View.VISIBLE);
            mHeaderPullDownImageView.setVisibility(View.GONE);
            mHeaderReleaseDownImageView.setVisibility(View.GONE);
            mPullRefreshState = EXIT_PULL_REFRESH;
            new Thread() {
                public void run() {
                    sleep(2000);//處理后臺加載數據
                    Message msg = mHandler.obtainMessage();
                    msg.what = REFRESH_DONE;
                    //通知主線程加載數據完成
                    mHandler.sendMessage(msg);
                };
            }.start();
            break;
        case REFRESH_RETURN:
            //未達到刷新界限,返回
            mHeaderTextView.setText("下拉刷新");
            mHeaderProgressBar.setVisibility(View.INVISIBLE);
            mHeaderPullDownImageView.setVisibility(View.VISIBLE);
            mHeaderReleaseDownImageView.setVisibility(View.GONE);
            mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                    0,
                    mHeaderLinearLayout.getPaddingRight(),
                    mHeaderLinearLayout.getPaddingBottom());
            mPullRefreshState = NONE_PULL_REFRESH;
            setSelection(1);
            break;
        case REFRESH_DONE:
            //刷新結束后,恢復原始默認狀態
            mHeaderTextView.setText("下拉刷新");
            mHeaderProgressBar.setVisibility(View.INVISIBLE);
            mHeaderPullDownImageView.setVisibility(View.VISIBLE);
            mHeaderReleaseDownImageView.setVisibility(View.GONE);
            mHeaderUpdateText.setText(getContext().getString(R.string.app_list_header_refresh_last_update,
                    mSimpleDateFormat.format(new Date())));
            mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                    0,
                    mHeaderLinearLayout.getPaddingRight(),
                    mHeaderLinearLayout.getPaddingBottom());
            mPullRefreshState = NONE_PULL_REFRESH;
            setSelection(1);
            break;
        default:
            break;
        }
    }
};

    為了一下子看的明確,我把效果中的數據處理代碼也貼出來了。

8. 切入數據加載過程
    上面數據后臺處理我們用sleep(2000)來處理,實際處理中,作為公共組件,我們也不好把具體代碼直接寫在這里,我們需要一個更靈活的分離:
    (1). 定義接口
    (2). 注入接口

復制代碼 代碼如下:

//定義接口
public interface RefreshListener {
    Object refreshing();                //加載數據
    void refreshed(Object obj);    //外部可擴展加載完成后的操作
}

//注入接口
private Object mRefreshObject = null; //傳值
private RefreshListener mRefreshListener = null;
public void setOnRefreshListener(RefreshListener refreshListener) {
    this.mRefreshListener = refreshListener;
}

 
//我們需要重寫上面的mHandler如下代碼
case REFRESH_BACED:
    ... ...
    new Thread() {
        public void run() {
            if (mRefreshListener != null) {
                mRefreshObject = mRefreshListener.refreshing();
            }
            Message msg = mHandler.obtainMessage();
            msg.what = REFRESH_DONE;
            mHandler.sendMessage(msg);
        };
    }.start();
    break;
case REFRESH_DONE:
    ... ...
    mPullRefreshState = NONE_PULL_REFRESH;
    setSelection(1);
    if (mRefreshListener != null) {
        mRefreshListener.refreshed(mRefreshObject);
    }
    break;

在其他地方我們就可以不修改這個listview組件的代碼,使用如下:
復制代碼 代碼如下:

public xxx implements RefreshListener{

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //類似如下
        ((RefreshListView) listView).setOnRefreshListener(this);
    }

    @Override
    public Object refreshing() {
        String result = null;
        //result = FileUtils.readTextFile(file);
        return result;
    }

    @Override
    public void refreshed(Object obj) {
        if (obj != null) {
           //擴展操作
        }
    };
}

  很方便了。

9. 擴展"更多"功能
    下拉刷新之外,我們也可以通過相同方法使用FooterView切入底部"更多"過程,這里我就不詳細說明了

10. 源碼
    上面的每段代碼都看做是"零部件",需要組合一下。
    因為我們上面實現了下拉刷新,還增加了"更多"功能,我們直接命名這個類為RefreshListView吧:

復制代碼 代碼如下:

package com.tianxia.lib.baseworld.widget;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.tianxia.lib.baseworld.R;

/**
 * 下拉刷新,底部更多
 *
 */
public class RefreshListView extends ListView implements OnScrollListener{

    private float mDownY;
    private float mMoveY;

    private int mHeaderHeight;

    private int mCurrentScrollState;

    private final static int NONE_PULL_REFRESH = 0;    //正常狀態
    private final static int ENTER_PULL_REFRESH = 1;   //進入下拉刷新狀態
    private final static int OVER_PULL_REFRESH = 2;    //進入松手刷新狀態
    private final static int EXIT_PULL_REFRESH = 3;    //松手后反彈和加載狀態
    private int mPullRefreshState = 0;                 //記錄刷新狀態

    private final static int REFRESH_BACKING = 0;      //反彈中
    private final static int REFRESH_BACED = 1;        //達到刷新界限,反彈結束后
    private final static int REFRESH_RETURN = 2;       //沒有達到刷新界限,返回
    private final static int REFRESH_DONE = 3;         //加載數據結束

    private LinearLayout mHeaderLinearLayout = null;
    private LinearLayout mFooterLinearLayout = null;
    private TextView mHeaderTextView = null;
    private TextView mHeaderUpdateText = null;
    private ImageView mHeaderPullDownImageView = null;
    private ImageView mHeaderReleaseDownImageView = null;
    private ProgressBar mHeaderProgressBar = null;
    private TextView mFooterTextView = null;
    private ProgressBar mFooterProgressBar = null;

    private SimpleDateFormat mSimpleDateFormat;

    private Object mRefreshObject = null;
    private RefreshListener mRefreshListener = null;
    public void setOnRefreshListener(RefreshListener refreshListener) {
        this.mRefreshListener = refreshListener;
    }

    public RefreshListView(Context context) {
        this(context, null);
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    void init(final Context context) {
        mHeaderLinearLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.refresh_list_header, null);
        addHeaderView(mHeaderLinearLayout);
        mHeaderTextView = (TextView) findViewById(R.id.refresh_list_header_text);
        mHeaderUpdateText = (TextView) findViewById(R.id.refresh_list_header_last_update);
        mHeaderPullDownImageView = (ImageView) findViewById(R.id.refresh_list_header_pull_down);
        mHeaderReleaseDownImageView = (ImageView) findViewById(R.id.refresh_list_header_release_up);
        mHeaderProgressBar = (ProgressBar) findViewById(R.id.refresh_list_header_progressbar);

        mFooterLinearLayout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.refresh_list_footer, null);
        addFooterView(mFooterLinearLayout);
        mFooterProgressBar = (ProgressBar) findViewById(R.id.refresh_list_footer_progressbar);
        mFooterTextView = (TextView) mFooterLinearLayout.findViewById(R.id.refresh_list_footer_text);
        mFooterLinearLayout.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (context.getString(R.string.app_list_footer_more).equals(mFooterTextView.getText())) {
                    mFooterTextView.setText(R.string.app_list_footer_loading);
                    mFooterProgressBar.setVisibility(View.VISIBLE);
                    if (mRefreshListener != null) {
                        mRefreshListener.more();
                    }
                }
            }
        });

        setSelection(1);
        setOnScrollListener(this);
        measureView(mHeaderLinearLayout);
        mHeaderHeight = mHeaderLinearLayout.getMeasuredHeight();

        mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        mHeaderUpdateText.setText(context.getString(R.string.app_list_header_refresh_last_update, mSimpleDateFormat.format(new Date())));
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                mMoveY = ev.getY();
                if (mPullRefreshState == OVER_PULL_REFRESH) {
                    mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                            (int)((mMoveY - mDownY)/3),
                            mHeaderLinearLayout.getPaddingRight(),
                            mHeaderLinearLayout.getPaddingBottom());
                }
                break;
            case MotionEvent.ACTION_UP:
                //when you action up, it will do these:
                //1. roll back util header topPadding is 0
                //2. hide the header by setSelection(1)
                if (mPullRefreshState == OVER_PULL_REFRESH || mPullRefreshState == ENTER_PULL_REFRESH) {
                    new Thread() {
                        public void run() {
                            Message msg;
                            while(mHeaderLinearLayout.getPaddingTop() > 1) {
                                msg = mHandler.obtainMessage();
                                msg.what = REFRESH_BACKING;
                                mHandler.sendMessage(msg);
                                try {
                                    sleep(5);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            msg = mHandler.obtainMessage();
                            if (mPullRefreshState == OVER_PULL_REFRESH) {
                                msg.what = REFRESH_BACED;
                            } else {
                                msg.what = REFRESH_RETURN;
                            }
                            mHandler.sendMessage(msg);
                        };
                    }.start();
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
                && firstVisibleItem == 0
                && (mHeaderLinearLayout.getBottom() >= 0 && mHeaderLinearLayout.getBottom() < mHeaderHeight)) {
            //進入且僅進入下拉刷新狀態
            if (mPullRefreshState == NONE_PULL_REFRESH) {
                mPullRefreshState = ENTER_PULL_REFRESH;
            }
        } else if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
                && firstVisibleItem == 0
                && (mHeaderLinearLayout.getBottom() >= mHeaderHeight)) {
            //下拉達到界限,進入松手刷新狀態
            if (mPullRefreshState == ENTER_PULL_REFRESH || mPullRefreshState == NONE_PULL_REFRESH) {
                mPullRefreshState = OVER_PULL_REFRESH;
                mDownY = mMoveY; //為下拉1/3折扣效果記錄開始位置
                mHeaderTextView.setText("松手刷新");//顯示松手刷新
                mHeaderPullDownImageView.setVisibility(View.GONE);//隱藏"下拉刷新"
                mHeaderReleaseDownImageView.setVisibility(View.VISIBLE);//顯示向上的箭頭
            }
        } else if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && firstVisibleItem != 0) {
            //不刷新了
            if (mPullRefreshState == ENTER_PULL_REFRESH) {
                mPullRefreshState = NONE_PULL_REFRESH;
            }
        } else if (mCurrentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0) {
            //飛滑狀態,不能顯示出header,也不能影響正常的飛滑
            //只在正常情況下才糾正位置
            if (mPullRefreshState == NONE_PULL_REFRESH) {
                setSelection(1);
            }
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);
        setSelection(1);
    }

    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                    MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                    MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case REFRESH_BACKING:
                mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                        (int) (mHeaderLinearLayout.getPaddingTop()*0.75f),
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
                break;
            case REFRESH_BACED:
                mHeaderTextView.setText("正在加載...");
                mHeaderProgressBar.setVisibility(View.VISIBLE);
                mHeaderPullDownImageView.setVisibility(View.GONE);
                mHeaderReleaseDownImageView.setVisibility(View.GONE);
                mPullRefreshState = EXIT_PULL_REFRESH;
                new Thread() {
                    public void run() {
                        if (mRefreshListener != null) {
                            mRefreshObject = mRefreshListener.refreshing();
                        }
                        Message msg = mHandler.obtainMessage();
                        msg.what = REFRESH_DONE;
                        mHandler.sendMessage(msg);
                    };
                }.start();
                break;
            case REFRESH_RETURN:
                mHeaderTextView.setText("下拉刷新");
                mHeaderProgressBar.setVisibility(View.INVISIBLE);
                mHeaderPullDownImageView.setVisibility(View.VISIBLE);
                mHeaderReleaseDownImageView.setVisibility(View.GONE);
                mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                        0,
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
                mPullRefreshState = NONE_PULL_REFRESH;
                setSelection(1);
                break;
            case REFRESH_DONE:
                mHeaderTextView.setText("下拉刷新");
                mHeaderProgressBar.setVisibility(View.INVISIBLE);
                mHeaderPullDownImageView.setVisibility(View.VISIBLE);
                mHeaderReleaseDownImageView.setVisibility(View.GONE);
                mHeaderUpdateText.setText(getContext().getString(R.string.app_list_header_refresh_last_update,
                        mSimpleDateFormat.format(new Date())));
                mHeaderLinearLayout.setPadding(mHeaderLinearLayout.getPaddingLeft(),
                        0,
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
                mPullRefreshState = NONE_PULL_REFRESH;
                setSelection(1);
                if (mRefreshListener != null) {
                    mRefreshListener.refreshed(mRefreshObject);
                }
                break;
            default:
                break;
            }
        }
    };
    public interface RefreshListener {
        Object refreshing();
        void refreshed(Object obj);
        void more();
    }

    public void finishFootView() {
        mFooterProgressBar.setVisibility(View.GONE);
        mFooterTextView.setText(R.string.app_list_footer_more);
    }

    public void addFootView() {
        if (getFooterViewsCount() == 0) {
            addFooterView(mFooterLinearLayout);
        }
    }

    public void removeFootView() {
        removeFooterView(mFooterLinearLayout);
    }
}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久视频在线观看免费| 国产视频久久久久久久| 精品成人av一区| 欧美成人午夜视频| 91精品国产高清久久久久久| 在线日韩中文字幕| 午夜精品理论片| 福利视频一区二区| 久久精品成人动漫| 亚洲色图偷窥自拍| 久久97精品久久久久久久不卡| 永久免费毛片在线播放不卡| 亚洲男人天堂2023| 在线播放国产一区中文字幕剧情欧美| 色综合伊人色综合网站| 精品伊人久久97| 日本欧美一级片| 亚洲级视频在线观看免费1级| 91综合免费在线| 日韩有码在线电影| 免费91麻豆精品国产自产在线观看| 91免费的视频在线播放| 精品国产乱码久久久久久天美| 久久久久国产视频| 欧美日韩另类字幕中文| 亚洲成人精品在线| 欧美性猛交99久久久久99按摩| 国产精品视频在线观看| 亚洲欧美成人网| 欧美男插女视频| 中文字幕欧美日韩在线| 精品国产拍在线观看| 成人激情视频小说免费下载| 欧美性高跟鞋xxxxhd| 亚洲一二在线观看| 亚洲天堂一区二区三区| 国产狼人综合免费视频| 欧美激情亚洲另类| 欧美另类极品videosbest最新版本| 国产精品jizz在线观看麻豆| 影音先锋日韩有码| 欧美激情精品在线| 精品国产网站地址| 国产在线久久久| 国产日韩欧美中文在线播放| 91在线播放国产| 国产精品99久久久久久久久久久久| 国产成人a亚洲精品| 成人亚洲欧美一区二区三区| 精品福利在线看| 亚洲精选中文字幕| 国产精品久久久久高潮| 美女国内精品自产拍在线播放| 亚洲美女视频网站| 国产91久久婷婷一区二区| 日韩欧美精品中文字幕| 波霸ol色综合久久| 午夜精品久久久久久久99黑人| 欧亚精品中文字幕| 国产精品夜色7777狼人| 亚洲综合第一页| 日韩在线视频免费观看高清中文| 国语自产精品视频在线看一大j8| 日本aⅴ大伊香蕉精品视频| 亚洲第一区中文字幕| 欧美一区二区影院| 久久视频免费在线播放| 久久久久亚洲精品成人网小说| 38少妇精品导航| 精品国产乱码久久久久久婷婷| 亚洲成色777777在线观看影院| 色999日韩欧美国产| 最近2019好看的中文字幕免费| 性夜试看影院91社区| 播播国产欧美激情| 久久久久国产精品免费| 中文字幕在线观看日韩| 美日韩在线视频| 热99久久精品| 一区二区三区视频免费| 成人在线播放av| 国产欧美精品一区二区| 成人黄色生活片| 欧美大片在线看免费观看| 亚洲乱码国产乱码精品精| 国产精品高精视频免费| 日韩高清av在线| 亚洲欧美三级在线| 91在线免费观看网站| 亚洲一区二区三区乱码aⅴ| 日本韩国欧美精品大片卡二| 久久久久久久久久久人体| 欧美午夜久久久| 成人在线中文字幕| 亚洲精品视频免费在线观看| 亚洲乱亚洲乱妇无码| 中文字幕日韩欧美在线视频| 国产精品白嫩美女在线观看| 国产日韩欧美夫妻视频在线观看| 国产日韩换脸av一区在线观看| 欧美日韩亚洲视频一区| 欧美黑人性视频| 亚洲欧美日韩中文在线| 亚洲综合大片69999| 亚洲欧美资源在线| 日韩在线播放av| 亚洲美女在线视频| 亚洲视频国产视频| 国产日韩精品在线播放| 国产啪精品视频网站| 日韩精品中文字幕在线观看| 久久综合亚洲社区| 欧美成人精品在线| 亚洲娇小xxxx欧美娇小| 91超碰caoporn97人人| 亚洲电影在线观看| 亚洲一区中文字幕| 欧美成人免费视频| 国产精品久久久久久超碰| 日韩在线观看成人| 欧美风情在线观看| 亚洲欧美制服丝袜| 欧美激情综合亚洲一二区| 欧美成人小视频| 成人乱人伦精品视频在线观看| 日韩大胆人体377p| 欧美激情欧美激情| 亚洲色图色老头| 日韩电影视频免费| 久久久国产成人精品| www.亚洲成人| 国产极品精品在线观看| 色哟哟网站入口亚洲精品| 国产日韩欧美在线| 最新69国产成人精品视频免费| 欧美激情精品久久久| 欧美成人免费全部| 中日韩美女免费视频网站在线观看| 日韩av在线精品| 精品国产1区2区| 欧美夫妻性生活视频| 日韩电影在线观看中文字幕| 日韩av免费在线播放| 91中文在线观看| 精品视频在线播放免| 久久人人97超碰精品888| 国产999精品| 亚洲激情第一页| 中文字幕在线看视频国产欧美| 国产精品一区二区久久久| 伊人久久五月天| 91亚洲va在线va天堂va国| 日韩电影中文字幕av| 亚洲精品欧美日韩| 欧美理论片在线观看| 欧美日韩午夜视频在线观看| 国内精品美女av在线播放| 亚洲电影免费观看高清完整版在线| 国产精品人成电影在线观看| 日韩电影大片中文字幕| 欧美成人免费网| 成人免费在线视频网站| 伊人伊成久久人综合网站| 精品久久久久久中文字幕大豆网|