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.