簡體   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