美文网首页DataBindingAndroid开发之路
【译】Animate Objects, not Views

【译】Animate Objects, not Views

作者: ditclear | 来源:发表于2017-09-16 21:33 被阅读31次

    原文地址:https://android.jlelse.eu/animate-objects-not-views-250fe7880196

    无可否认,我们都喜欢炫酷的动画效果。没有用户会喜欢生硬、没有过渡和动画的app。但同时没有开发者愿意花费大量的时间和ValueAnimator打交道,特别是当你需要创建很多ValueAnimators才能达到自己相要的效果。

    因此为什么不使用DataBinding来完成这些复杂的事?

    众所周知,BaseObservable可以包含标有@Bindable注释的字段。

    就像这样:

    public class ExpandStateItem extends BaseObservable {
    
        @Bindable
        private float padding;
        @Bindable
        private boolean expanded;
        @Bindable
        private boolean fast;
    }
    

    如果您调用notifyPropertyChanged(BR.field_name),任何使用此字段的视图将被通知到,然后进行更新。所以唯一剩下我们需要做的事情就是把正确的东西绑定到正确的值。

    在我的例子中有3个可以绑定的属性。 让我们一一看看他们。

    第一个值是padding,我们需要像这样绑定它

    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="@{state.padding}"
        android:paddingTop="@{state.padding}">
    ....
    </LinearLayout>
    

    state是在布局文件中声明的类型为ExpandStateItem的变量

    Android DataBinding库甚至知道如何设置padding属性,因此您不需要自己创建@BindingAdapter

    现在如果您为ExpandStateItem对象的padding设置某个值,您会看到它对应的所有视图实际上会改变它的padding值。

    如果您创建一些动画并在动画的CallBack中更改此值,则所有视图将相应地进行动画。

    这里是一个例子。 state是一个在RecyclerView中绑定到RecyclerView.ViewHolders的数组。

    private ValueAnimator paddingAnimator = ValueAnimator.ofFloat(MAX_MARGIN, MIN_MARGIN);
    paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            for (ExpandStateItem item :
                    states) {
                item.setPading((float) animation.getAnimatedValue());
            }
        }
    });
    

    下一个变量是expanded。如果您注意到文章顶部的GIF图像,它有一些可伸缩的卡片,它们随着expanded属性的改变而扩展/折叠。 它是这样绑定�的:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="gone"
        app:expand="@{state.expanded}"
        app:fastExpand="@{state.fast}">
    ...
    </LinearLayout>
    

    另外你可能注意到了app:fastExpand,稍后我会告诉你。

    对于app:expand属性,我们需要编写我们自己的@BindingAdapter。

    @BindingAdapter({"expand", "fastExpand"})
    public static void expandView(View view, boolean expand, boolean fast) {
        if (expand) {
            ViewAnimationUtils.expand(view, null, fast);
        } else if (view.getHeight() != 0) {
            ViewAnimationUtils.collapse(view, null, fast);
        }
    }
    

    现在,如果我向你展示ViewAnimationUtils类,我敢打赌你会弄清楚为什么我们需要fastExpand属性。

    package com.wldev.expandablecardviewlist.extra;
    
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.Animation;
    import android.view.animation.Transformation;
    
    public class ViewAnimationUtils {
    
        public static void expand(final View v, final AnimationEndCallback callback, boolean fast) {
            v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            final int targtetHeight = v.getMeasuredHeight();
    
            v.getLayoutParams().height = 0;
            v.setVisibility(View.VISIBLE);
            Animation a = new Animation() {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    v.getLayoutParams().height = interpolatedTime == 1
                            ? ViewGroup.LayoutParams.WRAP_CONTENT
                            : (int) (targtetHeight * interpolatedTime);
                    v.requestLayout();
                }
    
                @Override
                public boolean willChangeBounds() {
                    return true;
                }
            };
    
            int duration = 0;
    
            if (!fast) {
                duration = (int) (targtetHeight / v.getContext().getResources().getDisplayMetrics().density);
            }
    
            a.setDuration(duration);
            a.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    if (callback != null)
                        callback.onAnimationEnded();
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            v.startAnimation(a);
        }
    
        public static void collapse(final View v, final AnimationEndCallback callback, boolean fast) {
            final int initialHeight = v.getMeasuredHeight();
    
            Animation a = new Animation() {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    if (interpolatedTime == 1) {
                        v.setVisibility(View.GONE);
                    } else {
                        v.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
                        v.requestLayout();
                    }
                }
    
                @Override
                public boolean willChangeBounds() {
                    return true;
                }
            };
    
            int duration = 0;
            if (!fast) {
                duration = (int) (initialHeight / v.getContext().getResources().getDisplayMetrics().density);
            }
    
            a.setDuration(duration);
    
            a.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    if (callback != null)
                        callback.onAnimationEnded();
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            v.startAnimation(a);
        }
    
        interface AnimationEndCallback {
            void onAnimationEnded();
        }
    }
    

    通过将fastExpand指定为true,将不会有动画时长。

    但是为什么我们需要移除动画时长?事实上,如果你想在可回收的列表上使用它,完全不必这样做。当view将被回收并再次使用(RecyclerView里是onBindViewHolder),所有动画将再次出现在此特定视图中,因此需要指定我们只想展开的视图,并且不展示进度。

    例子就这样,如何与视图进行交互(动画),而不会触及视图(实际上你会,但是间接地)。 你可以用它做的事情只会受到你想象力的限制。Viewmargins, paddings, width, heightTextViewlines count, color, taste (no), thousands of them.。

    我的示例的源代码在GitHub上here

    Thanks for attention and happy coding :)

    相关文章

      网友评论

      • ditclear:发现google翻译翻译得蛮准确的 Save me a lot of time...

      本文标题:【译】Animate Objects, not Views

      本文链接:https://www.haomeiwen.com/subject/sfmjjxtx.html