Update RecyclerView with DiffUtil and AsyncListDiffer not Working

2.5k Views Asked by At

I'm creating a RecyclerView in Fragmentusing DiffUtil and AsyncListDiffer because I don't want to use notifyDataSetChanged() method. But I'm facing a problem while using this approach. I'm trying to retrieve items from the database, but every time the data changes (increase) the UI won't update directly. I have to close my app first and then open it again or go to another class to check the updated item.

This is my adapter:

public class AdapterSaham extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements SectionIndexer, SearchFilter {
   public static final int SECTION_VIEW = 0;
   public static final int CONTENT_VIEW = 1;

   List<DaftarSaham> daftarSahamList;
   private ArrayList<Integer> mSectionPositions;
   private static int  lastClicked = -1;
   WeakReference<Context> mContextWeakReference;
   private ItemClickListener itemClickListener;
   private int lastPosition = -1;
   int row_index = -1;

   public AdapterSaham(ArrayList<DaftarSaham> daftarSaham, Context context, ItemClickListener clickListener){
       this.daftarSahamList = daftarSaham;
       this.mContextWeakReference = new WeakReference<Context> ( context );
       this.itemClickListener = clickListener;
   }

   private static final DiffUtil.ItemCallback<DaftarSaham> DIFF_CALLBACK = new DiffUtil.ItemCallback<DaftarSaham> () {
       @Override
       public boolean areItemsTheSame(@NonNull DaftarSaham oldItem, @NonNull DaftarSaham newItem) {
           return newItem.getTicker ().equals ( oldItem.getTicker () ) && newItem.getCompanyName ().equals ( newItem.getCompanyName () );
       }

       @SuppressLint("DiffUtilEquals")
       @Override
       public boolean areContentsTheSame(@NonNull DaftarSaham oldItem, @NonNull DaftarSaham newItem) {
           return newItem.equals ( oldItem );
       }

   };

   private  AsyncListDiffer<DaftarSaham> differ = new AsyncListDiffer<DaftarSaham> (this, DIFF_CALLBACK  );

   @NonNull
   @Override
   public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

       Context ctx = mContextWeakReference.get ();
       View view = LayoutInflater.from ( parent.getContext () ).inflate ( R.layout.list_item,parent,false  );
      
   
      return new ItemViewHolder ( view, ctx , itemClickListener);

   }

   @Override
   public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
       Context ctx = mContextWeakReference.get ();
       if (ctx == null){
           return;
       }
      
       ItemViewHolder itemViewHolder= (ItemViewHolder) holder;
       DaftarSaham daftarSaham =  differ.getCurrentList ().get ( position);
       itemViewHolder.txtTickerSaham.setText ( daftarSaham.getTicker () );
       itemViewHolder.txtCompanyName.setText ( daftarSaham.getCompanyName () );
   }

   public class ItemViewHolder extends RecyclerView.ViewHolder {
       private TextView txtTickerSaham, txtCompanyName;
       private LinearLayout cardView;

       ItemClickListener itemClickListener;

       public ItemViewHolder(@NonNull View itemView, final Context context, ItemClickListener itemClickListener) {
           super ( itemView );
           txtTickerSaham = (TextView) itemView.findViewById ( R.id.tickerSaham );
           txtCompanyName = (TextView) itemView.findViewById ( R.id.tv_company_name );
           cardView = (LinearLayout) itemView.findViewById ( R.id.cvRecycler );

           this.itemClickListener = itemClickListener;

       }

   }

  @Override
   public int getItemCount() {

       return differ.getCurrentList ().size ();
   }

   public void submitList(List<DaftarSaham> newdataList){
       differ.submitList ( newdataList );
       daftarSahamList = new ArrayList<> (newdataList);
   }

Code in Fragment:

 ViewGroup.LayoutParams params=recyclerViewIndexer.getLayoutParams();
        params.height= h;
        recyclerViewIndexer.setLayoutParams(params);
        recyclerViewIndexer.setHasFixedSize ( true );
        recyclerViewIndexer.setLayoutManager ( new LinearLayoutManager ( getContext ()) );

 StringRequest stringRequest = new StringRequest ( Request.Method.GET, URL,
               new Response.Listener<String> () {
                   @Override
                   public void onResponse(String response) {
                       ArrayList<HashMap<String,String>> list = new ArrayList<> ();
                       try {
                           JSONObject jsonObject = new JSONObject (response);
                           JSONArray jsonArray = jsonObject.getJSONArray ( "data_emiten" );
                           for(int i = 0; i < jsonArray.length (); i++){
                               JSONObject jo = jsonArray.getJSONObject ( i );
                               String ticker = jo.getString ( "ticker" );
                               String companyName = jo.getString ( "companyName" );
                               daftarNonSection.add ( new DaftarSaham (ticker, companyName, false) );

                           }

                       } catch (JSONException e) {
                           e.printStackTrace ();
                       }

                       getHeaderListLetter ( daftarSahamList,daftarNonSection );
                       adapterSaham = new AdapterSaham ( daftarSahamList, getContext (), new AdapterSaham.ItemClickListener () {
                           @Override
                           public void onItemClick(Integer numbers, boolean isSelected) {
                               String ticker = "";
                               if (isSelected ){
                                   ticker = daftarSahamList.get ( Integer.valueOf ( numbers) ).ticker;
                               }
                               SlidingTab slidingTab = (SlidingTab) getActivity ();
                               slidingTab.setTicker (ticker);
                           }
                       } );
                       recyclerViewIndexer.setAdapter ( adapterSaham );
                       adapterSaham.submitList ( daftarSahamList );
                       Log.d ( "TAG", "onResponse: "+ response );
                   }
               },
               new Response.ErrorListener () {
                   @Override
                   public void onErrorResponse(VolleyError error) {
                       Log.d ( "TAG", "onErrorResponse: "+error );
                   }
               } );

       int socketTimeOut = 50000;
       RetryPolicy policy = new DefaultRetryPolicy (socketTimeOut, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
       stringRequest.setRetryPolicy ( policy );
       RequestQueue requestQueue = Volley.newRequestQueue ( getContext () );
       requestQueue.add ( stringRequest );
   }

My model:

public class DaftarSaham implements Comparable<DaftarSaham> {

    public String ticker;
    public String companyName;
    public boolean isSection;
    private boolean isSelected = false;

    public DaftarSaham() {

    }

    public DaftarSaham(String ticker, String companyName, boolean isSection) {
        this.ticker = ticker;
        this.companyName = companyName;
        this.isSection = isSection;
    }

    public String getCompanyName() {
        return companyName;
    }
    public void setTicker(String ticker) {
        this.ticker = ticker;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }


    public String getTicker() {
        return ticker;
    }
 }

I don't know why it's not working because I have tried using DiffUtil and AsyncListDiffer before and there is no problem. Any helps or suggestions would be great. Thanks in advance.

2

There are 2 best solutions below

0
On

You need to do this step. After spending a huge time I found that it's because of empty list. When upating list do this ::

fun updateList(updatedList: List<Notification>) {
    if (updatedList.isEmpty()) {
        differ.submitList(emptyList())
        return
    }
    differ.submitList(updatedList)
}
2
On

As far as I see from your code, you're probably misusing DiffUtil.ItemCallback. The purpose of areItemsTheSame(int oldItemPosition, int newItemPosition) is two decide wheather two object represent the same item but in your code you're using .equals method, that is for content comparison. Within areItemsTheSame you should check items id equality, since you don't have ids, you could simply do the check by using ticker string, but be aware that if two items have the same ticker the two items will match:

public boolean areItemsTheSame(@NonNull DaftarSaham oldItem, @NonNull DaftarSaham newItem) {
       return newItem.getTicker() == oldItem.getTicker()
}

Differently, areContentsTheSame checks whether two items have the same data and this method is called only if areItemsTheSame returns true. In here, you should probably check if class properties matches between the old and the new item, something like:

public boolean areContentsTheSame(@NonNull DaftarSaham oldItem, @NonNull DaftarSaham newItem) {
   return (newItem.getTicker() == oldItem.getTicker()) && (newItem.getCompanyName() == oldItem.getCompayName())
}

Be aware that areContentsTheSame business logic it's up to you, accordingly to the properties you identified to be part of the content. The standard approach is to use .equals() method, but doesn't seem to be your case.

public boolean areContentsTheSame(@NonNull DaftarSaham oldItem, @NonNull DaftarSaham newItem) {
   return (newItem.equals(oldItem))
}