版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年度房地产交易代理合同
- 框架开发效率对比
- 2024年度软件许可及技术支持合同:企业级云计算服务
- 福建省莆田市(2024年-2025年小学五年级语文)统编版摸底考试(上学期)试卷及答案
- 化瘀中药的现代技术研究与应用
- 安徽省芜湖市(2024年-2025年小学五年级语文)统编版期中考试(上学期)试卷及答案
- 2024年度标的:建筑施工投标失败后的和解协议
- 2024年度磷矿石采购合同的保险条款
- 紫外光生物安全评估
- 2024版智能交通系统安全防护合同
- 2024年大学生就业创业知识竞赛题库及答案(共350题)
- 基于SICAS模型的区域农产品品牌直播营销策略研究
- 《算法设计与分析基础》(Python语言描述) 课件 第6章分支限界法
- 2024年福建省残疾人岗位精英职业技能竞赛(美甲师)参考试题库(含答案)
- 2024秋期国家开放大学专科《液压与气压传动》一平台在线形考(形考任务+实验报告)试题及答案
- 田径训练论文开题报告
- 个人健康管理平台使用操作教程
- 新版《铁道概论》考试复习试题库(含答案)
- DB11T 2315-2024消防安全标识及管理规范
- 商业银行开展非法集资风险排查活动情况报告
- 有理数的概念 说课课件2024-2025学年人教版数学七年级上册
评论
0/150
提交评论