看了Hankkin这位老哥写的文章 ,我抱着试一试的态度copy下,原文链接http://blog.csdn.net/lyhhj/article/details/51120539。写完后下载了最新版的百度外卖app,发现其刷新已经做了改动,但是万变不离其宗,精髓依旧,然后新番出炉。
·points:
1.动画
·· 帧动画:一张张图片不断的切换,形成动画效果。在drawable文件夹下放入图片,并创建animation-list标签文件
·· 补间动画:通过在两个关键帧之间补充渐变的动画效果。
JAVA代码中设置:AlphaAnimation,TranslateAnimation,ScaleAnimation,RotateAnimation,AnimationSet;
XML文件中设置:在res目录下新建anim文件夹,在anim文件夹下创建对应的动画文件alpha,rotate,scale,translate,set。
ps:补间动画只是改变了View对象绘制的位置,而没有改变View对象本身
·· 属性动画:既有动画效果又使得View本身得到了真正改变。
JAVA代码中设置: ObjectAnimator-ofFloat(),ofInt(),ofObject(),ofArgb(),ofPropertyValuesHolder();
XML文件中设置:和补间动画基本一致,Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator);
2.onTouchEvent事件的处理
··MotionEvent.ACTION_DOWN:
记录按下点的Y坐标。
··MotionEvent.ACTION_MOVE:
通过.setPadding()更新下拉刷新视图的显示高度,并通过offsetY来记录其状态。
··MotionEvent.ACTION_UP:
对两种状态进行处理:没有达到刷新高度的回滚至隐藏,达到或超过其刷新高度的回滚至刷新的高度,并回调接口更新状态。
·talk is less:
1.layout_header_view布局(有坑也要小心)
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="350dp"
android:background="@drawable/pull_default_background">
<ImageView
android:id="@+id/iv_castle_left"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:padding="30dp"
android:src="@drawable/pull_back_castle"/>
<ImageView
android:id="@+id/iv_castle_right"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:padding="30dp"
android:src="@drawable/pull_back_castle"/>
<ImageView
android:id="@+id/iv_cloud_left"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:padding="30dp"
android:src="@drawable/pull_back_cloud"/>
<ImageView
android:id="@+id/iv_cloud_right"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_alignParentBottom="true"
android:padding="30dp"
android:src="@drawable/pull_back_cloud"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginTop="50dp">
<ImageView
android:id="@+id/iv_island"
android:layout_width="120dp"
android:layout_height="100dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:src="@drawable/pull_main_castle"/>
<ImageView
android:id="@+id/iv_postman"
android:layout_width="100dp"
android:layout_height="70dp"
android:layout_alignLeft="@id/iv_island"
android:layout_marginTop="10dp"
android:background="@drawable/loading_bear"/>
<ImageView
android:id="@+id/iv_sun"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_toLeftOf="@id/iv_island"
android:src="@drawable/pull_back_sun"/>
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
2.TakeRefreshLayout
public class TakeRefreshLayout extends ListView implements AbsListView.OnScrollListener {
private static final int PULL_FINISH = 0; //刷新完毕
private static final int PULL_TO_DOWN = 1; //下拉状态
private static final int PULL_TO_RELEASE = 2; //释放状态
private static final int PULL_REFRESHING = 3; //正在刷新
private Context mContext;
private RelativeLayout mHeadView; //刷新的头布局
private int mHeadViewH; //头布局的高度
private int mFirstVisibleItem; //第一项可见的item索引
private ImageView ivDriver, ivCloudLeft, ivCloudRight, ivCastleLeft, ivCastleRight, ivSun;
private Animation sunAnim, cloudLAnim, cloudRAnim, castleLAnim, castleRAnim;
private AnimationDrawable driverAnim;
private float startY; //开始时的Y坐标
private float offsetY; //Y轴偏移量
private int state; //状态
private boolean isRefreshable; //是否可刷新
private OnTakeRefreshListener mTakeRefreshListener;
public TakeRefreshLayout(Context context) {
super(context);
init(context);
}
public TakeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TakeRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 回调接口
*/
public interface OnTakeRefreshListener {
void onRefresh();
}
/**
* 接口回调提供的方法
*
* @param onTakeRefreshListener
*/
public void setOnTakeRefreshListener(OnTakeRefreshListener onTakeRefreshListener) {
mTakeRefreshListener = onTakeRefreshListener;
isRefreshable = true;
}
/**
* 是否正在刷新
*
* @return
*/
public boolean isRefreshing() {
return state == PULL_REFRESHING;
}
/**
* 刷新完成
*/
public void finishRefresh() {
state = PULL_FINISH;
//刷新完毕
mHeadView.setPadding(0, -mHeadViewH, 0, 0);
stopAnim();
}
/**
* 初始化
*
* @param context
*/
private void init(Context context) {
mContext = context;
//关闭view的OverScroll
setOverScrollMode(OVER_SCROLL_NEVER);
setOnScrollListener(this);
//加载头布局
mHeadView = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.layout_head_view, this, false);
//测量头布局
measureView(mHeadView);
//测量头布局
addHeaderView(mHeadView);
mHeadViewH = mHeadView.getMeasuredHeight();
mHeadView.setPadding(0, -mHeadViewH, 0, 0);
//获取头布局控件
ivDriver = (ImageView) mHeadView.findViewById(R.id.iv_postman);
ivCastleLeft = (ImageView) mHeadView.findViewById(R.id.iv_castle_left);
ivCastleRight = (ImageView) mHeadView.findViewById(R.id.iv_castle_right);
ivCloudLeft = (ImageView) mHeadView.findViewById(R.id.iv_cloud_left);
ivCloudRight = (ImageView) mHeadView.findViewById(R.id.iv_cloud_right);
ivSun = (ImageView) mHeadView.findViewById(R.id.iv_sun);
//获取动画
driverAnim = (AnimationDrawable) ivDriver.getBackground();
sunAnim = AnimationUtils.loadAnimation(context, R.anim.rotate_sun);
cloudLAnim = AnimationUtils.loadAnimation(context, R.anim.translate_cloud_left);
cloudRAnim = AnimationUtils.loadAnimation(context, R.anim.translate_cloud_right);
castleLAnim = AnimationUtils.loadAnimation(context, R.anim.translate_castle_left);
castleRAnim = AnimationUtils.loadAnimation(context, R.anim.translate_castle_right);
LinearInterpolator interpolator = new LinearInterpolator();
sunAnim.setInterpolator(interpolator);
cloudLAnim.setInterpolator(interpolator);
cloudRAnim.setInterpolator(interpolator);
castleLAnim.setInterpolator(interpolator);
castleRAnim.setInterpolator(interpolator);
state = PULL_FINISH;
isRefreshable = false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isRefreshable) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//在listview顶部
if (mFirstVisibleItem == 0) {
startY = ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
if (state != PULL_REFRESHING) {
float tempY = ev.getY();
offsetY = tempY - startY;
//放开刷新的状态
if (state == PULL_TO_RELEASE) {
setSelection(0);
if (offsetY / 3 < DensityUtil.dip2px(mContext, 150)) {
state = PULL_TO_DOWN;
} else if (offsetY <= 0) {
state = PULL_FINISH;
}
}
//下拉刷新状态
if (state == PULL_TO_DOWN) {
setSelection(0);
if (offsetY / 3 > DensityUtil.dip2px(mContext, 150)) {
state = PULL_TO_RELEASE;
} else if (offsetY <= 0) {
state = PULL_FINISH;
}
}
//finish状态
if (state == PULL_FINISH) {
if (offsetY >= 0) {
state = PULL_TO_DOWN;
startAnim();
}
}
//需要更新视图的两种状态
if (state == PULL_TO_DOWN) {
mHeadView.setPadding(0, (int) (-mHeadViewH + offsetY / 3), 0, 0);
}
if (state == PULL_TO_RELEASE) {
mHeadView.setPadding(0, (int) (-mHeadViewH + offsetY / 3), 0, 0);
}
}
break;
case MotionEvent.ACTION_UP:
//下拉没有超过规定的高度,不执行刷新,抬起手指更新state,停止动画
if (state == PULL_TO_DOWN) {
smoothScrollBy((int) offsetY / 3, 500);
stopAnim();
state = PULL_FINISH;
}
//当下拉高度超过150dp时执行平滑滚动到150dp并刷新
if (state == PULL_TO_RELEASE) {
if (offsetY / 3 > DensityUtil.dip2px(mContext, 150)) {
smoothScrollBy((int) (offsetY / 3 - DensityUtil.dip2px(mContext, 150)), 500);
}
state = PULL_REFRESHING;
//回调onRefresh方法
mTakeRefreshListener.onRefresh();
}
break;
default:
break;
}
}
return super.onTouchEvent(ev);
}
/**
* 开始动画
*/
private void startAnim() {
ivSun.startAnimation(sunAnim);
ivCloudLeft.startAnimation(cloudLAnim);
ivCloudRight.startAnimation(cloudRAnim);
ivCastleLeft.startAnimation(castleLAnim);
ivCastleRight.startAnimation(castleRAnim);
driverAnim.start();
}
/**
* 关闭动画
*/
private void stopAnim() {
ivSun.clearAnimation();
ivCloudLeft.clearAnimation();
ivCloudRight.clearAnimation();
ivCastleLeft.clearAnimation();
ivCastleRight.clearAnimation();
driverAnim.stop();
}
/**
* 测量View
*
* @param child
*/
private void measureView(View child) {
ViewGroup.LayoutParams params = child.getLayoutParams();
if (params == null) {
params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, child.getPaddingLeft(), params.width);
int pHeight = params.height;
int childHeightSpec;
if (pHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mFirstVisibleItem = firstVisibleItem;
}
}
3.MainActivity
public class MainActivity extends AppCompatActivity implements TakeRefreshLayout.OnTakeRefreshListener {
private final static int REFRESH_COMPLETE = 0;
private TakeRefreshLayout mRefreshLayout;
private List<String> mDatas;
private ArrayAdapter<String> mAdapter;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case REFRESH_COMPLETE:
if (mRefreshLayout.isRefreshing()) {
mRefreshLayout.finishRefresh();
}
Toast.makeText(MainActivity.this, "刷新成功", Toast.LENGTH_SHORT).show();
mAdapter.notifyDataSetChanged();
mRefreshLayout.setSelection(0);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRefreshLayout = (TakeRefreshLayout) findViewById(R.id.take_refresh_view);
String[] data = new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i",
"j", "k", "l", "m", "n", "o", "p", "q", "r", "s"};
mDatas = new ArrayList<>(Arrays.asList(data));
mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDatas);
mRefreshLayout.setAdapter(mAdapter);
mRefreshLayout.setOnTakeRefreshListener(this);
}
@Override
public void onRefresh() {
mDatas.add(0, "new data");
mHandler.sendEmptyMessageDelayed(REFRESH_COMPLETE, 4000);
}
}
·总结
敲一次代码就应该有所收货,每一次进步都应该玩的不亦乐乎。
就总结一下这个下拉刷新版本的不足吧:
1.没有多点触控
2.仅能用在ListView,不能无缝替换RecyclerView
3....