展会信息港展会大全

listView下拉刷新(sina微博Android客户端效果)
来源:互联网   发布日期:2015-11-26 09:35:00   浏览:2329次  

导读:先看下一个点击的效果,我看到其他的分析博客里面没有谈到这一点,在这个代码中,我们一直看到是listview的第二项,而listview的第一项被遮挡了起来,滑动至第一项: 点击头条,头条会变成以下:然后,过一......

先看下一个点击的效果,我看到其他的分析博客里面没有谈到这一点,在这个代码中,我们一直看到是listview的第二项,而listview的第一项被遮挡了起来,滑动至第一项:

\

点击头条,头条会变成以下:

\

然后,过一段时间,刷新完成以后,listview又setSelection(1),增加一条数据,同时,把顶部给遮挡住:

\

这是点击刷新,然后是下拉刷新:

\\

最后结果和点击刷新相同。那现在开始看下代码:

首先看下所用到的控件和变量:

// 状态

private static final int TAP_TO_REFRESH = 1;//点击刷新

private static final int PULL_TO_REFRESH = 2; //拉动刷新

private static final int RELEASE_TO_REFRESH = 3; //释放刷新

private static final int REFRESHING = 4; //正在刷新

// 当前滑动状态

private int mCurrentScrollState;

// 当前刷新状态

private int mRefreshState;

//头视图的高度

private int mRefreshViewHeight;

//头视图 原始的top padding 属性值

private int mRefreshOriginalTopPadding;

private int mLastMotionY;

// 监听对listview的滑动动作

private OnRefreshListener mOnRefreshListener;

//箭头图片

private static int REFRESHICON = R.drawable.goicon;

//listview 滚动监听器

private OnScrollListener mOnScrollListener;

private LayoutInflater mInflater;

private RelativeLayout mRefreshView;

//顶部刷新时出现的控件

private TextView mRefreshViewText;

private ImageView mRefreshViewImage;

private ProgressBar mRefreshViewProgress;

private TextView mRefreshViewLastUpdated;

// 箭头动画效果

//变为向下的箭头

private RotateAnimation mFlipAnimation;

//变为逆向的箭头

private RotateAnimation mReverseFlipAnimation;

//是否反弹

private boolean mBounceHack;

看下点击刷新的代码过程:

在init()方法中初始化各个控件及设置监听:

private void init(Context context) {

// Load all of the animations we need in code rather than through XML

mFlipAnimation = new RotateAnimation(0, -180,RotateAnimation.RELATIVE_TO_SELF,

0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);

mFlipAnimation.setInterpolator(new LinearInterpolator());

mFlipAnimation.setDuration(250);

mFlipAnimation.setFillAfter(true);

mReverseFlipAnimation = new RotateAnimation(-180, 0,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);

mReverseFlipAnimation.setInterpolator(new LinearInterpolator());

mReverseFlipAnimation.setDuration(250);

mReverseFlipAnimation.setFillAfter(true);

mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

mRefreshView = (RelativeLayout) mInflater.inflate(R.layout.pull_to_refresh_header, this, false);

mRefreshViewText =(TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);

mRefreshViewImage =(ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);

mRefreshViewProgress =(ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);

mRefreshViewLastUpdated =(TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);

mRefreshViewImage.setMinimumHeight(50);

mRefreshView.setOnClickListener(new OnClickRefreshListener());

mRefreshOriginalTopPadding = mRefreshView.getPaddingTop();

mRefreshState = TAP_TO_REFRESH;

//为listview头部增加一个view

addHeaderView(mRefreshView);

super.setOnScrollListener(this);

measureView(mRefreshView);

mRefreshViewHeight = mRefreshView.getMeasuredHeight();

}

我们看到,mRefreshView控件既是listview用于刷新的头控件,这里它设置了监听事件:

mRefreshView.setOnClickListener(new OnClickRefreshListener());

我们再来看下监听事件的定义:

private class OnClickRefreshListener implements OnClickListener {

@Override

public void onClick(View v) {

if (mRefreshState != REFRESHING) {

prepareForRefresh();

onRefresh();

}

}

}

调用了preparForRefresh()(准备刷新)和onRefresh()(刷新)两个方法,然后在查看这两个方法的定义:

public void prepareForRefresh() {

resetHeaderPadding(); // 恢复header的边距

mRefreshViewImage.setVisibility(View.GONE);

// We need this hack, otherwise it will keep the previous drawable.

// 注意加上,否则仍然显示之前的图片

mRefreshViewImage.setImageDrawable(null);

mRefreshViewProgress.setVisibility(View.VISIBLE);

// Set refresh view text to the refreshing label

mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);

mRefreshState = REFRESHING;

}

public void onRefresh() {

if (mOnRefreshListener != null) {

mOnRefreshListener.onRefresh();

}

}

其中,后者还是回调方法。

我们看下preparForRefresh()方法中,引用了resetHeadPadding()方法:

/**

* Sets the header padding back to original size.

* 将head的边距重置为初始的数值

*/

private void resetHeaderPadding() {

mRefreshView.setPadding(

mRefreshView.getPaddingLeft(),

mRefreshOriginalTopPadding,

mRefreshView.getPaddingRight(),

mRefreshView.getPaddingBottom());

}

从新设置下header距上下左右的距离。

最重要的方法应该是:onScroll()和onTouchEvent()方法,先看下onTouchEvent()方法:

@Override

public boolean onTouchEvent(MotionEvent event) {

//当前手指的Y值

final int y = (int) event.getY();

mBounceHack = false;

switch (event.getAction()) {

case MotionEvent.ACTION_UP:

//将垂直滚动条设置为可用状态

if (!isVerticalScrollBarEnabled()) {

setVerticalScrollBarEnabled(true);

}

if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {

// 拖动距离达到刷新需要

if ((mRefreshView.getBottom() >= mRefreshViewHeight

|| mRefreshView.getTop() >= 0)

&& mRefreshState == RELEASE_TO_REFRESH) {

// 把状态设置为正在刷新

// Initiate the refresh

mRefreshState = REFRESHING; //将标量设置为,正在刷新

// 准备刷新

prepareForRefresh();

// 刷新

onRefresh();

} else if (mRefreshView.getBottom() < mRefreshViewHeight

|| mRefreshView.getTop() <= 0) {

// Abort refresh and scroll down below the refresh view

//停止刷新,并且滚动到头部刷新视图的下一个视图

resetHeader();

setSelection(1); //定位在第二个列表项

}

}

break;

case MotionEvent.ACTION_DOWN:

// 获得按下y轴位置

mLastMotionY = y;

break;

case MotionEvent.ACTION_MOVE:

//更行头视图的toppadding 属性

applyHeaderPadding(event);

break;

}

return super.onTouchEvent(event);

}

当按下的时候,记录按下y轴的位置,然后在move中调用了applyHeaderPadding()方法,我们再看下这个方法:

// 获得header距离

private void applyHeaderPadding(MotionEvent ev) {

//获取累积的动作数

int pointerCount = ev.getHistorySize();

for (int p = 0; p < pointerCount; p++) {

//如果是释放将要刷新状态

if (mRefreshState == RELEASE_TO_REFRESH) {

if (isVerticalFadingEdgeEnabled()) {

setVerticalScrollBarEnabled(false);

}

//历史累积的高度

int historicalY = (int) ev.getHistoricalY(p);

// Calculate the padding to apply, we divide by 1.7 to

// simulate a more resistant effect during pull.

// 计算申请的边距,除以1.7使得拉动效果更好

int topPadding = (int) (((historicalY - mLastMotionY)- mRefreshViewHeight) / 1.7);

mRefreshView.setPadding(

mRefreshView.getPaddingLeft(),

topPadding,

mRefreshView.getPaddingRight(),

mRefreshView.getPaddingBottom());

}

}

}

通过记录滑动距离,实时变化头部mRefreshView的上下左右的距离。

最后,看下手指松开的ACTION_UP:

case MotionEvent.ACTION_UP:

//将垂直滚动条设置为可用状态

if (!isVerticalScrollBarEnabled()) {

setVerticalScrollBarEnabled(true);

}

if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {

// 拖动距离达到刷新需要

if ((mRefreshView.getBottom() >= mRefreshViewHeight

|| mRefreshView.getTop() >= 0)

&& mRefreshState == RELEASE_TO_REFRESH) {

// 把状态设置为正在刷新

// Initiate the refresh

mRefreshState = REFRESHING; //将标量设置为:正在刷新

// 准备刷新

prepareForRefresh();

// 刷新

onRefresh();

} else if (mRefreshView.getBottom() < mRefreshViewHeight

|| mRefreshView.getTop() <= 0) {

// Abort refresh and scroll down below the refresh view

//停止刷新,并且滚动到头部刷新视图的下一个视图

resetHeader();

setSelection(1); //定位在第二个列表项

}

}

break;

当滑动距离大于一个item的距离时,添加一个item,否则,弹回。

看完onTouchEvent(),然后再看一下onScroll()方法:

@Override

public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {

// When the refresh view is completely visible, change the text to say

// "Release to refresh..." and flip the arrow drawable.

// 在refreshview完全可见时,设置文字为松开刷新,同时翻转箭头

//如果是接触滚动状态,并且不是正在刷新的状态

if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL&& mRefreshState != REFRESHING) {

if (firstVisibleItem == 0) {

//如果显示出来了第一个列表项,显示刷新图片

mRefreshViewImage.setVisibility(View.VISIBLE);

//如果下拉了listiview,则显示上拉刷新动画

if ((mRefreshView.getBottom() >= mRefreshViewHeight + 20|| mRefreshView.getTop() >= 0)

&& mRefreshState != RELEASE_TO_REFRESH) {

mRefreshViewText.setText(R.string.pull_to_refresh_release_label);

mRefreshViewImage.clearAnimation();

mRefreshViewImage.startAnimation(mFlipAnimation);

mRefreshState = RELEASE_TO_REFRESH;

//如果下拉距离不够,则回归原来的状态

} else if (mRefreshView.getBottom() < mRefreshViewHeight + 20

&& mRefreshState != PULL_TO_REFRESH) {

mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);

if (mRefreshState != TAP_TO_REFRESH) {

mRefreshViewImage.clearAnimation();

mRefreshViewImage.startAnimation(mReverseFlipAnimation);

}

mRefreshState = PULL_TO_REFRESH;

}

} else {

mRefreshViewImage.setVisibility(View.GONE);

resetHeader();

}

//如果是滚动状态+ 第一个视图已经显示+ 不是刷新状态

} else if (mCurrentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0

&& mRefreshState != REFRESHING) {

setSelection(1);

mBounceHack = true;

} else if (mBounceHack && mCurrentScrollState == SCROLL_STATE_FLING) {

setSelection(1);

}

if (mOnScrollListener != null) {

mOnScrollListener.onScroll(view, firstVisibleItem,visibleItemCount, totalItemCount);

}

}

该方法是在滑动过程中,各种状况的处理。

onScroll()方法和onTouchEvent()方法的执行过程应该是,先onTouchEvent()的ACTION_DOWN,然后是ACTION_MOVE和onScroll()方法同时进行,最后是onTouchEvent()的ACTION_UP。也可以自己打log看一下。这样在onTouchEvent()处理header,就是mRefreshView的外部的各个熟悉,onScroll()里面处理header(mRefreshView)里面内部的控件变化,从逻辑上来说比较清晰。

在onScroll()中,引用方法resetHeader()方法:

/**

* Resets the header to the original state.

* 重置header为之前的状态

*/

private void resetHeader() {

if (mRefreshState != TAP_TO_REFRESH) {

mRefreshState = TAP_TO_REFRESH;

resetHeaderPadding();

// 将刷新图标换成箭头

// Set refresh view text to the pull label

mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);

// Replace refresh drawable with arrow drawable

// 清除动画

mRefreshViewImage.setImageResource(REFRESHICON);

// Clear the full rotation animation

mRefreshViewImage.clearAnimation();

// Hide progress bar and arrow.

// 隐藏图标和进度条

mRefreshViewImage.setVisibility(View.GONE);

mRefreshViewProgress.setVisibility(View.GONE);

}

}

resetHead就是header(mRefreshView)的内部的具体操作。

当一切都完成以后,就可以调用onRefreshComplete()方法:

/**

* Resets the list to a normal state after a refresh.

* 重置listview为普通的listview

* @param lastUpdated

* Last updated at.

*/

public void onRefreshComplete(CharSequence lastUpdated) {

setLastUpdated(lastUpdated);

onRefreshComplete();

}

/**

* Resets the list to a normal state after a refresh.

* 重置listview为普通的listview,

*/

public void onRefreshComplete() {

resetHeader();

// If refresh view is visible when loading completes, scroll down to

// the next item.

if (mRefreshView.getBottom() > 0) {

invalidateViews(); //重绘视图

setSelection(1);

}

}

重新绘制listivew,然后setSelection(1)。

赞助本站

人工智能实验室

相关热词: 下拉 刷新

AiLab云推荐
推荐内容
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港