Neste artigo, vamos criar um botão de carregamento personalizado, estendendo a classe View e animar as propriedades do botão personalizado depois de clicado. Havíamos nos encontrado muitas vezes durante o download de qualquer arquivo e mantendo nossos olhos no progresso do download. Aqui, criaremos apenas aquele botão personalizado com animação. Um exemplo de GIF é fornecido abaixo para se ter uma ideia sobre o que faremos neste artigo. Observe que vamos implementar este projeto usando a linguagem Kotlin

 Criar botão de carregamento personalizado estendendo GIF de amostra do ViewClass

Implementação passo a passo

Etapa 1: Criar um novo projeto

Para criar um novo projeto no Android Studio, consulte Como criar / iniciar um novo projeto no Android Studio . Observe que selecione Kotlin como a linguagem de programação.

Etapa 2: Crie uma classe Sealed, ButtonState , que descreve o estado (como clicado, carregando e concluído) do botão personalizado. Abaixo está o código para o arquivo ButtonState.kt

package com.gfg.article.customloadingbutton
  
// describes the state of the custom button
sealed class ButtonState() {
    object Clicked : ButtonState()   // when button is clicked for downloading
    object Loading : ButtonState()   // when downloading is in progress
    object Completed : ButtonState() // when downloading is finished
}

Passo 3: Crie outra classe, LoadingButton , na qual tudo relacionado ao botão como cor, texto, animação, etc será definido. Faça um construtor anotando-o com JvmOverloads . Abaixo está o código para o arquivo LoadingButton.kt

import android.animation.AnimatorInflater
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.util.AttributeSet
import android.view.View
import androidx.core.content.ContextCompat
import com.gfg.article.customloadingbutton.ButtonState
import kotlin.properties.Delegates
  
class LoadingButton @JvmOverloads constructor(
  
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
  
    private var bgColor: Int = Color.BLACK
    private var textColor: Int = Color.BLACK // default color
  
    // tells the compiler that the value of a variable 
    // must never be cached as its value may change outside
    @Volatile
    private var progress: Double = 0.0
    private var valueAnimator: ValueAnimator
  
    // observes the state of button
    private var buttonState: ButtonState by Delegates.observable(ButtonState.Completed) { p, old, new ->
    }
  
    private val updateListener = ValueAnimator.AnimatorUpdateListener {
        progress = (it.animatedValue as Float).toDouble()
        invalidate()    // redraw the screen
        requestLayout() // when rectangular progress dimension changes
    }
  
    // call after downloading is completed
    fun hasCompletedDownload() {
        // cancel the animation when file is downloaded
        valueAnimator.cancel()
        buttonState = ButtonState.Completed
        invalidate()
        requestLayout()
    }
  
    // initialize
    init {
        isClickable = true
        valueAnimator = AnimatorInflater.loadAnimator(
            context,
            // properties for downloading progress is defined
            R.animator.loading_animation    
        ) as ValueAnimator
  
        valueAnimator.addUpdateListener(updateListener)
  
        // initialize custom attributes of the button
        val attr = context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.LoadingButton,
            0,
            0
        )
        try {
              
            // button back-ground color
            bgColor = attr.getColor(
                R.styleable.LoadingButton_bgColor,
                ContextCompat.getColor(context, R.color.purple_200)
            )
              
            // button text color
            textColor = attr.getColor(
                R.styleable.LoadingButton_textColor,
                ContextCompat.getColor(context, R.color.white)
            )
        } finally {
            // clearing all the data associated with attribute
            attr.recycle()
        }
    }
      
    // set attributes of paint
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.FILL
        textAlign = Paint.Align.CENTER // button text alignment
        textSize = 55.0f //button text size
        typeface = Typeface.create("", Typeface.BOLD) // button text's font style
    }
  
    override fun performClick(): Boolean {
        super.performClick()
        if (buttonState == ButtonState.Completed) buttonState = ButtonState.Loading
        animation()
  
        return true
    }
  
    // start the animation when button is clicked
    private fun animation() {
        valueAnimator.start()
    }
      
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.strokeWidth = 0f
        paint.color = bgColor
        // draw custom button
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
  
        // to show rectangular progress on custom button while file is downloading
        if (buttonState == ButtonState.Loading) {
            paint.color = Color.parseColor("#004349")
            canvas.drawRect(
                0f, 0f,
                (width * (progress / 100)).toFloat(), height.toFloat(), paint
            )
        }
        // check the button state
        val buttonText = if (buttonState == ButtonState.Loading)
            resources.getString(R.string.loading)  // We are loading as button text
        else resources.getString(R.string.download)// download as button text
  
        // write the text on custom button
        paint.color = textColor
        canvas.drawText(buttonText, (width / 2).toFloat(), ((height + 30) / 2).toFloat(), paint)
    }
  
}

Etapa 4: Trabalhando com o arquivo XML

Navegue até app> res> layout> activity_main.xml e adicione o código abaixo a esse arquivo. Abaixo está o código para o arquivo activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <!--Custom Button-->
    <com.example.customloadingbutton.LoadingButton
        android:id="@+id/custom_button"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_marginStart="10dp"
        android:layout_marginTop="100dp"
        android:layout_marginEnd="10dp"
        android:layout_marginBottom="100dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:textColor="@color/white" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

attrs.xml (em res -> valores )

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LoadingButton">
        <!--create custom attributes for the view-->
        <attr name="bgColor" format="integer" />
        <attr name="textColor" format="integer" />
    </declare-styleable>
</resources>

loading_animation.xml (em res -> animador , crie o diretório do animador em res)

<?xml version="1.0" encoding="utf-8"?>
<animator 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:valueFrom="0f"
    android:valueTo="100f"
    android:valueType="floatType" />

Etapa 5: Trabalhar com o arquivo MainActivity.kt

Acesse o arquivo MainActivity.kt e consulte o código a seguir. Abaixo está o código para o arquivo MainActivity.kt . Os comentários são adicionados dentro do código para entender o código em mais detalhes.

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
  
class MainActivity : AppCompatActivity() {
  
    lateinit var loadingButton: LoadingButton
    private var complete = false
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        loadingButton = findViewById(R.id.custom_button)
  
        loadingButton.setOnClickListener {
            Toast.makeText(this, "File is downloading", Toast.LENGTH_LONG).show()
            complete = true
        }
  
        if (complete) {
            // call when download completed
            loadingButton.hasCompletedDownload()
        }
    }
}

Saída:

Código Fonte: Link

Quer um ambiente mais competitivo e acelerado para aprender os fundamentos do Android?
Clique aqui para acessar um guia com curadoria exclusiva de nossos especialistas com o objetivo de torná-lo pronto para a indústria em nenhum momento!