Android TV — Personalizando HeaderSupportFragment

Dec 24 2022
No post anterior, vimos como podemos começar a desenvolver Android TV com o BrowseSupportFragment. BrowseSupportFragment hospeda 2 fragmentos, o HeaderSupportFragment e o RowsSupportFragment.
Android TV — Noções básicas sobre BrowseSupportFragment — Parte 1

No post anterior, vimos como podemos começar a desenvolver Android TV com o BrowseSupportFragment. BrowseSupportFragment hospeda 2 fragmentos, o HeaderSupportFragment e o RowsSupportFragment. Também vimos como podemos começar a personalizar a IU de acordo com nossas necessidades.

Nesta postagem, veremos como podemos personalizar o HeaderSupportFrament para exibir ícones nos cabeçalhos de seção e também nos itens de linha, como este.

HeaderSupportFragment com ícones

Antes da parte aparentemente complexa, vamos ver como podemos mudar o fundo da barra lateral e também a cor do Search Orb que aparece na barra lateral.

Alterando a cor do HeaderSupportFragment

brandColor = ContextCompat.getColor(requireContext(), R.color.color_primary)

searchAffordanceColor = ContextCompat.getColor(requireContext(), R.color.color_primary)

Vamos para a parte complexa agora. Para conseguir isso, você precisará fazer as seguintes coisas.

  1. Melhore a estrutura de dados, ou seja, HeaderItem , para poder receber um recurso Image.
  2. Adicione os recursos de imagem aos dados que alimentam o HeaderSupportFragment.
  3. Crie apresentadores personalizados que podem inflar um layout que contém um ImageView adicional
  4. Deixe o HeaderSupportFragment usar seus apresentadores personalizados em vez das implementações padrão.

Crie uma classe Kotlin que estenda o HeaderItem, que agora pode receber um parâmetro inteiro extra.

class MenuItem(id: Long, name: String, val icon: Int): HeaderItem(id, name) {
}

class SectionItem(id: Long, val name: String, val icon: Int): SectionRow(id, name) {
}

No projeto de exemplo, em vez de passar instâncias de HeaderItems, use instâncias de suas classes recém-criadas.

rowsAdapter.add(SectionItem(100, getString(R.string.qtv), R.drawable.ic_section_icon))
val header = MenuItem(i.toLong(), MovieList.MOVIE_CATEGORY[i], MovieList.MOVIE_ICON[i])
rowsAdapter.add(ListRow(header, listRowAdapter))

val MOVIE_ICON = arrayOf(
        R.drawable.ic_home,
        R.drawable.ic_today,
        R.drawable.ic_puzzles,
        R.drawable.ic_topics,
        R.drawable.ic_fun_facts,
    );

QSectionPresenter.kt

class QSectionPresenter: Presenter() {
    override fun onCreateViewHolder(parent: ViewGroup?): QSectionViewHolder {
        val root: View = LayoutInflater.from(parent!!.context)
            .inflate(R.layout.presenter_section_row, parent, false)

        val viewHolder = QSectionViewHolder(root)
        return viewHolder
    }

    override fun onBindViewHolder(viewHolder: ViewHolder?, item: Any?) {
        val headerItem = if (item == null) null else (item as SectionItem)
        val sectionItem = headerItem as SectionItem
        val vh = viewHolder as QSectionViewHolder
        vh.tvTitle.text = sectionItem.name
        Glide.with(viewHolder.view.context).load(sectionItem?.icon).into(vh.ivIcon)
    }

    override fun onUnbindViewHolder(viewHolder: ViewHolder?) {
        val vh = viewHolder as QSectionViewHolder
        vh.tvTitle.text = null
    }
}

class QSectionViewHolder(view: View) : RowHeaderPresenter.ViewHolder(view) {
    val tvTitle: TextView
    val ivIcon: ImageView
    init {
        tvTitle = view.findViewById(R.id.tvTitle)
        ivIcon = view.findViewById(R.id.ivItemImage)
    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:gravity="center_vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/ivItemImage"
        android:src="@drawable/ic_home" android:tint="@color/color_accent"
        android:layout_width="@dimen/sidebar_section_image_size"
        android:layout_height="@dimen/sidebar_section_image_size"/>
    <TextView android:id="@+id/tvTitle"
        android:layout_marginStart="@dimen/margin_sidebar_image_text"
        android:textStyle="italic|bold"
        android:layout_width="match_parent" android:textColor="@color/color_accent"
        android:text="@string/app_name"
        android:textSize="@dimen/text_size_sidebar_section"
        android:layout_height="wrap_content"/>
</LinearLayout>

class QTVRowPresenter : Presenter() {
    override fun onCreateViewHolder(parent: ViewGroup?): QTVRowViewHolder {
        val root: View = LayoutInflater.from(parent!!.context)
            .inflate(R.layout.presenter_row, parent, false)

        val viewHolder = QTVRowViewHolder(root)
        return viewHolder
    }

    override fun onBindViewHolder(viewHolder: ViewHolder?, item: Any?) {
        val headerItem = if (item == null) null else (item as Row).headerItem
        val menuItem = headerItem as MenuItem
        val vh = viewHolder as QTVRowViewHolder
        vh.tvTitle.text = headerItem?.name
        Glide.with(viewHolder.view.context).load(menuItem?.icon).into(vh.ivIcon)
    }

    override fun onUnbindViewHolder(viewHolder: ViewHolder?) {
        val vh = viewHolder as QTVRowViewHolder
        vh.tvTitle.text = null
    }
}

class QTVRowViewHolder(view: View) : RowHeaderPresenter.ViewHolder(view) {
    val tvTitle: TextView
    val ivIcon: ImageView
    init {
        tvTitle = view.findViewById(R.id.tvTitle)
        ivIcon = view.findViewById(R.id.ivItemImage)
    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent" android:gravity="center_vertical"
    android:layout_height="wrap_content">
    <ImageView
        android:contentDescription="@string/app_name"
        android:id="@+id/ivItemImage"
        android:src="@drawable/ic_home"
        android:layout_width="@dimen/sidebar_image_size"
        android:layout_height="@dimen/sidebar_image_size"/>
    <TextView android:id="@+id/tvTitle"
        android:textSize="@dimen/text_size_sidebar_row"
        android:layout_marginStart="@dimen/margin_sidebar_image_text"
        android:layout_width="wrap_content"
        android:text="@string/app_name"
        android:layout_height="wrap_content"/>
</LinearLayout>

val sHeaderPresenter: PresenterSelector = ClassPresenterSelector()
            .addClassPresenter(DividerRow::class.java, DividerPresenter())
            .addClassPresenter(
                SectionRow::class.java,
                QSectionPresenter()
            )
            .addClassPresenter(Row::class.java, QTVRowPresenter())

        setHeaderPresenterSelector(sHeaderPresenter)

HeaderSupportFragment com ícones e layout personalizado

Agora a barra lateral parece muito melhor do que a implementação padrão. Você pode ir em frente e fazer quantas personalizações quiser, desde que tenha seus apresentadores personalizados instalados.

É tudo por agora.