1 效果图
2 需求
- 设计一个动画框架,一个列表中有需要做动画的,也有不需要动画;不单独对每个View写动画,仅做些声明
3 需求分析
- 自定义LinearLayout、ScrollView实现
- 自定义属性来声明每个child的动画
- 自定义ScrollView监听滑动的距离,将滑动距离和childView的高度比值传给每个child来控制动画
- 自定义LinearLayout通过addView时 将每个子控件声明的属性获取到,
- 设计一个包裹ViewGroup将每个做动画的childView封装,保存其声明的动画属性,执行动画动作
4 主要代码
MyFrameLayout
/**
* 执行动画--控制里面的子空间执行动画
*/
public class MyFrameLayout extends FrameLayout implements DiscrollInterface{
//保存自定义属性
//定义很多的自定义属性
/**
* <attr name="discrollve_translation">
<flag name="fromTop" value="0x01" />
<flag name="fromBottom" value="0x02" />
<flag name="fromLeft" value="0x04" />
<flag name="fromRight" value="0x08" />
</attr>
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(@NonNull 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) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
onResetDiscroll();
}
@Override
public void onDiscroll(float ratio) {
//执行动画ratio:0~1
if(mDiscrollveAlpha){
setAlpha(ratio);
}
if(mDiscrollveScaleX){
setScaleX(ratio);
}
if(mDiscrollveScaleY){
setScaleY(ratio);
}
//平移动画 int值:left,right,top,bottom left|bottom
if(isTranslationFrom(TRANSLATION_FROM_BOTTOM)){//是否包含bottom
setTranslationY(mHeight*(1-ratio));//height--->0(0代表恢复到原来的位置)
}
if(isTranslationFrom(TRANSLATION_FROM_TOP)){//是否包含bottom
setTranslationY(-mHeight*(1-ratio));//-height--->0(0代表恢复到原来的位置)
}
if(isTranslationFrom(TRANSLATION_FROM_LEFT)){
setTranslationX(-mWidth*(1-ratio));//mWidth--->0(0代表恢复到本来原来的位置)
}
if(isTranslationFrom(TRANSLATION_FROM_RIGHT)){
setTranslationX(mWidth*(1-ratio));//-mWidth--->0(0代表恢复到本来原来的位置)
}
//判断从什么颜色到什么颜色
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,bottom left|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;
}
}
MyLinearLayout
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, @Nullable 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)){//判断是否有自定义属性,没有则不包裹一层容器
super.addView(child,params);
}else {
//偷天换日
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.setmDisCrollveTranslation(p.mDisCrollveTranslation);
super.addView(mf, 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 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();
}
}
}
**MyScrollView **
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<mContent.getChildCount();i++){
View child = mContent.getChildAt(i);//MyFrameLayout
int childHeight = child.getHeight();
if(!(child instanceof DiscrollInterface)){
continue;
}
//接口回掉,传递执行的百分比给MyFrameLayout
//低耦合高内聚
// MyFrameLayout mf = (MyFrameLayout) child;
DiscrollInterface discrollInterface = (DiscrollInterface) child;
//child离parent顶部的高度
int childTop = child.getTop();
//滑出去的这一截高度:t
// child离屏幕顶部的高度
int absoluteTop = childTop -t;
if(absoluteTop<=scrollViewHeight) {
//child浮现的高度 = ScrollView的高度 - child离屏幕顶部的高度
int visibleGap = scrollViewHeight - absoluteTop;
//float ratio = child浮现的高度/child的高度
float ratio = visibleGap / (float) childHeight;
//确保ratio是在0~1的范围
discrollInterface.onDiscroll(clamp(ratio,1f,0f));
}else{
discrollInterface.onResetDiscroll();
}
}
}
//求三个数的中间大小的一个数。
public static float clamp(float value, float max, float min){
return Math.max(Math.min(value, max), min);
}
}
DiscrollInterface
public interface DiscrollInterface {
/**
* 当滑动的时候调用该方法,用来控制里面的控件执行相应的动画
* @param ratio 动画执行的百分比(child view画出来的距离百分比)
*/
public void onDiscroll(float ratio);
/**
* 重置动画--让view所有的属性都恢复到原来的样子
*/
public void onResetDiscroll();
}