com.shizhefei.view.multitype.MultiTypeAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.shizhefei.view.multitype.MultiTypeAdapter.java

Source

/*
Copyright 2016 shizhefeiLuckyJaycehttps://github.com/LuckyJayce
Copyright 2016 drakeet.   https://github.com/drakeet
    
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    
   http://www.apache.org/licenses/LICENSE-2.0
    
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */

package com.shizhefei.view.multitype;

import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import com.shizhefei.view.multitype.data.SerializableData;
import com.shizhefei.view.multitype.provider.FragmentData;
import com.shizhefei.view.multitype.provider.FragmentDataProvider;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;

public class MultiTypeAdapter<ITEM_DATA> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    protected final ItemBinderFactory factory;
    protected List<ItemBinder<ITEM_DATA>> itemBinders = new ArrayList<>();
    protected ArrayList<ITEM_DATA> datas = new ArrayList<>();
    protected HashMap<ITEM_DATA, ItemBinder<ITEM_DATA>> data_Providers = new HashMap<>();
    protected SparseArray<ItemViewProvider<ITEM_DATA>> providerIndex = new SparseArray<>();
    protected RecyclerView recyclerView;
    private LayoutInflater layoutInflater;

    public MultiTypeAdapter(@NonNull ItemBinderFactory factory) {
        this.factory = factory;
        registerAdapterDataObserver(observer);
    }

    public MultiTypeAdapter(@NonNull List<? extends ITEM_DATA> addList, @NonNull ItemBinderFactory factory) {
        this.factory = factory;
        datas.addAll(addList);
        itemBinders.addAll(toItemData(addList, false));
        registerAdapterDataObserver(observer);
    }

    private RecyclerView.AdapterDataObserver observer = new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            super.onChanged();
            if (!isSelfNotify) {
                updateAllData(datas);
            }
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            super.onItemRangeChanged(positionStart, itemCount);
            if (!isSelfNotify) {
                LinkedHashMap<ITEM_DATA, ItemBinder<ITEM_DATA>> map = new LinkedHashMap<>();
                int end = positionStart + itemCount;
                for (int i = positionStart; i < end; i++) {
                    ItemBinder<ITEM_DATA> itemBinder = itemBinders.get(i);
                    map.put(itemBinder.getData(), itemBinder);
                }
                for (int i = positionStart; i < end; i++) {
                    ITEM_DATA data = datas.get(i);
                    ItemBinder<ITEM_DATA> itemBinder = map.remove(data);
                    if (itemBinder == null) {
                        itemBinder = factory.buildItemData(data);
                    }
                    itemBinders.set(i, itemBinder);
                }
                doRemoveItemData(map.values());
            }
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
            if (!isSelfNotify) {
                List<ITEM_DATA> addList = datas.subList(positionStart, positionStart + itemCount);
                List<ItemBinder<ITEM_DATA>> addItemBinder = toItemData(addList, false);
                itemBinders.addAll(positionStart, addItemBinder);
            }
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            super.onItemRangeRemoved(positionStart, itemCount);
            if (!isSelfNotify) {
                List<ItemBinder<ITEM_DATA>> removeItemBinder = itemBinders.subList(positionStart,
                        positionStart + itemCount);
                doRemoveItemData(removeItemBinder);
                int end = positionStart + itemCount - 1;
                for (int i = 0; i < itemCount; i++) {
                    itemBinders.remove(end - i);
                }
            }
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount);
            if (!isSelfNotify) {
                int end = fromPosition + itemCount - 1;
                List<ItemBinder<ITEM_DATA>> moveFromItemBinder = new ArrayList<>(
                        itemBinders.subList(fromPosition, fromPosition + itemCount));
                itemBinders.addAll(toPosition, moveFromItemBinder);
                for (int i = 0; i < itemCount; i++) {
                    itemBinders.remove(end - i);
                }
            }
        }
    };

    public void notifyDataChanged(List<ITEM_DATA> addList, boolean isRefresh) {
        notifyDataChanged2(addList, isRefresh);
    }

    public void notifyDataChanged2(List<? extends ITEM_DATA> addList, boolean isRefresh) {
        if (isRefresh) {
            updateAllData(addList);
            isSelfNotify = true;
            notifyDataSetChanged();
            isSelfNotify = false;
        } else {
            if (addList != datas) {
                datas.addAll(addList);
            }
            itemBinders.addAll(toItemData(addList, false));
            isSelfNotify = true;
            notifyItemRangeInserted(itemBinders.size() - addList.size(), addList.size());
            isSelfNotify = false;
        }
    }

    private boolean isSelfNotify;

    private void updateAllData(List<? extends ITEM_DATA> addList) {
        if (addList != datas) {
            datas.clear();
            datas.addAll(addList);
        }
        providerIndex.clear();
        itemBinders.clear();
        itemBinders.addAll(toItemData(addList, true));

    }

    @Override
    public int getItemViewType(int position) {
        return itemBinders.get(position).getProviderType();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int providerType) {
        if (layoutInflater == null) {
            layoutInflater = LayoutInflater.from(parent.getContext());
        }
        ItemViewProvider<?> provider = providerIndex.get(providerType);
        return provider.onCreateViewHolder(layoutInflater, parent, providerType);
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        this.recyclerView = recyclerView;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ItemBinder itemBinder = itemBinders.get(position);
        itemBinder.provider.onBindViewHolder(holder, SerializableData.getData(itemBinder.getData()));
    }

    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        ItemViewProvider<?> provider = providerIndex.get(holder.getItemViewType());
        if (provider != null) {
            provider.onViewAttachedToWindow(holder);
        }
    }

    @Override
    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        ItemViewProvider<?> provider = providerIndex.get(holder.getItemViewType());
        if (provider != null) {
            provider.onViewDetachedFromWindow(holder);
        }
    }

    protected void onRestoreInstanceState(MultiTypeView multiTypeView, Bundle savedInstanceState) {
        for (ItemBinder binder : itemBinders) {
            ItemViewProvider provider = binder.provider;
            provider.onRestoreInstanceState(savedInstanceState, binder.data);
        }
        FragmentDataProvider provider = factory.getFragmentDataProvider();
        if (provider == null) {
            return;
        }
    }

    protected void onSaveInstanceState(MultiTypeView multiTypeView, Bundle savedInstanceState) {
        for (ItemBinder binder : itemBinders) {
            ItemViewProvider provider = binder.provider;
            provider.onSaveInstanceState(savedInstanceState, binder.data);
        }
    }

    @Override
    public void onViewRecycled(RecyclerView.ViewHolder holder) {
        super.onViewRecycled(holder);
    }

    public int getItemCount() {
        return datas.size();
    }

    public List<ITEM_DATA> getData() {
        return datas;
    }

    public boolean isEmpty() {
        return datas.isEmpty();
    }

    private List<ItemBinder<ITEM_DATA>> toItemData(@NonNull List<? extends ITEM_DATA> addList, boolean refresh) {
        HashMap<? extends ITEM_DATA, ItemBinder<ITEM_DATA>> removeItemData = null;
        if (refresh) {
            removeItemData = data_Providers;
            data_Providers = new HashMap<>();
        }
        List<ItemBinder<ITEM_DATA>> providers = new ArrayList<>(addList.size());
        for (ITEM_DATA object : addList) {
            ItemBinder<ITEM_DATA> itemBinder;
            if (refresh) {
                itemBinder = removeItemData.remove(object);
            } else {
                itemBinder = data_Providers.get(object);
            }
            if (itemBinder == null) {
                itemBinder = factory.buildItemData(object);
            }
            data_Providers.put(object, itemBinder);
            providers.add(itemBinder);
            providerIndex.put(itemBinder.providerType, itemBinder.provider);
        }

        if (refresh) {
            //?Fragment
            doRemoveItemData(removeItemData.values());
        }
        return providers;
    }

    protected void doRemoveItemData(Collection<ItemBinder<ITEM_DATA>> removeItemBinder) {
        FragmentManager fragmentManager = factory.getFragmentManager();
        if (fragmentManager == null) {
            return;
        }
        FragmentTransaction transaction = null;
        for (ItemBinder<?> itemBinder : removeItemBinder) {
            Object data = itemBinder.data;
            Fragment fragment = null;
            if (data instanceof FragmentData) {
                FragmentData fragmentData = (FragmentData) data;
                fragment = fragmentData.getFragment();
                fragmentData.resetState();
            } else if (data instanceof Fragment) {
                fragment = (Fragment) data;
            }
            if (fragment != null) {
                if (transaction == null) {
                    transaction = fragmentManager.beginTransaction();
                }
                transaction.remove(fragment);
            }
            if (recyclerView != null) {
                ItemViewProvider provider = itemBinder.provider;
                if (provider.isUniqueProviderType(SerializableData.getData(data))) {
                    RecyclerView.RecycledViewPool pool = recyclerView.getRecycledViewPool();
                    //providerType
                    pool.setMaxRecycledViews(itemBinder.providerType, 0);
                    //?providerType?
                    pool.setMaxRecycledViews(itemBinder.providerType, 5);
                }
            }
        }
        if (transaction != null) {
            transaction.commitNowAllowingStateLoss();
        }
    }

    public int size() {
        return datas.size();
    }

    public static <DATA extends Parcelable> void saveState(String key, MultiTypeAdapter<DATA> adapter,
            Bundle outState) {
        saveState(key, adapter, outState, adapter.size());
    }

    public static <DATA extends Parcelable> void saveState(String key, MultiTypeAdapter<DATA> adapter,
            Bundle outState, int maxSize) {
        List<DATA> datas = adapter.getData();
        int size = adapter.size();
        int saveCount = maxSize < size ? maxSize : size;
        ArrayList<Parcelable> list = new ArrayList<>(maxSize);
        for (int i = 0; i < saveCount; i++) {
            list.add(datas.get(i));
        }
        outState.putParcelableArrayList(key, list);
    }

    /**
     * ???
     *
     * @param savedInstanceState
     */
    public static <DATA extends Parcelable> boolean restoreState(String key, MultiTypeAdapter<DATA> adapter,
            Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            return false;
        }
        List<DATA> list = savedInstanceState.getParcelableArrayList(key);
        if (list != null) {
            adapter.notifyDataChanged(list, true);
            return true;
        }
        return false;
    }

}