Android(安卓)手写动画框架
16lz
2021-01-26
效果图
我们先来看看效果图:
- 根据ScrollView或者HorizontalScrollView中的滑动距离设置对应子view的动画效果
- 自定义ScrollView、HorizontalScrollView,以及LinearLayoutView
- 获取并记录在布局文件中定义的自定义动画属性值
定义属性值
定义属性值,在layout.xml即布局文件中可以给view(泛指,可以为任何view)指定动画属性值,例如是否进行alpha动画,或者指定出场方式。
<?xml version="1.0" encoding="UTF-8"?><resources> <declare-styleable name="XAnimator"> <attr name="x_alpha" format="boolean"/> <attr name="x_scaleX" format="boolean"/> <attr name="x_scaleY" format="boolean"/> <attr name="x_startBgColor" format="color"/> <attr name="x_endBgColor" format="color"/> <attr name="from_direction"> <flag name="top" value="1" /> <flag name="bottom" value="2" /> <flag name="left" value="3" /> <flag name="right" value="4" /> attr> declare-styleable>resources>
属性 | 说明 |
---|---|
x_alpha | 是否执行透明值变化动画(范围为0-1) |
x_scaleX | 是否执行x轴缩放动画(范围为0-1) |
x_scaleY | 是否执行y轴缩放动画(范围为0-1) |
x_startBgColor | 背景颜色渐变动画起始颜色值 |
x_endBgColor | 背景颜色渐变动画结束颜色值 |
from_direction | 出现方向,top, bottom, left, right,分别为从顶部,底部,左边,右边出现 |
定义属性类
用于记录在布局文件中指定的动画属性值XAnimator
(即上面所定义的属性值)
在这里就不列出来了,可查看 XAnimatorAttr
布局代码
我们可以看到,使用就像ScrollView+LinearLayout一样,只不过在需要进行动画的子view(例如ImageView和LinearLayout)中指定了动画属性,在XAnimatorScrollView进行滑动的时候就可以根据指定的动画属性进行对应的设置。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.iigo.library.XAnimatorScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <com.iigo.library.XAnimatorLinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/duola_big" /> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/duola1" app:x_alpha="true" app:x_scaleX="true" app:x_scaleY="true" app:from_direction="left"/> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/duola2" app:from_direction="right" app:x_alpha="true" app:x_scaleX="true" app:x_scaleY="true" /> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/duola3" app:from_direction="left" app:x_alpha="true" /> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/duola4" app:from_direction="right" app:x_alpha="true" /> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/duola5" app:x_endBgColor="@android:color/holo_red_light" app:x_startBgColor="@android:color/holo_green_light" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:x_endBgColor="@android:color/holo_orange_light" app:x_startBgColor="@android:color/holo_green_light"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/duola6" /> LinearLayout> com.iigo.library.XAnimatorLinearLayout> com.iigo.library.XAnimatorScrollView>RelativeLayout>
获取动画属性值
上面的布局代码中,我们在XAnimatorLinearLayout中使用了系统控件,例如ImageView和LinearLayout,那我们如何获取定义的动画属性值(即XAnimator
)呢?
可以根据XmlResourceParser
来进行获取
还不了解的可以看我文章 Android 关于XmlResourceParser
我们可以通过接口
XmlResourceParser parser = context.getResources().getLayout(layoutId);
解析layout资源文件来遍历获取动画属性
主要接口可参考 XAnimator
动画执行
在我们自定义的XAnimatorLinearLayout 中,增加onScrollChanged接口,在XAnimatorScrollView或XAnimatorHorizontalScrollView 调用onScrollChanged的时候,调用XAnimatorLinearLayout 中的onScrollChanged接口,再遍历子view,根据其设置的动画属性,设置对应scroll百分比的动画值。
public final class XAnimatorLinearLayout extends LinearLayout{ private static ArgbEvaluator argbEvaluator = new ArgbEvaluator(); public XAnimatorLinearLayout(Context context) { super(context); } public XAnimatorLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public XAnimatorLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public XAnimatorLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public void onScrollChanged(int l, int t, int oldl, int oldt){ boolean isVertical = getOrientation() == VERTICAL; for (int i = 0;i < getChildCount();i++) { View child = getChildAt(i); XAnimatorAttr xAnimatorAttr = (XAnimatorAttr) child.getTag(R.id.XAnimator); if (xAnimatorAttr == null){ continue; } int referDistance = isVertical ? child.getTop() : child.getLeft(); int referParentSize = isVertical ? ((ViewGroup)getParent()).getHeight() : ((ViewGroup)getParent()).getWidth(); int referChildSize = isVertical ? child.getHeight() : child.getWidth(); int absolute = referDistance - (isVertical ? t : l); int visibleSize = referParentSize - absolute; float ratio = limitValue(visibleSize / (float) referChildSize, 0, 1); if (absolute <= referParentSize) { performAnimator(child, xAnimatorAttr, ratio); } else { drawHideState(child, xAnimatorAttr); } } } /** * Now perform animator for the child view. * * @param child The child view. * @param xAnimatorAttr The {@link XAnimatorAttr} of the child view. * @param ratio The ratio of the scroll(0 <= ration <= 1). * */ private void performAnimator(View child, XAnimatorAttr xAnimatorAttr, float ratio){ if (child == null || xAnimatorAttr == null){ return; } if (xAnimatorAttr.isAlpha()){ child.setAlpha(ratio); } if (xAnimatorAttr.isScaleX()){ child.setScaleX(ratio); } if (xAnimatorAttr.isScaleY()){ child.setScaleY(ratio); } if (xAnimatorAttr.getStartBgColor() != -1 && xAnimatorAttr.getEndBgColor() != -1){ child.setBackgroundColor((Integer) argbEvaluator.evaluate(ratio, xAnimatorAttr.getStartBgColor(), xAnimatorAttr.getEndBgColor())); } switch (xAnimatorAttr.getFromDirection()){ case XAnimatorAttr.FROM_DIRECTION_BOTTOM: child.setTranslationY(child.getHeight() * (1 - ratio)); break; case XAnimatorAttr.FROM_DIRECTION_TOP: child.setTranslationY(-child.getHeight() * (1 - ratio)); break; case XAnimatorAttr.FROM_DIRECTION_LEFT: child.setTranslationX(-child.getWidth() * (1 - ratio)); break; case XAnimatorAttr.FROM_DIRECTION_RIGHT: child.setTranslationX(child.getWidth() * (1 - ratio)); break; default: break; } } /** * When the child view is not yet displayed on the screen, will draw its hide state. * * @param child The child view. * @param xAnimatorAttr The attr of the child view. * */ private void drawHideState(View child, XAnimatorAttr xAnimatorAttr){ if (child == null || xAnimatorAttr == null){ return; } if(xAnimatorAttr.isAlpha()){ child.setAlpha(0); } if(xAnimatorAttr.isScaleX()){ child.setScaleX(0); } if(xAnimatorAttr.isScaleY()){ child.setScaleY(0); } switch (xAnimatorAttr.getFromDirection()){ case XAnimatorAttr.FROM_DIRECTION_BOTTOM: child.setTranslationY(child.getHeight()); break; case XAnimatorAttr.FROM_DIRECTION_TOP: child.setTranslationY(-child.getHeight()); break; case XAnimatorAttr.FROM_DIRECTION_LEFT: child.setTranslationX(-child.getWidth()); break; case XAnimatorAttr.FROM_DIRECTION_RIGHT: child.setTranslationX(child.getWidth()); break; default: break; } } /** * Limit the value between min and max. * * @param value The limit value. * @param min The min value. * @param max The max value. * */ private float limitValue(float value, float min ,float max) { return Math.max(Math.min(value,max), min); }}
Github
更多相关文章
- Android(安卓)自定义控件属性,自定义Dialog定位
- android中自定义的控件,使用自定义属性attrs.xml
- Android中动画的详细讲解
- 自定义TextView实现跑马灯
- Android高德地图开发(2)——地图显示+自定义控件
- Android(安卓)ApiDemos示例解析(21):App->Device Admin
- Android7.1启动系统App必须配置加密
- Android(安卓)Api demo系列(一) (App>Activity>Animation)
- Android(安卓)自定义View——拖动选择时间控件