前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >视差特效、回弹动画、overScrollBy

视差特效、回弹动画、overScrollBy

作者头像
六月的雨
发布2022-01-17 20:18:07
6380
发布2022-01-17 20:18:07
举报

视差特效 * 应用场景: 微信朋友圈, QQ空间, 微博个人展示,向下拉出,松开回弹 * 功能实现: > 1. 重写overScrollBy

> 2. 松手之后执行动画, 类型估值器

activity_main

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    <com.itheima.parallaxdemo.ui.MyListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

view_header

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:scaleType="centerCrop"
        android:src="@drawable/parallax_img" />
</LinearLayout>

MyListView

 /**
 * 视差特效ListView
 * overScrollBy
 * @author poplar
 *
 */
public class MyListView extends ListView {
    private static final String TAG = "TAG";
    private int mOriginalHeight;
    private int drawableHeight;
    private ImageView mImage;
    public MyListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MyListView(Context context) {
        super(context);
    }
     
    /**
     * 设置ImageView图片, 拿到引用
     * @param mImage
     */
    public void setParallaxImage(ImageView mImage) {
        this.mImage = mImage;
        mOriginalHeight = mImage.getHeight(); // 160
        drawableHeight = mImage.getDrawable().getIntrinsicHeight(); // 488,图片的高,而不是显示的高
         
        Log.d(TAG, "height: " + mOriginalHeight + " drawableHeight: " + drawableHeight);
    }
     
    @Override
    protected boolean overScrollBy(int deltaX, int deltaY,
            int scrollX, int scrollY, int scrollRangeX, int scrollRangeY,
            int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        // deltaY : 竖直方向的瞬时偏移量 / 变化量 dx   顶部到头下拉为-, 底部到头上拉为+
        // scrollY : 竖直方向的偏移量 / 变化量
        // scrollRangeY : 竖直方向滑动的范围
        // maxOverScrollY : 竖直方向最大滑动范围
        // isTouchEvent : 是否是手指触摸滑动, true为手指, false为惯性
         
        Log.d(TAG, "deltaY: " +deltaY + " scrollY: " + scrollY + " scrollRangeY: " + scrollRangeY
                + " maxOverScrollY: " + maxOverScrollY + " isTouchEvent: " + isTouchEvent);
         
        // 手指拉动 并且 是下拉
        if(isTouchEvent && deltaY < 0){
            // 把拉动的瞬时变化量的绝对值交给Header, 就可以实现放大效果
            if(mImage.getHeight() <= drawableHeight){
                int newHeight = (int) (mImage.getHeight() + Math.abs(deltaY / 3.0f));
             
                // 高度不超出图片最大高度时,才让其生效
                mImage.getLayoutParams().height = newHeight;
                mImage.requestLayout();
            }
        }
         
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
                scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    }
     
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
         
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                // 执行回弹动画, 方式一: 属性动画\值动画
                // 从当前高度mImage.getHeight(), 执行动画到原始高度mOriginalHeight
                final int startHeight = mImage.getHeight();
                final int endHeight = mOriginalHeight;
                 
//              valueAnimator(startHeight, endHeight);
                // 执行回弹动画, 方式二: 自定义Animation
                ResetAnimation animation = new ResetAnimation(mImage, startHeight, endHeight);
                startAnimation(animation);
                 
                break;
        }
        return super.onTouchEvent(ev);
    }
    private void valueAnimator(final int startHeight, final int endHeight) {
        ValueAnimator mValueAnim = ValueAnimator.ofInt(1);//不起作用,写几都行
        mValueAnim.addUpdateListener(new AnimatorUpdateListener() {
             
            @Override
            public void onAnimationUpdate(ValueAnimator mAnim) {
                float fraction = mAnim.getAnimatedFraction();
                // percent 0.0 -> 1.0
                Log.d(TAG, "fraction: " +fraction);
                Integer newHeight = evaluate(fraction, startHeight, endHeight);//固值器
                mImage.getLayoutParams().height = newHeight;//设置imageview高度
                mImage.requestLayout();
            }
        });
         
        mValueAnim.setInterpolator(new OvershootInterpolator());
        mValueAnim.setDuration(500);
        mValueAnim.start();
    }
     
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
     
     
}

ResetAnimation

  public class ResetAnimation extends Animation {
    private final ImageView mImage;
    private final int startHeight;
    private final int endHeight;
    public ResetAnimation(ImageView mImage, int startHeight, int endHeight) {
        this.mImage = mImage;
        this.startHeight = startHeight;
        this.endHeight = endHeight;
         
        setInterpolator(new OvershootInterpolator());
        //设置动画执行时长
        setDuration(500);
    }
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        // interpolatedTime 0.0f -> 1.0f
         
        Integer newHeight = evaluate(interpolatedTime, startHeight, endHeight);
        mImage.getLayoutParams().height = newHeight;//设置imageview高
        mImage.requestLayout();
         
        super.applyTransformation(interpolatedTime, t);
    }
     
     
    /**
     * 类型估值器
     * @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));
    }
     
     
     
}

MainActivity

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         
         
        final MyListView mListView = (MyListView) findViewById(R.id.lv);
        mListView.setOverScrollMode(View.OVER_SCROLL_NEVER);
        // 加Header
        final View mHeaderView = View.inflate(MainActivity.this, R.layout.view_header, null);
        final ImageView mImage = (ImageView) mHeaderView.findViewById(R.id.iv);
        mListView.addHeaderView(mHeaderView);
         
        mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
             
            @Override
            public void onGlobalLayout() {
                // 当布局填充结束之后, 此方法会被调用
                mListView.setParallaxImage(mImage);
                 
                mHeaderView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }
        });
         
        // 填充数据
        mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, Cheeses.NAMES));
         
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-12-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档