En este artículo, vamos a crear un botón de carga personalizado extendiendo la clase Ver y animar las propiedades del botón personalizado una vez que se hace clic en él. Nos habíamos encontrado muchas veces al descargar cualquier archivo y vigilar el progreso de la descarga. Aquí solo crearemos ese botón personalizado con animación. A continuación se proporciona un GIF de muestra para tener una idea de lo que vamos a hacer en este artículo. Tenga en cuenta que vamos a implementar este proyecto utilizando el lenguaje Kotlin .
Implementación paso a paso
Paso 1: Crear un nuevo proyecto
Para crear un nuevo proyecto en Android Studio, consulte Cómo crear/iniciar un nuevo proyecto en Android Studio . Tenga en cuenta que seleccione Kotlin como lenguaje de programación.
Paso 2: cree una clase sellada, ButtonState , que describa el estado (como se hizo clic, se cargó y se completó) del botón personalizado. A continuación se muestra el código para el archivo ButtonState.kt .
Kotlin
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 }
Paso 3: Cree otra clase, LoadingButton en la que se definirá todo lo relacionado con el botón, como el color, el texto, la animación, etc. Cree un constructor anotándolo con JvmOverloads . A continuación se muestra el código para el archivo LoadingButton.kt .
Kotlin
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) } }
Paso 4: trabajar con el archivo XML
Vaya a la aplicación > res > diseño > actividad_principal.xml y agregue el siguiente código a ese archivo. A continuación se muestra el código para el archivo activity_main.xml .
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 (bajo res -> valores )
XML
<?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 (en res -> animador , crea el directorio animador en res)
XML
<?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" />
Paso 5: trabajar con el archivo MainActivity.kt
Vaya al archivo MainActivity.kt y consulte el siguiente código. A continuación se muestra el código del archivo MainActivity.kt . Se agregan comentarios dentro del código para comprender el código con más detalle.
Kotlin
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() } } }
Producción:
Código fuente: Enlace