Este tutorial faz parte do curso Android avançado em Kotlin. Você obterá o máximo valor deste curso se trabalhar com os tutoriais em sequência, mas isso não é obrigatório. Todos os tutoriais do curso estão listados na página de destino Android avançado em Kotlin.
O Android oferece um grande conjunto de subclasses de View
, como Button
, TextView
, EditText
, ImageView
, CheckBox
ou RadioButton
. Você pode usar essas subclasses para construir uma IU que permite a interação do usuário e exibe informações em seu aplicativo. Se nenhuma das subclasses de View
atender às suas necessidades, você pode criar uma subclasse View
conhecida como vista personalizada.
Para criar uma vista personalizada, você pode estender uma subclasse View
existente (como um Button
ou EditText
) ou criar sua própria subclasse de View
. Ao estender View
diretamente, você pode criar um elemento de IU interativo de qualquer tamanho e forma substituindo o método onDraw()
para que View
seja desenhado isto.
Depois de criar uma vista personalizada, você pode adicioná-la aos seus layouts de atividade da mesma forma que adicionaria uma TextView
ou Button
.
Esta lição mostra como criar uma vista personalizada do zero estendendo a View
.
View
para criar uma vista personalizada.O aplicativo CustomFanController demonstra como criar uma subclasse de vista personalizada estendendo a classe View
. A nova subclasse é chamada de DialView
.
O aplicativo exibe um elemento de IU circular que se assemelha a um controle de ventilador físico, com configurações para desligado (0), baixo (1), médio (2) e alto (3). Quando o usuário toca na vista, o indicador de seleção se move para a próxima posição: 0-1-2-3 e volta para 0. Além disso, se a seleção for 1 ou superior, a cor de fundo da parte circular da vista muda de cinza para verde (indicando que o ventilador está ligado).
As vistas são os blocos básicos de construção da IU de um aplicativo. A classe View
fornece muitas subclasses, conhecidas como widgets de IU, que cobrem muitas das necessidades de uma interface de usuário típica de aplicativo Android.
Os blocos de construção da IU, como Button
e TextView
são subclasses que estendem a classe View
. Para economizar tempo e esforço de desenvolvimento, você pode estender uma dessas subclasses de View
. A vista personalizada herda a aparência e o comportamento de seu pai, e você pode substituir o comportamento ou aspecto da aparência que deseja alterar. Por exemplo, se você estender EditText
para criar uma vista personalizada, a vista atua como uma vista EditText
, mas também pode ser personalizada para mostrar, por exemplo, um X que limpa o texto do campo de entrada de texto.
Você pode estender qualquer subclasse View
, como EditText
, para obter uma vista personalizada - escolha aquela mais próxima do que você deseja realizar. Você pode então usar a vista personalizada como qualquer outra subclasse View
em um ou mais layouts como um elemento XML com atributos.
Para criar sua própria vista personalizada do zero, estenda a própria classe View
. Seu código substitui os métodos View
para definir a aparência e funcionalidade da vista. A chave para criar sua própria vista customizada é que você é responsável por desenhar todo o elemento da IU de qualquer tamanho e forma na tela. Se você criar uma subclasse de uma vista existente, como Button
, essa classe cuidará do desenho para você. (Você aprenderá mais sobre desenho posteriormente neste tutorial.)
Para criar uma vista personalizada, siga estas etapas gerais:
View
ou estenda uma subclasse View
(como Button
ou EditText
).View
existente, substitua apenas o comportamento ou aspectos da aparência que deseja alterar.View
, desenhe a forma da vista personalizada e controle sua aparência substituindo os métodos View
como onDraw()
e onMeasure()
na nova classe. Nesta tarefa você irá:
ImageView
como um espaço reservado temporário para a vista personalizada.View
para criar a vista personalizada.CustomFanController
usando o modelo Empty Activity. Certifique-se de que o nome do pacote seja com.example.android.customfancontroller
.activity_main.xml
na guia Text para editar o código XML.TextView
existente por este código. Este texto atua como um rótulo na atividade para a vista personalizada. <TextView
android:id="@+id/customViewLabel"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@android:color/black"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="24dp"
android:text="Fan Control"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
ImageView
ao layout. Este é um espaço reservado para a vista personalizada que você criará neste tutorial. <ImageView
android:id="@+id/dialView"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@android:color/darker_gray"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"/>
DialView
. View
. Importe android.view.View
quando solicitado.View
e depois clique na lâmpada vermelha. Escolha Add Android View constructors using '@JvmOverloads'. O Android Studio adiciona o construtor da classe View
. A anotação @JvmOverloads
instrui o compilador Kotlin a gerar sobrecargas para esta função que substituem os valores de parâmetro padrão.class DialView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
DialView
, logo abaixo das importações, adicione um enum
de nível superior para representar as velocidades do ventilador disponíveis. Observe que este enum
é do tipo Int
porque os valores são recursos de string em vez de strings reais. O Android Studio mostrará erros para os recursos de string ausentes em cada um desses valores; você corrigirá isso em uma etapa posterior. private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
}
enum
, adicione essas constantes. Você os usará como parte do desenho dos indicadores de mostrador e rótulos. private const val RADIUS_OFFSET_LABEL = 30
private const val RADIUS_OFFSET_INDICATOR = -35
DialView
, defina várias variáveis que você precisa para desenhar a vista personalizada. Importe android.graphics.PointF
se solicitado. private var radius = 0.0f // Radius of the circle.
private var fanSpeed = FanSpeed.OFF // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)
radius
é o raio atual do círculo. Este valor é definido quando a vista é desenhada na tela. fanSpeed
é a velocidade atual do ventilador, que é um dos valores no FanSpeed
enumeração. Por padrão, esse valor é OFF
. postPosition
é um ponto X, Y que será usado para desenhar vários elementos da vista na tela. Esses valores são criados e inicializados aqui, em vez de quando a vista é realmente desenhada, para garantir que a etapa de desenho real seja executada o mais rápido possível.
DialView
, inicialize um objeto Paint
com um punhado de estilos básicos. Importe android.graphics.Paint
e android.graphics.Typeface
quando solicitado. Como anteriormente com as variáveis, esses estilos são inicializados aqui para ajudar a acelerar a etapa de desenho. private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
textAlign = Paint.Align.CENTER
textSize = 55.0f
typeface = Typeface.create( "", Typeface.BOLD)
}
res/values/strings.xml
e adicione os recursos de string para as velocidades do ventilador:<string name="fan_off">off</string>
<string name="fan_low">1</string>
<string name="fan_medium">2</string>
<string name="fan_high">3</string>
Depois de criar uma vista personalizada, você precisa ser capaz de desenhá-la. Quando você estende uma subclasse View
como EditText
, essa subclasse define a aparência e os atributos da vista e se desenha na tela. Consequentemente, você não precisa escrever código para desenhar a vista. Você pode substituir os métodos do pai para personalizar sua vista.
Se você estiver criando sua própria vista do zero (estendendo View
), você é responsável por desenhar a vista inteira cada vez que a tela for atualizada e por substituir os métodos de View
que tratar com o desenho. Para desenhar adequadamente uma vista personalizada que estenda View
, você precisa:
onSizeChanged()
. onDraw()
para desenhar a vista personalizada, usando um objeto Canvas
estilizado por um objeto Paint
.invalidate()
ao responder a um clique do usuário que muda a forma como a vista é desenhada para invalidar toda a vista, forçando assim uma chamada para onDraw()
para redesenhar a vista. O método onDraw()
é chamado toda vez que a tela é atualizada, o que pode ser muitas vezes por segundo. Por motivos de desempenho e para evitar falhas visuais, você deve trabalhar o mínimo possível em onDraw()
. Em particular, não coloque alocações em onDraw()
, porque as alocações podem levar a uma coleta de lixo que pode causar uma falha visual.
As classes Canvas
e Paint
oferecem vários atalhos de desenho úteis:
drawText()
. Especifique o tipo de letra chamando setTypeface()
, e a cor do texto chamando setColor()
.drawRect()
, drawOval()
e drawArc()
. Altere se as formas são preenchidas, contornadas ou ambas chamando setStyle()
.drawBitmap()
.Você aprenderá mais sobre Canvas
e Paint
em um tutorial posterior. Para saber mais sobre como o Android desenha vistas, consulte Como o Android desenha vistas.
Nesta tarefa, você desenhará a vista personalizada do controlador do ventilador na tela - o próprio dial, o indicador de posição atual e os rótulos dos indicadores - com os métodos onSizeChanged()
e onDraw()
. Você também criará um método auxiliar, computeXYForSpeed(),
para calcular a posição X, Y atual do rótulo do indicador no mostrador.
DialView
, abaixo das inicializações, substitua o método onSizeChanged()
da classe View
para calcular o tamanho do mostrador da vista personalizada. Importe kotlin.math.min
quando solicitado. onSizeChanged()
é chamado sempre que o tamanho da vista muda, incluindo a primeira vez que ela é desenhada quando o layout é inflado. Substitua onSizeChanged()
para calcular posições, dimensões e quaisquer outros valores relacionados ao tamanho de sua vista personalizada, em vez de recalculá-los toda vez que você desenhar. Neste caso, você usa onSizeChanged()
para calcular o raio atual do elemento circular do mostrador. override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
radius = (min(width, height) / 2.0 * 0.8).toFloat()
}
onSizeChanged()
, adicione este código para definir uma função de extensão computeXYForSpeed()
para a classe PointF
. Importe kotlin.math.cos
e kotlin.math.sin
quando solicitado. Esta função de extensão na classe PointF
calcula as coordenadas X, Y na tela para o rótulo de texto e o indicador atual (0, 1, 2 ou 3), dado o FanSpeed
posição e raio do mostrador. Você usará isso em onDraw().
private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
// Angles are in radians.
val startAngle = Math.PI * (9 / 8.0)
val angle = startAngle + pos.ordinal * (Math.PI / 4)
x = (radius * cos(angle)).toFloat() + width / 2
y = (radius * sin(angle)).toFloat() + height / 2
}
onDraw()
para renderizar a vista na tela com as classes Canvas
e Paint
. Importe android.graphics.Canvas
quando solicitado. Esta é a substituição do esqueleto: override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}
onDraw()
, adicione esta linha para definir a cor da pintura para cinza (Color.GRAY
) ou verde (Color.GREEN
) dependendo de se a velocidade do ventilador é OFF
ou qualquer outro valor. Importe android.graphics.Color
quando solicitado. // Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
drawCircle()
. Este método usa a largura e altura da vista atual para encontrar o centro do círculo, o raio do círculo e a cor de pintura atual. As propriedades width
e height
são membros da superclasse View
e indicam as dimensões atuais da vista. // Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
drawCircle()
. Esta parte usa o PointF
computeXYforSpeed()
método de extensão para calcular as coordenadas X, Y para o centro do indicador com base na velocidade do ventilador atual. // Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
PointF.computeXYForSpeed()
novamente para obter a posição de cada rótulo e reutiliza o objeto pointPosition
a cada vez para evitar alocações. Use drawText()
para desenhar os rótulos. // Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}
O método onDraw()
concluído tem a seguinte aparência:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}
}
Para adicionar uma vista personalizada à IU de um aplicativo, você a especifica como um elemento no layout XML da atividade. Controle sua aparência e comportamento com atributos de elemento XML, como faria com qualquer outro elemento de IU.
activity_main.xml
, altere a etiquetaImageView
para dialView
para com.example.android.customfancontroller.DialView
e exclua o atributo android:background
. Tanto o DialView
quanto o ImageView
original herdam os atributos padrão da classe View
, portanto, não há necessidade de alterar nenhum dos outros atributos. O novo elemento DialView
tem a seguinte aparência: <com.example.android.customfancontroller.DialView
android:id="@+id/dialView"
android:layout_width="@dimen/fan_dimen"
android:layout_height="@dimen/fan_dimen"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" />
A tarefa final é permitir que sua vista personalizada execute uma ação quando o usuário tocar na vista. Cada toque deve mover o indicador de seleção para a próxima posição: desligado-1-2-3 e de volta para desligado. Além disso, se a seleção for 1 ou superior, altere o fundo de cinza para verde, indicando que o ventilador está ligado.
Para permitir que sua vista personalizada seja clicável, você:
isClickable
da vista como true
. Isso permite que sua vista personalizada responda a cliques. performClick
()
da classe View
para executar operações quando a vista for clicada. invalidate()
. Isso diz ao sistema Android para chamar o método onDraw()
para redesenhar a vista.Normalmente, com uma vista padrão do Android, você implementa OnClickListener()
para realizar uma ação quando o usuário clica nessa vista. Para uma vista personalizada, você implementa o método performClick()
da classe
View
e chama super.performClick()
. O método
performClick()
padrão também chama onClickListener()
, então você pode adicionar suas ações a performClick()
e deixe onClickListener()
disponível para personalização posterior por você ou outros desenvolvedores que possam usar sua vista personalizada.
DialView.kt
, dentro da enumeração FanSpeed
, adicione uma função de extensão next()
que altera a velocidade atual do ventilador para a próxima velocidade na lista (de OFF
para LOW
, MEDIUM
e HIGH
, e depois de volta para OFF
). A enumeração completa agora se parece com isto:private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
fun next() = when (this) {
OFF -> LOW
LOW -> MEDIUM
MEDIUM -> HIGH
HIGH -> OFF
}
}
DialView
, logo antes do método onSizeChanged()
, adicione um bloco init()
. Definir a propriedade isClickable
da vista como true permite que a vista aceite a entrada do usuário. init {
isClickable = true
}
init(),
substitua o método performClick()
com o código abaixo.override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
contentDescription = resources.getString(fanSpeed.label)
invalidate()
return true
}
A chamada para super
performClick()
deve acontecer primeiro, o que ativa eventos de acessibilidade, bem como chamadas onClickListener()
.
As próximas duas linhas incrementam a velocidade do ventilador com o método next()
e definem a descrição do conteúdo da vista para o recurso de string que representa a velocidade atual (desligado, 1, 2 ou 3).
Finalmente, o método invalidate()
invalida a vista inteira, forçando uma chamada para onDraw()
para redesenhar a vista. Se algo em sua vista personalizada mudar por qualquer motivo, incluindo interação do usuário, e a mudança precisar ser exibida, chame invalidate().
DialView
para mover o indicador de desligado para 1. O dial deve ficar verde. A cada toque, o indicador deve passar para a próxima posição. Quando o indicador voltar a desligar, o dial deve ficar cinza novamente.Este exemplo mostra a mecânica básica do uso de atributos personalizados com sua vista personalizada. Você define atributos personalizados para a classe DialView
com uma cor diferente para cada posição do botão do ventilador.
res/values/attrs.xml
.<resources>
, adicione um elemento de recurso <declare-styleable>
.
<declare-styleable>
, adicione três elementos attr
, um para cada atributo, com um name
e format
. O format
é como um tipo e, neste caso, é color
.
<resources>
<declare-styleable name="DialView">
<attr name="fanColor1" format="color" />
<attr name="fanColor2" format="color" />
<attr name="fanColor3" format="color" />
</declare-styleable>
</resources>
activity_main.xml
.DialView
, adicione atributos para fanColor1
, fanColor2
e fanColor3
e defina seus valores para as cores mostradas abaixo. Use app:
como o prefácio do atributo personalizado (como em app:fanColor1
) em vez de android:
porque seus atributos personalizados pertencem ao schemas.android.com/apk/res/
your_app_package_name
em vez do namespace android
.app:fanColor1="#FFEB3B"
app:fanColor2="#CDDC39"
app:fanColor3="#009688"
Para usar os atributos em sua classe DialView
, você precisa recuperá-los. Eles são armazenados em um AttributeSet
, que é entregue à sua classe na criação, se existir. Você recupera os atributos em init
e atribui os valores dos atributos às variáveis locais para armazenamento em cache.
DialView.kt
.DialView
, declare as variáveis para armazenar em cache os valores dos atributos.private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSpeedMaxColor = 0
init
, adicione o seguinte código usando a função de extensão withStyledAttributes
. Você fornece os atributos e a vista e define suas variáveis locais. Importar withStyledAttributes
também importará a função getColor()
correta.context.withStyledAttributes(attrs, R.styleable.DialView) {
fanSpeedLowColor = getColor(R.styleable.DialView_fanColor1, 0)
fanSpeedMediumColor = getColor(R.styleable.DialView_fanColor2, 0)
fanSeedMaxColor = getColor(R.styleable.DialView_fanColor3, 0)
}
onDraw()
para definir a cor do dial com base na velocidade atual do ventilador. Substitua a linha onde a cor da tinta está definida (paint
.
color
=
if
(
fanSpeed
== FanSpeed.
OFF
) Color.
GRAY
else
Color.
GREEN
) com o código abaixo. paint.color = when (fanSpeed) {
FanSpeed.OFF -> Color.GRAY
FanSpeed.LOW -> fanSpeedLowColor
FanSpeed.MEDIUM -> fanSpeedMediumColor
FanSpeed.HIGH -> fanSeedMaxColor
} as Int
Para aprender mais sobre os atributos de vista customizada, consulte Criando uma classe de vista.
Acessibilidade é um conjunto de técnicas de design, implementação e teste que permitem que seu aplicativo seja usado por todos, incluindo pessoas com deficiência.
As deficiências comuns que podem afetar o uso de um dispositivo Android por uma pessoa incluem cegueira, baixa vista, daltonismo, surdez ou perda auditiva e habilidades motoras restritas. Ao desenvolver seus aplicativos com acessibilidade em mente, você torna a experiência do usuário melhor não apenas para usuários com essas deficiências, mas também para todos os seus outros usuários.
O Android fornece vários recursos de acessibilidade por padrão nas vistas da IU padrão, como TextView
e Button
. Ao criar uma vista personalizada, no entanto, você precisa considerar como essa vista personalizada fornecerá recursos acessíveis, como descrições faladas do conteúdo na tela.
Nesta tarefa, você aprenderá sobre TalkBack, o leitor de tela do Android, e modificará seu aplicativo para incluir dicas faláveis e descrições para a vista personalizada DialView
.
TalkBack é o leitor de tela integrado do Android. Com o TalkBack ativado, o usuário pode interagir com seu dispositivo Android sem ver a tela, porque o Android descreve os elementos da tela em voz alta. Usuários com deficiência visual podem contar com o TalkBack para usar seu aplicativo.
Nesta tarefa, você habilita o TalkBack para entender como os leitores de tela funcionam e como navegar pelos aplicativos.
CustomFanController
ou abra-o com o botão Overview ou Recents em seu dispositivo. Com o TalkBack ativado, observe que o nome do aplicativo é anunciado, bem como o texto do rótulo TextView
("Fan Control"). No entanto, se você tocar na própria vista DialView
, nenhuma informação será falada sobre o estado da vista (a configuração atual do dial) ou a ação que ocorrerá quando você tocar na vista para ative-o. As descrições de conteúdo descrevem o significado e a finalidade das vistas em seu aplicativo. Esses rótulos permitem que leitores de tela, como o recurso TalkBack do Android, expliquem a função de cada elemento com precisão. Para vistas estáticas, como ImageView
, você pode adicionar a descrição do conteúdo à vista no arquivo de layout com o atributo contentDescription
. As vistas de texto (TextView
e EditText
) usam automaticamente o texto na vista como a descrição do conteúdo.
Para a vista de controle de ventilador customizada, você precisa atualizar dinamicamente a descrição do conteúdo cada vez que a vista é clicada, para indicar a configuração de ventilador atual.
DialView
, declare uma função updateContentDescription()
sem argumentos ou tipo de retorno. fun updateContentDescription() {
}
updateContentDescription()
, altere a propriedade contentDescription
da vista personalizada para o recurso de string associado à velocidade do ventilador atual (desligado, 1, 2 ou 3). Estes são os mesmos rótulos usados em onDraw()
quando o dial é desenhado na tela. fun updateContentDescription() {
contentDescription = resources.getString(fanSpeed.label)
}
init()
e, no final desse bloco, adicione uma chamada para updateContentDescription()
. Isso inicializa a descrição do conteúdo quando a vista é inicializada. init {
isClickable = true
// ...
updateContentDescription()
}
updateContentDescription()
no método performClick()
, logo antes de invalidate()
. override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
updateContentDescription()
invalidate()
return true
}
Você poderia parar por aí e sua visualização seria utilizável no TalkBack. Mas seria útil se sua visualização pudesse indicar não só que pode ser ativada ("Toque duas vezes para ativar"), mas também explicar o que irá acontecer quando a visualização está ativado ("Toque duas vezes para alterar." ou "Toque duas vezes para redefinir.")
Para fazer isso, você adiciona informações sobre a ação da vista (aqui, uma ação de clique ou toque) a um objeto de informações do nó de acessibilidade, por meio de um delegado de acessibilidade. Um delegado de acessibilidade permite que você personalize os recursos relacionados à acessibilidade de seu aplicativo por meio de composição (em vez de herança).
Para esta tarefa, você usará as classes de acessibilidade nas bibliotecas do Android Jetpack (androidx.*
), para garantir a compatibilidade com versões anteriores.
DialView.kt
, no bloco init
, defina um delegado de acessibilidade na vista como um novo objeto AccessibilityDelegateCompat
. Importe androidx.core.view.ViewCompat
e androidx.core.view.AccessibilityDelegateCompat
quando solicitado. Essa estratégia permite o máximo de compatibilidade com versões anteriores em seu aplicativo. ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
})
AccessibilityDelegateCompat
, substitua a função onInitializeAccessibilityNodeInfo()
por um objeto AccessibilityNodeInfoCompat
e chame o método super. Importe androidx.core.view.accessibility.AccessibilityNodeInfoCompat
quando solicitado. ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
}
})
Cada vista possui uma árvore de nós de acessibilidade, que podem ou não corresponder aos componentes de layout reais da vista. Os serviços de acessibilidade do Android navegam nesses nós para descobrir informações sobre a vista (como descrições de conteúdo faláveis ou possíveis ações que podem ser executadas nessa vista). Quando você cria uma vista personalizada, também pode precisar substituir as informações do nó em a fim de fornecer informações personalizadas para acessibilidade. Nesse caso, você substituirá as informações do nó para indicar que há informações personalizadas para a ação da vista.
onInitializeAccessibilityNodeInfo()
, crie um novo objeto AccessibilityNodeInfoCompat.AccessibilityActionCompat
e atribua-o à variável customClick
. Passe para o construtor a constante AccessibilityNodeInfo.ACTION_CLICK
e uma string de espaço reservado. Importe AccessibilityNodeInfo
quando solicitado. ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
"placeholder"
)
}
})
A classe AccessibilityActionCompat
representa uma ação em uma vista para fins de acessibilidade. Um típico a ação é um clique ou toque, como você usa aqui, mas outras ações podem incluir ganhar ou perder o foco, uma operação da área de transferência (cortar / copiar / colar) ou rolar dentro da vista. O construtor para esta classe requer uma constante de ação (aqui, AccessibilityNodeInfo.ACTION_CLICK
) e uma string que é usada pelo TalkBack para indicar qual é a ação.
"placeholder"
por uma chamada para context.getString()
para recuperar um recurso de string. Para o recurso específico, teste a velocidade atual do ventilador. Se a velocidade for atualmente FanSpeed.HIGH
, a string será "Reset"
. Se a velocidade do ventilador for qualquer outra, a string será "Change."
Você criará esses recursos de string em uma etapa posterior. ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH) R.string.change else R.string.reset)
)
}
})
customClick
, use o método addAction()
para adicionar a nova ação de acessibilidade ao objeto de informações do nó. ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH)
R.string.change else R.string.reset)
)
info.addAction(customClick)
}
})
res/values/strings.xml
, adicione os recursos de string para "Alterar" e "Redefinir". <string name="change">Change</string>
<string name="reset">Reset</string>
Baixe o código para o tutorial concluído.
$ git clone https://github.com/googletutoriais/android-kotlin-drawing-custom-views
Alternativamente, você pode baixar o repositório como um arquivo Zip, descompactá-lo e abri-lo no Android Studio.
View
como EditText
, adicione um novo classe que estende essa subclasse e faça ajustes substituindo alguns dos métodos da subclasse.View
. View
como onDraw()
para definir a forma da vista e a aparência básica.invalidate()
para forçar um desenho ou redesenho da vista.onDraw()
, como na inicialização de variáveis de membro.
performClick()
em vez de OnClickListener()
para a vista personalizada para fornecer o comportamento interativo da vista. Isso permite que seus ou outros desenvolvedores Android que podem usar sua classe de vista personalizada usem onClickListener()
para fornecer comportamento adicional. attrs.xml
na pasta values
para definir os atributos personalizados. Você pode então usar os atributos personalizados para a vista personalizada no arquivo de layout XML.
Documentação do desenvolvedor Android:
@JvmOverloads
onMeasure()
onSizeChanged()
onDraw()
Canvas
Paint
drawText()
setTypeface()
setColor()
drawRect()
drawOval()
drawArc()
drawBitmap()
setStyle()
invalidate()
withStyledAttributes
AccessibilityDelegateCompat
AccessibilityNodeInfoCompat
AccessibilityNodeInfoCompat.AccessibilityActionCompat
Vídeos:
Se você estiver trabalhando neste tutorial por conta própria, sinta-se à vontade para usar essas tarefas de lição de casa para testar seus conhecimentos
Para calcular as posições, dimensões e quaisquer outros valores quando o tamanho da vista personalizada é atribuído pela primeira vez, qual método você substitui?
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ onDraw()
Para indicar que você gostaria que sua vista fosse redesenhada com onDraw()
, qual método você chama a partir da thread de interface do usuário, após a alteração de um valor de atributo?
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ getVisibility()
Qual método de View
você deve substituir para adicionar interatividade à vista personalizada?
▢ setOnClickListener()
▢ onSizeChanged()
▢ isClickable()
▢ performClick()
Para obter links para outros tutoriais neste curso, consulte a página inicial de tutoriais do Android avançado em Kotlin.