效果图
我们先来看看效果图:
screenshot1.gif screenshot2.gif
- 根据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);
}
}
网友评论