Einsätze: abgerundete Ecken verwenden

Ab Android 12 (API-Level 31) können Sie RoundedCorner und Sie erhalten WindowInsets.getRoundedCorner(int position) Radius und Mittelpunkt für abgerundete Ecken des Gerätebildschirms Diese APIs Du kannst verhindern, dass die UI-Elemente deiner App auf Bildschirmen mit abgerundeten Ecken abgeschnitten werden. Ecken. Das Framework liefert getPrivacyIndicatorBounds() API, die das Bounded Rectangle jedes sichtbaren Mikrofons und der Kamera zurückgibt Indikatoren.

Wenn diese APIs in Ihrer App implementiert sind, haben sie keine Auswirkungen auf Geräte mit nicht gerundete Bildschirme.

<ph type="x-smartling-placeholder">
</ph> Bild mit abgerundeten Ecken mit Radien und einem Mittelpunkt
Abbildung 1: Abgerundete Ecken mit Radien und einem Mittelpunkt Punkt.

Rufen Sie zum Implementieren dieser Funktion die RoundedCorner-Informationen mit WindowInsets.getRoundedCorner(int position) relativ zu den Grenzen der . Wenn die App nicht den gesamten Bildschirm einnimmt, wendet die API die abgerundete Ecke, indem Sie den Mittelpunkt der abgerundeten Ecke auf das Fenster stützen Grenzen der App festlegen.

Das folgende Code-Snippet zeigt, wie eine App verhindern kann, dass ihre Benutzeroberfläche durch Sie legen einen Rand der Ansicht basierend auf den Informationen von RoundedCorner fest. In dieser ist es die abgerundete Ecke oben rechts.

Kotlin

// Get the top-right rounded corner from WindowInsets.
val insets = rootWindowInsets
val topRight = insets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT) ?: return

// Get the location of the close button in window coordinates.
val location = IntArray(2)
closeButton!!.getLocationInWindow(location)
val buttonRightInWindow = location[0] + closeButton.width
val buttonTopInWindow = location[1]

// Find the point on the quarter circle with a 45-degree angle.
val offset = (topRight.radius * Math.sin(Math.toRadians(45.0))).toInt()
val topBoundary = topRight.center.y - offset
val rightBoundary = topRight.center.x + offset

// Check whether the close button exceeds the boundary.
if (buttonRightInWindow < rightBoundary << buttonTopInWindow > topBoundary) {
   return
}

// Set the margin to avoid truncating.
val parentLocation = IntArray(2)
getLocationInWindow(parentLocation)
val lp = closeButton.layoutParams as FrameLayout.LayoutParams
lp.rightMargin = Math.max(buttonRightInWindow - rightBoundary, 0)
lp.topMargin = Math.max(topBoundary - buttonTopInWindow, 0)
closeButton.layoutParams = lp

Java

// Get the top-right rounded corner from WindowInsets.
final WindowInsets insets = getRootWindowInsets();
final RoundedCorner topRight = insets.getRoundedCorner(POSITION_TOP_RIGHT);
if (topRight == null) {
   return;
}

// Get the location of the close button in window coordinates.
int [] location = new int[2];
closeButton.getLocationInWindow(location);
final int buttonRightInWindow = location[0] + closeButton.getWidth();
final int buttonTopInWindow = location[1];

// Find the point on the quarter circle with a 45-degree angle.
final int offset = (int) (topRight.getRadius() * Math.sin(Math.toRadians(45)));
final int topBoundary = topRight.getCenter().y - offset;
final int rightBoundary = topRight.getCenter().x + offset;

// Check whether the close button exceeds the boundary.
if (buttonRightInWindow < rightBoundary << buttonTopInWindow > topBoundary) {
   return;
}

// Set the margin to avoid truncating.
int [] parentLocation = new int[2];
getLocationInWindow(parentLocation);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) closeButton.getLayoutParams();
lp.rightMargin = Math.max(buttonRightInWindow - rightBoundary, 0);
lp.topMargin = Math.max(topBoundary - buttonTopInWindow, 0);
closeButton.setLayoutParams(lp);

Achten Sie darauf,

Wenn Ihre Benutzeroberfläche den gesamten Bildschirm ausfüllt, können abgerundete Ecken Probleme mit Inhalten verursachen. zu beschneiden. Abbildung 2 zeigt beispielsweise ein Symbol in der Ecke des Displays mit das Layout hinter den Systemleisten:

<ph type="x-smartling-placeholder">
</ph> Ein Symbol, das durch abgerundete Ecken abgeschnitten wird
Abbildung 2: Ein Symbol, das durch abgerundete Ecken abgeschnitten wird Ecken.

Um dies zu vermeiden, achten Sie auf abgerundete Ecken und verwenden Sie Innenabstände, Ihre App-Inhalte aus den Ecken des Geräts zu übertragen, wie im Folgenden Beispiel:

Kotlin

class InsetsLayout(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        val insets = rootWindowInsets

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && insets != null) {
            applyRoundedCornerPadding(insets)
        }
        super.onLayout(changed, left, top, right, bottom)

    }

    @RequiresApi(Build.VERSION_CODES.S)
    private fun applyRoundedCornerPadding(insets: WindowInsets) {
        val topLeft = insets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)
        val topRight = insets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT)
        val bottomLeft = insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT)
        val bottomRight = insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT)

        val leftRadius = max(topLeft?.radius ?: 0, bottomLeft?.radius ?: 0)
        val topRadius = max(topLeft?.radius ?: 0, topRight?.radius ?: 0)
        val rightRadius = max(topRight?.radius ?: 0, bottomRight?.radius ?: 0)
        val bottomRadius = max(bottomLeft?.radius ?: 0, bottomRight?.radius ?: 0)

        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val windowBounds = windowManager.currentWindowMetrics.bounds
        val safeArea = Rect(
            windowBounds.left + leftRadius,
            windowBounds.top + topRadius,
            windowBounds.right - rightRadius,
            windowBounds.bottom - bottomRadius
        )

        val location = intArrayOf(0, 0)
        getLocationInWindow(location)

        val leftMargin = location[0] - windowBounds.left
        val topMargin = location[1] - windowBounds.top
        val rightMargin = windowBounds.right - right - location[0]
        val bottomMargin = windowBounds.bottom - bottom - location[1]

        val layoutBounds = Rect(
            location[0] + paddingLeft,
            location[1] + paddingTop,
            location[0] + width - paddingRight,
            location[1] + height - paddingBottom
        )

        if (layoutBounds != safeArea && layoutBounds.contains(safeArea)) {
            setPadding(
                calculatePadding(leftRadius, leftMargin, paddingLeft),
                calculatePadding(topRadius, topMargin, paddingTop),
                calculatePadding(rightRadius, rightMargin, paddingRight),
                calculatePadding(bottomRadius, bottomMargin, paddingBottom)
            )
        }
    }

    private fun calculatePadding(radius1: Int?, radius2: Int?, margin: Int, padding: Int): Int =
        (max(radius1 ?: 0, radius2 ?: 0) - margin - padding).coerceAtLeast(0)
}

Java

public class InsetsLayout extends FrameLayout {
    public InsetsLayout(@NonNull Context context) {
        super(context);
    }

    public InsetsLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        WindowInsets insets = getRootWindowInsets();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && insets != null) {
            applyRoundedCornerPadding(insets);
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    @RequiresApi(Build.VERSION_CODES.S)
    private void applyRoundedCornerPadding(WindowInsets insets) {
        RoundedCorner topLeft = insets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT);
        RoundedCorner topRight = insets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT);
        RoundedCorner bottomLeft = insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
        RoundedCorner bottomRight = insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
        int radiusTopLeft = 0;
        int radiusTopRight = 0;
        int radiusBottomLeft = 0;
        int radiusBottomRight = 0;
        if (topLeft != null) radiusTopLeft = topLeft.getRadius();
        if (topRight != null) radiusTopRight = topRight.getRadius();
        if (bottomLeft != null) radiusBottomLeft = bottomLeft.getRadius();
        if (bottomRight != null) radiusBottomRight = bottomRight.getRadius();

        int leftRadius = Math.max(radiusTopLeft, radiusBottomLeft);
        int topRadius = Math.max(radiusTopLeft, radiusTopRight);
        int rightRadius = Math.max(radiusTopRight, radiusBottomRight);
        int bottomRadius = Math.max(radiusBottomLeft, radiusBottomRight);

        WindowManager windowManager =
                (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds();
        Rect safeArea = new Rect(
                windowBounds.left + leftRadius,
                windowBounds.top + topRadius,
                windowBounds.right - rightRadius,
                windowBounds.bottom - bottomRadius
        );
        int[] location = {0, 0};
        getLocationInWindow(location);

        int leftMargin = location[0] - windowBounds.left;
        int topMargin = location[1] - windowBounds.top;
        int rightMargin = windowBounds.right - getRight() - location[0];
        int bottomMargin = windowBounds.bottom - getBottom() - location[1];

        Rect layoutBounds = new Rect(
                location[0] + getPaddingLeft(),
                location[1] + getPaddingTop(),
                location[0] + getWidth() - getPaddingRight(),
                location[1] + getHeight() - getPaddingBottom()
        );

        if (!layoutBounds.equals(safeArea) && layoutBounds.contains(safeArea)) {
            setPadding(
                    calculatePadding(radiusTopLeft, radiusBottomLeft,
                                         leftMargin, getPaddingLeft()),
                    calculatePadding(radiusTopLeft, radiusTopRight,
                                         topMargin, getPaddingTop()),
                    calculatePadding(radiusTopRight, radiusBottomRight,
                                         rightMargin, getPaddingRight()),
                    calculatePadding(radiusBottomLeft, radiusBottomRight,
                                         bottomMargin, getPaddingBottom())
            );
        }
    }

    private int calculatePadding(int radius1, int radius2, int margin, int padding) {
        return Math.max(Math.max(radius1, radius2) - margin - padding, 0);
    }
}

Mit diesem Layout wird festgelegt, ob sich die Benutzeroberfläche auf den Bereich der abgerundeten Ecken erstreckt und fügt dort, wo es kommt, einen Abstand hinzu. Abbildung 3 zeigt die Layoutgrenzen anzeigen. Entwickler aktiviert, um den angewendeten Abstand deutlicher darzustellen:

<ph type="x-smartling-placeholder">
</ph> Ein Symbol mit einem Rahmen, der es von der Ecke wegbewegt.
Abbildung 3: Ein Symbol mit einer angewendeten Füllung, um es wegzubewegen aus der Ecke.

Dazu berechnet das Layout zwei Rechtecke: safeArea ist die Fläche innerhalb der Radien der abgerundeten Ecken und layoutBounds ist die Größe des Layouts abzüglich der Abstände. Wenn layoutArea safeArea vollständig enthält, dann: werden die untergeordneten Elemente des Layouts möglicherweise abgeschnitten. In diesem Fall wird der Abstand wurde hinzugefügt, damit das Layout weiterhin innerhalb von safeArea verbleibt.

Wenn Sie prüfen, ob layoutBounds safeArea vollständig enthält, vermeiden Sie es, Abstand, wenn sich das Layout nicht bis zum Rand des Bildschirms erstreckt. Abbildung 4 zeigt das Layout an, wenn es nicht hinter der Navigationsleiste gezeichnet ist. In diesem Fall erweitert sich das Layout nicht weit genug nach unten, um sich innerhalb der abgerundeten Ecken zu befinden, sie in den Bereich der Navigationsleiste passen. Ein Abstand ist nicht erforderlich.

<ph type="x-smartling-placeholder">
</ph> Ein Layout, das nicht hinter dem System und den Navigationsleisten gezeichnet wird.
Abbildung 4: Ein Layout, das nicht hinter dem System zeichnet und Navigationsleisten.