Divider line in RecyclerView behaves strange when expand and collapse items

Viewed 271

When expanding and collapsing my Recyclerview items, divider lines drawn multiple times or overdrawn with items.

Also when expanding and collapsing views, dividers thickness is getting reduced.

My problem is divider line drawn each and every time I expand and collapse item in Recyclerview. So is it possible to prevent divider line drawing if it already drawn? While expanding an item, divider line will move according to the view.?

Below is my RecyclerView Decoration Class used for divider line,

public  class SeparatorDecoration extends RecyclerView.ItemDecoration {

    private final Paint mPaint;

    /**
     * Create a decoration that draws a line in the given color and width between the items in the view.
     * @param context  a context to access the resources.
     * @param color    the color of the separator to draw.
     * @param heightDp the height of the separator in dp.
     */
    public SeparatorDecoration(@NonNull Context context, @ColorInt int color,
                               @FloatRange(from = 0, fromInclusive = false) float heightDp) {
        mPaint = new Paint();
        mPaint.setColor(color);
        final float thickness = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                heightDp, context.getResources().getDisplayMetrics());
        mPaint.setStrokeWidth(thickness);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
        // we want to retrieve the position in the list
        final int position = params.getViewAdapterPosition();
        // and add a separator to any view but the last one
        if (position <state.getItemCount()) {
            outRect.set(40, 0, 40, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
        } else {
            outRect.setEmpty(); // 0, 0, 0, 0
        }
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        final int offset = (int) (mPaint.getStrokeWidth() / 2);

        // this will iterate over every visible view
        for (int i = 0; i < parent.getChildCount(); i++) {
            // get the view
            final View view = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
            // get the position
            final int position = params.getViewAdapterPosition();

            // and finally draw the separator
            if (position < parent.getChildCount()) {
                final int ty = (int)(view.getTranslationY() + 0.5f);
                final int top = view.getBottom() - params.bottomMargin + ty;
                final int bottom = top + (int) mPaint.getStrokeWidth();
                c.drawLine(view.getLeft(), view.getBottom() + offset, view.getRight(), view.getBottom() + offset, mPaint);
            }

        }
    }
}

below is my RecyclerView Adapter class,



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

        private Context context;
        private List<NotificationDetails> notificationRecords;

        private DeleteNotificationListener deleteNotificationListener;


        private String TAG = DisplayNotificationAdapter.class.getSimpleName();


        

        interface DeleteNotificationListener {
            void updateNotificationList(List<NotificationDetails> details);
        }

        public DisplayNotificationAdapter(Context context, DeleteNotificationListener listener, List < NotificationDetails > notificationRecordsList) {
            this.context = context;
            this.deleteNotificationListener = listener;
            this.notificationRecords = notificationRecordsList;
        }


        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType){
            LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
            AndroidLogger.log(5,TAG,"oncreate");
            View listItem = layoutInflater.inflate(R.layout.display_notification_recycler_view_list_item, parent, false);
            return new NotificationViewHolder(listItem);
        }

        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public void onBindViewHolder (@NonNull RecyclerView.ViewHolder holder,int position){
            
            NotificationDetails notification = notificationRecords.get(position);
            NotificationViewHolder viewHolder = (NotificationViewHolder) holder;
            String currentDateString = DateFormat.getDateInstance().format(Long.parseLong(notification.getTimeStamp()));
           
            String filePath=generateFilePath(notification.getFileName());
            Bitmap myBitmap = BitmapFactory.decodeFile(filePath);
            @SuppressLint("SimpleDateFormat")
            DateFormat dateFormat = new SimpleDateFormat("hh:mm aa");
            
            
            String time = dateFormat.format(Long.parseLong(notification.getTimeStamp()));
           
            if (notification.isExpanded()) {
                viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
                expandView(viewHolder.notificationImageview);
               
            }
            else {
                viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
                collapseView(viewHolder.notificationImageview);
            }
            viewHolder.notificationImageview.setImageBitmap(myBitmap);
            viewHolder.notificationTextView.setText(notification.getMessage());
            viewHolder.notificationTimeTextView.setText(time);
            Calendar now = Calendar.getInstance();
            Calendar date = Calendar.getInstance();
            date.setTimeInMillis(Long.parseLong(notification.getTimeStamp()));
           
            viewHolder.expandCollapseImageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(notification.isExpanded()) {
                        notification.setExpanded(false);
                        viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
                       
                        viewHolder.notificationTextView.setMaxLines(1);
                        notifyItemChanged(position);
                    }
                    else {
                        notification.setExpanded(true);
                        viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
                      
                        viewHolder.notificationTextView.setMaxLines(Integer.MAX_VALUE);
                        notifyItemChanged(position);
                    }
                }
            });

            if (now.get(Calendar.DATE) == date.get(Calendar.DATE))
                viewHolder.notificationDateTextView.setText("Today");
            else if (now.get(Calendar.DATE) - date.get(Calendar.DATE) == 1)
                viewHolder.notificationDateTextView.setText("Yesterday");
            else
                viewHolder.notificationDateTextView.setText(currentDateString);
            if(notification.getTitle()==null)
                viewHolder.notificationTitleTextView.setText("title");
            else
                viewHolder.notificationTitleTextView.setText(notification.getTitle());
        }

    private String generateFilePath(String fileName) {
        
        File imageFileDirectory = context.getDir("image", Context.MODE_PRIVATE); //Creating an internal dir;
        if (!imageFileDirectory.exists()) {
            imageFileDirectory.mkdirs();
        }
        /*
         * app server provide "U" file name after we set read status they provide same file name as "R"
         */
        String createFilePath = imageFileDirectory + "/" + fileName;
       
        return createFilePath;
    }


    public void removeSingleNotification ( int position){
            DatabaseHelper databaseHelper = new DatabaseHelper(context);
            databaseHelper.deleteSingleNotificationRecord(notificationRecords.get(position).getId());
            notificationRecords.remove(position);
            deleteNotificationListener.updateNotificationList(notificationRecords);
            notifyDataSetChanged();
        }
       
         
        private void removeFromList (String id) {
            for (NotificationDetails detail : notificationRecords) {
                if (detail.getId().equalsIgnoreCase(id))
                    notificationRecords.remove(detail);
            }
        }


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

        public  void expandView(final View v) {
           
            int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
            int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
            final int targetHeight = v.getMeasuredHeight();

        // Older versions of android (pre API 21) cancel animations for views with a height of 0.
            v.getLayoutParams().height = 1;
            v.setVisibility(View.VISIBLE);
            Animation a = new Animation()
            {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    v.getLayoutParams().height = interpolatedTime == 1
                            ? ViewGroup.LayoutParams.WRAP_CONTENT
                            : (int)(targetHeight * interpolatedTime);
                    v.requestLayout();
            }

            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };

        // Expansion speed of 1dp/ms
       a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
        
            v.startAnimation(a);
    }

    public  void collapseView(final View v) {
        //collapse(pos);
        final int initialHeight = v.getMeasuredHeight();

        Animation a = new Animation()
        {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                if(interpolatedTime == 1){
                    v.setVisibility(View.GONE);
                }else{
                    v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                    v.requestLayout();
                }
            }

            @Override
            public boolean willChangeBounds() {
                return true;
            }
        };

        // Collapse speed of 1dp/ms
        a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
       
        v.startAnimation(a);
    }
   
    public static class NotificationViewHolder extends RecyclerView.ViewHolder {
        private TextView notificationTextView, notificationDateTextView, notificationTimeTextView, notificationTitleTextView;
        private ImageView notificationImageview,expandCollapseImageView;
        private ConstraintLayout parent;
        public NotificationViewHolder(@NonNull View itemView) {
            super(itemView);
            notificationTextView = itemView.findViewById(R.id.notification_text_view);
            notificationDateTextView = itemView.findViewById(R.id.notification_date_text_view);
            notificationTimeTextView = itemView.findViewById(R.id.notification_time_text_view);
            notificationTitleTextView = itemView.findViewById(R.id.notification_title_text_view);
            notificationImageview = itemView.findViewById(R.id.notification_image_view);
            expandCollapseImageView = itemView.findViewById(R.id.expand_collapse_arrow);
            parent = itemView.findViewById(R.id.notification_parent);
        }
    }
}

UPDATE I doesn't able to solve this issue. So instead of using RecyclerView.ItemDecoration I have used a View inside layout like below,

<View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:id="@+id/view_div"
        android:background="@color/grey"

Doing like above solves the issue. [This is my screen contains the issue][1]][1]

https://i.stack.imgur.com/CBY80.jpg

0 Answers
Related