Este codelab faz parte do curso Android avançado no Kotlin. Você vai aproveitar mais este curso se fizer os codelabs em sequência, mas isso não é obrigatório. Todos os codelabs do curso estão listados na página de destino dos codelabs do Android avançado em Kotlin.
Introdução
Para este codelab, corte é uma maneira de definir regiões de uma imagem, tela ou bitmap que são desenhadas ou não desenhadas seletivamente na tela. Um dos objetivos do corte é reduzir o excesso de renderização. O overdraw acontece quando um pixel na tela é desenhado mais de uma vez para mostrar a imagem final. Ao reduzir o overdraw, você minimiza o número de vezes que um pixel ou uma região da tela é desenhada para maximizar o desempenho do desenho. Você também pode usar o recorte para criar efeitos interessantes no design e na animação da interface do usuário.
Por exemplo, ao desenhar uma pilha de cards sobrepostos, conforme mostrado abaixo, em vez de desenhar cada card por completo de baixo para cima, geralmente é mais eficiente desenhar apenas as partes visíveis. "Geralmente", porque as operações de corte também têm um custo e, no geral, o sistema Android faz muita otimização de desenho.

Para desenhar apenas as partes visíveis dos cards, especifique uma região de recorte para cada um deles. Por exemplo, no diagrama abaixo, quando um retângulo de recorte é aplicado a uma imagem, apenas a parte dentro desse retângulo é mostrada.

A região de corte geralmente é um retângulo, mas pode ser qualquer forma ou combinação de formas, até mesmo texto. Você também pode especificar se quer incluir ou excluir a região dentro da região de recorte. Por exemplo, você pode criar uma região de recorte circular e mostrar apenas o que está fora do círculo.
Neste codelab, você vai testar várias formas de corte.
O que você já precisa saber
Você precisa:
- Como criar um app com um
Activitye executá-lo usando o Android Studio. - Como criar e desenhar em um
Canvas. - Como criar um
Viewpersonalizado e substituironDraw()eonSizeChanged().
O que você vai aprender
- Como cortar objetos para desenhar em um
Canvas. - Como salvar e restaurar estados de desenho de uma tela.
- Como aplicar transformações a um canvas e a um texto.
Atividades deste laboratório
- Crie um app que desenhe formas cortadas na tela, demonstrando diferentes maneiras de cortar e o resultado na visibilidade dessas formas.
- Você também vai desenhar um texto traduzido e distorcido.
O app ClippingExample demonstra como usar e combinar formas para especificar quais partes de uma tela são mostradas em uma visualização. O app final vai ficar assim:

Você vai criar esse app do zero. Por isso, será necessário configurar um projeto, definir dimensões e strings e declarar algumas variáveis.
Etapa 1: criar o projeto ClippingExample
- Crie um projeto Kotlin chamado
ClippingExamplecom o modelo Empty Activity. Usecom.example.androidcomo o prefixo do nome do pacote. - Abra
MainActivity.kt. - No método
onCreate(), substitua a visualização de conteúdo padrão e defina a visualização de conteúdo como uma nova instância deClippedView. Essa será sua visualização personalizada para os exemplos de corte que você vai criar em seguida.
setContentView(ClippedView(this))- No mesmo nível de
MainActivity.kt, crie um novo arquivo e classe do Kotlin para uma visualização personalizada chamadaClippedViewque estendeView. Dê a ela a assinatura mostrada abaixo. O restante do seu trabalho vai ficar dentro desseClippedView. A anotação@JvmOverloadsinstrui o compilador Kotlin a gerar sobrecargas para essa função que substituem os valores de parâmetro padrão.
class ClippedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}Etapa 2: adicionar dimensões e recursos de string
- Defina as dimensões que você vai usar para as visualizações cortadas em um novo arquivo de recursos em
res/values/dimens.xml. Essas dimensões padrão são codificadas e dimensionadas para caber em uma tela bem pequena.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">90dp</dimen>
<dimen name="clipRectBottom">90dp</dimen>
<dimen name="clipRectTop">0dp</dimen>
<dimen name="clipRectLeft">0dp</dimen>
<dimen name="rectInset">8dp</dimen>
<dimen name="smallRectOffset">40dp</dimen>
<dimen name="circleRadius">30dp</dimen>
<dimen name="textOffset">20dp</dimen>
<dimen name="strokeWidth">4dp</dimen>
<dimen name="textSize">18sp</dimen>
</resources>Para que o app fique bom em uma tela maior (e para ver detalhes com mais facilidade), crie um arquivo dimens com valores maiores que só se aplicam a telas maiores.
- No Android Studio, clique com o botão direito do mouse na pasta values e escolha New > Values resource file.
- Na caixa de diálogo New Resource File, chame o arquivo de
dimens. Em Qualificadores disponíveis, selecione Menor largura da tela e clique no botão >> para adicionar aos Qualificadores escolhidos. Insira 480 na caixa Menor largura da tela e clique em OK.

- O arquivo vai aparecer na pasta de valores, conforme mostrado abaixo.

- Se você não encontrar o arquivo, mude para a visualização Arquivos do projeto do app. O caminho completo do novo arquivo é mostrado abaixo:
ClippingExample/app/src/main/res/values-sw480dp/dimens.xml.

- Substitua o conteúdo padrão do arquivo
values-sw480dp/dimens.xmlpelas dimensões abaixo.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">120dp</dimen>
<dimen name="clipRectBottom">120dp</dimen>
<dimen name="rectInset">10dp</dimen>
<dimen name="smallRectOffset">50dp</dimen>
<dimen name="circleRadius">40dp</dimen>
<dimen name="textOffset">25dp</dimen>
<dimen name="strokeWidth">6dp</dimen>
</resources>- Em
strings.xml, adicione as seguintes strings. Eles serão usados para mostrar texto na tela.
<string name="clipping">Clipping</string>
<string name="translated">translated text</string>
<string name="skewed">"Skewed and "</string>Etapa 3: criar e inicializar um objeto Paint e um Path
- Volte para a visualização Android do projeto.
- Em
ClippedView, defina uma variávelPaintpara desenhar. Ative o anti-aliasing e use a largura do traço e o tamanho do texto definidos nas dimensões, conforme mostrado abaixo.
private val paint = Paint().apply {
// Smooth out edges of what is drawn without affecting shape.
isAntiAlias = true
strokeWidth = resources.getDimension(R.dimen.strokeWidth)
textSize = resources.getDimension(R.dimen.textSize)
}- Em
ClippedView, crie e inicialize umPathpara armazenar localmente o caminho do que foi desenhado. Importeandroid.graphics.Path.
private val path = Path()Etapa 4: configurar as formas
Neste app, você está mostrando várias linhas e duas colunas de formas cortadas de várias maneiras.
Todos eles têm em comum:
- Um retângulo grande (quadrado) que funciona como um contêiner
- Uma linha diagonal no retângulo grande
- Círculo
- Uma string de texto curta

Nesta etapa, você configura as dimensões dessas formas com base nos recursos. Assim, só precisa extrair as dimensões uma vez quando as usar mais tarde.
- Em
ClippedView, abaixo dopath, adicione variáveis para dimensões de um retângulo de corte ao redor de todo o conjunto de formas.
private val clipRectRight = resources.getDimension(R.dimen.clipRectRight)
private val clipRectBottom = resources.getDimension(R.dimen.clipRectBottom)
private val clipRectTop = resources.getDimension(R.dimen.clipRectTop)
private val clipRectLeft = resources.getDimension(R.dimen.clipRectLeft)- Adicione variáveis para o encarte de um retângulo e o deslocamento de um retângulo pequeno.
private val rectInset = resources.getDimension(R.dimen.rectInset)
private val smallRectOffset = resources.getDimension(R.dimen.smallRectOffset)- Adicione uma variável para o raio de um círculo. É o raio do círculo desenhado dentro do retângulo.
private val circleRadius = resources.getDimension(R.dimen.circleRadius)- Adicione um deslocamento e um tamanho de texto para o texto desenhado dentro do retângulo.
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)Etapa 4: definir os locais de linhas e colunas
As formas desse app são mostradas em duas colunas e quatro linhas, determinadas pelos valores das dimensões configuradas acima. O cálculo matemático não faz parte deste codelab, mas confira ao copiar o código fornecido nesta etapa.
- Defina as coordenadas para duas colunas.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight- Adicione as coordenadas de cada linha, incluindo a linha final do texto transformado.
private val rowOne = rectInset
private val rowTwo = rowOne + rectInset + clipRectBottom
private val rowThree = rowTwo + rectInset + clipRectBottom
private val rowFour = rowThree + rectInset + clipRectBottom
private val textRow = rowFour + (1.5f * clipRectBottom)- Execute o app. Ele vai abrir com uma tela branca em branco abaixo do nome dele.

Em onDraw(), você chama métodos para desenhar sete retângulos cortados diferentes, conforme mostrado na captura de tela do app abaixo. Os retângulos são todos desenhados da mesma forma. A única diferença é a região de corte definida e a localização na tela.

O algoritmo usado para desenhar os retângulos funciona como mostrado no diagrama e na explicação abaixo. Em resumo, você desenha uma série de retângulos movendo a origem do Canvas. Conceitualmente, isso consiste nas seguintes etapas:

(1) Primeiro, traduza Canvas para onde você quer que o retângulo seja desenhado. Em vez de calcular onde o próximo retângulo e todas as outras formas precisam ser desenhados, você move a origem Canvas, ou seja, o sistema de coordenadas.
(2) Em seguida, desenhe o retângulo na nova origem da tela. Ou seja, você desenha as formas no mesmo local no sistema de coordenadas traduzido. Isso é muito mais simples e um pouco mais eficiente.
(3) Por fim, restaure o Canvas para o Origin original.
Este é o algoritmo que você vai implementar:
- Em
onDraw(), chame uma função para preencher oCanvascom a cor de fundo cinza e desenhar as formas originais. - Chame uma função para cada retângulo cortado e o texto a ser desenhado.
Para cada retângulo ou texto:
- Salve o estado atual do
Canvaspara poder redefinir para esse estado inicial. - Traduza o
Originda tela para o local em que você quer desenhar. - Aplicar formas e caminhos de recorte.
- Desenhe o retângulo ou o texto.
- Restaure o estado do
Canvas.
Etapa: substituir onDraw()
- Substitua
onDraw()conforme mostrado no código abaixo. Você chama uma função para cada forma que está desenhando, o que será implementado mais tarde.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawBackAndUnclippedRectangle(canvas)
drawDifferenceClippingExample(canvas)
drawCircularClippingExample(canvas)
drawIntersectionClippingExample(canvas)
drawCombinedClippingExample(canvas)
drawRoundedRectangleClippingExample(canvas)
drawOutsideClippingExample(canvas)
drawSkewedTextExample(canvas)
drawTranslatedTextExample(canvas)
// drawQuickRejectExample(canvas)
}- Crie stubs para cada uma das funções de desenho para que o código continue sendo compilado. Você pode copiar o código abaixo.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
}
private fun drawDifferenceClippingExample(canvas: Canvas){
}
private fun drawCircularClippingExample(canvas: Canvas){
}
private fun drawIntersectionClippingExample(canvas: Canvas){
}
private fun drawCombinedClippingExample(canvas: Canvas){
}
private fun drawRoundedRectangleClippingExample(canvas: Canvas){
}
private fun drawOutsideClippingExample(canvas: Canvas){
}
private fun drawTranslatedTextExample(canvas: Canvas){
}
private fun drawSkewedTextExample(canvas: Canvas){
}
private fun drawQuickRejectExample(canvas: Canvas){
}O app desenha o mesmo retângulo e formas sete vezes. Primeiro, sem recorte e, depois, seis vezes com vários caminhos de recorte aplicados. O método drawClippedRectangle() extrai o código para desenhar um retângulo, conforme mostrado abaixo.

Etapa 1: criar o método drawClippedRectangle()
- Crie um método
drawClippedRectangle()que receba um argumentocanvasdo tipoCanvas.
private fun drawClippedRectangle(canvas: Canvas) {
}- No método
drawClippedRectangle(), defina os limites do retângulo de recorte para toda a forma. Aplique um retângulo de recorte que restrinja o desenho apenas ao quadrado.
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)O método Canvas.clipRect(...) reduz a região da tela em que as futuras operações de desenho podem gravar. Ele define os limites de recorte como a interseção espacial do retângulo de recorte atual e o retângulo transmitido para clipRect(). Há muitas variantes do método clipRect() que aceitam diferentes formas para regiões e permitem operações diferentes no retângulo de recorte.
- Preencha o
canvascom a cor branca. Sim. Toda a tela, porque você não está desenhando retângulos, está cortando! Devido ao retângulo de recorte, apenas a região definida por ele é preenchida, criando um retângulo branco. O restante da superfície permanece cinza.
canvas.drawColor(Color.WHITE)- Mude a cor para vermelho e desenhe uma linha diagonal dentro do retângulo de recorte.
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)- Defina a cor como verde e desenhe um círculo dentro do retângulo de recorte.
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)- Defina a cor como azul e desenhe o texto alinhado à borda direita do retângulo de recorte. Use
canvas.drawText()para desenhar texto.
paint.color = Color.BLUE
// Align the RIGHT side of the text with the origin.
paint.textSize = textSize
paint.textAlign = Paint.Align.RIGHT
canvas.drawText(
context.getString(R.string.clipping),
clipRectRight,textOffset,paint
)Etapa 2: implementar o método drawBackAndUnclippedRectangle()
- Para ver o método
drawClippedRectangle()em ação, desenhe o primeiro retângulo sem corte implementando o métododrawBackAndUnclippedRectangle(), conforme mostrado abaixo. Salve acanvas, faça a translação para a primeira posição de linha e coluna, desenhe chamandodrawClippedRectangle()e restaure acanvasao estado anterior.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
canvas.drawColor(Color.GRAY)
canvas.save()
canvas.translate(columnOne,rowOne)
drawClippedRectangle(canvas)
canvas.restore()
}- Execute o app. Você vai ver o primeiro retângulo branco com o círculo, a linha vermelha e o texto em um plano de fundo cinza.

Nos exemplos de métodos de recorte a seguir, você aplica várias combinações de regiões de recorte para alcançar efeitos gráficos e aprende a combinar regiões de recorte para criar qualquer forma que precisar.
Cada um desses métodos segue o mesmo padrão.
- Salvar o estado atual da tela:
canvas.save()
O contexto de atividade mantém uma pilha de estados de desenho. Os estados de desenho consistem na matriz de transformação e na região de corte atuais. Você pode salvar o estado atual, realizar ações que mudam o estado do desenho (como translação ou rotação da tela) e restaurar o estado salvo. (Observação: isso é como o comando "stash" no git.)
Quando seu desenho inclui transformações, encadeá-las e desfazê-las invertendo-as é propenso a erros. Por exemplo, se você traduzir, esticar e girar, a situação vai ficar complexa rapidamente. Em vez disso, salve o estado da tela, aplique as transformações, desenhe e restaure o estado anterior.
Por exemplo, você pode definir uma região de corte e salvar esse estado. Em seguida, traduza a tela, adicione uma região de recorte e gire. Depois de desenhar, você pode restaurar o estado de recorte original e fazer uma transformação de tradução e distorção diferente, conforme mostrado no diagrama.

- Traduza a origem da tela para as coordenadas de linha/coluna:
canvas.translate()
É muito mais simples mover a origem da tela e desenhar a mesma coisa em um novo sistema de coordenadas do que mover todos os elementos para desenhar. (Dica: use a mesma técnica para girar elementos.)
- Aplique transformações ao
path, se houver. - Aplicar corte:
canvas.clipPath(path) - Desenhe as formas:
drawClippedRectangle() or drawText() - Restaurar o estado anterior da tela:
canvas.restore()
Etapa 1: implementar drawDifferenceClippingExample(canvas)
Adicione o código para desenhar o segundo retângulo, que usa a diferença entre dois retângulos de recorte para criar um efeito de moldura.

Use o código abaixo, que faz o seguinte:
- Salve a tela.
- Traduza a origem da tela para o espaço aberto na primeira linha, segunda coluna, à direita do primeiro retângulo.
- Aplicar dois retângulos de recorte. O operador
DIFFERENCEsubtrai o segundo retângulo do primeiro.
- Chame o método
drawClippedRectangle()para desenhar a tela modificada. - Restaura o estado da tela.
private fun drawDifferenceClippingExample(canvas: Canvas) {
canvas.save()
// Move the origin to the right for the next rectangle.
canvas.translate(columnTwo,rowOne)
// Use the subtraction of two clipping rectangles to create a frame.
canvas.clipRect(
2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset
)
// The method clipRect(float, float, float, float, Region.Op
// .DIFFERENCE) was deprecated in API level 26. The recommended
// alternative method is clipOutRect(float, float, float, float),
// which is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
canvas.clipRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset,
Region.Op.DIFFERENCE
)
} else {
canvas.clipOutRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset
)
}
drawClippedRectangle(canvas)
canvas.restore()
}- Execute o app. Ele vai ficar assim:

Etapa 2: implementar drawCircularClippingExample(canvas)
Em seguida, adicione código para desenhar um retângulo que usa uma região de recorte circular criada com um caminho circular, removendo (não desenhando) o círculo e mostrando o plano de fundo cinza.

private fun drawCircularClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowTwo)
// Clears any lines and curves from the path but unlike reset(),
// keeps the internal data structure for faster reuse.
path.rewind()
path.addCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,Path.Direction.CCW
)
// The method clipPath(path, Region.Op.DIFFERENCE) was deprecated in
// API level 26. The recommended alternative method is
// clipOutPath(Path), which is currently available in
// API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipPath(path, Region.Op.DIFFERENCE)
} else {
canvas.clipOutPath(path)
}
drawClippedRectangle(canvas)
canvas.restore()
}Etapa 3: implementar drawIntersectionClippingExample(canvas)
Em seguida, adicione o código para desenhar a interseção de dois retângulos de recorte na segunda linha e coluna.


Dependendo da resolução da tela, a aparência dessa região vai variar. Teste a dimensão smallRectOffset para mudar o tamanho da região visível. Um smallRectOffset menor resulta em uma região maior na tela.
private fun drawIntersectionClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowTwo)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight - smallRectOffset,
clipRectBottom - smallRectOffset
)
// The method clipRect(float, float, float, float, Region.Op
// .INTERSECT) was deprecated in API level 26. The recommended
// alternative method is clipRect(float, float, float, float), which
// is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom,
Region.Op.INTERSECT
)
} else {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom
)
}
drawClippedRectangle(canvas)
canvas.restore()
}
Etapa 4: implementar drawCombinedClippingExample(canvas)
Em seguida, combine formas, um círculo e um retângulo, e desenhe qualquer caminho para definir uma região de recorte.

private fun drawCombinedClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowThree)
path.rewind()
path.addCircle(
clipRectLeft + rectInset + circleRadius,
clipRectTop + circleRadius + rectInset,
circleRadius,Path.Direction.CCW
)
path.addRect(
clipRectRight / 2 - circleRadius,
clipRectTop + circleRadius + rectInset,
clipRectRight / 2 + circleRadius,
clipRectBottom - rectInset,Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
Etapa 5: implementar drawRoundedRectangleClippingExample(canvas)
Em seguida, adicione um retângulo arredondado, que é uma forma de recorte usada com frequência.

- No nível superior, crie e inicialize uma variável de retângulo.
RectFé uma classe que contém coordenadas de retângulo em ponto flutuante.
private var rectF = RectF(
rectInset,
rectInset,
clipRectRight - rectInset,
clipRectBottom - rectInset
)- Implemente a função
drawRoundedRectangleClippingExample(). A funçãoaddRoundRect()usa um retângulo, valores para os valores x e y do raio do canto e a direção para envolver o contorno do retângulo arredondado.Path.Directionespecifica como as formas fechadas (por exemplo, retângulos, ovais) são orientadas quando adicionadas a um caminho.CCWsignifica sentido anti-horário.
private fun drawRoundedRectangleClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowThree)
path.rewind()
path.addRoundRect(
rectF,clipRectRight / 4,
clipRectRight / 4, Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
Etapa 6: implementar drawOutsideClippingExample(canvas)
Corte a parte de fora ao redor do retângulo dobrando os encartes do retângulo de corte.

private fun drawOutsideClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne,rowFour)
canvas.clipRect(2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset)
drawClippedRectangle(canvas)
canvas.restore()
}
Etapa 7: implementar drawTranslatedTextExample(canvas)
Desenhar texto não é muito diferente de outras formas, e você pode aplicar transformações a ele. Por exemplo, é possível traduzir o texto traduzindo a tela e desenhando o texto.

- Implemente a função abaixo.
private fun drawTranslatedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.GREEN
// Align the RIGHT side of the text with the origin.
paint.textAlign = Paint.Align.LEFT
// Apply transformation to canvas.
canvas.translate(columnTwo,textRow)
// Draw text.
canvas.drawText(context.getString(R.string.translated),
clipRectLeft,clipRectTop,paint)
canvas.restore()
}- Execute o app para conferir o texto traduzido.

Etapa 8: implementar drawSkewedTextExample(canvas)
Também é possível distorcer o texto. Ou seja, distorcer de várias maneiras.

- Crie a função abaixo em
ClippedView.
private fun drawSkewedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.YELLOW
paint.textAlign = Paint.Align.RIGHT
// Position text.
canvas.translate(columnTwo, textRow)
// Apply skew transformation.
canvas.skew(0.2f, 0.3f)
canvas.drawText(context.getString(R.string.skewed),
clipRectLeft, clipRectTop, paint)
canvas.restore()
}- Execute o app para ver o texto distorcido desenhado antes do texto traduzido.

O método quickReject() Canvas permite verificar se um retângulo ou caminho especificado ficaria completamente fora das regiões visíveis no momento, depois que todas as transformações forem aplicadas.
O método quickReject() é muito útil quando você está criando desenhos mais complexos e precisa fazer isso o mais rápido possível. Com o quickReject(), você pode decidir de forma eficiente quais objetos não precisam ser desenhados, e não é necessário escrever sua própria lógica de interseção.
- O método
quickReject()retornatruese o retângulo ou o caminho não estiver visível na tela. Em caso de sobreposições parciais, você ainda precisa fazer sua própria verificação. - O
EdgeTypeéAA(Antialiased: trate as bordas arredondando, porque elas podem ter antialiasing) ouBW(Black-White: trate as bordas arredondando para o limite de pixel mais próximo) para arredondar para o pixel mais próximo.
Há várias versões de quickReject(), que também podem ser encontradas na documentação.
| quickReject |
| quickReject |
| quickReject |
Neste exercício, você vai desenhar uma nova linha abaixo do texto e dentro do clipRect, como antes.
- Primeiro, chame
quickReject()com um retânguloinClipRectangle, que se sobrepõe aclipRect. Assim,quickReject()retorna "false",clipRecté preenchido comBLACKe o retânguloinClipRectangleé desenhado.

- Em seguida, mude o código e chame
quickReject()comnotInClipRectangle.quickReject()agora retorna "true",clipRecté preenchido comWHITE, enotInClipRectanglenão é desenhado.

Quando você tem desenhos complexos, isso pode informar rapidamente quais formas estão completamente fora da região de corte e para quais você pode ter que fazer cálculos e desenhos adicionais, porque estão parcial ou totalmente dentro da região de corte.
Etapa: testar quickReject()
- No nível superior, crie uma variável para as coordenadas y de uma linha adicional.
private val rejectRow = rowFour + rectInset + 2*clipRectBottom- Adicione a seguinte função
drawQuickRejectExample()aClippedView. Leia o código, porque ele contém tudo o que você precisa saber para usarquickReject().
private fun drawQuickRejectExample(canvas: Canvas) {
val inClipRectangle = RectF(clipRectRight / 2,
clipRectBottom / 2,
clipRectRight * 2,
clipRectBottom * 2)
val notInClipRectangle = RectF(RectF(clipRectRight+1,
clipRectBottom+1,
clipRectRight * 2,
clipRectBottom * 2))
canvas.save()
canvas.translate(columnOne, rejectRow)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
if (canvas.quickReject(
inClipRectangle, Canvas.EdgeType.AA)) {
canvas.drawColor(Color.WHITE)
}
else {
canvas.drawColor(Color.BLACK)
canvas.drawRect(inClipRectangle, paint
)
}
canvas.restore()
}- Em
onDraw(), remova a marca de comentário da invocação dedrawQuickRejectExample(). - Execute o app. Você vai ver um retângulo preto, que é a região de recorte preenchida, e partes do
inClipRectangle, porque os dois retângulos se sobrepõem. Assim,quickReject()retornafalseeinClipRectangleé desenhado.

- Em
drawQuickRejectExample(), mude o código para executarquickReject()emnotInClipRectangle.. AgoraquickReject()retornatruee a região de corte é preenchida com branco.

Faça o download do código do codelab concluído.
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-clipping
Se preferir, você pode fazer o download do repositório como um arquivo ZIP, descompactar e abrir no Android Studio.
- O
Contextde uma atividade mantém um estado que preserva transformações e regiões de corte para oCanvas. - Use
canvas.save()ecanvas.restore()para desenhar e voltar ao estado original da tela. - Para desenhar várias formas em uma tela, calcule a localização delas ou mova (traduza) a origem da superfície de desenho. O último pode facilitar a criação de métodos utilitários para sequências de desenho repetidas.
- As regiões de recorte podem ter qualquer forma, combinação de formas ou caminho.
- É possível adicionar, subtrair e cruzar regiões de recorte para conseguir exatamente a região necessária.
- É possível aplicar transformações ao texto transformando a tela.
- O método
quickReject()Canvaspermite verificar se um retângulo ou caminho especificado ficaria completamente fora das regiões visíveis no momento.
Curso da Udacity:
Documentação do desenvolvedor Android:
- Classe
Canvas - Classe
Bitmap - Classe
View - Classe
Paint - Configurações do
Bitmap.config - Operadores
Region.Op - Classe
Path - Classe
Canvas - Classe
Bitmap - Classe
View - Classe
Paint - Configurações do
Bitmap.config - Operadores
Region.Op - Classe
Path - Ferramentas de gráficos
android.graphics - Configurações do
Bitmap.ConfigCanvas - Tela e elementos gráficos
- O que canvas.translate() faz?
- Entenda save() e restore() para o contexto do Canvas
- corte
- overdraw.
@JvmOverloads
Consulte também a série de artigos Arquitetura de gráficos para uma explicação detalhada de como o framework Android desenha na tela.
Esta seção lista as possíveis atividades de dever de casa para os alunos que estão fazendo este codelab como parte de um curso ministrado por um professor. Cabe ao professor fazer o seguinte:
- Atribuir o dever de casa, se necessário.
- Informar aos alunos como enviar deveres de casa.
- Atribuir nota aos deveres de casa.
Os professores podem usar essas sugestões o quanto quiserem, podendo passar os exercícios que acharem mais apropriados como dever de casa.
Se você estiver seguindo este codelab por conta própria, sinta-se à vontade para usar esses deveres de casa para testar seu conhecimento.
Responda estas perguntas
Pergunta 1
Qual método você chama para excluir formas de serem desenhadas de maneira eficiente?
▢ excludeFromDrawing()
▢ quickReject()
▢ onDraw()
▢ clipRect()
Pergunta 2
Quais informações Canvas.save() e Canvas.restore() salvam e restauram?
▢ Cor, largura da linha etc.
▢ Somente transformações atuais
▢ Transformações atuais e região de corte
▢ Apenas a região de corte atual
Pergunta 3
Paint.Align especifica:
▢ Como alinhar as seguintes formas de desenho
▢ De qual lado da origem o texto é extraído
▢ Onde na região de corte ele está alinhado
▢ Qual lado do texto alinhar à origem
Para acessar links de outros codelabs deste curso, consulte a página inicial dos codelabs do curso Android avançado no Kotlin.