先上效果图
分析
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体验>_
点击链接下载apk:https://wwa.lanzous.com/iXxsjgf73ta
二维码下载