7

所以,我有一个非常奇怪的设置,我从中得到了一些奇怪的视觉错误。基本上,我在相对布局中有两个视图:第一个只是 ImageView 背景图像;第二个是相同的背景图像,但模糊以产生一种毛玻璃后效果:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/profile_holder"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- background -->
    <ImageView
        android:id="@+id/backgroundImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="0dp"
        android:scaleType="centerCrop"
        android:src="@drawable/default_logo" />

    <!-- blurry background -->
    <com.custom.MaskedBlurredBackgroundView_
        android:id="@+id/blurred_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="0dp" />

<ScrollView
    android:id="@+id/scrolly"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true" >

            <com.custom.HalvedLinearLayout_
                android:paddingTop="100dp"
                android:id="@+id/profileHolder"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                 >

                <Button
                    android:id="@+id/profileButtonTracks"
                    style="@style/ProfileButtons"
                    android:drawableLeft="@drawable/ic_profile_menu_music"
                    android:text="@string/profile_button_tracks" />

...

模糊的背景应该只在 HalvedLinearLayout_ 视图的后面,该视图向上滚动,所以我需要模糊的背景将自己掩盖在 HalvedLinearLayout_ 上方,这样非模糊背景就会显示出来:

scrolly  = ((ScrollView) rootView.findViewById(R.id.scrolly));
scrolly.setOnScrollListener(new ScrollView.OnScrollListener() {

    private boolean hasScrolled = false;

    @Override
    public void onScrollChanged(int l, int t, int oldl, int oldt) {

        if (!hasScrolled) {
            hasScrolled = true;
            Logger.action(Logger.Action.BioView, band);
        }

        positionMask();
    }
});

...

protected void positionMask() {
    if (blurredBackground == null) return;

    Logger.d(TAG, "positionMask() " + rootView.getId());

    //blurredBackground.setMaskBottom(blurredBackground.getHeight() - profileHolder.getPaddingTop() - scrollY);
    blurredBackground.setMaskBottom(profileHolder.getTop() + profileHolder.getPaddingTop() - scrolly.getScrollY());
    //blurredBackground.invalidate();
    //profileHolder.invalidate();
    rootView.postInvalidate();
}

现在,问题是一切都按原样工作,除了一个明显的事实,即当您滚动 ScrollView 时,blurredBackground 实际上会覆盖 HalvedLinearLayout_,从而消除其中的按钮。(但有时只是中途。有时会很好,有时按钮会被画掉一半,而另一半会保留……各种奇怪的故障。)

我开始调试,我注意到一些有趣的事情:在 rootView 上调用 invalidate() 实际上并没有使所有子视图 invalidate()。我用一些日志记录东西覆盖了所有的无效和绘制函数:

public class MaskedBlurredBackgroundView extends BlurredBackgroundView {
    /***
     * Sets the mask to cover everything above the given Y coordinate
     * @param t Y Coordinate in pixels
     */
    public void setMaskBottom(int y) {
        maskBottom  = y;
    }

    private void clipCanvas(Canvas canvas) {
        Logger.d(TAG, "dispatchDraw clipping: " + maskBottom + ", " + getWidth() + "," + getHeight());
        canvas.clipRect(0, maskBottom, getWidth(), getHeight(), Region.Op.REPLACE);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.save();
        clipCanvas(canvas);
        super.dispatchDraw(canvas);

        canvas.restore();
    }
    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        Logger.d(TAG, "drawChild: " + child + getWidth() + "," + getHeight());

        return super.drawChild(canvas, child, drawingTime);
    }

    @Override
    public void invalidate() {
        Logger.d(TAG, "invalidate: " + getWidth() + "," + getHeight());
        super.invalidate();
    }

现在,当我查看日志时,滚动时 rootView.invalidate() 正在触发,但孩子们从不重绘:

07-23 12:36:49.328: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.348: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.348: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.368: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.368: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.378: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.398: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.418: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.448: D/BandDetailFrag(12982): positionMask() 2131165298
07-23 12:36:49.478: D/BandDetailFrag(12982): positionMask() 2131165298

我如何强迫孩子们以正确的顺序重绘?现在我猜他们画得乱七八糟,这就是为什么模糊的背景在其他一切之上。

这是故障的屏幕截图:

故障截图:看到模糊的背景被正确掩盖,但在 HalvedLinearLayout_ 上绘制

4

2 回答 2

6

我遇到了同样的问题invalidate(),并且由于在 SO 的其他地方没有好的答案,我想我会发布我的解决方案。

这是一个递归函数,它将遍历 a 的所有子视图ViewGroup。如果孩子是 a ViewGroup,它将递归并搜索孩子。否则,它将调用invalidate()孩子(这是终止案例)。

public void invalidateRecursive(ViewGroup layout) {
    int count = layout.getChildCount();
    View child;
    for (int i = 0; i < count; i++) {
        child = layout.getChildAt(i);
        if(child instanceof ViewGroup)
            invalidateRecursive((ViewGroup) child);
        else
            child.invalidate();
    }
}

参数layout应该可以是任何类型ViewGroupLinearLayout,RelativeLayout等),这使得这是一个非常通用的解决方案。

它适用于我的目的,但我还没有对其进行彻底的测试。如果有任何错误,请评论并让我知道。

于 2015-09-13T00:25:28.963 回答
1

好的,我至少想出了如何摆脱故障:我所要做的就是将“Region.Op.REPLACE”更改为“Region.Op.INTERSECT”,现在它可以工作了!

Invalidate() 仍然不会重绘孩子,但我想现在它正在工作,我会让它滑动。

于 2013-07-23T22:04:09.780 回答