视差特效(仿微信朋友圈)

模仿微信朋友圈界面,主要实现: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(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));
    }
}




你可能感兴趣的:(Android)