0

当我将可见性分别设置为 VISIBLE 和 GONE 时,我正在使用 LayoutTransition 淡入和淡出具有半透明背景的视图。标准的东西。我在该过渡视图之上(之后,在 XML 中)有一个具有纯色背景的视图。我希望用户会在整个过渡过程中看到纯色背景不变的顶视图,这与覆盖视图出现时运行的动画完全相反。

动画师按预期工作:用户可以在整个APPEARING动画中看到顶视图。DISAPPEARING动画师没有按预期工作:覆盖视图最终绘制在所有其他视图之上。

值得注意的是,即使您没有设置自己的设置LayoutTransition而是依赖于android:animateLayoutChanges="true"XML,也会发生这种情况;我添加了自己的以增加持续时间,从而更容易看到过渡。

关于如何解决此问题的任何想法?我猜这很常见,而且我必须遗漏一些明显的东西,因为这是默认行为。我尝试了一些方法,例如附加一个以使每帧的顶视图无效,使用更新侦听AnimatorUpdateListener器设置我自己的,使每帧的顶视图无效,并用 a和其他视图类型替换覆盖视图以防万一在某些情况下表现特殊的方式。DISAPPEARING ObjectAnimatorTextViewFrameLayout

如果我用常规替换过渡动画师,ObjectAnimator我会得到预期的行为,除了视图不是GONE,因此接受触摸事件和所有垃圾(这使得“解决方案”站不住脚)。因此,我不认为问题仅仅是过渡视图具有关联的动画师。似乎这特别是LayoutTransition代码或调用所说的东西的问题。

主要活动:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final View overlay = findViewById(R.id.overlay);

        final LayoutTransition lt = new LayoutTransition();

        lt.setDuration(LayoutTransition.APPEARING, 300);
        lt.setStartDelay(LayoutTransition.APPEARING, 0);
        lt.setDuration(LayoutTransition.DISAPPEARING, 1000);
        lt.setStartDelay(LayoutTransition.DISAPPEARING, 0);
        ((ViewGroup) overlay.getParent()).setLayoutTransition(lt);

        final Runnable runnable = new Runnable() {
            @Override
            public void run() {
                if (overlay.getVisibility() == View.VISIBLE) {
                    overlay.setVisibility(View.GONE);
                } else {
                    overlay.setVisibility(View.VISIBLE);
                }

                overlay.postDelayed(this, 1500);
            }
        };

        overlay.post(runnable);
    }
}

活动主.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#ffffff"
                android:paddingBottom="@dimen/activity_vertical_margin"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                tools:context=".MainActivity"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff0000"
        android:text="THIS IS BEHIND THE OVERLAY AND THUS SHOULD TINT"
        android:textColor="#ffffff"
        />

    <FrameLayout
        android:id="@+id/overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#7f00ff00"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:layout_margin="64dp"
        android:background="#ffffff"
        android:gravity="center"
        android:text="THIS VIEW IS IN FRONT OF THE OVERLAY AND THUS SHOULD NOT SUFFER TINTING"
        android:textColor="#000000"
        android:textSize="32sp"
        />

</RelativeLayout>

我的设备运行 API 22,我也设置targetSdkVersion为 22。基本上我创建了一个全新的项目并修改了生成的MainActivityactivity_main.xml几乎完全匹配这些粘贴的文件(为简洁起见,我只排除了importandpackage行)。

4

1 回答 1

3

我今天遇到了同样的问题,所以我查看了 ViewGroup.java 源代码。结果是消失的孩子总是会利用其他孩子。

这是ViewGroup.dispatchDraw(Canvas)API 23 中的一个片段,我很确定它在 API 22 中几乎相同。

for (int i = 0; i < childrenCount; i++) {
    while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
        final View transientChild = mTransientViews.get(transientIndex);
        if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                transientChild.getAnimation() != null) {
            more |= drawChild(canvas, transientChild, drawingTime);
        }
        transientIndex++;
        if (transientIndex >= transientCount) {
            transientIndex = -1;
        }
    }
    int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
    final View child = (preorderedList == null)
            ? children[childIndex] : preorderedList.get(childIndex);
    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
        more |= drawChild(canvas, child, drawingTime);
    }
}
while (transientIndex >= 0) {
    // there may be additional transient views after the normal views
    final View transientChild = mTransientViews.get(transientIndex);
    if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
            transientChild.getAnimation() != null) {
        more |= drawChild(canvas, transientChild, drawingTime);
    }
    transientIndex++;
    if (transientIndex >= transientCount) {
        break;
    }
}
if (preorderedList != null) preorderedList.clear();

// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
    final ArrayList<View> disappearingChildren = mDisappearingChildren;
    final int disappearingCount = disappearingChildren.size() - 1;
    // Go backwards -- we may delete as animations finish
    for (int i = disappearingCount; i >= 0; i--) {
        final View child = disappearingChildren.get(i);
        more |= drawChild(canvas, child, drawingTime);
    }
}

消失的观点在mDisappearingChildren.

正如源代码所说,普通视图和瞬态视图首先绘制,然后消失视图绘制。因此,消失的孩子总是会利用其他孩子。应用程序开发人员无法更改顺序。

我的建议是不要使用LayoutTransition,自己写动画。

编辑:

我发现了一个技巧,可以在其他视图之前绘制消失的孩子,需要反思。

您需要一个转储视图以使消失的孩子将其替换为ViewGroup.drawChild(Canvas, View, long).

一个例子是here。

public class TrickLayout extends FrameLayout {

    private Field mDisappearingChildrenField;
    private ArrayList<View> mSuperDisappearingChildren;
    // The dump view to draw disappearing children
    // Maybe you need more than one dump view
    private View mDumpView;
    private boolean mDoTrick;

    public TrickLayout(Context context) {
        super(context);
        init(context);
    }

    public TrickLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public TrickLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        try {
            mDisappearingChildrenField = ViewGroup.class.getDeclaredField("mDisappearingChildren");
            mDisappearingChildrenField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            // Ignore
        }

        if (mDisappearingChildrenField != null) {
            // You can add dump view in xml or somewhere else in code
            mDumpView = new View(context);
            addView(mDumpView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        }
    }


    @SuppressWarnings("unchecked")
    private void getSuperDisappearingChildren() {
        if (mDisappearingChildrenField == null || mSuperDisappearingChildren != null) {
            return;
        }

        try {
            mSuperDisappearingChildren = (ArrayList<View>) mDisappearingChildrenField.get(this);
        } catch (IllegalAccessException e) {
            // Ignore
        }
    }

    private boolean iWantToDoTheTrick() {
        // Do I need do the trick?
        return true;
    }

    private boolean beforeDispatchDraw() {
        getSuperDisappearingChildren();

        if (mSuperDisappearingChildren == null ||
                mSuperDisappearingChildren.size() <= 0 || getChildCount() <= 1) { // dump view included
            return false;
        }

        return iWantToDoTheTrick();
    }

    private void afterDispatchDraw() {
        // Clean up here
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        mDoTrick = beforeDispatchDraw();
        super.dispatchDraw(canvas);
        if (mDoTrick) {
            afterDispatchDraw();
            mDoTrick = false;
        }
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        ArrayList<View> disappearingChildren = mSuperDisappearingChildren;

        if (mDoTrick) {
            if (child == mDumpView) {
                boolean more = false;
                for (int i = disappearingChildren.size() - 1; i >= 0; i--) {
                    more |= super.drawChild(canvas, disappearingChildren.get(i), drawingTime);
                }
                return more;
            } else if (disappearingChildren.contains(child)) {
                // Skip
                return false;
            }
        }

        return super.drawChild(canvas, child, drawingTime);
    }
}
于 2016-01-29T14:45:21.630 回答