




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
【移動應用開發(fā)技術(shù)】App列表之下拉刷新
Android的ListView是應用最廣的一個組件,功能強大,擴展性靈活(不局限于ListView本身一個類),前面的文章有介紹分組,拖拽,3D立體,游標,圓角,而今天我們要介紹的是另外一個擴展ListView:下拉刷新的ListView。
下拉刷新界面最初流行于iphone應用界面,如圖:
然后在Android中也逐漸被應用,比如微博,資訊類。
所以,今天要實現(xiàn)的結(jié)果應該也是類似的,先貼出最終完成效果,如下圖,接下來我們一步一步實現(xiàn)。1.流程分析
下拉刷新最主要的流程是:
(1).下拉,顯示提示頭部界面(HeaderView),這個過程提示用戶"下拉刷新"
(2).下拉到一定程度,超出了刷新最基本的下拉界限,我們認為達到了刷新的條件,提示用戶可以"松手刷新"了,效果上允許用戶繼續(xù)下拉
(3).用戶松手,可能用戶下拉遠遠不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然后提示用戶"正在加載"。
(4).加載完成后,隱藏提示頭部界面。
示意圖如下:2.實現(xiàn)分析
當前我們要實現(xiàn)上述流程,是基于ListView的,所以對應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。判斷當前的下拉操作狀態(tài),ListView提供了publicvoidonScrollStateChanged(AbsListViewview,intscrollState){}。
c.下拉的過程中,我們可能還需要下拉到多少的邊界值處理,重寫onTouchEvent(MotionEventev){}方法,可依據(jù)ACTION_DOWN,ACTION_MOVE,ACTION_UP實現(xiàn)更精細的判斷。
(2).下拉到一定程度,超出了刷新最基本的下拉界限,我們認為達到了刷新的條件,提示用戶可以"松手刷新"了,效果上允許用戶繼續(xù)下拉
a.達到下拉刷新界限,一般指達到header的高度的,所以有兩步,第一,獲取header的高度,第二,當header.getBottom()>=header的高度時,我們認為就達到了刷新界限值
b.繼續(xù)允許用戶下拉,當header完全下拉后,默認無法繼續(xù)下拉,但是可以增加header的PaddingTop實現(xiàn)這種效果
(3).用戶松手,可能用戶下拉遠遠不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然后提示用戶"正在加載"。
a.松手后反彈,這個不能一下***回去,看上去太突然,需要一步一步柔性的彈回去,像彈簧一樣,我們可以new一個Thread循環(huán)計算減少PaddingTop,直到PaddingTop為0,反彈結(jié)束。
b.正在加載,在子線程里處理后臺任務
(4).加載完成后,隱藏提示頭部界面。
a.后臺任務完成后,我們需要隱藏header,setSelection(1)即實現(xiàn)了從第2項開始顯示,間接隱藏了header。上面我們分析了實現(xiàn)過程的輪廓,接下來,通過細節(jié)說明和代碼具體實現(xiàn)。3.初始化
一切狀態(tài)顯示都是用HeaderView顯示的,所以我們需要一個HeaderView的layout,使用addHeaderView方法添加到ListView中。
同時,默認狀態(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);
}
默認就顯示完成了。4.HeaderView的默認高度測量
因為下拉到HeaderView全部顯示出來,就由提示"下拉刷新"變?yōu)?松手刷新",全部顯示的出來的測量標準就是header.getBottom()>=header的高度。
所以,首先我們需要測量HeaderView的默認高度。
//因為是在構(gòu)造函數(shù)里測量高度,應該先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.下拉效果的特殊處理
所謂的特殊處理,當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))
{
//下拉達到界限,進入松手刷新狀態(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(這個值取一個小值,合適即可)。
松手后,當然是在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é)束后,恢復原始默認狀態(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切入底部"更多"過程,這里我就不詳細說明了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)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 江西省上饒市2024-2025學年高一上學期1月期末英語試題【含答案】
- 江蘇省常州市2024-2025學年高三(上)期末生物試卷(含解析)
- 青貯池施工方案
- 排澇水系改造施工方案
- 生物觀察池施工方案
- co2加氫制甲醇總反應
- 4年級數(shù)學手抄報內(nèi)容
- 地平關(guān)環(huán)機理
- 青海墻面防水施工方案
- 2025年廣西農(nóng)業(yè)職業(yè)技術(shù)大學單招職業(yè)技能測試題庫匯編
- 2025口腔科年度工作計劃
- 商業(yè)辦公樓網(wǎng)絡(luò)改造施工方案
- 2024年中國主題公園競爭力評價報告-中國主題公園研究院
- 2023年湖北省生態(tài)環(huán)保有限公司招聘考試真題
- 化療藥物外滲的預防及處理-2
- DB35T 1933-2020 熔融沉積3D打印品幾何精度評價規(guī)范
- 《大氣污染物控制工程》-揮發(fā)性有機物污染控制
- 2024-2030年冷凍面團產(chǎn)品行業(yè)市場現(xiàn)狀供需分析及投資評估規(guī)劃分析研究報告
- LED基礎(chǔ)知識題庫100道及答案(完整版)
- 新版高中物理必做實驗目錄及器材-(電子版)
- 涉密項目保密工作方案
評論
0/150
提交評論