下拉刷新控件很常见,Android官方也推出了自己的下拉组件SwipeRefreshLayout。之前项目中有用到下拉刷新控件,要求根据手势播放动画效果,由于没有现成的,就参考第三方下拉控件,改造成自己项目所需的。改造过程还算比较顺利,现分享出来,仅供参考。
效果图

布局pull_to_refresh_header.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#dadada"> <ImageView android:id="@+id/cart" android:layout_width="39dp" android:layout_height="27dp" android:layout_marginTop="50dp" android:layout_alignParentLeft="true" android:visibility="visible" android:src="@drawable/loading_car"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:drawableBottom="@drawable/loading_wenan"/> <TextView android:id="@+id/loading_middle" android:layout_width="120dp" android:layout_height="55dp" android:visibility="invisible" android:background="@drawable/refresh_anim"/> <TextView android:id="@+id/refresh_text" android:layout_width="wrap_content" android:layout_height="20dp" android:gravity="center" android:text="正在刷新..." android:textSize="10sp" android:textColor="#999"/> </LinearLayout> </RelativeLayout>
package com.miloisbadboy.view;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.miloisbadboy.R;
public class PullToRefreshView extends LinearLayout {
private static final String TAG = "PullToRefreshView";
// refresh states
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4;
// pull state
private static final int PULL_UP_STATE = 0;
private static final int PULL_DOWN_STATE = 1;
private boolean enablePullTorefresh = true;
private boolean enablePullLoadMoreDataStatus = true;
/**
* last y
*/
private int mLastMotionY;
/**
* lock
*/
private boolean mLock;
/**
* header view
*/
private View mHeaderView;
/**
* list or grid
*/
private AdapterView<?> mAdapterView;
/**
* scrollview
*/
private ScrollView mScrollView;
/**
* header view height
*/
private int mHeaderViewHeight;
/**
* layout inflater
*/
private LayoutInflater mInflater;
/**
* header view current state
*/
private int mHeaderState;
/**
* footer view current state
*/
private int mFooterState;
/**
* pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE
*/
private int mPullState;
/**
* header refresh listener
*/
private OnHeaderRefreshListener mOnHeaderRefreshListener;
/**
* 正在刷新动画
*/
protected AnimationDrawable animationMiddle;
/**
* 刷新提示语
*/
protected TextView mLoddingTextView;
/**
* 敞篷车
*/
protected ImageView mCart;
/**
* 刷新提示
*/
protected TextView refreshText;
/**
* 记录第一次按下的y坐标
*/
protected int mYDown;
/**
* y方向移动的距离
*/
protected int moveDistance;
/**
* 最小的有效滑动距离
*/
protected int mTouchSlop;
/**
* 屏幕宽度
*/
protected int mScreenWidth;
/**
* 屏幕高度
*/
protected int mScreenHeight;
public PullToRefreshView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public PullToRefreshView(Context context) {
this(context,null);
}
public PullToRefreshView(Context context, AttributeSet attributeSet, int defStyle) {
super(context,attributeSet,defStyle);
init(context);
}
private void init(Context context) {
mInflater = LayoutInflater.from(getContext());
setOrientation(VERTICAL);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
// header view 在此添加,保证是第一个添加到linearlayout的最上端
addHeaderView();
}
private void addHeaderView() {
// header view
mHeaderView = mInflater.inflate(R.layout.pull_to_refresh_header, this, false);
mCart = (ImageView) mHeaderView.findViewById(R.id.cart);
mLoddingTextView = (TextView) mHeaderView.findViewById(R.id.loading_middle);
animationMiddle = (AnimationDrawable) mLoddingTextView.getBackground();
refreshText = (TextView) mHeaderView.findViewById(R.id.refresh_text);
// header layout
measureView(mHeaderView);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight);
// 设置topMargin的值为负的header View高度,即将其隐藏在最上方
params.topMargin = -(mHeaderViewHeight);
// mHeaderView.setLayoutParams(params1);
addView(mHeaderView, params);
}
/**
* init AdapterView like ListView,GridView and so on;or init ScrollView
*/
private void initContentAdapterView() {
int count = getChildCount();
if (count < 2) {
throw new IllegalArgumentException("this layout must contain 2 child views,and AdapterView or ScrollView must in the second position!");
}
View view = null;
for (int i = 0; i < count; ++i) {
view = getChildAt(i);
if (view instanceof AdapterView<?>) {
mAdapterView = (AdapterView<?>) view;
}
if (view instanceof ScrollView) {
// finish later
mScrollView = (ScrollView) view;
}
}
if (mAdapterView == null && mScrollView == null) {
throw new IllegalArgumentException("must contain a AdapterView or ScrollView in this layout!");
}
}
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);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// 首先拦截down事件,记录y坐标
mLastMotionY = y;
mYDown = y;
break;
case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
if (isRefreshViewScroll(deltaY)) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return false;
}
/*
* 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return
* false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mLock) {
return true;
}
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// onInterceptTouchEvent已经记录
// mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
moveDistance = (int) event.getRawY() - mYDown;
if(Math.abs(moveDistance) > mTouchSlop) {
if (mPullState == PULL_DOWN_STATE) {
// PullToRefreshView执行下拉
Log.i(TAG, " pull down!parent view move!");
headerPrepareToRefresh(deltaY);
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {
// PullToRefreshView执行上拉
Log.i(TAG, "pull up!parent view move!");
// footerPrepareToRefresh(deltaY);
}
mLastMotionY = y;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int topMargin = getHeaderTopMargin();
if (mPullState == PULL_DOWN_STATE) {
if (mHeaderState == RELEASE_TO_REFRESH) {
// 开始刷新
headerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
} else if (mPullState == PULL_UP_STATE) {
if (Math.abs(topMargin) >= mHeaderViewHeight) {
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
}
break;
}
return super.onTouchEvent(event);
}
/**
* 是否应该到了父View,即PullToRefreshView滑动
* @param deltaY > 0 是向下运动,< 0是向上运动
*/
private boolean isRefreshViewScroll(int deltaY) {
if (mHeaderState == REFRESHING || mFooterState == REFRESHING) {
return false;
}
// 对于ListView和GridView
if (mAdapterView != null) {
// 子view(ListView or GridView)滑动到最顶端
if (deltaY > 0) {
// 判断是否禁用下拉刷新操作
if (!enablePullTorefresh) {
return false;
}
View child = mAdapterView.getChildAt(0);
if (child == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
if (mAdapterView.getFirstVisiblePosition() == 0 && child.getTop() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
}
int top = child.getTop();
int padding = mAdapterView.getPaddingTop();
if (mAdapterView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 11) {// 这里之前用3可以判断,但现在不行,还没找到原因
mPullState = PULL_DOWN_STATE;
return true;
}
} else if (deltaY < 0) {
// 判断是否禁用上拉加载更多操作
if (!enablePullLoadMoreDataStatus) {
return false;
}
View lastChild = mAdapterView.getChildAt(mAdapterView.getChildCount() - 1);
if (lastChild == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
// 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
// 等于父View的高度说明mAdapterView已经滑动到最后
if (lastChild.getBottom() <= getHeight() && mAdapterView.getLastVisiblePosition() == mAdapterView.getCount() - 1) {
mPullState = PULL_UP_STATE;
return true;
}
}
}
// 对于ScrollView
if (mScrollView != null) {
// 子scroll view滑动到最顶端
View child = mScrollView.getChildAt(0);
if (deltaY > 0 && mScrollView.getScrollY() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
} else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) {
mPullState = PULL_UP_STATE;
return true;
}
}
return false;
}
/**
* header 准备刷新,手指移动过程,还没有释放
* @param deltaY,手指滑动的距离
*/
private void headerPrepareToRefresh(int deltaY) {
// 计算车的偏移量
int distance = (int)((moveDistance / (float)mScreenHeight) * mScreenWidth);
if(distance > mScreenWidth/2 - mCart.getMeasuredWidth()) {
distance = mScreenWidth/2 - mCart.getMeasuredWidth();
}
// 设置车的x轴偏移量
mCart.setTranslationX(distance);
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态
if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) {
refreshText.setText(R.string.pull_to_refresh_release_label);
mHeaderState = RELEASE_TO_REFRESH;
} else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放
refreshText.setText(R.string.pull_to_refresh_pull_label);
mHeaderState = PULL_TO_REFRESH;
}
}
/**
* 修改Header view top margin的值
*/
private int changingHeaderViewTopMargin(int deltaY) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
float newTopMargin = params.topMargin + deltaY * 0.7f;
params.topMargin = (int) newTopMargin;
mHeaderView.setLayoutParams(params);
invalidate();
return params.topMargin;
}
/**
* header refreshing
*/
public void headerRefreshing() {
mHeaderState = REFRESHING;
setHeaderTopMargin(0);
mCart.setVisibility(GONE);
mLoddingTextView.setVisibility(VISIBLE);
animationMiddle.start();
refreshText.setText(R.string.pull_to_refresh_refreshing_label);
if (mOnHeaderRefreshListener != null) {
mOnHeaderRefreshListener.onHeaderRefresh(this);
}
}
/**
* 设置header view 的topMargin的值
* @param topMargin 为0时,说明header view 刚好完全显示出来;为-mHeaderViewHeight时,说明完全隐藏了
*/
private void setHeaderTopMargin(int topMargin) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
params.topMargin = topMargin;
mHeaderView.setLayoutParams(params);
invalidate();
}
/**
* header view 完成更新后恢复初始状态
*/
public void onHeaderRefreshComplete() {
mCart.setVisibility(VISIBLE);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mCart,"translationX",mScreenWidth/2,mScreenWidth);
objectAnimator.setDuration(300);
objectAnimator.start();
mLoddingTextView.setVisibility(INVISIBLE);
animationMiddle.stop();
refreshText.setText("");
mHeaderState = PULL_TO_REFRESH;
postDelayed(new Runnable() {
@Override
public void run() {
setHeaderTopMargin(-mHeaderViewHeight);
}
},200);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
initContentAdapterView();
}
/**
* Resets the list to a normal state after a refresh.
*/
public void onHeaderRefreshComplete(CharSequence lastUpdated) {
onHeaderRefreshComplete();
}
/**
* 获取当前header view 的topMargin
*/
private int getHeaderTopMargin() {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
return params.topMargin;
}
/**
* set headerRefreshListener
*/
public void setOnHeaderRefreshListener(OnHeaderRefreshListener headerRefreshListener) {
mOnHeaderRefreshListener = headerRefreshListener;
}
/**
* Interface definition for a callback to be invoked when list/grid header
* view should be refreshed.
*/
public interface OnHeaderRefreshListener {
public void onHeaderRefresh(PullToRefreshView view);
}
public boolean isEnablePullTorefresh() {
return enablePullTorefresh;
}
public void setEnablePullTorefresh(boolean enablePullTorefresh) {
this.enablePullTorefresh = enablePullTorefresh;
}
}作者:風中赏雪