English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
1. Resumo
DiffUtil é support-v7:24.2A nova classe de ferramentas em .0, usada para comparar dois conjuntos de dados, encontrar o conjunto de dados antigo-A menor quantidade de variação do novo conjunto de dados.
Quando se fala em conjunto de dados, acho que todos sabem com quem está relacionado, é o meu favorito, RecyclerView.
Pelo que pude ver nos últimos dias que usei, a sua maior utilidade é evitar que o mAdapter.notifyDataSetChanged() seja usado sem pensamento ao atualizar o RecyclerView.
Antes, o método mAdapter.notifyDataSetChanged() tinha duas desvantagens:
1.Não aciona a animação do RecyclerView (remoção, adição, deslocamento, animação de mudança)
2.A performance é baixa, pois atualiza sem pensar todo o RecyclerView, em casos extremos: se os conjuntos de dados novos e antigos forem idênticos, a eficiência é a mais baixa.
Depois de usar o DiffUtil, a código passa a ser o seguinte:
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true);
diffResult.dispatchUpdatesTo(mAdapter);
Ele calculará automaticamente as diferenças entre os conjuntos de dados novos e antigos e, com base nas diferenças, automaticamente chamará os seguintes quatro métodos
adapter.notifyItemRangeInserted(position, count);
adapter.notifyItemRangeRemoved(position, count);
adapter.notifyItemMoved(fromPosition, toPosition);
adapter.notifyItemRangeChanged(position, count, payload);
Claramente, esses quatro métodos executam a animação do RecyclerView, e todos são métodos de atualização direcionados, aumentando a eficiência da atualização.
Como de costume, vamos mostrar as imagens primeiro,
A imagem 1 mostra o efeito de usar o método notifyDataSetChanged() sem pensar, onde é possível ver que a interação de atualização é rígida e os itens aparecem subitamente em certas posições:
A imagem 2 mostra o efeito da utilização de DiffUtils, onde é evidente a animação de inserção e movimentação de itens:
A conversão para GIF é um pouco ruim, é melhor baixar o Demo no final para ver o efeito.
Este artigo incluirá e não se limitará aos seguintes conteúdos:
1 Primeiro, apresentarei o uso simples do DiffUtil, implementando o efeito de atualização incremental ao atualizar (o nome 'atualização incremental' é meu).
2 Uso avançado do DiffUtil, completando atualizações parciais (oficialmente chamadas de Partial bind, parte de ligação) quando apenas o conteúdo (data) do item muda, e a posição (position) não muda.
3 Conheça o método public void onBindViewHolder(VH holder, int position, List<Object> payloads) do RecyclerView.Adapter e domine-o.
4 Calcule o DiffResult na thread secundária e atualize o RecyclerView na thread principal.
5 Como remover a animação de brilho dos itens, que alguns não gostam, causada por notifyItemChanged().
6 Tradução para chinês simplificado de parte da documentação oficial de classes e métodos do DiffUtil
Uso simples de DiffUtil
Como mencionado anteriormente, DiffUtil ajuda-nos a calcular as diferenças entre conjuntos de dados novos e antigos ao atualizar o RecyclerView e a chamar automaticamente o método de atualização do RecyclerView.Adapter para completar atualizações eficientes com efeitos de animação de itens.
Portanto, antes de aprender sobre ele, precisamos fazer algumas preparações, escrevendo um Demo de versão jovem comum que atualiza sem pensar no método notifyDataSetChanged().
1 Um JavaBean comum, mas que implementou o método clone, usado apenas para simular atualizações no Demo, não necessário em projetos reais, pois os dados são puxados da rede durante a atualização.
class TestBean implements Cloneable { private String name; private String desc; ....//Métodos get e set omitidos //Apenas DEMO - Implementar método de clonagem @Override public TestBean clone() throws CloneNotSupportedException { TestBean bean = null; try { bean = (TestBean) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return bean; }
2 Implementar um RecyclerView.Adapter comum.
public class DiffAdapter extends RecyclerView.Adapter<DiffAdapter.DiffVH> { private final static String TAG = "zxt"; private List<TestBean> mDatas; private Context mContext; private LayoutInflater mInflater; public DiffAdapter(Context mContext, List<TestBean> mDatas) { this.mContext = mContext; this.mDatas = mDatas; mInflater = LayoutInflater.from(mContext); } public void setDatas(List<TestBean> mDatas) { this.mDatas = mDatas; } @Override public DiffVH onCreateViewHolder(ViewGroup parent, int viewType) { return new DiffVH(mInflater.inflate(R.layout.item_diff, parent, false)); } @Override public void onBindViewHolder(final DiffVH holder, final int position) { TestBean bean = mDatas.get(position); holder.tv1.setText(bean.getName()); holder.tv2.setText(bean.getDesc()); holder.iv.setImageResource(bean.getPic()); } @Override public int getItemCount() { return mDatas != null ? mDatas.size() : 0; } class DiffVH extends RecyclerView.ViewHolder { TextView tv1, tv2; ImageView iv; public DiffVH(View itemView) { super(itemView); tv1 = (TextView) itemView.findViewById(R.id.tv1); tv2 = (TextView) itemView.findViewById(R.id.tv2); iv = (ImageView) itemView.findViewById(R.id.iv); } } }
3 Código da Activity:
public class MainActivity extends AppCompatActivity { private List<TestBean> mDatas; private RecyclerView mRv; private DiffAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); mRv = (RecyclerView) findViewById(R.id.rv); mRv.setLayoutManager(new LinearLayoutManager(this)); mAdapter = new DiffAdapter(this, mDatas); mRv.setAdapter(mAdapter); } private void initData() { mDatas = new ArrayList<>(); mDatas.add(new TestBean("张旭童"1"Android", R.drawable.pic1)); mDatas.add(new TestBean("张旭童"2"Java", R.drawable.pic2)); mDatas.add(new TestBean("张旭童"3"Fazer a tarefa", R.drawable.pic3)); mDatas.add(new TestBean("张旭童"4"Teste de produto", R.drawable.pic4)); mDatas.add(new TestBean("张旭童"5"Teste de desmembramento", R.drawable.pic5)); } /** * Operação de atualização simulada * * @param view */ public void onRefresh(View view) { try { List<TestBean> newDatas = new ArrayList<>(); for (TestBean bean : mDatas) { newDatas.add(bean.clone());//Clonar os dados antigos, simular operação de atualização } newDatas.add(new TestBean("Zhao Zilong", "Gostoso", R.drawable.pic6));//Simulação de adição de dados newDatas.get(0).setDesc("Android+"); newDatas.get(0).setPic(R.drawable.pic7);//Simulação de modificação de dados TestBean testBean = newDatas.get(1);//Simulação de deslocamento de dados newDatas.remove(testBean); newDatas.add(testBean); //Não se esqueça de fornecer os novos dados ao Adapter mDatas = newDatas; mAdapter.setDatas(mDatas); mAdapter.notifyDataSetChanged();//Antes, a maioria das vezes que só podíamos fazer assim } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
É muito simples, mas quando constrói o novo conjunto de dados newDatas, percorre o conjunto de dados antigo mDatas, chama o método clone() de cada data, para garantir que os conjuntos de dados novos e antigos sejam consistentes em termos de dados, mas diferentes em termos de endereço de memória (ponteiro), para que, quando modificar os valores no newDatas, não afete os valores no mDatas.
4 activity_main.xml Removi了一些códigos de largura e altura, é apenas um RecyclerView e um Button para simular a atualização.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" > <android.support.v7.widget.RecyclerView android:id="@"+id/rv" /> <Button android:id="@"+id/btnRefresh" android:layout_alignParentRight="true" android:onClick="onRefresh" android:text="Simulate Refresh" /> </RelativeLayout>
The above is a demo that a normal youth can easily write, a brainless notifyDataSetChanged() demo, running effect as shown in Figure 1 of the first section.
But we all want to be artistic youth, so
Let's get to the main topic, simply using DiffUtil, we need to write only one additional class.
To become an artistic youth, we need to implement a class inheriting from DiffUtil.Callback and implement its four abstract methods.
Although this class is called Callback, it is more appropriate to understand it as: a class that defines some contracts (Contracts) and rules (Rules) for comparing whether new and old Items are equal.
The DiffUtil.Callback abstract class is as follows:
public abstract static class Callback { public abstract int getOldListSize();//tamanho do conjunto de dados antigos public abstract int getNewListSize();//tamanho do conjunto de dados novos public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);//Is the Item at the same position in the new and old data sets an object? (The content may be different; if this returns true, the following method will be called) public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);//This method is only called if the above method returns true, my understanding is that only notifyItemRangeChanged() will call it, to determine if the item content has changed //This method is used in the advanced usage of DiffUtil, and will not be discussed here @Nullable public Object getChangePayload(int oldItemPosition, int newItemPosition) { return null; } }
This Demo implements DiffUtil.Callback as follows, with core method annotations in both Chinese and English (in other words, the official English annotations have been translated to facilitate better understanding for everyone).
/** * Description: A core class used to determine if the new and old Items are equal * Author: zhangxutong * Email: [email protected] * Data: 2016/9/12. */ public class DiffCallBack extends DiffUtil.Callback { private List<TestBean> mOldDatas, mNewDatas;//veja o nome public DiffCallBack(List<TestBean> mOldDatas, List<TestBean> mNewDatas) { this.mOldDatas = mOldDatas; this.mNewDatas = mNewDatas; } //tamanho do conjunto de dados antigos @Override public int getOldListSize() { return mOldDatas != null ? mOldDatas.size() : 0; } //tamanho do conjunto de dados novos @Override public int getNewListSize() { return mNewDatas != null ? mNewDatas.size() : 0; } /** * Chamado pelo DiffUtil para decidir se dois objetos representam o mesmo Item. * Chamado pelo DiffUtil para determinar se dois objetos são itens idênticos. * Por exemplo, se seus itens tiverem ids únicos, este método deve verificar a igualdade dos ids. * Por exemplo, se o seu Item tiver um campo id único, este método deve verificar se o id é igual. * Este exemplo determina se o campo name é idêntico. * * @param oldItemPosition A posição do item na lista antiga * @param newItemPosition A posição do item na nova lista * @return Verdadeiro se os dois itens representam o mesmo objeto ou falso se eles forem diferentes. */ @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { retorna mOldDatas.get(oldItemPosition).getName().equals(mNewDatas.get(newItemPosition).getName()); } /** * Chamado pelo DiffUtil quando ele deseja verificar se dois itens têm os mesmos dados. * é chamado pelo DiffUtil para verificar se dois itens contêm dados idênticos * DiffUtil usa esta informação para detectar se o conteúdo de um item mudou. * DiffUtil usa esta informação para detectar se o conteúdo do item mudou. * DiffUtil usa este método para verificar a igualdade em vez de {@link Object#equals(Object)} * DiffUtil usa este método para verificar a igualdade em vez de {@link Object#equals(Object)} * de modo que você possa alterar seu comportamento dependendo da sua UI. * Portanto, você pode alterar seu valor de retorno com base em sua UI * Por exemplo, se você estiver usando DiffUtil com um * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, você deve * retornar se as representações visuais dos itens são as mesmas. * Por exemplo, se você usar RecyclerView.Adapter juntamente com DiffUtil, você precisa retornar se a representação visual dos itens é a mesma. * Este método é chamado apenas se o {@link #areItemsTheSame(int, int)} retornar * {@code true} for these items. * Este método é chamado apenas se o areItemsTheSame() retornar true. * @param oldItemPosition A posição do item na lista antiga * @param newItemPosition The position of the item in the new list which replaces the * oldItem * @return True if the contents of the items are the same or false if they are different. */ @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {}} TestBean beanOld = mOldDatas.get(oldItemPosition); TestBean beanNew = mNewDatas.get(newItemPosition); if (!beanOld.getDesc().equals(beanNew.getDesc())) { return false;//Se o conteúdo for diferente, retorne false } if (beanOld.getPic() != beanNew.getPic()) { return false;//Se o conteúdo for diferente, retorne false } return true; //O conteúdo padrão dos dois dados é o mesmo }
Comentários detalhados foram escritos+Código simples, acredito que você pode entender com um olhar.
Então, ao usar, comente o método notifyDatasetChanged() que você escreveu anteriormente e substitua pelo seguinte código:
//Novo favorito dos jovens literários //Utilize o método DiffUtil.calculateDiff(), passe um objeto DiffUtil.Callback como regra e uma variável booleana que detecta o movimento do item, para obter o objeto DiffUtil.DiffResult. DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true); //Utilize o método dispatchUpdatesTo() do objeto DiffUtil.DiffResult, passe-o para o Adapter do RecyclerView, e facilmente se torne um jovem literário. diffResult.dispatchUpdatesTo(mAdapter); //Não se esqueça de fornecer os novos dados ao Adapter mDatas = newDatas; mAdapter.setDatas(mDatas);
Explicação:
Passo um
Antes de definir newDatas para o Adapter, chame o método DiffUtil.calculateDiff().Calcula o conjunto de atualizações mínimo entre o conjunto de dados novo e antigo.
O objeto DiffUtil.DiffResult.
A definição do método DiffUtil.calculateDiff() é a seguinte:
O primeiro parâmetro é o objeto DiffUtil.Callback.
O segundo parâmetro representa se deve detectar o movimento do Item, altere para false para que a algoritmo seja mais eficiente, configure conforme necessário, aqui estamos usando true.
public static DiffResult calculateDiff(Callback cb, boolean detectMoves)
Passo dois
Em seguida, utilize o método dispatchUpdatesTo() do objeto DiffUtil.DiffResult, passando o Adapter do RecyclerView, para substituir o método mAdapter.notifyDataSetChanged() usado pelos jovens comuns.
Ver o código-fonte revela que, internamente, o método chama os quatro métodos de atualização direcionada do adapter com base na situação.
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) { dispatchUpdatesTo(new ListUpdateCallback() { @Override public void onInserted(int position, int count) { adapter.notifyItemRangeInserted(position, count); } @Override public void onRemoved(int position, int count) { adapter.notifyItemRangeRemoved(position, count); } @Override public void onMoved(int fromPosition, int toPosition) { adapter.notifyItemMoved(fromPosition, toPosition); } @Override public void onChanged(int position, int count, Object payload) { adapter.notifyItemRangeChanged(position, count, payload); } }); }
Resumo:
Portanto, o DiffUtil não só pode ser usado com o RecyclerView, mas também podemos implementar os quatro métodos da interface ListUpdateCallback para fazer algumas coisas. (Por enquanto, não assumo responsabilidade por qualquer uma dessas opções, pensei em combinar com um controle de nove quadrados em meu projeto? Ou otimizar o NestFullListView que escrevi no meu artigo anterior? Pequena recomendação, veja a solução relativamente elegante para ListView, RecyclerView e ScrollView com ListView aninhado: http://blog.csdn.net/zxt0601/article/details/52494665)
Até aqui, já evoluímos para intelectuais, o efeito de execução é basicamente o mesmo que a imagem 2 do primeiro capítulo,
A única diferença é que neste momento, adapter.notifyItemRangeChanged() terá uma animação de atualização de Item brilhando (o item de Demo deste artigo está no position 0). Alguém gosta dessa animação de flash de Item, alguém odeia, mas isso não importa.
Porque quando aprendemos o uso avançado do DiffUtil no terceiro capítulo, se você gosta ou não dessa animação de ItemChange, ela vai se dissipar ao vento. (Não sei se é um bug oficial)
O efeito é como a imagem 2 do primeiro capítulo, o nosso item0 realmente mudou a imagem e o texto, mas essa mudança não veio com nenhum efeito animado.
Vamos seguir o caminho dos intelectuais entre os intelectuais.
Três usos avançados do DiffUtil
Teoria:
Uso avançado envolve apenas dois métodos,
Precisamos implementar separadamente DiffUtil.Callback
método public Object getChangePayload(int oldItemPosition, int newItemPosition),
O Object retornado representa o que mudou no item.
em conjunto com RecyclerView.Adapter
método public void onBindViewHolder(VH holder, int position, List<Object> payloads),
Conclua a atualização direcionada. (Tornar-se o intelectual entre os intelectuais, intelectual intelectual.)
Atenção, isso é um novo método, note que ele tem três parâmetros, os dois primeiros que conhecemos, e o terceiro parâmetro contém o Object que retornamos em getChangePayload().
Bem, então vamos primeiro ver quem é esse método:
em v7-24.2.0 do código-fonte, ele se parece com isso:
/** * Chamado pelo RecyclerView para exibir os dados na posição especificada. Este método * deve atualizar o conteúdo do {@link ViewHolder#itemView} para refletir o item em * a posição fornecida. * <p> * Note que, ao contrário de {@link android.widget.ListView}, RecyclerView não chamará este método * novamente se a posição do item mudar no conjunto de dados, a menos que o item em si seja * inválida ou a nova posição não pode ser determinada. Por essa razão, você deve apenas * use o <code>position</parametro enquanto obtendo o item de dados relacionado dentro * este método e não deve manter uma cópia dele. Se precisar da posição de um item mais tarde * no (por exemplo, em um ouvinte de clique), use {@link ViewHolder#getAdapterPosition()} que retornará * possuir a posição do adaptador atualizada. * <p> * Ligação parcial vs ligação completa: * <p> * O parâmetro payloads é uma lista de merge de {@link #notifyItemChanged(int, Object)} ou * {@link #notifyItemRangeChanged(int, int, Object)}. Se a lista de payloads não estiver vazia, * o ViewHolder está atualmente vinculado aos dados antigos e o Adapter pode executar uma operação eficiente parcial * update usando a informação do payload. Se o payload estiver vazio, o Adapter deve executar um bind completo. * Adapter não deve assumir que o payload passado nos métodos de notificação será recebido por * onBindViewHolder(). Por exemplo, quando a view não está anexada à tela, o * os payloads notificados em notifyItemChange() serão simplesmente descartados. * * @param holder O ViewHolder que deve ser atualizado para representar o conteúdo do * item na posição fornecida no conjunto de dados. * @param position A posição do item dentro do conjunto de dados do adapter. * @param payloads Um não-Lista nula de payloads misturados. Pode ser uma lista vazia se exigir bind completo * update. */ public void onBindViewHolder(VH holder, int position, List<Object> payloads) { else { }
Originalmente, ele interna chamada apenas do onBindView com dois parâmetros (por falar nisso, meu NestFullListView Adapter também tem um pouco de semelhança com essa escrita, parece que estou mais perto do grande gênio da Google).
É só quando vi isso que percebi que, na verdade, a entrada onBindView é este método, que é o método correspondente ao onCreateViewHolder.
Vários linhas abaixo do código podemos ver que há um public final void bindViewHolder(VH holder, int position),que interna chamada do método onBindView com três parâmetros.
Sobre RecyclerView.Adapter, não é possível explicar em apenas algumas palavras. (Na verdade, eu só cheguei até aqui)
Bem, não vou divagar mais. Voltemos ao nosso onBindViewHolder com três parâmetros (VH holder, int position, List<Object> payloads), a cabeça desse método tem muitos comentários em inglês, acho que ler esses comentários é muito útil para entender o método, então traduzi.
Tradução:
Chamado pelo RecyclerView para exibir dados na posição especificada.
Esse método deve atualizar o conteúdo do ItemView no ViewHolder para refletir a mudança no item (na posição fornecida).
Atenção, ao contrário da ListView, se os dados do item na posição fornecida mudarem, o RecyclerView não chama esse método novamente, a menos que o item em si esteja inválido (invalidated) ou a nova posição não puder ser determinada.
Por essa razão, dentro desse método, você deve usar apenas o parâmetro position para obter o item de dados relacionado e não deve manter uma cópia do item de dados.
Se precisar da posição do item mais tarde, por exemplo, para configurar o clickListener, deve usar ViewHolder.getAdapterPosition(), que pode fornecer a posição atualizada.
(Aqui, eu notei que está explicando o método onBindViewHolder de dois parâmetros)
A parte única deste método de três parâmetros é a seguinte:)
**vínculo parcial (partial)**vs vínculo completo (full)
O parâmetro payloads é uma lista combinada obtida de (notifyItemChanged(int, Object) ou notifyItemRangeChanged(int, int, Object)).
Se a lista de payloads não estiver vazia, o ViewHolder atualmente vinculado a dados antigos e o Adapter podem usar os dados do payload para realizar uma atualização parcial eficiente.
Se o payload estiver vazio, o Adapter deve realizar um vínculo completo (chamar o método de dois parâmetros).
Adapter não deve assumir (pensar que é óbvio) que os payloads passados nas notificações notifyxxxx estarão necessariamente recebidos no método onBindViewHolder(). (Essa frase é difícil de traduzir QAQ, dê uma olhada nas exemplos)
Por exemplo, quando a View não está anexada na tela, simplesmente descarte o payload vindo de notifyItemChange().
o objeto payloads não é nulo, mas pode estar vazio (vazio), neste caso, é necessário vincular completamente (portanto, só precisamos verificar isEmpty no método, sem verificar repetidamente o vazio).
Palavras do autor: Este método é uma maneira eficiente. Sou um tradutor ineficiente, vi40+minutos. Finalmente, percebi que a parte importante já está destacada.
Prática:
Tão muitas palavras, na verdade, é super simples de usar:
Vamos ver como usar o método getChangePayload() e também vem com comentários em chinês e inglês
/** * Quando {@link #areItemsTheSame(int, int)} retorna {@code true} para dois itens e * Quando {@link #areContentsTheSame(int, int)} retorna false para eles, DiffUtil * chama este método para obter uma carga útil sobre a mudança. * * Quando {@link #areItemsTheSame(int, int)} retorna true e {@link #areContentsTheSame(int, int)} retorna false, DiffUtils chama este método para obter uma carga útil sobre a mudança * para obter o payload (quais mudanças) do item. * * Por exemplo, se você estiver usando DiffUtil com {@link RecyclerView}, você pode retornar esses * campo específico que mudou no item e seu * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} pode usar essas * informação para executar a animação correta. * * Por exemplo, se você usar RecyclerView com DiffUtils, você pode retornar esses campos que mudaram no item * {@link android.support.v7A ItemAnimator pode usar quais informações para executar a animação correta * * A implementação padrão retorna {@code null}. * A implementação padrão é retornar null * * @param oldItemPosition A posição do item na lista antiga * @param newItemPosition A posição do item na nova lista * @return Um objeto payload que representa a mudança entre os dois itens. * Retorna um objeto payload que representa a mudança entre os dois itens. */ @Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { //Ao implementar este método, você se torna o mais culto dos jovens literários // Atualização parcial na atualização direcionada // A maior eficiência //Só que não há o efeito de brilho da animação ItemChange (de qualquer forma, acho que não é muito importante) TestBean oldBean = mOldDatas.get(oldItemPosition); TestBean newBean = mNewDatas.get(newItemPosition); //Aqui não precisamos comparar os campos principais, eles sempre serão iguais Bundle payload = new Bundle(); if (!oldBean.getDesc().equals(newBean.getDesc())) { payload.putString("KEY_DESC", newBean.getDesc()); } if (oldBean.getPic() != newBean.getPic()) { payload.putInt("KEY_PIC", newBean.getPic()); } if (payload.size() == 0)//Se não houver mudanças, passe vazio return null; return payload;// }
Em termos simples, este método retorna um objeto do tipo payload que contém o conteúdo alterado de algum item.
Aqui usamos Bundle para salvar essas mudanças.
No Adapter, como reescrever o onBindViewHolder de três parâmetros:
@Override public void onBindViewHolder(DiffVH holder, int position, List<Object> payloads) { onBindViewHolder(holder, position); else { } //Jovens literários literários Bundle payload = (Bundle) payloads.get(0); TestBean bean = mDatas.get(position); for (String key : payload.keySet()) { switch (key) { case "KEY_DESC": //Aqui pode-se usar os dados do payload, mas também pode-se usar holder.tv2.setText(bean.getDesc()); break; case "KEY_PIC": holder.iv.setImageResource(payload.getInt(key)); break; padrão: break; } } } }
Os payloads passados aqui são uma lista, conforme os comentários, não pode ser null, então julgamos se é vazio
Se estiver vazio, chame a função de dois parâmetros para realizar um bind completo uma vez
Se não for vazio, realize um bind parcial
Extraindo o payload que retornamos no método getChangePayload pelo índice 0, então percorrendo as chaves do payload, pesquisando conforme a chave, se o payload contiver a mudança correspondente, retire-a e atualize-a no ItemView.
(Aqui, os dados mais recentes obtidos através de mDatas também são os dados da fonte mais recente, então é possível atualizar com os dados do payload ou os dados novos).
Até agora, já dominamos a atualização do RecyclerView, o método mais literário dos jovens literários.
Quarto, usar DiffUtil em uma thread secundária
As informações do DiffUtil são introduzidas no cabeçalho dos comentários da fonte do DiffUtil
DiffUtil utiliza o algoritmo de diferença de Eugene W. Myers, mas esse algoritmo não pode detectar a movimentação de itens, então a Google melhorou sobre ele para suportar a detecção de itens movidos, mas a detecção de itens movidos consome mais desempenho.
No existe10ao fazer 00 itens de dados,}}2ao fazer 00 alterações, o tempo de execução dessa algoritmo é:
Ao ativar a detecção de movimento: média:27.07ms, mediana:26.92ms.
Ao desativar a detecção de movimento: média:13.54ms, mediana:13.36ms.
Quem tiver interesse pode ler os comentários no cabeçalho do código-fonte, o que nos é útil é um trecho que menciona:
Se nossa lista for muito grande, o tempo necessário para calcular o DiffResult é bastante longo, então devemos colocar o processo de obtenção de DiffResult em uma subthread e atualizar o RecyclerView na thread principal.
Aqui, uso Handler em conjunto com DiffUtil:
O código está conforme a seguir:
private static final int H_CODE_UPDATE = 1; private List<TestBean> mNewDatas;//Adicione uma variável para armazenar newList temporariamente private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case H_CODE_UPDATE: //Extraia o Result DiffUtil.DiffResult diffResult = (DiffUtil.DiffResult) msg.obj; diffResult.dispatchUpdatesTo(mAdapter); //Não se esqueça de fornecer os novos dados ao Adapter mDatas = mNewDatas; mAdapter.setDatas(mDatas); break; } } }); new Thread(new Runnable() { @Override public void run() { //Coloque a calculação de DiffResult em uma subthread DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, mNewDatas), true); Message message = mHandler.obtainMessage(H_CODE_UPDATE); message.obj = diffResult;//obj armazena DiffResult message.sendToTarget(); } }).start();
É uma simples utilização do Handler, não há necessidade de repetir.
Resumo e outros
1 Na verdade, o código deste artigo é muito pouco, você pode baixar o Demo para verificar, são apenas quatro classes.
Mas, de repente, escrevi isso tão longo, principalmente envolvendo a tradução de comentários de código-fonte, para facilitar a compreensão das pessoas.
2 DiffUtil é muito adequado para esse tipo de cena de atualização de arrasto
A eficiência da atualização foi melhorada, e há animação, e ainda não precisa pensar.
No entanto, se for apenas fazer algo como deletar, gostar, não é necessário usar DiffUtils. Lembre-se da posição, verifique se a posição está na tela, chame os métodos de atualização direcionados, etc.
3 Na verdade, DiffUtil não pode ser usado apenas com RecyclerView.Adapter
Podemos implementar o interface ListUpdateCallback, usar DiffUtil para encontrar o conjunto de diferenças mínimo entre os conjuntos de dados antigo e novo para fazer mais coisas.
4 Atenção: ao escrever o DEMO, o conjunto de dados antigo e novo para comparação, não apenas ArrayList é diferente, mas cada data dentro deles também deve ser diferente. Caso contrário, changed não será acionado.
Não podemos encontrar isso em projetos reais, porque os novos dados geralmente vêm da rede.
5 Hoje é o último dia do Festival de Luna, e minha empresa começou a trabalhar!!! Em vez de estar chateado, escrevi um artigo sobre DiffUtil, mesmo que eu não precise usar DiffUtil, posso comparar facilmente as diferenças entre minha empresa e outras empresas. QAQ, e hoje estou em péssimo estado, escrevi8Foi concluído em uma hora. Pensei que este artigo poderia ser incluído na compilação de micro-ensaios, mas surpreendentemente é longo. Quem não tiver paciência pode baixar o DEMO para ver, não há muitos códigos, é muito fácil usá-lo.
Porta de entrada do github:
https://github.com/mcxtzhang/DiffUtils
Aqui está a análise de Android7Organização de materiais do utilitário DiffUtil .0, continuaremos a complementar materiais relevantes, obrigado pelo apoio da comunidade!