模仿微信朋友圈界面,主要实现:1、能够下拉背景图随着变化,2、松手能够弹回并带着动画效果。
一.自定义一个View并继承ListView;
实现以上效果,要重写ListView的两个方法:
overScrollBy();onTouchEvent()
overScrollBy()方法有9个参数,这里解释几个实现以上效果的主要参数:
//deltaY:竖直方向的瞬时值移量、变化量dx,顶部到头为 - ,底部到头为 + //scrollY:竖直方向的偏移量/变化量 //scrollRangeY:竖直方向滑动的范围 //maxOverScrollY:竖直方向最大滑动范围 //isTouchEvent:是否是手指触摸,如果是手指拉为true,如果是惯性为false
根据deltaY、isTouchEvent的值,可以进行判断是否是在进行手指下拉。如果是,我们就可以把拉动瞬间变化量的绝对值给ListView的Header,从而实现放大的效果,注意在进行放大时,放大的高度不能超过原始图片的大小。
代码如下
@Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { Log.i(TAG, "overScrollBy: deltaY:" + deltaY + " scrollY:" + scrollY + " scrollRangeY:" + scrollRangeY + " maxOverScrollY:" + maxOverScrollY + " isTouchEvent:" + isTouchEvent); //手指拉动并且是下拉 if (isTouchEvent && deltaY < 0) { //把拉动的瞬时变化量的绝对值交给Header,就可以实现放大的效果 if (mImageView.getHeight() <= drawableHeight) { //高度不超出图片的大小时,才让其生效 int newHeight = (int) (mImageView.getHeight()+ Math.abs(deltaY/3.0f)); mImageView.getLayoutParams().height = newHeight; mImageView.requestLayout(); } } return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); } onTouchEvent()方法主要是判断手指抬起,抬起时进行一系列的动画:弹回动画执行方式可以为属性动画、自定义动画
属性动画:创建一个属性动画对象,添加动画更新监听,AnimatorUpdateListener(),实现该接口的onAnimationUpdate方法,然后获取一个百分数,利用估值器获取更新后的图片高度,将新的高度设置为背景图片的高度。然后设置震动,代码如下:
@Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_UP: //执行回弹动画,方式一:属性动画/值动画 //从当前的imageView.getHeight(),执行到原始的高 final int startHeight=mImageView.getHeight(); final int endHeight=originalHeight; valueAnimation(startHeight,endHeight); break; default: break; } return super.onTouchEvent(ev); } private void valueAnimation(final int startHeight, final int endHeight){ ValueAnimator mValueAnimator=ValueAnimator.ofInt(1); mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction=animation.getAnimatedFraction(); //百分数 Log.i(TAG, "onAnimationUpdate: "+fraction); Integer newHeight= evaluate(fraction,startHeight,endHeight); mImageView.getLayoutParams().height=newHeight; mImageView.requestLayout(); } }); mValueAnimator.setInterpolator(new OvershootInterpolator(4)); mValueAnimator.setDuration(500); mValueAnimator.start(); } /** * 估值器 * @param fraction * @param startValue * @param endValue * @return */ public Integer evaluate(float fraction, Integer startValue, Integer endValue){ int startInt=startValue; return (int)(startInt+fraction*(endValue-startInt)); }自定义动画:代码如下: public class ResetAnimation extends Animation { private ImageView mImageView; private int mStartHeight; private int mEndHeight; public ResetAnimation(ImageView imageView, int startHeight, int endHeight) { mImageView=imageView; mStartHeight=startHeight; mEndHeight=endHeight; setInterpolator(new OvershootInterpolator()); setDuration(500); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { //interpolatedTime 0.0f~1.0f Integer newHeight= evaluate(interpolatedTime,mStartHeight,mEndHeight); mImageView.getLayoutParams().height=newHeight; mImageView.requestLayout(); super.applyTransformation(interpolatedTime, t); } public Integer evaluate(float fraction, Integer startValue, Integer endValue){ int startInt=startValue; return (int)(startInt+fraction*(endValue-startInt)); } }然后在onTouchEvent方法中修改: @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_UP: // 方式二:自定义Animation //从当前的imageView.getHeight(),执行到原始的高 final int startHeight=mImageView.getHeight(); final int endHeight=originalHeight; // valueAnimation(startHeight,endHeight); ResetAnimation reset=new ResetAnimation(mImageView,startHeight,endHeight); startAnimation(reset); break; default: break; } return super.onTouchEvent(ev); }注意:在引用自定义的ListView时,要给它添加一个绘制的监听,就是监听布局是否填充完毕,然后再去获取背景图片和所在控件的高,不然获取到的控件高为0。完整代码:
MainActivity
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyListView myListView= (MyListView) findViewById(R.id.lv); //添加Header final View headerView =View.inflate(this,R.layout.view_header,null); final ImageView imageView= (ImageView) headerView.findViewById(R.id.iv); myListView.addHeaderView(headerView); myListView.setOverScrollMode(View.OVER_SCROLL_NEVER); headerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //当布局填充之后,此方法会被调用 myListView.setParallaxImage(imageView); headerView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); //填充数据 myListView.setAdapter(new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1, Cheeses.NAMES)); } }MyListView import android.animation.ValueAnimator; import android.content.Context; import android.support.annotation.IntegerRes; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.animation.OvershootInterpolator; import android.widget.ImageView; import android.widget.ListView; /** * 视差特效ListView * Created by Administrator on 2017/4/13. */ public class MyListView extends ListView { private final String TAG = "SENDI"; private int drawableHeight; private int originalHeight; private ImageView mImageView; public MyListView(Context context) { super(context); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); } public MyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { Log.i(TAG, "overScrollBy: deltaY:" + deltaY + " scrollY:" + scrollY + " scrollRangeY:" + scrollRangeY + " maxOverScrollY:" + maxOverScrollY + " isTouchEvent:" + isTouchEvent); //手指拉动并且是下拉 if (isTouchEvent && deltaY < 0) { //把拉动的瞬时变化量的绝对值交给Header,就可以实现放大的效果 if (mImageView.getHeight() <= drawableHeight) { //高度不超出图片的大小时,才让其生效 int newHeight = (int) (mImageView.getHeight()+ Math.abs(deltaY/3.0f)); mImageView.getLayoutParams().height = newHeight; mImageView.requestLayout(); } } return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); } /** * 设置ImageView图片,拿到引用 */ public void setParallaxImage(ImageView imageView) { mImageView = imageView; originalHeight = imageView.getHeight();//160 // int measureHeight=imageView.getMeasuredHeight(); drawableHeight = imageView.getDrawable().getIntrinsicHeight();//图片的高 Log.i(TAG, "setParallaxImage: height:" + originalHeight + " drawableHeight:" + drawableHeight); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_UP: //执行回弹动画,方式一:属性动画/值动画 // 方式二:自定义Animation //从当前的imageView.getHeight(),执行到原始的高 final int startHeight=mImageView.getHeight(); final int endHeight=originalHeight; // valueAnimation(startHeight,endHeight); ResetAnimation reset=new ResetAnimation(mImageView,startHeight,endHeight); startAnimation(reset); break; default: break; } return super.onTouchEvent(ev); } private void valueAnimation(final int startHeight, final int endHeight){ ValueAnimator mValueAnimator=ValueAnimator.ofInt(1); mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction=animation.getAnimatedFraction(); //百分数 Log.i(TAG, "onAnimationUpdate: "+fraction); Integer newHeight= evaluate(fraction,startHeight,endHeight); mImageView.getLayoutParams().height=newHeight; mImageView.requestLayout(); } }); mValueAnimator.setInterpolator(new OvershootInterpolator(4)); mValueAnimator.setDuration(500); mValueAnimator.start(); } /** * 估值器 * @param fraction * @param startValue * @param endValue * @return */ public Integer evaluate(float fraction, Integer startValue, Integer endValue){ int startInt=startValue; return (int)(startInt+fraction*(endValue-startInt)); } } 自定义动画: import android.view.animation.Animation; import android.view.animation.OvershootInterpolator; import android.view.animation.Transformation; import android.widget.ImageView; /** * Created by Administrator on 2017/4/13. */ public class ResetAnimation extends Animation { private ImageView mImageView; private int mStartHeight; private int mEndHeight; public ResetAnimation(ImageView imageView, int startHeight, int endHeight) { mImageView=imageView; mStartHeight=startHeight; mEndHeight=endHeight; setInterpolator(new OvershootInterpolator()); setDuration(500); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { //interpolatedTime 0.0f~1.0f Integer newHeight= evaluate(interpolatedTime,mStartHeight,mEndHeight); mImageView.getLayoutParams().height=newHeight; mImageView.requestLayout(); super.applyTransformation(interpolatedTime, t); } public Integer evaluate(float fraction, Integer startValue, Integer endValue){ int startInt=startValue; return (int)(startInt+fraction*(endValue-startInt)); } }