繁体   English   中英

在Android视图剪辑边界外绘图时:如何防止底层视图在我的自定义视图上绘制?

[英]When drawing outside the view clip bounds with Android: how do I prevent underling views from drawing on top of my custom view?

我写了一个自定义Android视图,需要在其剪切边界之外绘制。

这就是我所拥有的:

在此输入图像描述

这是当我点击一个按钮时发生的情况,例如右键:

在此输入图像描述

如何防止下面的视图在我的“句柄”上绘制?

我项目中的一些相关伪代码如下。

我的自定义视图MyHandleView如下所示:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Path p = mPath;
    int handleWidth = mHandleWidth;
    int handleHeight = mHandleHeight;
    int left = (getWidth() >> 1) - handleWidth;

    canvas.save();

    // allow drawing out of bounds vertically
    Rect clipBounds = canvas.getClipBounds();
    clipBounds.inset(0, -handleHeight);
    canvas.clipRect(clipBounds, Region.Op.REPLACE);

    // translate up to draw the handle
    canvas.translate(left, -handleHeight);

    // draw my background shape
    canvas.drawPath(p, mPaint);

    canvas.restore();
}

布局是这样的(我简化了一点):

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Main content of the SlidingUpPanel -->
    <fragment
        android:above=@+id/panel"
        class="com.neosperience.projects.percassi.android.home.HomeFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="?android:attr/actionBarSize"
        tools:layout="@layout/fragment_home" />

    <!-- The Sliding Panel -->
    <LinearLayout
        android:id="@id/panel"
        android:layout_width="match_parent"
        android:layout_height="@dimen/myFixedSize"
        android:alignParentBottom="true"
        android:orientation="vertical"
        android:clipChildren="false">

        <MyHandleView  xmlns:custom="http://schemas.android.com/apk/res-auto.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            custom:handleHeight="@dimen/card_panel_handle_height"
            custom:handleWidthRatio="@dimen/card_panel_handle_width_ratio"
            custom:handleBackgroundColor="#000"/>

        <TextView
            android:id="@+id/loyaltyCardPanelTitle"
            android:layout_width="match_parent"
            android:layout_height="@dimen/card_panel_height"
            android:background="#000"
            android:gravity="center"
            android:textStyle="bold"
            android:textSize="24sp"
            android:textColor="#fff"
            android:text="My TEXT"/>
    </LinearLayout>

</RelativeLayout>

您可以将片段视为包含底部两个按钮的视图,位于LinearLayout中。

代替外部RelativeLayout,我实际上正在使用库中的视图:SlidingUpPanelLayout( https://github.com/umano/AndroidSlidingUpPanel )。 但我测试了这个RelativeLayout的行为:同样的事情,意味着库没有关系。

我这样说只是为了让你知道我不能只在那里放置一个FrameLayout并避免在剪切边界之外绘制

我怀疑这与以下事实有关:当我触摸按钮时它重绘自己,但我的另一个视图(在层次结构中的其他位置)不会被重新绘制(这是一个优化,我怀疑它可以是禁用)。

我希望能够在任何其他“近”(或任何)视图失效时使我的自定义视图无效,因此我需要某种关于视图失效的监听器,但我不知道任何。

有人可以帮忙吗?

我自己找到了解决方案,即使这不是表演的最佳选择。

只需添加:

android:clipChildren="false"

到RelativeLayout(或你有的任何布局)。

这有2个效果(可能更多,这是我感兴趣的两个): - ViewGroup不剪切他的孩子的绘图(显而易见) - ViewGroup在考虑时不检查与脏区域的交集(无效)哪些孩子要重绘

我挖掘了有关无效的View代码。

这个过程更像或更喜欢这样:

  1. 一个视图使自身无效,它通常绘制的区域(矩形)成为要重绘的“脏区域”
  2. 视图告诉其父级(某种类型的ViewGroup)需要重绘自己
  3. 父母对其根的父母做同样的事
  4. 层次结构中的每个父项为每个子项循环,并检查脏区域是否与其中一些相交
  5. 如果它这样做也重绘它们

在第4步中,涉及剪切:仅当clipChildren为true时,ViewGroup检查其子项的视图边界:这意味着如果将其置于false,则当它们中的任何一个无效时,总是重绘其所有子项

所以,我的View层次结构是这样的:

ViewGroup A
|
|----- fragment subtree (containing buttons, map,
|       whatever element that I don't want to draw
|       on top of my handle)
|
|----- ViewGroup B
       |
       |---- my handle (which draw outside its clip bounds)

在我的情况下,“句柄”绘制它的束缚,在通常由片段子树的某个元素绘制的东西之上。

当片段内的任何视图无效时,它会在视图树中向上传递其“脏区域”,并且每个视图组都会检查是否有其他子项要在该区域中重绘。

如果我没有在其上设置clipBounds =“false” ,ViewGroup B会剪切我在剪辑边界外绘制的内容。

如果片段子树中的任何内容无效,则ViewGroup A将看到ViewGroup B脏区域未与片段子树区域相交,并将跳过重绘ViewGroup B.

但是,如果我还告诉ViewGroup A不剪辑子节点,它仍然会给ViewGroup B一个无效命令,这将导致重新绘制我的句柄。

所以解决方案是确保设置

android:clipChildren="false"

在视图上方的层次结构中的任何ViewGroup上绘制出内容可能落在您正在绘制的越界区域“下方”的边界。

这种明显的副作用是,每当我使ViewGroup A中的任何视图无效时,无效区域将被转发到其中的所有视图。

但是,将跳过任何与使用clipChildren =“true”(默认值)的ViewGroup内的脏区域不相交的视图。

因此,为避免在执行此操作时出现性能问题,请确保具有clipChildren =“true”的视图组没有多少“标准”直接子项。 而对于“标准”,我的意思是他们不会超出他们的视角。

因此,例如,如果在我的示例中,ViewGroup B包含许多视图,请考虑使用clipChildren =“true”将所有这些视图包装在ViewGroup中,并且只将此视图组和在其区域之外绘制的视图作为ViewGroup B的直接子项保留。同样如此对于ViewGroup A.

这个简单的事实将确保没有其他View将重绘,如果它们不在无效的脏区域中,最小化所需的重绘。

如果有人有话,我仍然愿意再听一次;)

所以我会等一下,然后将其标记为已接受的答案。

编辑:许多设备在处理clipChildren =“false”时做了一些不同的事情。 我发现我必须在我的自定义窗口小部件的所有父视图上设置clipChildren =“false”,这些视图可能包含其层次结构中应该绘制窗口小部件的“越界区域”的元素,或者您可能会看到自定义绘图显示应该覆盖它的另一个视图的TOP。 例如,在我的布局中,我有一个导航抽屉,应该覆盖我的“手柄”。 如果我没有在NavigationDrawer布局上设置clipChildren =“false”,我有时会看到我的手柄弹出打开的抽屉前面。

编辑2:我的自定义小部件有0高度,并绘制自己“顶部”。 在Nexus设备上工作得很好,但其他许多设备都有一些“优化”,可以完全跳过绘制高度为0或宽度为0的视图。 因此,如果您想要编写一个从其界限中绘制的组件,请注意这一点:您必须为其指定至少1个像素的高度/宽度。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM