1

so I am trying to add loading item in the last index of my recycler view when fetching data from the internet, like Youtube when we keep scrolling and do pagination. something like this

enter image description here

now my recycler view extends ListAdapter and not using RecyclerView.Adapter<RecyclerView.ViewHolder> anymore

if using RecyclerView.Adapter<RecyclerView.ViewHolder> (the old way) I will write code for my adapter like this:

public class PaginationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int ITEM = 0;
    private static final int LOADING = 1;
    private static final String BASE_URL_IMG = "https://image.tmdb.org/t/p/w150";

    private List<Movie> movieResults;
    private Context context;

    private boolean isLoadingAdded = false;

    public PaginationAdapter(Context context) {
        this.context = context;
        movieResults = new ArrayList<>();
    }

    public List<Movie> getMovies() {
        return movieResults;
    }

    public void setMovies(List<Movie> movieResults) {
        this.movieResults = movieResults;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder viewHolder = null;
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());

        switch (viewType) {
            case ITEM:
                viewHolder = getViewHolder(parent, inflater);
                break;
            case LOADING:
                View v2 = inflater.inflate(R.layout.item_progress, parent, false);
                viewHolder = new LoadingVH(v2);
                break;
        }
        return viewHolder;
    }

    @NonNull
    private RecyclerView.ViewHolder getViewHolder(ViewGroup parent, LayoutInflater inflater) {
        RecyclerView.ViewHolder viewHolder;
        View v1 = inflater.inflate(R.layout.item_list, parent, false);
        viewHolder = new MovieVH(v1);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        Movie result = movieResults.get(position); // Movie

        switch (getItemViewType(position)) {
            case ITEM:

              // some code here

                break;

            case LOADING:
              //  Do nothing
                break;
        }

    }

    @Override
    public int getItemCount() {
        return movieResults == null ? 0 : movieResults.size();
    }

    @Override
    public int getItemViewType(int position) {
        return (position == movieResults.size() - 1 && isLoadingAdded) ? LOADING : ITEM;
    }





    public void addLoadingFooter() {
        isLoadingAdded = true;
        movieResults.add(new Moview());
        notifyItemInserted(movieResults.size() - 1);
    }

    public void removeLoadingFooter() {
        isLoadingAdded = false;

        int position = movieResults.size() - 1;
        Movie result = getItem(position);

        if (result != null) {
            movieResults.remove(position);
            notifyItemRemoved(position);
        }
    }



}

but now I am trying to use ListAdapter for my Recycler View. here is my code if my recycler view using ListAdapter

class GeneralEventRecyclerViewAdapter(val mContext: Context): ListAdapter<Event, RecyclerView.ViewHolder>(DIFF_CALLBACK) {

    lateinit var mOnEventKMListener : OnEventKMListener

    private val ITEM = 0
    private val LOADING = 1
    private var isLoadingAdded = false


    fun setOnItemClickListener(listener: OnEventKMListener) {
        mOnEventKMListener = listener
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {


        if (viewType == ITEM) {

            val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_general_event, parent, false)
            return GeneralEventViewHolder(itemView, mOnEventKMListener)

        } else  {

            val loadingView = LayoutInflater.from(parent.context).inflate(R.layout.item_progress, parent, false)
            return LoadingViewHolder(loadingView)

        }



    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        if (getItemViewType(position) == ITEM) {

            // some code here

        }





    }

    override fun getItemViewType(position: Int): Int {
        if (position == currentList.size - 1 && isLoadingAdded) {
            return LOADING
        } else {
            return ITEM
        }
    }

    fun addLoadingFooter() {
        isLoadingAdded = true
        currentList.add(Event())
        notifyItemInserted(currentList.size - 1)
    }

    fun removeLoadingFooter() {
        isLoadingAdded = false
        val position: Int = currentList.size - 1
        val result: Event? = getItem(position)
        if (result != null) {
            currentList.removeAt(position)
            notifyItemRemoved(position)
        }
    }


    companion object {

        private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Event>() {
            override fun areItemsTheSame(oldItem: Event, newItem: Event): Boolean {
                return oldItem.eventID == newItem.eventID
            }

            override fun areContentsTheSame(oldItem: Event, newItem: Event): Boolean {
                return oldItem == newItem
            }


        }
    }


}

here is my problem.....

I am trying to imitate these 2 methods (using RecyclerView.Adapter) when adding and removing loading item in my recycler:

        public void addLoadingFooter() {
            isLoadingAdded = true;
            movieResults.add(new Moview());
            notifyItemInserted(movieResults.size() - 1);
        }

        public void removeLoadingFooter() {
            isLoadingAdded = false;

            int position = movieResults.size() - 1;
            Movie result = getItem(position);

            if (result != null) {
                movieResults.remove(position);
                notifyItemRemoved(position);
            }
        }

I assume that those 2 methods above will be the same as my 2 methods below if using ListAdapter

    fun addLoadingFooter() {
        isLoadingAdded = true
        currentList.add(Event())
        notifyItemInserted(currentList.size - 1)
    }

    fun removeLoadingFooter() {
        isLoadingAdded = false
        val position: Int = currentList.size - 1
        val result: Event? = getItem(position)
        if (result != null) {
            currentList.removeAt(position)
            notifyItemRemoved(position)
        }
    }  

but when I run the app I get crash

    java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)

on line currentList.add(Event())

it seems I can't add new item on the list if using ListAdapter. so how to modify list in recycler view using List Adapter ?

I don't know how to access the existing list if using ListAdapter, it seems the currentList can't be modified

java or kotlin is ok

Alexa289
  • 8,089
  • 10
  • 74
  • 178

2 Answers2

2

Late to party, but your current answer does not make ListAdapter's best usage. You should use ListAdapter.submitList instead, it does most of notifyItemXXXXXX jobs.

val newList = ArrayList(listAdapter.currentList)
newList.add(Event()) // or to remove / update / ... etc.
listAdapter.submitList(newList)

And an important thing to know: submitList MUST use a new object (of list) from original one.

val objList = mutableListOf(myObjects)
listAdapter.submitList(objList)

objList.add(newObject)
listAdapter.submitList(objList) // will NOT do updates.
listAdapter.submitList(ArrayList(objList)) // will do updates.

it's mentioned at another Stack Overflow question.

Samuel T. Chou
  • 521
  • 6
  • 31
1

finally find the solution using

override fun getItemViewType(position: Int): Int {
        if ( position == currentList.lastIndex && isLoadingAdded) {
            return LOADING
        } else {
            return ITEM
        }
    }

    fun addLoadingFooter(currentList: ArrayList<Event>) {
        isLoadingAdded = true
        currentList.add(Event())
        submitList(currentList)
        notifyItemInserted(currentList.lastIndex)


    }

    fun removeLoadingFooter(currentList: ArrayList<Event>) {
        isLoadingAdded = false

        val result = currentList[currentList.lastIndex]
        currentList.remove(result)
        submitList(currentList)
        notifyItemRemoved(currentList.size)

    }
Alexa289
  • 8,089
  • 10
  • 74
  • 178