FastAdapter мигает после загрузки из LiveData, даже если данные остаются теми же - PullRequest
0 голосов
/ 30 октября 2018

Для вопроса я подготовил простой и рабочий пример на Github :

app screenshot

Мой пример приложения загружает массив JSON, содержащий 30 лучших игроков в игре, используя okhttp, и сохраняет их в SQLite Room. Во фрагменте я наблюдаю соответствующий объект LiveData<List<TopEntity>> и обновляю экземпляр FastAdapter:

public class TopFragment extends Fragment {
    private final ItemAdapter<TopItem> mItemAdapter = new ItemAdapter<>();
    private final FastAdapter<TopItem> mFastAdapter = FastAdapter.with(mItemAdapter);

    private TopViewModel mViewModel;
    private ProgressBar mProgressBar;
    private RecyclerView mRecyclerView;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {

        mViewModel = ViewModelProviders.of(this).get(TopViewModel.class);
        mViewModel.getTops().observe(this, tops -> {
            mItemAdapter.clear();
            for (TopEntity top: tops) {
                TopItem item = new TopItem(top);
                mItemAdapter.add(item);
            }
        });

        View v = inflater.inflate(R.layout.top_fragment, container, false);
        mProgressBar = v.findViewById(R.id.progressBar);
        mRecyclerView = v.findViewById(R.id.recyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        mRecyclerView.setAdapter(mFastAdapter);

        fetchJsonData();

        return v;
    }

Моя проблема заключается в следующем: вид утилизатора мигает один раз - каждый раз, когда данные загружаются.

Несмотря на то, что верхние 30 меняются не часто, то есть данные остаются неизменными.

Когда я смотрю на пример приложения в превосходной библиотеке FastAdapter от Mr. Penz, я не вижу там мерцания.

UPDATE:

У меня есть намек , что мерцание явно вызвано вызовом mItemAdapter.clear(); в методе onChanged, и я должен вместо этого использовать класс DiffUtil.

Итак, я добавил новый класс:

public class DiffCallback extends DiffUtil.Callback {
    private final List<TopItem> mOldList;
    private final List<TopItem> mNewList;

    public DiffCallback(List<TopItem> oldStudentList, List<TopItem> newStudentList) {
        this.mOldList = oldStudentList;
        this.mNewList = newStudentList;
    }

    @Override
    public int getOldListSize() {
        return mOldList.size();
    }

    @Override
    public int getNewListSize() {
        return mNewList.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        TopItem oldItem = mOldList.get(oldItemPosition);
        TopItem newItem = mNewList.get(newItemPosition);

        return oldItem.uid == newItem.uid;
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        TopItem oldItem = mOldList.get(oldItemPosition);
        TopItem newItem = mNewList.get(newItemPosition);

        return oldItem.elo == newItem.elo &&
                oldItem.given.equals(newItem.given) &&
                //oldItem.photo != null && oldItem.photo.equals(newItem.photo) &&
                oldItem.avg_time != null && oldItem.avg_time.equals(newItem.avg_time) &&
                oldItem.avg_score == newItem.avg_score;
    }

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        return super.getChangePayload(oldItemPosition, newItemPosition);
    }
}

А потом я пытаюсь использовать его в методе onChanged вместо простого вызова mItemAdapter.clear(), но RecyclerView остается пустым, даже если tops имеет элементы:

mViewModel = ViewModelProviders.of(this).get(TopViewModel.class);
mViewModel.getTops().observe(this, tops -> {
    List<TopItem> oldList = mItemAdapter.getAdapterItems();
    List<TopItem> newList = new ArrayList<>();
    for (TopEntity top: tops) {
        TopItem item = new TopItem(top);
        newList.add(item);
    }

    DiffCallback diffCallback = new DiffCallback(oldList, newList);
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

    mItemAdapter.getAdapterItems().clear();
    mItemAdapter.getAdapterItems().addAll(newList);
    diffResult.dispatchUpdatesTo(mFastAdapter);
});

КРАТКОЕ ИЗЛОЖЕНИЕ МОЕЙ ПРОБЛЕМЫ:

Мое настоящее приложение использует FastAdapter по нескольким причинам, и я ищу способ "жениться" на нем с DiffUtil , чтобы исключить однократное мерцание, когда данные загружаются по HTTPS и обновлено в номере.

Ответы [ 3 ]

0 голосов
/ 02 ноября 2018

Простой способ избежать мигания - отключить RecyclerView 's ItemAnimator.

mRecyclerView.setItemAnimator(null);
0 голосов
/ 02 ноября 2018

С помощью автора FastAdapter мне удалось устранить одноразовое мерцание, добавив идентификатор для моего TopItem , отображаемого в RecyclerView:

@Override
public long getIdentifier() {
    return uid;
}

И с помощью FastAdapterDiffUtil в моем TopFragment :

mViewModel = ViewModelProviders.of(this).get(TopViewModel.class);
mViewModel.getTops().observe(this, tops -> {
    List<TopItem> newList = new ArrayList<>();
    for (TopEntity top: tops) {
        TopItem item = new TopItem(top);
        newList.add(item);
    }

    DiffUtil.DiffResult diffResult = FastAdapterDiffUtil.calculateDiff(mItemAdapter, newList);
    FastAdapterDiffUtil.set(mItemAdapter, diffResult);
});
0 голосов
/ 02 ноября 2018

Попробуйте вернуть некоторый маркерный объект из метода getChangePayload ().

@Nullable
@Override
public Object getChangePayload(final int oldItemPosition, final int newItemPosition) {
    return mNewList.get(newItemPosition);
}
...