Search code examples
androidandroid-recyclerviewscrollhorizontal-scrolling

Get center visible item of RecycleView when scrolling


This is what I want:

enter image description here

As image above, I want to draw a center line on RecycleView, then get the center item when scrolling (as well as move left or right)
Here is my try to draw a horizontal RecycleView:

    HorizontalAdapter adapter = new HorizontalAdapter(data);
    LinearLayoutManager layoutManager
            = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
    recycleView.setLayoutManager(layoutManager);
    recycleView.setAdapter(adapter);

Is there any way to know which item is moved to the center of RecycleView? And how can I scroll RecycleView to left or right just one position?

Update: I tried to use a scroll listener to get the middle position, but it doesn't work as an aspect.

  @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            int firstPos = layoutManager.findFirstVisibleItemPosition();
            int lastPos = layoutManager.findLastVisibleItemPosition();
            int middle = Math.abs(lastPos - firstPos) / 2 + firstPos;

            int selectedPos = -1;
            for (int i = 0; i < adapter.getItemCount(); i++) {
                if (i == middle) {
                    adapter.getItem(i).setSelected(true);
                    selectedPos = i;
                } else {
                    adapter.getItem(i).setSelected(false);
                }
            }

            adapter.notifyDataSetChanged();
        }

And get the result:

enter image description here

I only want to change the selected item (make text to white color) when it is on the blue Rect


Solution

  • I made something just like this. I can do exactly what you need. First of all, this is how is my alogrithm work enter image description here

    This is my recyclerView Adapter

    public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
    private ArrayList<LabelerDate> dateDataList;
    
    
    private static final int VIEW_TYPE_PADDING = 1;
    private static final int VIEW_TYPE_ITEM = 2;
    private int paddingWidthDate = 0;
    
    private int selectedItem = -1;
    
    public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
        this.dateDataList = dateData;
        this.paddingWidthDate = paddingWidthDate;
    
    }
    
    
    @Override
    public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == VIEW_TYPE_ITEM) {
            final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_date,
                    parent, false);
            return new DateViewHolder(view);
        } else {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding,
                    parent, false);
    
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
            layoutParams.width = paddingWidthDate;
            view.setLayoutParams(layoutParams);
            return new DateViewHolder(view);
        }
    }
    
    @Override
    public void onBindViewHolder(DateViewHolder holder, int position) {
        LabelerDate labelerDate = dateDataList.get(position);
        if (getItemViewType(position) == VIEW_TYPE_ITEM) {
            if(labelerDate.dateType.equals(BirthDayActivity.DateType.C31))
                    holder.tvDate.setText(String.valueOf(labelerDate.valueDate));
                    holder.tvDate.setVisibility(View.VISIBLE);
                    holder.imgSmall.setVisibility(View.VISIBLE);
    
            if (position == selectedItem) {
                holder.tvDate.setTextColor(Color.parseColor("#094673"));
                holder.tvDate.setTextSize(35);
                holder.imgSmall.setBackgroundResource(R.color.textviewbold);
    
            } else {
                holder.tvDate.setTextColor(Color.GRAY);
                holder.tvDate.setTextSize(35);
                holder.imgSmall.setBackgroundResource(R.color.gray);
            }
        }
    }
    
    public void setSelecteditem(int selecteditem) {
        this.selectedItem = selecteditem;
        notifyDataSetChanged();
    }
    
    @Override
    public int getItemCount() {
        return dateDataList.size();
    }
    
    @Override
    public int getItemViewType(int position) {
        LabelerDate labelerDate = dateDataList.get(position);
        if (labelerDate.dateType.equals(BirthDayActivity.DateType.NONE)) {
            return VIEW_TYPE_PADDING;
        }
        return VIEW_TYPE_ITEM;
    }
    
    
    public class DateViewHolder extends RecyclerView.ViewHolder {
        public TextView tvDate;
        public ImageView imgSmall;
    
        public DateViewHolder(View itemView) {
            super(itemView);
            tvDate = (TextView) itemView.findViewById(R.id.tvNumberDate);
            imgSmall = (ImageView) itemView.findViewById(R.id.small_marked_dob);
        }
    }}
    

    This is most important alogrithm:

    public void getRecyclerviewDate() {
        recyclerViewDate = (RecyclerView) findViewById(R.id.recyclerViewDay);
        ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
        vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
                finalWidthDate = recyclerViewDate.getMeasuredWidth();
                itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
                paddingDate = (finalWidthDate - itemWidthDate) / 2;
                firstItemWidthDate = paddingDate ;
                allPixelsDate = 0;
    
                final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
                dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
                recyclerViewDate.setLayoutManager(dateLayoutManager);
                recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                        super.onScrollStateChanged(recyclerView, newState);
                        synchronized (this) {
                             if(newState == RecyclerView.SCROLL_STATE_IDLE){           
                                calculatePositionAndScrollDate(recyclerView);
                            }
                        }
    
                    }
    
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);
                        allPixelsDate += dx;
                    }
                });
                if (labelerDates == null)
                    labelerDates = new ArrayList<>();
                labelerDates.addAll(genLabelerDate(currentMonth, currentYear));
                dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
                recyclerViewDate.setAdapter(dateAdapter);
                return true;
            }
        });
    }
    /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
    
    private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
        int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
    
        if (expectedPositionDate == -1) {
            expectedPositionDate = 0;
        } else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
            expectedPositionDate--;
        }
        scrollListToPositionDate(recyclerView, expectedPositionDate);
    
    }
    /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
    private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
        float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
        float missingPxDate = targetScrollPosDate - allPixelsDate;
        if (missingPxDate != 0) {
            recyclerView.smoothScrollBy((int) missingPxDate, 0);
        }
    }
    private void setDateValue() {
        int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
        setColorDate = expectedPositionDateColor + 1;
        //set color here
        dateAdapter.setSelecteditem(setColorDate);
    }
     @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);   
        allPixelsDate = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE);
        allPixelsDateChanged = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED);
    }
    
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putFloat(BUNDLE_LIST_PIXELS_DATE, allPixelsDate);
        outState.putFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED, allPixelsDateChanged);
    }
    

    And this is my result: enter image description here

    Look at this video link, this is my app demo