Search code examples
androidandroid-recyclerviewcustom-adapter

getitemcount() value not updating until the fragment is restarted


My Custom Adapter, when i add a task, the getitemcount() valued does not increase unless or until it restart the app or switch between fragments. and niether the itemtouchelper updates the UI unless i do same steps for that too. I am using activityresult method to updated the adapter.

public class CustomAdapterReminders extends RecyclerView.Adapter<CustomAdapterReminders.MyRecyclerView2> implements ItemMoveCallback.ItemTouchHelperContractReminder{
    private List<String[]> customListView;
    private String[] mRecentlyDeletedItem;
    private int mRecentlyDeletedItemPosition;
    private View snackbarView;
    private static final String dueDatedTime = "Due on: ";
    private Context context;
    public int positionAfterNewTAsk;

    public void setPositionAfterNewTAsk(int positionAfterNewTAsk) {
        this.positionAfterNewTAsk = positionAfterNewTAsk;
    }

    public CustomAdapterReminders(List<String[]> customListView) {
        this.customListView = customListView;
        Log.d("Item count" , String.valueOf(getItemCount()));
    }

    public CustomAdapterReminders() {
    }

    @NonNull
    @Override
    public MyRecyclerView2 onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View itemView = inflater.inflate(R.layout.recycler_item_reminders,parent,false);
        MyRecyclerView2 viewHolder = new MyRecyclerView2(itemView);
        snackbarView = parent.getRootView();
        return  viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull MyRecyclerView2 holder, int position) {
        String[] temp = customListView.get(position);
        holder.taskText.setText(temp[1]);
        holder.taskDate.setText(dueDatedTime + temp[2]);
    }

    public void changeData(){
        notifyDataSetChanged();
    }

    @Override
    public int getItemCount() {
        return customListView.size();
    }


    @Override
    public void onRowMoved(int fromPosition, int toPosition) {
        try {
            if (fromPosition < toPosition) {
                for (int i = fromPosition; i < toPosition; i++) {
                    Collections.swap(customListView, i, i + 1);
                }
            } else {
                for (int i = fromPosition; i > toPosition; i--) {
                    Collections.swap(customListView, i, i - 1);
                }
            }
            notifyItemMoved(fromPosition, toPosition);
        } catch (Exception ex) {
            Log.d("TAG", "Error in moving items");
        }
    }

    @Override
    public void onRowSelected(MyRecyclerView2 myViewHolder) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            myViewHolder.rowView.setBackgroundResource(R.drawable.border);
        }
    }

    @Override
    public void onItemDismiss(int position) {
        Log.d("Position", String.valueOf(position) +"Count"+ String.valueOf(getItemCount()) + "Pos" + positionAfterNewTAsk);
        String[] deleteIndex = customListView.get(position);
        mRecentlyDeletedItemPosition = position;
        mRecentlyDeletedItem = customListView.get(position);
        customListView.remove(position);
        TaskDBHelper taskDBHelper = new TaskDBHelper(context);
        final Handler handler = new Handler();
        showUndoSnackbar();
        handler.postDelayed(() -> taskDBHelper.DeleteFromReminders(deleteIndex[0]), 3000);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position,getItemCount());
        positionAfterNewTAsk = 0;
    }


    @Override
    public void onRowClear(MyRecyclerView2 myViewHolder) {
        myViewHolder.rowView.setBackgroundResource(R.drawable.seleted_border);
    }

    private void undoDelete() {
        customListView.add(mRecentlyDeletedItemPosition,
                mRecentlyDeletedItem);
        notifyItemInserted(mRecentlyDeletedItemPosition);
        notifyItemRangeChanged(mRecentlyDeletedItemPosition,getItemCount());
    }

    private void showUndoSnackbar() {
        Snackbar.make(snackbarView, "Task deleted", Snackbar.LENGTH_LONG)
                .setAction("Undo", v -> undoDelete()).show();
    }

    public class MyRecyclerView2 extends RecyclerView.ViewHolder {
        TextView taskText, taskDate;
        View rowView;
        private MyRecyclerView2(@NonNull View itemView) {
            super(itemView);
            rowView = itemView;
            taskText = itemView.findViewById(R.id.reminderstextView);
            taskDate = itemView.findViewById(R.id.remindersDateView);
        }
    }
}


> **
  1. Home Fragment where the adapter is set on recyclerview

**

     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        remindersViewModel = ViewModelProviders.of(this).get(RemindersViewModel.class);
        View root = inflater.inflate(R.layout.fragment_home, container, false);

        OneSignal.startInit(this.getContext())
                .inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
                .unsubscribeWhenNotificationsAreDisabled(true)
                .init();
        OneSignal.startInit(this.getContext())
                .setNotificationReceivedHandler(new ExampleNotificationReceivedHandler())
                .inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
                .unsubscribeWhenNotificationsAreDisabled(true)
                .init();

        setAdapter();
        FloatingActionButton fab = root.findViewById(R.id.floatingActionButton);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent addTask = new Intent(getActivity(), AddTask.class);
                startActivityForResult(addTask,ADD_TASK);
            }
        });
        return root;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == 1) {
            refreshUI();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        setAdapter();
    }

    private void refreshUI() {
        setAdapter();
        adapter.changeData();

    }

    private void setAdapter() {
        try {
            taskDBHelper = new TaskDBHelper(this.getContext());
            try {
                taskList = taskDBHelper.browseReminders();
                adapter = new CustomAdapterReminders(taskList);
            }
            catch (Exception e){
                Log.d(TAG, "Exception in browsing");
            }
            staggaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, 1);
            recyclerViewItems = getActivity().findViewById(R.id.rv);
            recyclerViewItems.setLayoutManager(staggaggeredGridLayoutManager);
            recyclerViewItems.setAdapter(adapter);
            adapter.changeData();
            ItemMoveCallback itemMoveCallback = new ItemMoveCallback();
            ItemTouchHelper.Callback callback = new ItemMoveCallback(adapter);
            touchHelper = new ItemTouchHelper(callback);
            touchHelper.attachToRecyclerView(recyclerViewItems);
        } catch (NullPointerException ex) {
            Log.d(TAG, "Exception in setAdapter");
        }
    }

    @Override
    public void requestDrag(RecyclerView.ViewHolder viewHolder) {
        touchHelper.startDrag(viewHolder);
    }

Solution

  • I think I might know where the issue is but its a guess at this point since I havnt seen the addTask class. DB operations are usually asychronous i.e. not done on the main thread so basically when onActivityResult is called it is not guaranteed that the process inside addTask is done inserting the data into the DB at the moment you are reading it.

    a simple fix is to just rearrange the way your code works. what you are doing now is that the user decides to add a task, then you are adding a some data into a DB (in Addtask) then you read whats in the DB and use that to completely reset the adapter using the setAdapter method.

    instead when the user decides to add a task feed the corresponding String[] object directly into customListView and call notify datasetchanged. at the same time you excecute a command to insert that same data into the DB.

    since you are modifying the adapter from the main thread only everything will happen sequentially and you wont run the risk of reading the DB before the insert operation has taken place.

    the other alternative is that you use the Room framework to handle your database using LiveData and the onChanged listener. This implies a lot of effort reading up on the literature but the room framework would really offer you a lot to simplify your code