Android

27 abr, 2018

Android Custom Componente com Attr em outras coisas + Higher-Order Functions

Publicidade

Sempre existe a necessidade de criar componentes customizados para projetos, e onde eu trabalho não é diferente. Então, como acabei desenvolvendo um componente novo, utilizando o ATTR e outras coisas + Higher-Order Functions para facilitar o que era para ser mostrado e suas ações, resolvi criar esse artigo para compartilhar o conhecimento.

Declare-styleable

Primeiro vamos definir nossos atributos customizares no arquivo res/values/attr.xml:

<declare-styleable name="WidgetButton">
    <attr name="blueStyle" format="boolean" />
</declare-styleable>

WidgetButton

class WidgetButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {

    @BindView(R.id.txtName)
    lateinit var txtName: TextView

    init {
        val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        inflater.inflate(R.layout.view_name, this)

        ButterKnife.bind(this, this)

        val array = getContext().obtainStyledAttributes(attrs, R.styleable.WidgetButton)
        var isBlue = array.getBoolean(R.styleable.WidgetButton_blueStyle, false)

        txtName.setTextColor(ContextCompat.getColor(btnFollow.context, if(isBlue) R.color.blue else R.color.black_aplha))

        array.recycle()
    }
}

Mudamos a cor do texto de acordo com o boolean que colocamos no xml, como vamos ver mais à frente:

O layout utilizado no WidgetButton

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/txtName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</FrameLayout>

Aplicando em outros xml’s de views

<com.example.app.widget.WidgetButton
            android:id="@+id/widgetButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:isBlue="true"/>

Passamos o atributo app:isBlue=”true” para o texto do widgetButton ficar azul e app:isBlue=”false” para ficar a cor default que colocamos no WidgetButton.kt.

Declaração do WidgetButton em classes (utilizei o ButterKnife para ler as views)

@BindView(R.id.widgetButton)
 lateinit var widgetButton: WidgetButton

Agora é só criar o seu e correr para o abraço. Mas pode ficar melhor.

Extra

Pode-se ir mais além na customização. Vamos lá!

class WidgetButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {

    @BindView(R.id.txtName)
    lateinit var txtName: TextView

    init {
        val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        inflater.inflate(R.layout.view_name, this)

        ButterKnife.bind(this, this)

        val array = getContext().obtainStyledAttributes(attrs, R.styleable.WidgetButton)
        var isBlue = array.getBoolean(R.styleable.WidgetButton_blueStyle, false)

        txtName.colors = ContextCompact(context, if(isBlue) R.color.blue else R.color.black)

        array.recycle()
    }
  
    fun bindData(name: String, listener: () -> Unit) {
        txtName.text = name

        txtName.setOnClickListener {
            listener.invoke()
        }
    }
}

Criamos o método binData passando dois parâmetros: o name e uma função (mais sobre isso no próximo tópico). O name utilizamos para setar o texto do txtName e a função para que cada classe que utilizar o widgetButton possa implementar a própria ação de click como quiser.

A utilização dessa função genérica nos deu o poder de não ter que mudar todas as implementações que já tínhamos para o click da nossa view, pois quando chamamos o invoke no setOnClickListener, a função (Higher-Order Functions) que foi passada como parâmetro é executada.

O método bindData deve ser chamado dentro da classe onde utilizamos o nosso widgetButton, para que a customização do texto e do click seja aplicada como no exemplo a seguir.

@BindView(R.id.txtName)
 lateinit var txtName: TextView
 ...

 fun setData(name: String) {
    txtName.bindData(name) {
        listener.onClickName(name)
    }
 }

 fun setData(name: String) {
    txtName.bindData(name) {
        listener.openConfirmDialog()
    }
 }

Mais a fundo, Higher-Order Functions

É uma função que tem como parâmetro uma ou mais funções, ou o seu retorno é uma função. É um pouco confuso, assim que possível vou criar um artigo falando somente desse assunto. Para aprofundar mais a teoria, sugiro as seguintes leituras:

Bom, galera, ai está mais um artigo para os amigos. Se alguém tiver alguma dúvida ou quiser acrescentar algo, fiquem a vontade!

Me siga no Twitter.