角丸

Android 12(API レベル 31)以降では、RoundedCornerWindowInsets.getRoundedCorner(int position) を使用して角丸の半径と中心点を設定できます。これらの API を使用すると、角の丸い画面で UI 要素が切り詰められることを防止できます。また、マイクとカメラのインジケーターが表示されている場合にその境界付き長方形を返す、getPrivacyIndicatorBounds() API も用意されています。

これらの API をアプリに実装しても、角の丸くない画面では効果がありません。

半径と中心点が示された角丸の画像
図 1: 半径と中心点が示された角丸

この機能を実装するには、アプリの境界に相対する WindowInsets.getRoundedCorner(int position)RoundedCorner 情報を取得します。アプリが画面全体を占有しない場合、API は中心点をアプリのウィンドウ境界上に置いて角丸を適用します。

次のコード スニペットは、アプリが RoundedCorner の情報に基づいてビューのマージンを設定することで、UI の切り詰めを防止する簡単な例を示しています。これは右上の角丸の場合です。

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 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);