自定义属性动画框架

【自定义属性动画框架】先来观察完整的动画效果

分析:每个child都可以设置自己的属性动画 根据滑动出自身的高度比例来进行开发者设置的属性动画(平移 渐变 缩放 透明度...)既然是动画框架封装 肯定是希望开发者用的更爽,代码简洁 意图清晰。
首先设定好自定义的属性参数


解析为什么discrollve_translation 各个参数0x01 0x02 0x04 0x08
0x01 0000 0000 0000 0001 fromTop
0x02 0000 0000 0000 0010 fromBottom
0x04 0000 0000 0000 0100 fromLeft
0x08 0000 0000 0000 1000 fromRight
这样设置的目的 因为这个平移可以同时多个方向移动 比如我们希望他 左上方向移动 就可以这样设置
app:discrollve_translation = "fromTop|fromLeft"
既 它们的值 0000 0000 0000 0101 将来我们判断的时候 可以这样判断
比如判断是否设置了fromTop就拿这个值 0101 & 0001fromTop = 0001 如果得出来的值等于fromTop就说明有设置这个值
我们希望开发者用这个属性动画框架的时候 只需要对对应的child设置相应属性参数 将来运行的时候就会执行相应的属性动画
理想开发者在布局文件设定即可


理想中 我们只需要这样写 运行起来 对应child就会自己根据用户滑动自身可见高度比例 执行自身设置的属性动画
问题1:这样的话 首先我得所对应得child对象本身 设置属性动画
问题2:获取到用户对所对应child对象设置属性的参数
问题3:监听用户上下滑动
针对上述问题 我们必须自定义一个LinearLayout和FramLayout容器
自定义FramLayout容器 是用来包裹有设置属性动画的控件 目的是为了在该控件 设置对应的属性动画
自定义LinerLayout容器 有两个目的 获取到自定义属性参数 在addView实现偷天换日 首先将child的添加到我们自定义
FramLayout容器 再将该容器添加到LinerLayout容器
问题3我们需要自定ScrollView以便于我们监听用户上下滑动

先来看看自定义的MyFrameLayout 此定义的控件主要是用来包裹有设置属性参数的控件 是用来执行属性动画 这样的话
我们让它实现一个接口 用来规范 执行动画 和恢复原来的样子

public interface DiscrollInterface { /** * 当滑动的时候调用该方法,用来控制里面的控件执行相应的动画 * @param ratio 动画执行的百分比(child view画出来的距离百分比) */ public void onDiscroll(float ratio); /** * 重置动画--让view所有的属性都恢复到原来的样子 */ public void onResetDiscroll(); }

将来要执行动画 就执行child.onDiscroll(ratio)
public class MyFrameLayout extends FrameLayout implements DiscrollInterface {//保存自定义属性 //定义很多的自定义属性 /** * 0000000001 0000000010 0000000100 0000001000 top|left 0000000001 top 0000000100 left 或运算 | 0000000101 反过来就使用& 与运算 */ private static final int TRANSLATION_FROM_TOP = 0x01; private static final int TRANSLATION_FROM_BOTTOM = 0x02; private static final int TRANSLATION_FROM_LEFT = 0x04; private static final int TRANSLATION_FROM_RIGHT = 0x08; //颜色估值器 private static ArgbEvaluator sArgbEvaluator = new ArgbEvaluator(); /** * 自定义属性的一些接收的变量 */ private int mDiscrollveFromBgColor; //背景颜色变化开始值 private int mDiscrollveToBgColor; //背景颜色变化结束值 private boolean mDiscrollveAlpha; //是否需要透明度动画 private int mDisCrollveTranslation; //平移值 private boolean mDiscrollveScaleX; //是否需要x轴方向缩放 private boolean mDiscrollveScaleY; //是否需要y轴方向缩放 private int mHeight; //本view的高度 private int mWidth; //宽度public MyFrameLayout(Context context) { super(context); }public void setmDiscrollveFromBgColor(int mDiscrollveFromBgColor) { this.mDiscrollveFromBgColor = mDiscrollveFromBgColor; }public void setmDiscrollveToBgColor(int mDiscrollveToBgColor) { this.mDiscrollveToBgColor = mDiscrollveToBgColor; }public void setmDiscrollveAlpha(boolean mDiscrollveAlpha) { this.mDiscrollveAlpha = mDiscrollveAlpha; }public void setmDisCrollveTranslation(int mDisCrollveTranslation) { this.mDisCrollveTranslation = mDisCrollveTranslation; }public void setmDiscrollveScaleX(boolean mDiscrollveScaleX) { this.mDiscrollveScaleX = mDiscrollveScaleX; }public void setmDiscrollveScaleY(boolean mDiscrollveScaleY) { this.mDiscrollveScaleY = mDiscrollveScaleY; }@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; }@Override public void onDiscroll(float ratio) { if(mDiscrollveAlpha){ setAlpha(ratio); } if(mDiscrollveScaleX){ setScaleX(ratio); } if(mDiscrollveScaleY){ setScaleY(ratio); } if(isTranslationFrom(TRANSLATION_FROM_LEFT)){ setTranslationX(-mWidth*(1-ratio)); //mWidth--->0(0代表恢复到原来的位置) } if(isTranslationFrom(TRANSLATION_FROM_RIGHT)){ setTranslationX(mWidth*(1-ratio)); } if(isTranslationFrom(TRANSLATION_FROM_TOP)){ setTranslationY(-mHeight*(1-ratio)); mHeight--->0(0代表恢复到原来的位置) } if(isTranslationFrom(TRANSLATION_FROM_BOTTOM)){ setTranslationY(mHeight*(1-ratio)); }//判断从什么颜色到什么颜色 if(mDiscrollveFromBgColor!=-1&&mDiscrollveToBgColor!=-1){ setBackgroundColor((int) sArgbEvaluator.evaluate(ratio, mDiscrollveFromBgColor, mDiscrollveToBgColor)); }}@Override public void onResetDiscroll() { if(mDiscrollveAlpha){ setAlpha(0); } if(mDiscrollveScaleX){ setScaleX(0); } if(mDiscrollveScaleY){ setScaleY(0); } //平移动画int值:left,right,top,bottomleft|bottom if(isTranslationFrom(TRANSLATION_FROM_BOTTOM)){//是否包含bottom setTranslationY(mHeight); //height--->0(0代表恢复到原来的位置) } if(isTranslationFrom(TRANSLATION_FROM_TOP)){//是否包含bottom setTranslationY(-mHeight); //-height--->0(0代表恢复到原来的位置) } if(isTranslationFrom(TRANSLATION_FROM_LEFT)){ setTranslationX(-mWidth); //mWidth--->0(0代表恢复到本来原来的位置) } if(isTranslationFrom(TRANSLATION_FROM_RIGHT)){ setTranslationX(mWidth); //-mWidth--->0(0代表恢复到本来原来的位置) } }private boolean isTranslationFrom(int translationMask){ if(mDisCrollveTranslation ==-1){ return false; } //fromLeft|fromeBottom & fromBottom = fromBottom return (mDisCrollveTranslation & translationMask) == translationMask; }}

自定义LinearLayout 主要两个目的 为每个子控件获取到开发者设定的属性参数另外实现偷天换日 就是将设有属性的控件用 自定义的FramLayout的控件包裹起来 再将此控件添加到Linerlayout里面去

public class MyLinearLayout extends LinearLayout { public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(VERTICAL); }@Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MyLayoutParams(getContext(),attrs); }@Override public void addView(View child, ViewGroup.LayoutParams params) { MyLayoutParams p = (MyLayoutParams) params; if(isDiscrollvable(p)){ //偷天换日 MyFrameLayout mf = new MyFrameLayout(getContext()); mf.addView(child); mf.setmDiscrollveAlpha(p.mDiscrollveAlpha); mf.setmDiscrollveFromBgColor(p.mDiscrollveFromBgColor); mf.setmDiscrollveToBgColor(p.mDiscrollveToBgColor); mf.setmDiscrollveScaleX(p.mDiscrollveScaleX); mf.setmDiscrollveScaleY(p.mDiscrollveScaleY); mf.setmDisCrollveTranslation(p.mDisCrollveTranslation); super.addView(mf,params); }else { super.addView(child, params); } }private boolean isDiscrollvable(MyLayoutParams p){ return p.mDiscrollveAlpha || p.mDiscrollveScaleX || p.mDiscrollveScaleY || p.mDisCrollveTranslation != -1 || (p.mDiscrollveFromBgColor !=-1 && p.mDiscrollveToBgColor != -1); }public class MyLayoutParams extends LinearLayout.LayoutParams{ public int mDiscrollveFromBgColor; //背景颜色变化开始值 public int mDiscrollveToBgColor; //背景颜色变化结束值 public boolean mDiscrollveAlpha; //是否需要透明度动画 public int mDisCrollveTranslation; //平移值 public boolean mDiscrollveScaleX; //是否需要x轴方向缩放 public boolean mDiscrollveScaleY; //是否需要y轴方向缩放 public MyLayoutParams(Context c, AttributeSet attrs) { super(c, attrs); //解析attrs得到自定义的属性,保存 TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.DiscrollView_LayoutParams); mDiscrollveAlpha = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_alpha,false); mDiscrollveScaleX = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleX, false); mDiscrollveScaleY = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleY, false); mDisCrollveTranslation = a.getInt(R.styleable.DiscrollView_LayoutParams_discrollve_translation, -1); mDiscrollveFromBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_fromBgColor, -1); mDiscrollveToBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_toBgColor, -1); a.recycle(); } }}

最后自定义ScrollView 监听用户滑动 算出child浮现高度 和child高度比例 0~1 以便根据该值 去执行该控件的属性动画
public class MyScrollView extends ScrollView { private MyLinearLayout mContent; public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); }@Override protected void onFinishInflate() { super.onFinishInflate(); mContent = (MyLinearLayout) getChildAt(0); }@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); View first = mContent.getChildAt(0); first.getLayoutParams().height = getHeight(); }@Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); int scrollViewHeight = getHeight(); //监听滑动的程度---childView从下面冒出来多少距离/childView.getHeight(); ----0~1:动画执行的百分比ratio //动画执行的百分比ratio控制动画执行 for(int i = 0; i

ratio值的计算
自定义属性动画框架
文章图片

以上就是该自定义属性框架的分析

    推荐阅读