Antes do trabalho, o gerente de produto apresentou uma nova demanda, com urgência, como o Android percebe o efeito linha do tempo?

fundo

Antes de sair do trabalho naquele dia, o chefe encontrou Xiaozhuang: "Há uma página a ser otimizada, pequena demanda, você acompanha."
Xiaozhuang: "Bom chefe!" Ele olhou para o tempo, inconscientemente tirou o protótipo e viu isso Uma página:

 

Protótipo

 

Depois de pensar um pouco, Xiao Zhuang habilmente abriu um determinado mecanismo de busca, mas não encontrou a roda certa . Xiao Zhuang sabia que o primeiro passo no desenvolvimento de software deve ser conduzir primeiro a análise de requisitos e design, em vez de arregaçar as mangas. Então ele decidiu analisar as funções e organizar ideias.

 

Aviso: este artigo é muito prolixo e não há produtos secos (fear.jpg)

análise

Análise funcional

Função geral da página:

  • Esta página é uma lista que mostra um determinado processo , e cada item da lista tem um status diferente (concluído, em andamento, não iniciado)
  • Em um lado da lista, há uma visualização semelhante à linha do tempo. De acordo com o status de cada item, pontos e linhas verticais de cores diferentes são exibidos

Análise detalhada

Para a visualização da linha do tempo de um dos itens, quais são os detalhes?

item

 

 

  • Em primeiro lugar, descobri que esta visualização da linha do tempo é composta por duas partes principais, a saber: círculo e linha
  • Então podemos notar naturalmente que na linha do tempo de um item, duas cores aparecem novamente: a linha acima do círculo (doravante referida como a linha superior) é verde, e o próprio círculo e a linha abaixo do círculo (doravante referida como a linha inferior) são novamente É vermelho
    • Em outras palavras, esta visão não só precisa saber sua própria cor, mas também a cor do item anterior
    • Em outras palavras, o desenho desta vista deve ser dividido em três partes, a saber: linha superior, círculo e linha inferior
  • Este é um item normal do meio. No entanto, para o primeiro e o último item, eles não estão online e offline, respectivamente

Ideia de plano

Vários pensamentos passaram rapidamente pela mente de Xiao Zhuang:

  1. A primeira ideia é definir a exibição de cor e linha no adaptador de acordo com o estado dos dados.
    • Mas você ainda precisa de um designer para descobrir um círculo e uma linha tão simples? Não parece que ele é muito bom nisso? Isso é necessário Drawable? No entanto, será difícil alterar a cor no futuro e vários documentos devem ser escritos. Então essa ideia foi passada rapidamente
  2. A segunda ideia é usar uma visualização personalizada, desenhar círculos e linhas em cada item e, em seguida, definir a cor com atributos personalizados.
    • Ele imediatamente escreveu uma demonstração e a experimentou.O resultado foi que sua visualização personalizada não era boa para aprender habilidades e encontrou um problema difícil [Nota], então ele desistiu de chorar.
  3. Talvez esteja destinado a abrir uma porta: amigos, talvez já tenham ouvido falar RecyclerView.ItemDecoration?

Nota: 2.000 anos depois, descobri que não conseguia reproduzir o problema de forma alguma, talvez seja o destino

Introdução ao RecyclerView.ItemDecoration
Este é um artefato poderoso, usado para adicionar divisores a listas é apenas sua habilidade mais comum. Aqui está uma breve introdução, não o conteúdo principal deste artigo. Porque pode ter muitos efeitos, não consigo aprender (ಥ_ಥ)

Para implementar um personalizado ItemDecoration, você precisa herdá-lo e reescrever os dois métodos a seguir, conforme necessário:

  • onDraw: Usado para conteúdo de desenho específico
    • O método possui um parâmetro parent: RecyclerView, a própria lista, então podemos pegar o conteúdo de cada subitem daqui
    • Deve-se notar que a dimensão do desenho neste método é a lista inteira , então precisamos percorrer a lista, calcular a posição e desenhar para cada filho
  • getItemOffsets: Usado para controlar o deslocamento ao redor do item, o conteúdo desenhado no onDraw será desenhado nesses espaços
    • No entanto, a dimensão do desenho deste método é para cada itemView, então as margens superior, inferior, esquerda e direita de cada item são definidas

Comece a codificar

Xiao Zhuang agora tem ideias básicas e reservas de conhecimento e abre o IDE para começar a codificar. Porém, o desenvolvimento de software é um processo iterativo, mesmo para um requisito tão pequeno, ele pretende começar com uma versão simples.

Primeira edição

Na primeira versão, Xiaozhuang pretende desenhar apenas círculos e linhas sem estado ou cor. O objetivo principal é verificar se sua ideia é viável. A implementação específica precisa fazer o seguinte:

  • Prepare-se para definir dois atributos importantes, que participarão do cálculo da posição e do conteúdo do desenho
    • radius: Usado para determinar o raio do círculo
    • offset: Usado para indicar itema distância do ponto ao topo
  • E getItemOffsetsdeixe espaço para desenhar toda a linha itemdo tempo na margem esquerda
  • O conteúdo de trabalho mais importante é que calculamos e desenhamos círculos e linhas (veja o código para cálculos específicos)
class FirstVerTimeline : RecyclerView.ItemDecoration() {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    var radius = 8f
    var offset = 15

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)
        val count = parent.childCount
        for (i in 0 until count) {
            // 获取当前的itemView
            val itemView = parent.getChildAt(i)
            // 整个轴线的x坐标都是相同的
            val xPosition = radius

            // 画上线。第一个item不画
            if (i != 0) {
                c.drawLine(xPosition, itemView.top.toFloat(),
                            xPosition, itemView.top.toFloat() + offset, paint)
            }
            // 画下线。最后一个item不画
            if (i != count - 1) {
                c.drawLine(xPosition, itemView.top  + radius * 2 + offset, 
                            xPosition, itemView.bottom.toFloat(),paint)
            }
            // 画圆
            c.drawCircle(xPosition, itemView.top + offset + radius, radius, paint)
        }
    }

    override fun getItemOffsets(outRect: Rect, view: View, : RecyclerView, state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)
        // 设置item在左边的偏移量
        outRect.left = radius.toInt() * 2
    }
}

Agora podemos definir uma fonte de dados virtual Recorde ItemDecorationaplicar isso a um RecyclerViewefeito Shangkang:

rv_timeline1.adapter = RecordAdapter(ArrayList<Record>())// 省略构造假数据
rv_timeline1.addItemDecoration(FirstVerTimeline())

 

1st.ver.demo1

 

Começou a tomar forma! É que há um pouco de compressão entre a linha do tempo e o texto. Só precisamos adicionar um preenchimento adequado, alterar os dados de teste e parecerá real!

1st.ver.demo2

Para ir da Figura 1 à Figura 2, precisamos fazer:

 

  • Definição paddingLefte paddingRightatributos, usados ​​para indicar o preenchimento esquerdo e direito do eixo
    • Modificado getItemOffsetspara outRect.left = paddingLeft + paddingRight + radius.toInt() * 2deixar a posição de deslocamento
    • O xPositionvalor inicial modificado é radius + paddingLeftpara mudar a coordenada x do eixo

Agora que a primeira versão está completa, quais novos recursos a segunda versão terá? ↓↓↓

segunda edição

Xiao Zhuang pretende implementar cores diferentes do estado na segunda edição. Para realizar essa demanda, ele caiu em profunda contemplação:

  • É definitivamente impossível acoplar a implementação da IU da cor na classe de dados, então uma maneira de obter a cor do estado é necessária
  • Devido a desenhar uma itemnecessidade de conhecer em uma itemcor, simplesmente direcionar toda a lista de fontes de dados dataaferentes ItemDecorationboas
  • Combinando os dois pontos acima, podemos definir um atributo de um tipo de var color: (item: T) -> Intfunção.Ao implementar este atributo, o usuário pode definir a cor desejada através do estado dos dados.

O tipo de função é uma das características do Kotlin (ou programação funcional). Se for Java, você pode considerar o uso do modo de modelo, ou seja, definir um método abstrato para as subclasses reescrever

class SecondVerTimeline<T> : RecyclerView.ItemDecoration() {
    // 其他属性...
    var data: List<T> = ArrayList()  //-->这里有更新,定义了数据源

    var color: (item: T) -> Int = { _ -> Color.GRAY }  //-->这里有更新,通过这个属性设置颜色选择策略

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)
        val count = parent.childCount
        for (i in 0 until count) {
            // ...
            val adapterPosition = parent.getChildAdapterPosition(itemView)  //-->这里有更新,获取当前项的真正位置
            val item = data[adapterPosition]  //-->这里有更新,获取当前项的数据源

            // 画上线。第一个item不画
            if (adapterPosition != 0) {
                paint.color = color(data[adapterPosition - 1])  //-->这里有更新,设置上线的颜色
                c.drawLine(...)
            }

            paint.color = color(item)  //-->这里有更新,设置圆和下线的颜色
            // 画下线。最后一个item不画
            if (adapterPosition != data.size - 1) {//-->这里有更新,改用数据源的大小判断是否为最后一个item
                c.drawLine(...)
            }
            // 画圆...
        }
    }
    // getItemOffsets...
}

Pontos que podem precisar de atenção no código:

  • Antes de desenhar a linha, você precisa data数据源pegá-la 上一个iteme usá-la para color属性obter a cor correspondente ao seu estado
  • A mesma 这一个itemcor que precisa ser alterada antes de desenhar o círculo e a linha inferior
  • O parent.childCountnúmero de subitens obtidos refere-se à parte visível da tela, parent.getChildAdapterPositione a posição real do item na lista deve ser obtida para determinar se desenha o offline. Caso contrário, haverá uma cena embaraçosa de [o último item visível na tela atual não é o último item real, mas não está offline, mas depois de deslizar para baixo, está offline novamente]
  • Observe que itemo método usado para determinar se é o último neste momento count - 1mudou de para data.size - 1, usando o tamanho da fonte de dados para determinar a proporção é countmais preciso (o motivo é o mesmo que o anterior)

Existem também algumas mudanças ao usar:

  • O dataconjunto para oItemDecoration
  • colorDefinir estratégia de cores por meio de atributos
val secondVerTimeline = SecondVerTimeline<Record>()
secondVerTimeline.data = records
secondVerTimeline.color = { item ->
    when (item.status) {
        1 -> color1
        2 -> color2
        ...
    }
}
rv_timeline2.addItemDecoration(secondVerTimeline)

Então você pode correr para ver o efeito:

2nd.ver.demo

 

Nossa, gansa, então você percebeu a função de mudar as cores de acordo com o estado! As funções da segunda edição também foram implementadas com sucesso!

Posfácio

Mais tarde, Xiao Zhuang fez modificações com base na IU e rapidamente completou esse requisito ~ Mas Xiao Zhuang é um programador persistente e começou a pensar na escalabilidade e versatilidade desse código. Não importa se você não quiser, apenas pense em descobrir que não há pato nenhum! E se o produto quiser transformar pontos em imagens? Ou o produto deseja voar mais livremente com o vento?

No entanto, nenhum design pode ser feito de uma vez por todas. A única constante no trabalho de software é a mudança. Ao mesmo tempo, não devemos projetar em excesso em resposta às chamadas "mudanças que podem ocorrer no futuro".

Acho que você gosta

Origin blog.csdn.net/qq_39477770/article/details/108731012
Recomendado
Clasificación