Este codelab faz parte do curso Android avançado no Kotlin. Você aproveitará mais o 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 avançados do Android em Kotlin (link em inglês).
Introdução
Para os fins deste codelab, o clipe é uma maneira de definir regiões de uma imagem, tela ou bitmap que são desenhados seletivamente ou não na tela. Uma das finalidades do recorte é reduzir o excesso de renderização. O overdraw ocorre quando um pixel na tela é desenhado mais de uma vez para exibir a imagem final. Ao reduzir o overdraw, você minimiza o número de vezes que um pixel ou uma região da tela é desenhado, a fim de maximizar o desempenho do desenho. Também é possível usar clipes para criar efeitos interessantes no design e na animação da interface do usuário.
Por exemplo, quando você desenha uma pilha de cards sobrepostos, como mostrado abaixo, em vez de desenhar todos os cards de baixo para cima, geralmente é mais eficiente desenhar apenas as partes visíveis. "Normalmente, porque as operações de recorte também têm um custo. No geral, o sistema Android faz muitas otimizações de desenho.
Para desenhar apenas as partes visíveis dos cards, especifique uma região de clipes para cada card. Por exemplo, no diagrama abaixo, quando um retângulo de corte é aplicado a uma imagem, apenas a parte dentro desse retângulo é exibida.
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 que a região dentro da região de recorte seja incluída ou excluída. Por exemplo, você pode criar uma região de recorte circular e exibir apenas o que está fora do círculo.
Neste codelab, você vai testar várias formas de recorte.
O que você já precisa saber
Você precisa:
- Como criar um app com um
Activity
e executá-lo usando o Android Studio. - Como criar e desenhar em uma
Canvas
. - Como criar um
View
personalizado e substituironDraw()
eonSizeChanged()
.
O que você vai aprender
- Como cortar objetos para desenhar em uma
Canvas
. - Como salvar e restaurar estados de desenho de uma tela.
- Como aplicar transformações a uma tela e ao texto.
Atividades do laboratório
- Crie um app que desenhe formas cortadas na tela demonstrando diferentes maneiras de recortar e o resultado na visibilidade dessas formas.
- Você também desenhará textos traduzidos e distorcidos.
O app ClippingExample demonstra como usar e combinar formas para especificar quais partes de uma tela são exibidas em uma visualização. O app final ficará assim:
Você vai criar esse app do zero, então precisará configurar um projeto, definir dimensões e strings e declarar algumas variáveis.
Etapa 1: criar o projeto ClippingExample
- Crie um projeto Kotlin chamado
ClippingExample
com o modelo Empty Activity. Usecom.example.android
como o prefixo do nome do pacote. - Abra o
MainActivity.kt
- No método
onCreate()
, substitua a visualização de conteúdo padrão e defina-a como uma nova instância deClippedView
. Esta será sua visualização personalizada para os exemplos de recorte que você criará a seguir.
setContentView(ClippedView(this))
- No mesmo nível que
MainActivity.kt
, crie um novo arquivo e uma classe Kotlin para uma visualização personalizada chamadaClippedView
que estenda oView
. Atribua a assinatura abaixo. O restante do seu trabalho estará nesseClippedView
. A anotação@JvmOverloads
instrui o compilador Kotlin a gerar sobrecargas para essa função que substituem valores de parâmetros 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ê usará para as visualizações cortadas em um novo arquivo de recursos no
res/values/dimens.xml
. Essas dimensões padrão são fixadas no código e dimensionadas para caber em uma tela muito 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 tenha uma boa aparência em uma tela maior e veja detalhes com mais facilidade, crie um arquivo dimens
com valores maiores que só se apliquem 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
dimens
. Em Available qualifiers, selecione Smallest Screen Width e clique no botão >> para adicioná-lo a Chosen qualifiers. Digite 480 na caixa Menor largura da tela e clique em OK.
- O arquivo aparecerá na sua pasta de valores, conforme mostrado abaixo.
- Caso você não consiga ver o arquivo, mude para a visualização Project Files do app. O caminho completo do novo arquivo está como mostrado abaixo:
ClippingExample/app/src/main/res/values-sw480dp/dimens.xml
.
- Substitua o conteúdo padrão do arquivo
values-sw480dp/dimens.xml
pelas 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 exibir o 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 os objetos Paint e Path
- Volte para a visualização Android do seu projeto.
- Em
ClippedView
, defina uma variávelPaint
para desenhar. Ative o anti-aliasing e use a largura do traço e o tamanho do texto definidos nas dimensões, como 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 umPath
para armazenar localmente o caminho do que foi desenhado. Importeandroid.graphics.Path
.
private val path = Path()
Etapa 4: configurar as formas
Neste app, você está exibindo 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 contêiner
- Uma linha diagonal no retângulo grande
- Círculo
- Uma string curta de texto
Nesta etapa, você configurará dimensões para essas formas a partir dos recursos, de modo que só precise acessar as dimensões uma vez quando usá-las mais tarde.
- No
ClippedView
, abaixo dopath
, adicione variáveis para as 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 a inserção de um retângulo e o deslocamento de um 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 para o texto que é desenhado dentro do retângulo.
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)
Etapa 4: configurar locais de linhas e colunas
As formas deste app são exibidas em duas colunas e quatro linhas, determinadas pelos valores das dimensões configuradas acima. A matemática não faz parte deste codelab, mas dê uma olhada nele conforme você copia para o código fornecido nesta etapa.
- Configure as coordenadas para duas colunas.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight
- Adicione as coordenadas a 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 será aberto com uma tela em branco em branco abaixo do nome do app.
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 desenhados da mesma forma. A única diferença são as regiões de recorte definidas e a localização na tela.
O algoritmo usado para desenhar os retângulos funciona conforme mostrado no diagrama e na explicação abaixo. Em resumo, desenhe 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. Ou seja, em vez de calcular onde o próximo retângulo e todas as outras formas precisam ser desenhados, mova 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. É muito mais simples e um pouco mais eficiente.
(3) Por fim, você restaura a Canvas
para o Origin
original.
Veja o algoritmo que você implementará:
- No
onDraw()
, chame uma função para preencher oCanvas
com 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, faça o seguinte:
- Salve o estado atual do
Canvas
para poder redefini-lo para o estado inicial. - Traduza a
Origin
da tela para o local onde você quer desenhar. - Aplique formas e caminhos de recorte.
- Desenhe o retângulo ou o texto.
- Restaure o estado do
Canvas
.
Etapa: modificar onDraw()
- Modifique o
onDraw()
conforme mostrado no código abaixo. Chame uma função para cada forma que estiver desenhando, que será implementada 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 forma sete vezes, primeiro sem recortar e depois seis vezes com vários caminhos de recorte aplicados. O método drawClippedRectangle()
considera o código para desenhar um retângulo, conforme mostrado abaixo.
Etapa 1: criar o método drawClippedRectangle()
- Crie um método
drawClippedRectangle()
que recebe um argumentocanvas
do tipoCanvas
.
private fun drawClippedRectangle(canvas: Canvas) {
}
- Dentro do método
drawClippedRectangle()
, defina os limites do retângulo de corte para a forma inteira. Aplique um retângulo de corte que restrinja apenas o desenho ao quadrado.
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
O método Canvas.clipRect(...)
reduz a região da tela em que as operações futuras 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 distintas no retângulo de corte.
- Preencha a
canvas
com cor branca. Sim. A tela inteira, como você não está desenhando retângulos, está sendo recortada. Por causa do retângulo de corte, somente a região definida pelo retângulo de corte é criada, 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 corte.
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)
- Defina a cor como verde e desenhe um círculo no retângulo de corte.
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)
- Defina a cor como azul e desenhe um texto alinhado com a borda direita do retângulo de corte. Use o
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 não cortado implementando o métododrawBackAndUnclippedRectangle()
, conforme mostrado abaixo. Salve acanvas
, traduza para a primeira posição da linha e da coluna, desenhe chamandodrawClippedRectangle()
e restaure ocanvas
para o 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ê verá o primeiro retângulo branco com um círculo, uma linha vermelha e um texto em um plano de fundo cinza.
Nos seguintes métodos de exemplo de recorte, você aplica várias combinações de regiões de recorte para alcançar efeitos gráficos e aprender como combinar regiões de recorte para criar qualquer forma necessária.
Cada um desses métodos segue o mesmo padrão.
- Salve o estado atual da tela:
canvas.
save(
)
.
O contexto da atividade mantém uma pilha de estados de desenho. Os estados de desenho consistem na matriz de transformação atual e na região de recorte atual. Você pode salvar o estado atual, realizar ações que mudam o estado de desenho (como traduzir ou girar a tela) e restaurar o estado de desenho salvo. Observação: o comando é parecido com o ""stash" no git!".
Quando o desenho inclui transformações, encadear e desfazer transformações se elas forem revertidas serão propensos a erros. Por exemplo, se você traduzir, esticar e girar, isso ficará complexo 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 recorte 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 original do recorte e fazer uma tradução diferente e uma transformação diferente, conforme mostrado no diagrama.
- Converta a origem da tela para as coordenadas da 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: você pode usar a mesma técnica para elementos giratórios.
- Aplique transformações ao
path
, se houver. - Aplicar clipe:
canvas.clipPath(path)
- Desenhe as formas:
drawClippedRectangle() or drawText()
- Restaura o estado de tela anterior:
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 de imagem.
Use o código abaixo, que faz o seguinte:
- Salve a tela.
- Converta a origem da tela em espaço aberto para a primeira linha, segunda coluna, à direita do primeiro retângulo.
- Aplique dois retângulos de recorte. O operador
DIFFERENCE
subtrai 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 e ele ficará assim.
Etapa 2: implementar drawCircularClippingExample(tela)
Em seguida, adicione o código para desenhar um retângulo que usa uma região de recorte circular criada a partir de um caminho circular. Esse processo remove o círculo (não desenha) e mostra 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(tela)
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 da região varia. 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(tela)
Em seguida, combine formas, um círculo e um retângulo e desenhe um 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 drawRoundedRectangleClippingExemplo(tela)
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 retangular.
RectF
é uma classe que contém coordenadas retangulares 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 de x e y do raio do canto e a direção para enrolar o contorno do retângulo circular. O íconePath.Direction
especifica como as formas fechadas (como retângulos e ovais) são orientadas quando são adicionadas a um caminho.CCW
significa 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(tela)
Corte o exterior do retângulo ao dobrar as inserções do retângulo cortado.
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 é possível aplicar transformações a ele. Por exemplo, você pode traduzir 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 ver o texto traduzido.
Etapa 8: implementar drawSkewedTextExample(Canvas)
Também é possível inclinar o texto. Ou seja, distorça-o 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 Canvas
quickReject()
permite verificar se um retângulo ou caminho especificado estaria completamente fora das regiões visíveis no momento, depois que todas as transformações fossem aplicadas.
O método quickReject()
é incrivelmente útil quando você está criando desenhos mais complexos e precisa fazer isso o mais rápido possível. Com o quickReject()
, é possível decidir com eficiência quais objetos não é necessário desenhar, e não é necessário escrever a própria lógica de interseção.
- O método
quickReject()
retornarátrue
se o retângulo ou caminho não estiver visível na tela. Para sobreposições parciais, você ainda precisará fazer sua própria verificação. - O
EdgeType
éAA
(anti-aliasing: trata as bordas arredondando para baixo, porque elas podem ser antialiases) ouBW
(branco-branco: trata as bordas apenas arredondando para o limite de pixels mais próximo) para arredondar para o pixel mais próximo.
Há várias versões de quickReject()
, e você também pode encontrá-las na documentação.
| quickje |
| quickje |
| quickRejected |
Neste exercício, você desenhará em uma nova linha, abaixo do texto, e dentro da clipRect
, como antes.
- Primeiro, chame
quickReject()
com um retânguloinClipRectangle
que se sobrepõe aoclipRect
. Portanto,quickReject()
retorna falso,clipRect
é preenchido comBLACK
e o retânguloinClipRectangle
é desenhado.
- Em seguida, mude o código e chame
quickReject()
, comnotInClipRectangle
.quickReject()
agora retorna "true",clipRect
é preenchido comWHITE
enotInClipRectangle
não é exibido
Quando você tem desenhos complexos, isso pode informar rapidamente quais formas estão completamente fora da região de recorte e para as quais você pode precisar fazer cálculos e desenhos adicionais, porque eles estão parcial ou totalmente dentro da região de recorte.
Etapa: testar com quickRejected()
- No nível superior, crie uma variável para as coordenadas y de outra linha.
private val rejectRow = rowFour + rectInset + 2*clipRectBottom
- Adicione a seguinte função
drawQuickRejectExample()
aClippedView
. Leia o código, já que ele contém tudo o que você precisa saber para usar oquickReject()
.
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 do comentário da invocação dedrawQuickRejectExample()
. - Execute o app e você verá um retângulo preto, que é a região de recorte preenchida, e partes do
inClipRectangle
, porque os dois retângulos se sobrepõem.quickReject()
retornafalse
einClipRectangle
é desenhado.
- Em
drawQuickRejectExample()
, mude o código para executarquickReject()
emnotInClipRectangle.
agoraquickReject()
retornatrue
e a região de corte é preenchida em branco.
Faça o download do código do codelab concluído.
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-clipping
Como alternativa, é possível fazer o download do repositório como um arquivo ZIP, descompactá-lo e abri-lo no Android Studio.
- O
Context
de uma atividade mantém um estado que preserva as transformações e as regiões de recorte daCanvas
. - Use
canvas.save()
ecanvas.restore()
para desenhar e retornar ao estado original da tela. - Para desenhar várias formas em uma tela, calcule o local 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 corte podem ser qualquer forma, combinação de formas ou caminho.
- É possível adicionar, subtrair e cruzar regiões de recorte para chegar exatamente à região de que você precisa.
- Transforme o Canvas para aplicar transformações em texto.
- O método
quickReject()
Canvas
permite verificar se um retângulo ou caminho especificado estaria completamente fora das regiões visíveis no momento.
Curso da Udacity:
- Como desenvolver apps Android com Kotlin (link em inglês)
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 gráficas do
android.graphics
- Configurações do
Bitmap.Config
Canvas
- Canvas e drawables
- O que Canvas.translate() faz
- Noções básicas de save() e restore() para o contexto de Canvas
- clipe
- draw over.
@JvmOverloads
Veja também a série de artigos Arquitetura de gráficos para uma explicação detalhada de como o framework do Android é exibido na tela.
Esta seção lista as possíveis atividades para os alunos que estão trabalhando neste codelab como parte de um curso ministrado por um instrutor. Cabe ao instrutor fazer o seguinte:
- Se necessário, atribua o dever de casa.
- Informe aos alunos como enviar o dever de casa.
- Atribua nota aos trabalhos de casa.
Os professores podem usar essas sugestões o quanto quiserem, e eles devem se sentir à vontade para passar o dever de casa como achar adequado.
Se você estiver fazendo este codelab por conta própria, use essas atividades para testar seu conhecimento.
Responda a estas perguntas
Pergunta 1
Qual método você chama para excluir formas de forma eficiente?
▢ excludeFromDrawing()
▢ quickReject()
▢ onDraw()
▢ clipRect()
Pergunta 2
Canvas.save()
e Canvas.restore()
salvam e restauram quais informações?
▢ Cor, largura de linha etc.
▢ Apenas transformações atuais
▢ Transformações atuais e região de recorte
▢ Somente a área de corte atual
Pergunta 3
Paint.Align
especifica:
▢ Como alinhar as formas de desenho a seguir
▢ De que lado da origem o texto é retirado
▢ Onde na região de corte está alinhada
▢ Qual lado do texto se alinhará à origem
Para ver links de outros codelabs neste curso, consulte a página de destino dos codelabs avançados no Android.