仿微信朋友圈下拉刷新(头部放大动画效果)
现在比较流行的Material Design控件已经可以实现更加炫酷的效果,以下我根据微信朋友圈(QQ空间也如此)原始的写法,基本可以达到预期效果。
先看下效果图
效果展示
布局文件由一张美女图和一个圆形icon组成,代码如下:
<?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="120dp">
<ImageView
android:id="@+id/layout_header_image"
android:layout_width="match_parent"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:src="@drawable/image"/>
<ImageView
android:id="@+id/icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/icon"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_marginLeft="20dp"
android:layout_marginBottom="20dp"
/>
</RelativeLayout>
看完Demo可以想到下面必定是一个list列表,上面的美女图片我以headerView的形式加在列表之上,而向下滑动的过程则是调用了overScrollBy方法中两个重要参数 dx,dy。于是,我重写了一个listview去实现该方法:
public class PushListView extends ListView {
private int mImageViewHeight = 0;
private boolean refresh = false;
private ImageView mImageView,image_icon;
public PushListView(Context context, AttributeSet attrs) {
super(context, attrs);
mImageViewHeight = context.getResources().getDimensionPixelSize(R.dimen.header_height);
}
public void setZoomImageView(ImageView iv,ImageView icon) {
mImageView = iv;
image_icon = icon;
}
public void isRefresh( boolean a){
if(a == true){
image_icon.clearAnimation();
image_icon.setVisibility(View.GONE);
}
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
/*
dx,dy增量
deltaY:
-:下拉过度
+:上拉过度
*/
if (deltaY < 0) {
mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY;
image_icon.setRotation(image_icon.getRotation() - deltaY);
mImageView.requestLayout();
} else {
//缩小
if (mImageView.getHeight()>mImageViewHeight) {
mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY;
mImageView.requestLayout();
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
//让头部在上滑时缩小,监听
View header = (View) mImageView.getParent();
//header.getTop()<0头部滑出的距离
if (header.getTop() < 0 && mImageView.getHeight() > mImageViewHeight) {
mImageView.getLayoutParams().height = mImageView.getHeight() + header.getTop();
header.layout(header.getLeft(), 0, header.getRight(), header.getHeight());
mImageView.requestLayout();
}
super.onScrollChanged(l, t, oldl, oldt);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if(action == MotionEvent.ACTION_UP){//松开手弹回初始位置
ResetAnimation animation = new ResetAnimation(mImageView,mImageViewHeight);
animation.setDuration(300);
mImageView.startAnimation(animation);
image_icon.startAnimation(rotationAnimation());
}
return super.onTouchEvent(ev);
}
public class ResetAnimation extends Animation{
private ImageView mTv;
private int targetHeight;
private int originalHeight;
private int extraHeight;
public ResetAnimation(ImageView iv, int targetHeight) {
this.mTv = iv;
this.targetHeight = targetHeight;//最终恢复的高度
this.originalHeight = mTv.getHeight();//现在的高度
this.extraHeight = originalHeight - targetHeight;//高度差
}
//下拉过度,弹回去动画
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
//interpolatedTime(0.0 to 1.0) 执行的百分比
mImageView.getLayoutParams().height = (int) (originalHeight - extraHeight * interpolatedTime);
mImageView.requestLayout();
super.applyTransformation(interpolatedTime, t);
}
}
//刷新icon动画集(旋转刷新)
public Animation rotationAnimation(){
Animation mRotateAnimation = new RotateAnimation(0.0f, 720.0f, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
mRotateAnimation.setFillAfter(true);
mRotateAnimation.setInterpolator(new LinearInterpolator());
mRotateAnimation.setDuration(1000);
mRotateAnimation.setRepeatCount(Animation.INFINITE);
mRotateAnimation.setRepeatMode(Animation.RESTART);
return mRotateAnimation;
}
}
最后将重写的listview应用到Mainactivity中,布局为一个listview,这里就不贴出了。我在handler中模拟了一次下拉刷新的过程,具体操作还要看业务逻辑去具体实现:
public class MainActivity extends Activity {
private PushListView listView;
ImageView iv,icon;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1){
listView.isRefresh(true);//模拟下拉刷新
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View header = View.inflate(this,R.layout.item_header,null);
iv = (ImageView) header.findViewById(R.id.layout_header_image);
icon = (ImageView) header.findViewById(R.id.icon);
listView = (PushListView) findViewById(R.id.listview);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,
new String[] {
"周一上班",
"周二继续上班",
"周三打豆豆",
"周四打篮球",
"周五放假"
});
listView.addHeaderView(header);
listView.setZoomImageView(iv,icon);
listView.setAdapter(adapter);
mHandler.sendEmptyMessageDelayed(1,6000);
}
}
以上为整个效果的全部过程,代码比较简洁,主要需要理解的是listview的onscrollby方法,对于头部高度做实时控制,开发过程中也是遇到了不少问题,但是不要急躁,一步一个脚印,总能实现。