Android简易实战教程--第五十四话《视差特效》

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32059827/article/details/78351456

查看更多进阶知识,可以关注我的公众号,微信搜索:Android小菜

这个简易实战教程系列专栏发现已经大半年没更新了啊,赶紧添点东西。

本文实现一个视差特效效果,动态效果如下:



代码十分简单,自定义View代码添加在下面:

public class ParallaxListView extends ListView {

    private static final String TAG = "ParallaxListView";
    private ImageView ParallaxHeaderImageView;//头部视视差效果的图片
    private int mOldHeight;//此时ImageView的高度
    private int mRealHeight;//图片的真正高度

    public ParallaxListView(Context context) {
        this(context,null);
    }

    public ParallaxListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setHeadImageView(ImageView ivHeadImageView) {
        this.ParallaxHeaderImageView = ivHeadImageView;
        mOldHeight = ParallaxHeaderImageView.getMeasuredHeight();
//        int height = ParallaxHeaderImageView.getHeight();
//        Log.e(TAG,mOldHeight+"----"+height);//两个值一样
        mRealHeight = ParallaxHeaderImageView.getDrawable().getIntrinsicHeight();
    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        System.out.println("deltaY: "+ deltaY + " scrollY: " + scrollY
                + " scrollRangeY: " + scrollRangeY
                + " maxOverScrollY: " + maxOverScrollY
                + " isTouchEvent: " + isTouchEvent);
        // deltaY : 竖直方向的瞬时偏移量, 顶部下拉- , 底部上拉+, 速度越快绝对值越大.
        // scrollY : 竖直方向ListView滚动的距离, 顶部- , 底部+
        // scrollRangeY : 竖直方向滚动距离
        // maxOverScrollY : 最大的超出滚动范围
        // isTouchEvent : 是否是手指触摸到顶部/底部. true触摸, false惯性
        if(deltaY <0 && isTouchEvent){
            int newHeight = ParallaxHeaderImageView.getHeight() + Math.abs(deltaY)/2;//减少拖动的距离
            setCurrentImageViewHeight(newHeight);
        }
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    }

    private void setCurrentImageViewHeight(int newHeight) {
        ParallaxHeaderImageView.getLayoutParams().height = newHeight;
        ParallaxHeaderImageView.requestLayout();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
                case MotionEvent.ACTION_UP:
                    int startHeight = ParallaxHeaderImageView.getHeight();
                    ValueAnimator animator = ValueAnimator.ofInt(startHeight, mOldHeight);
                    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            // 500 毫秒内多次调用
                            // 0.0 -> 1.0
                            System.out.println("animation: " + animation.getAnimatedFraction());

                            // 模拟出来的中间值
                            Object animatedValue = animation.getAnimatedValue();
                            System.out.println("animation: " + animatedValue);

                            // 让新的高度生效
                            setCurrentImageViewHeight((Integer)animatedValue);
                        }
                    });
                    animator.setInterpolator(new OvershootInterpolator(4));
                    animator.setDuration(500);
                    animator.start();
                    break;

                default:
                    break;
         }
        return super.onTouchEvent(ev);
    }
}
代码解析:

通过setHeadImageView方法传入一头布局,然后拿到当前ImageView的高度和图片的真实高度(注意:真实高度和控件的高度是不一样的)。然后重写overScrollBy方法,这个方法会对ListView滑动到边缘时候做一些判断,具体的参数含义如下:

        // deltaY : 竖直方向的瞬时偏移量, 顶部下拉- , 底部上拉+, 速度越快绝对值越大.
        // scrollY : 竖直方向ListView滚动的距离, 顶部- , 底部+
        // scrollRangeY : 竖直方向滚动距离
        // maxOverScrollY : 最大的超出滚动范围
        // isTouchEvent : 是否是手指触摸到顶部/底部. true触摸, false惯性

因为是在头部下拉,所以判断deltaY<0 且是触摸状态(非惯性滑动状态),才去处理事件。得到最新的ImageView的高度【因为手指下滑有一个滑动了多少的绝对值,类加后就是当前ImagView的当前高度】,并把最新的高度设置给ImageView。这样就实现了ImagView的放大效果了。

那么松开手要做的逻辑,肯定是放在onTouchEvent的UP事件里面了,主要做的工作就是,记录松开手时候ImagView的高度,使用属性动画把ImagView控件不断地缩小,这里还使用了插值器,让ImagView恢复原来高度值后有一个弹性效果。这样整个简易的自定义View就完成了。

然后看看客户端如何使用这个控件:

MainActivity的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.itydl.a04.MainActivity">

    <com.itydl.a04.view.ParallaxListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>
然后在onCreat方法中:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mListView = (ParallaxListView) findViewById(R.id.lv);
    mHeadView = View.inflate(this, R.layout.layout_head,null);
    mIvHead = (ImageView) mHeadView.findViewById(R.id.iv);
    mListView.addHeaderView(mHeadView);

    mIvHead.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            mListView.setHeadImageView(mIvHead);
            mIvHead.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    });

    mListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,Strings.NAMES));
}
拿到ListView的实例,给其设置数据进去。在试图绘制完毕的时候,把拿到的一张图片设置到刚才的自定义View中去了。然后运行后,就是开头的效果了。


最后可能会存在一点点的bug:

    if(deltaY <0 && isTouchEvent){
        int newHeight = ParallaxHeaderImageView.getHeight() + Math.abs(deltaY)/2;//减少拖动的距离
        if(newHeight <= mRealHeight){
            //只有在滑动的距离小于图片的真正高度的时候才去重新设置,超过了图片的高度就不去设置了。
            setCurrentImageViewHeight(newHeight);
        }
    }
超过图片的实际高度不应该再往下下拉设置高度给ImagView了吧。

如果觉得有点作用记得加个关注哈,要想看更高级的文章,欢迎关注微信公众号“Android小菜”


代码下载链接地址:源码下载


没有更多推荐了,返回首页