Bordas arredondadas

A partir do Android 12 (API de nível 31), é possível usar RoundedCorner e WindowInsets.getRoundedCorner(int position) para definir o raio e o ponto central para cantos arredondados. Com essas APIs, o app pode evitar que os elementos da IU sejam truncados em telas com cantos arredondados. O framework também fornece a API getPrivacyIndicatorBounds(), que retorna o retângulo limitado de qualquer indicador de microfone e câmera visível.

Quando implementadas no app, essas APIs não afetam dispositivos com telas não arredondadas.

Imagem mostrando cantos arredondados com raios e um ponto central
Figura 1: cantos arredondados com raio e um ponto central

Para implementar esse recurso, acesse as informações do RoundedCorner usando WindowInsets.getRoundedCorner(int position) em relação aos limites do aplicativo. Se o app não ocupar toda a tela, a API aplicará o canto arredondado baseando o ponto central do canto arredondado nos limites da janela do app.

O snippet de código a seguir mostra um exemplo simples para um app evitar o truncamento da IU com a definição de uma margem para a visualização com base nas informações de RoundedCorner. Neste caso, a margem é o canto arredondado da parte superior direita.

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