【移動應(yīng)用開發(fā)技術(shù)】App列表之下拉刷新_第1頁
【移動應(yīng)用開發(fā)技術(shù)】App列表之下拉刷新_第2頁
【移動應(yīng)用開發(fā)技術(shù)】App列表之下拉刷新_第3頁
【移動應(yīng)用開發(fā)技術(shù)】App列表之下拉刷新_第4頁
【移動應(yīng)用開發(fā)技術(shù)】App列表之下拉刷新_第5頁
已閱讀5頁,還剩23頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

【移動應(yīng)用開發(fā)技術(shù)】App列表之下拉刷新

Android的ListView是應(yīng)用最廣的一個組件,功能強大,擴展性靈活(不局限于ListView本身一個類),前面的文章有介紹分組,拖拽,3D立體,游標(biāo),圓角,而今天我們要介紹的是另外一個擴展ListView:下拉刷新的ListView。

下拉刷新界面最初流行于iphone應(yīng)用界面,如圖:

然后在Android中也逐漸被應(yīng)用,比如微博,資訊類。

所以,今天要實現(xiàn)的結(jié)果應(yīng)該也是類似的,先貼出最終完成效果,如下圖,接下來我們一步一步實現(xiàn)。1.流程分析

下拉刷新最主要的流程是:

(1).下拉,顯示提示頭部界面(HeaderView),這個過程提示用戶"下拉刷新"

(2).下拉到一定程度,超出了刷新最基本的下拉界限,我們認(rèn)為達到了刷新的條件,提示用戶可以"松手刷新"了,效果上允許用戶繼續(xù)下拉

(3).用戶松手,可能用戶下拉遠(yuǎn)遠(yuǎn)不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然后提示用戶"正在加載"。

(4).加載完成后,隱藏提示頭部界面。

示意圖如下:2.實現(xiàn)分析

當(dāng)前我們要實現(xiàn)上述流程,是基于ListView的,所以對應(yīng)ListView本身的功能我們來分析一下實現(xiàn)原理:

(1).下拉,顯示提示頭部界面,這個過程提示用戶"下拉刷新"

a.下拉的操作,首先是監(jiān)聽滾動,ListView提供了onScroll()方法

b.與下拉類似一個動作向下飛滑,所以ListView的scrollState有3種值:SCROLL_STATE_IDLE,

SCROLL_STATE_TOUCH_SCROLL,

SCROLL_STATE_FLING,意思容易理解,而我們要下拉的觸發(fā)條件是SCROLL_STATE_TOUCH_SCROLL。判斷當(dāng)前的下拉操作狀態(tài),ListView提供了publicvoidonScrollStateChanged(AbsListViewview,intscrollState){}。

c.下拉的過程中,我們可能還需要下拉到多少的邊界值處理,重寫onTouchEvent(MotionEventev){}方法,可依據(jù)ACTION_DOWN,ACTION_MOVE,ACTION_UP實現(xiàn)更精細(xì)的判斷。

(2).下拉到一定程度,超出了刷新最基本的下拉界限,我們認(rèn)為達到了刷新的條件,提示用戶可以"松手刷新"了,效果上允許用戶繼續(xù)下拉

a.達到下拉刷新界限,一般指達到header的高度的,所以有兩步,第一,獲取header的高度,第二,當(dāng)header.getBottom()>=header的高度時,我們認(rèn)為就達到了刷新界限值

b.繼續(xù)允許用戶下拉,當(dāng)header完全下拉后,默認(rèn)無法繼續(xù)下拉,但是可以增加header的PaddingTop實現(xiàn)這種效果

(3).用戶松手,可能用戶下拉遠(yuǎn)遠(yuǎn)不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然后提示用戶"正在加載"。

a.松手后反彈,這個不能一下***回去,看上去太突然,需要一步一步柔性的彈回去,像彈簧一樣,我們可以new一個Thread循環(huán)計算減少PaddingTop,直到PaddingTop為0,反彈結(jié)束。

b.正在加載,在子線程里處理后臺任務(wù)

(4).加載完成后,隱藏提示頭部界面。

a.后臺任務(wù)完成后,我們需要隱藏header,setSelection(1)即實現(xiàn)了從第2項開始顯示,間接隱藏了header。上面我們分析了實現(xiàn)過程的輪廓,接下來,通過細(xì)節(jié)說明和代碼具體實現(xiàn)。3.初始化

一切狀態(tài)顯示都是用HeaderView顯示的,所以我們需要一個HeaderView的layout,使用addHeaderView方法添加到ListView中。

同時,默認(rèn)狀態(tài)下,HeaderView是不顯示的,只是在下拉后才顯示,所以我們需要隱藏HeaderView且不影響后續(xù)的下拉顯示,用setSelection(1)。

refresh_list_header.xml布局如下:<?xml

version="1.0"

encoding="utf-8"?>

<LinearLayout

xmlns:android="/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"

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>

代碼中在構(gòu)造函數(shù)中添加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);

}

默認(rèn)就顯示完成了。4.HeaderView的默認(rèn)高度測量

因為下拉到HeaderView全部顯示出來,就由提示"下拉刷新"變?yōu)?松手刷新",全部顯示的出來的測量標(biāo)準(zhǔn)就是header.getBottom()>=header的高度。

所以,首先我們需要測量HeaderView的默認(rèn)高度。

//因為是在構(gòu)造函數(shù)里測量高度,應(yīng)該先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的上述代碼后面加上調(diào)用measureView后,使用getMeasureHeight()方法獲取header的高度:

private

int

mHeaderHeight;

void

init(final

Context

context)

{

...

...

measureView(mHeaderLinearLayout);

mHeaderHeight

=

mHeaderLinearLayout.getMeasuredHeight();

}后面我們就會用到這個mHeaderHeight.5.scrollState監(jiān)聽記錄

scrollState有3種,使用onScrollStateChanged()方法監(jiān)聽記錄。

private

int

mCurrentScrollState;

@Override

public

void

onScrollStateChanged(AbsListView

view,

int

scrollState)

{

mCurrentScrollState

=

scrollState;

}

然后即可使用mCurrentScrollState作為后面判斷的條件了。6.刷新狀態(tài)分析

因為一些地方需要知道我們處在正常狀態(tài)下還是進入下拉刷新狀態(tài)還是松手反彈狀態(tài),比如,

(1).在非正常的狀態(tài)下,我們不小心飛滑了一下(松手的瞬間容易出現(xiàn)這種情況),我們不能setSelection(1)的,否則總是松手后header跳的一下消失掉了。

(2).下拉后要做一個下拉效果的特殊處理,需要用到OVER_PULL_REFRESH(松手刷新狀態(tài)下)

(3).松手反彈后要做一個反彈效果的特殊處理,需要用到OVER_PULL_REFRESH和ENTER_PULL_REFRESH。

private

final

static

int

NONE_PULL_REFRESH

=

0;

//正常狀態(tài)

private

final

static

int

ENTER_PULL_REFRESH

=

1;

//進入下拉刷新狀態(tài)

private

final

static

int

OVER_PULL_REFRESH

=

2;

//進入松手刷新狀態(tài)

private

final

static

int

EXIT_PULL_REFRESH

=

3;

//松手后反彈后加載狀態(tài)

private

int

mPullRefreshState

=

0;

//記錄刷新狀態(tài)

@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))

{

//進入且僅進入下拉刷新狀態(tài)

if

(mPullRefreshState

==

NONE_PULL_REFRESH)

{

mPullRefreshState

=

ENTER_PULL_REFRESH;

}

}

else

if

(mCurrentScrollState

==SCROLL_STATE_TOUCH_SCROLL

&&

firstVisibleItem

==

0

&&

(mHeaderLinearLayout.getBottom()

>=

mHeaderHeight))

{

//下拉達到界限,進入松手刷新狀態(tài)

if

(mPullRefreshState

==

ENTER_PULL_REFRESH

||

mPullRefreshState

==

NONE_PULL_REFRESH)

{

mPullRefreshState

=

OVER_PULL_REFRESH;

//下面是進入松手刷新狀態(tài)需要做的一個顯示改變

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)

{

//飛滑狀態(tài),不能顯示出header,也不能影響正常的飛滑

//只在正常情況下才糾正位置

if

(mPullRefreshState

==

NONE_PULL_REFRESH)

{

setSelection(1);

}

}

}mPullRefreshState將是后面我們處理邊界的重要變量。6.下拉效果的特殊處理

所謂的特殊處理,當(dāng)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);

}

//重復(fù)貼出下面這段需要注意的代碼

@Override

public

void

onScroll(AbsListView

view,

int

firstVisibleItem,

int

visibleItemCount,

int

totalItemCount)

{

...

...

else

if

(mCurrentScrollState

==

SCROLL_STATE_TOUCH_SCROLL

&&

firstVisibleItem

==

0

&&

(mHeaderLinearLayout.getBottom()

>=

mHeaderHeight))

{

//下拉達到界限,進入松手刷新狀態(tài)

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里監(jiān)聽到了進入松手刷新狀態(tài),onTouchEvent就開始在ACTION_MOVE中處理1/3折扣問題。7.反彈效果的特殊處理

松手后我們需要一個柔性的反彈效果,意味著我們彈回去的過程需要分一步步走,我的解決方案是:

在子線程里計算PaddingTop,并減少到原來的3/4,循環(huán)通知主線程,直到PaddingTop小于1(這個值取一個小值,合適即可)。

松手后,當(dāng)然是在onTouchEvent的ACTION_UP條件下處理比較方便:

//因為涉及到handler數(shù)據(jù)處理,為方便我們定義如下常量

private

final

static

int

REFRESH_BACKING

=

0;

//反彈中

private

final

static

int

REFRESH_BACED

=

1;

//達到刷新界限,反彈結(jié)束后

private

final

static

int

REFRESH_RETURN

=

2;

//沒有達到刷新界限,返回

private

final

static

int

REFRESH_DONE

=

3;

//加載數(shù)據(jù)結(jié)束

@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;//加載數(shù)據(jù)完成,結(jié)束返回

}

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);//處理后臺加載數(shù)據(jù)

Message

msg

=

mHandler.obtainMessage();

msg.what

=

REFRESH_DONE;

//通知主線程加載數(shù)據(jù)完成

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:

//刷新結(jié)束后,恢復(fù)原始默認(rèn)狀態(tài)

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;

}

}

};

為了一下子看的明確,我把效果中的數(shù)據(jù)處理代碼也貼出來了。8.切入數(shù)據(jù)加載過程

上面數(shù)據(jù)后臺處理我們用sleep(2000)來處理,實際處理中,作為公共組件,我們也不好把具體代碼直接寫在這里,我們需要一個更靈活的分離:

(1).定義接口

(2).注入接口

//定義接口

public

interface

RefreshListener

{

Object

refreshing();

//加載數(shù)據(jù)

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切入底部"更多"過程,這里我就不詳細(xì)說明了10.源碼

上面的每段代碼都看做是"零部件",需要組合一下。

因為我們上面實現(xiàn)了下拉刷新,還增加了"更多"功能,我們直接命名這個類為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;

//正常狀態(tài)

private

final

static

int

ENTER_PULL_REFRESH

=

1;

//進入下拉刷新狀態(tài)

private

final

static

int

OVER_PULL_REFRESH

=

2;

//進入松手刷新狀態(tài)

private

final

static

int

EXIT_PULL_REFRESH

=

3;

//松手后反彈和加載狀態(tài)

private

int

mPullRefreshState

=

0;

//記錄刷新狀態(tài)

private

final

static

int

REFRESH_BACKING

=

0;

//反彈中

private

final

static

int

REFRESH_BACED

=

1;

//達到刷新界限,反彈結(jié)束后

private

final

static

int

REFRESH_RETURN

=

2;

//沒有達到刷新界限,返回

private

final

static

int

REFRESH_DONE

=

3;

//加載數(shù)據(jù)結(jié)束

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

{

//進入且僅進入下拉刷新狀態(tài)

if

(mPullRefreshState

==

NONE_PULL_REFRESH)

{

mPullRefreshState

=

ENTER_PULL_REFRESH;

}

}

else

if

(mCurrentScrollState

==

SCROLL_STATE_TOUCH_SCROLL

&&

firstVisibleItem

==

0

&&

(mHeaderLinearLayout.getBottom()

>=

mHeaderHeight))

{

//下拉達到界限,進入松手刷新狀態(tài)

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)

{

//飛滑狀態(tài),不能顯示出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

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論