我有一个Sliding Fragments DevByte的实现。除了将 fragment 滑入 View 之外,我还想在其遮挡的内容上绘制阴影。我已将视频中的FractionalLinearLayout修改为屏幕宽度两倍的measure本身,并在右半部分将layout的子元素修改为。在动画循环中,我在左半部分绘制一个黑色的Alpha递增矩形,并将我的X坐标设置为负值,以使右半部分可见。

我的问题是,当我将内容滑入 View 时,此方法工作正常,但当我将内容滑出 View 时,此方法失败。在前进的过程中,我确切地得到了我想要的行为:一个平移和一个变暗的阴影。在出局时,我只得到了翻译,并且阴影在整个动画过程中始终只是其第一帧的颜色。

我在日志记录中看到的是,在进入过程中,正如预期的那样,setPercentOnScreen()onDraw()方法被交替调用。但是,在出路中,我收到了一个对setPercentageOnScreen()的调用,接着是对onDraw()的一个调用,随后仅对setPercentOnScreen()的了调用。 Android正在优化绘图,但我不知道为什么。

更新:有趣的是,我只在运行Android 4.4的模拟器上看到此行为。运行4.0.3和4.3的仿真器可以在两个方向上按预期运行动画。旧的Nexus 7出现了问题,而其他模拟器4.4则没有。它在设备上似乎是一致的,但在设备之间会有所不同。

再次更新:我已经提取了一个示例项目,并将其放在GitHub上:barend/android-slidingfragment。 GitHub上的自述文件包含许多设备上的测试结果。对于仿真器,该问题与“启用主机GPU”功能有关,但仅在Jelly Bean和KitKat上存在。不在ICS上。

再次更新:进一步测试表明,该问题发生在运行Jelly Bean和更高版本的物理设备上,以及在启用Jelly Bean或更高版本的启用“使用主机GPU”的ARM仿真器上。在x86模拟器和没有“使用主机GPU”的ARM模拟器上,无论使用什么Android版本,都不会发生这种情况。我的测试的确切表可以在上面链接的github项目中找到。

// Imports left out
public class HorizontalSlidingLayout extends FrameLayout {
    /**
     * The fraction by which the content has slid into view. Legal range: from 0.0 (all content
     * off-screen) to 1.0 (all content visible).
     */
    private float percentOnScreen;
    private int screenWidth, screenHeight, shift;
    private Paint shadowPaint;

    // Constructors left out, all three call super, then init().

    private void init() {
        if (isInEditMode()) {
            // Ensure content is visible in edit mode.
            percentOnScreen = 1.0f;
        } else {
            setWillNotDraw(false);
            percentOnScreen = 0.0f;
            shadowPaint = new Paint();
            shadowPaint.setAlpha(0x00);
            shadowPaint.setColor(0x000000);
            shadowPaint.setStyle(Paint.Style.FILL);
        }
    }

    /** Reports our own size as (2w, h) and measures all children at (w, h). */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        screenWidth = MeasureSpec.getSize(widthMeasureSpec);
        screenHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(2 * screenWidth, screenHeight);
        for (int i = 0, max = getChildCount(); i < max; i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    /** Lays out the children in the right half of the view. */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        for (int i = 0, max = getChildCount(); i < max; i++) {
            View child = getChildAt(i);
            child.layout(screenWidth, top, right, bottom);
        }
    }

    /**
     * Draws a translucent shadow in the left half of the view, darkening by
     * {@code percentOnScreen}, then lets superclass draw children in the right half.
     */
    @Override
    protected void onDraw(Canvas canvas) {
        // Maintain 30% translucency
        if (percentOnScreen < 0.7f) {
            shadowPaint.setAlpha((int) (percentOnScreen * 0xFF));
        }
        android.util.Log.i("Slider", "onDraw(" + percentOnScreen + ") -> alpha(" + shadowPaint.getAlpha() + ')');
        canvas.drawRect(shift, 0, screenWidth, screenHeight, shadowPaint);
        super.onDraw(canvas);
    }

    @SuppressWarnings("unused")
    public float getPercentOnScreen() {
        return percentOnScreen;
    }

    /** Repeatedly invoked by an Animator. */
    @SuppressWarnings("unused")
    public void setPercentOnScreen(float fraction) {
        this.percentOnScreen = fraction;
        shift = (int)(fraction < 1.0 ? fraction * screenWidth : screenWidth);
        setX(-shift);
        android.util.Log.i("Slider", "setPOS(" + fraction + ") -> invalidate(" + shift + ',' + screenWidth + ')');
        invalidate(shift, 0, screenWidth, screenHeight);
        //invalidate() // Makes no difference
    }
}

奇怪的是,这违反了对称性。滑出时,我做的事情与滑入时,我做的事情完全相反,但是行为不同。我可能忽略了一些愚蠢的事情。有任何想法吗?

最佳答案

这是Android中硬件加速 View 的失效/重画逻辑中的一个错误。我希望默认情况下会在JB中看到相同的错误,但只有在您选择了硬件加速时才会在ICS上看到(默认情况下,从JB开始启用硬件加速)。

问题在于,当从层次结构中删除 View 然后将其动画化时(例如,应用程序的 fragment 事务中或淡出删除的 View 时在LayoutTransition中发生的情况,或淡出删除的 View 的AlphaAnimation中发生的情况) ,它们不会像普通/父级 subview 那样参与相同的失效/重画逻辑。它们可以正确地重新显示(因此我们可以看到 fragment 滑出),但是它们不会重新绘制,因此,如果在此期间它们的内容实际发生更改,则不会随这些更改重新显示。应用程序中的错误的作用是使 fragment 正确滑出,但未绘制阴影,因为这需要重新绘制 View 以拾取这些更改。

您使 View 无效的方式是正确的,但是该错误表示该无效无效。

该错误以前没有出现过,因为我相信消失的 View 在被动画化时改变其外观并不常见(它们通常只是滑动或淡出,效果很好)。

该错误应在将来的版本中修复(我已经对其进行了修复)。同时,针对您的特定情况的一种解决方法是也添加一个无效的父容器。这将强制重新绘制 View 并正确显示阴影:

if (getParent() instanceof ViewGroup) {
    ((ViewGroup) getParent()).invalidate();
}

10-07 19:31
查看更多