android使用属性动画--高仿B站频道详情页头部动效

先上效果图

B站的效果
demo效果

分析

1.点击按钮时列表向下移动的动画
2.点击按钮背景图片放大并且渐出
3.点击按钮横向滑动列表拉伸渐出
4.滑动列表标题栏渐变色的效果

解析

1.使用属性动画 ValueAnimator动态改变横向滑动列表的高度来实现往下移动的效果
2.监听向下移动的动画,根据动画执行过程计算图片的透明度和列表宽高实现渐出的效果
3.监听appbar的滑动,动态改变标题的透明度

附上一个万能的公式,根据偏移量计算值:大值-(大值-小值)(当前偏移量/最大的偏移量的范围)

public float getSize(int max, int min, float absOffset, float totalScrollRange) {
        return max - ((max - min) * (absOffset / totalScrollRange));
    }

布局文件

使用自定义组合控件





    

    

        

        


            

                

                    

                    

                    

                    

                

            


        


        


    



自定义组合控件java代码--注释的很全面

public class TopicsDetailsTopView extends RelativeLayout {

    private final int ANIMATION_DURATION = 300;//动画持续的时间
    private ValueAnimator mValueAnimation;//上下平移的动画
    private boolean isAnimationExecuting = false;//动画是否正在执行
    private boolean mIsShow = false;//是否展开了
    private ImageView mSwitchView;
    private View mScrollView;//横向的列表
    private int mScrollViewHeight;//底部的横向滑动控件的高度
    private int mBgHeight;//背景的高度
    private ImageView mBgImageView;//背景图片
    private RelativeLayout mRootView;//根布局
    public float mBgImageAlpha;//背景的透明度
    private TextView mTopicsTv;

    public TopicsDetailsTopView(Context context) {
        super(context);
        initView(context);
    }

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

    public TopicsDetailsTopView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        View inflate = View.inflate(context, R.layout.topics_details_top_view, this);
        mTopicsTv = inflate.findViewById(R.id.topics_tv_data);
        mRootView = inflate.findViewById(R.id.top_view_root);
        mSwitchView = inflate.findViewById(R.id.switch_view);
        mBgImageView = inflate.findViewById(R.id.bg_image);
        mScrollView = inflate.findViewById(R.id.cc);
        getViewHeight();
        //动画执行完才可点击
        mSwitchView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isAnimationExecuting) {
                    return;
                }
                startLayoutAnimator();
            }
        });
    }

    //获取view的高度
    private void getViewHeight() {
        if (mScrollView == null || mBgImageView == null) {
            return;
        }
        ViewTreeObserver vto = mScrollView.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                mScrollViewHeight = mScrollView.getHeight();
                ViewGroup.LayoutParams layoutParams = mScrollView.getLayoutParams();
                layoutParams.height = 0;
                mScrollView.setLayoutParams(layoutParams);
            }
        });

        ViewTreeObserver mPicVto = mBgImageView.getViewTreeObserver();
        mPicVto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mBgImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                mBgHeight = mBgImageView.getHeight();
                ViewGroup.LayoutParams layoutParams = mBgImageView.getLayoutParams();
                mBgImageView.setLayoutParams(layoutParams);
            }
        });

    }

    /**
     * 开启动画
     */
    private void startLayoutAnimator() {
        if (mScrollView == null || mSwitchView == null || mBgImageView == null) {
            return;
        }
        //设置按钮的旋转动画
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mSwitchView, "rotation", mIsShow ? 0f : 180f);
        objectAnimator.setDuration(ANIMATION_DURATION);
        objectAnimator.start();
        //动态设置高度的属性动画
        mValueAnimation = mIsShow ? ValueAnimator.ofInt(mScrollViewHeight, 0) : ValueAnimator.ofInt(0, mScrollViewHeight);
        mValueAnimation.setDuration(ANIMATION_DURATION);
        mValueAnimation.start();

        mValueAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //获取当前的height值
                int currentHeight = (int) valueAnimator.getAnimatedValue();

                //动态更新view的高度
                ViewGroup.LayoutParams layoutParams = mScrollView.getLayoutParams();
                layoutParams.height = Math.round(currentHeight);
                mScrollView.setLayoutParams(layoutParams);

                //设置背景的高度--逐渐放大的效果
                ViewGroup.LayoutParams mPicParams = mBgImageView.getLayoutParams();
                mPicParams.height = Math.round((currentHeight + mBgHeight));
                mBgImageView.setLayoutParams(mPicParams);

                //设置背景和滑动列表的透明度渐出渐入的效果
                float maxAlpha = mBgImageAlpha;
                float minAlpha = 0.0f;
                float height = currentHeight;
                float maxHeight = mScrollViewHeight;
                float offSetAlpha = (maxAlpha - ((maxAlpha - minAlpha) * (height / maxHeight)));
                float bgAlpha = Float.parseFloat(String.format("%.2f", offSetAlpha));
                setBgImageAlpha(bgAlpha);//背景逐渐退出
                mScrollView.setAlpha(1 - bgAlpha);//滑动控件逐渐出来
                isAnimationExecuting = true;
                mTopicsTv.setText(bgAlpha+"");
            }
        });
        mValueAnimation.addListener(new DefaultAnimatorListener() {
            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                mIsShow = !mIsShow;
                isAnimationExecuting = false;
            }
        });
    }

    /**
     * 根view
     */
    public RelativeLayout getTopRootView() {
        return mRootView;
    }

    /**
     * 图片
     */
    public ImageView getBgImageView() {
        return mBgImageView;
    }

    //设置背景透明度
    public void setBgImageAlpha(float mBgImageAlpha) {
        if (mBgImageView == null) {
            return;
        }
        mBgImageView.setAlpha(mBgImageAlpha);
    }

    /**
     * 是否展开了
     */
    public boolean getIsShow() {
        return mIsShow;
    }

    /**
     * 是否正在执行动画
     */
    public boolean getAnimationExecuting() {
        return isAnimationExecuting;
    }

}


外层的列表用到CoordinatorLayout




    

        

    

    


    

        

        
        

        


        


    


activity里面的主要的代码

  //滑动值变化的的最大范围
  private final float SCROLL_RANGE = ResourceUtils.dp2px(50);
  private final float DEFAULT_BG_ALPHA = 0.7f; //默认的背景图片透明度

    TextView mTitleView = findViewById(R.id.tv_title);//标题
    View mTitleRootView = findViewById(R.id.title_root_bg);//标题的背景
    AppBarLayout  mAppBarLayout = findViewById(R.id.app_bar);
    TopicsDetailsTopView mTopView = findViewById(R.id.topics_details_top_view);
    ImageView mBgImageView = mTopView.getBgImageView();//背景图片
    RelativeLayout mTopRootView = mTopView.getTopRootView();//标题的根布局

 if (mTitleView == null || mAppBarLayout == null || mTitleRootView == null) {
            return;
        }

//设置头部背景颜色
 mTitleRootView.setBackgroundColor(Color.parseColor("#D6B675"));
 mTopRootView.setBackgroundColor(Color.parseColor("#D6B675"));

        mAppBarOffsetChangedListener = new AppBarStatesChangeListener() {
            @Override
            public void onStateChanged(AppBarLayout appBarLayout, State state, int verticalOffset) {
                float absOffset = Math.abs(verticalOffset);
                //标题的渐变
                float titleAlpha = getBgAlpha(1, 0, absOffset, SCROLL_RANGE);
                mTitleRootView.setAlpha(1 - titleAlpha);

                //背景的渐变
                float bgAlpha = getBgAlpha(DEFAULT_BG_ALPHA, 0, absOffset, SCROLL_RANGE);
                //不展开才设置背景的渐出
                if (!mTopView.getIsShow() && !mTopView.getAnimationExecuting()) {
                    mTopView.setBgImageAlpha(bgAlpha);
                }
                mTopView.mBgImageAlpha = bgAlpha;
                mTitleView.setText(bgAlpha + "");
            }
        };
        mAppBarLayout.addOnOffsetChangedListener(mAppBarOffsetChangedListener);
/**
 * 根据偏移量获取透明度
 */
private float getBgAlpha(float maxAlpha, float minAlpha, float absOffset, float scrollRange) {
    float bgAlphaOffset = maxAlpha - ((maxAlpha - minAlpha) * (absOffset / scrollRange));
    if (bgAlphaOffset <= 0) {
        bgAlphaOffset = 0;
    }
    return Float.parseFloat(String.format("%.2f", bgAlphaOffset));
}

不懂的小伙伴请在评论区提问,欢迎大家下载demo体验>_
demo.png

点击链接下载apk:https://wwa.lanzous.com/iXxsjgf73ta

二维码下载

扫描二维码下载

你可能感兴趣的:(android使用属性动画--高仿B站频道详情页头部动效)