【移动应用开发技术】App列表之下拉刷新_第1页
【移动应用开发技术】App列表之下拉刷新_第2页
【移动应用开发技术】App列表之下拉刷新_第3页
【移动应用开发技术】App列表之下拉刷新_第4页
【移动应用开发技术】App列表之下拉刷新_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】App列表之下拉刷新

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提供了publicvoidonScrollStateChanged(AbsListViewview,intscrollState){}。

c.下拉的过程中,我们可能还需要下拉到多少的边界值处理,重写onTouchEvent(MotionEventev){}方法,可依据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="/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>

代码中在构造函数中添加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

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论