【问题标题】:Get center visible item of RecycleView when scrolling滚动时获取 RecycleView 的中心可见项
【发布时间】:2016-04-07 19:10:57
【问题描述】:

这就是我想要的:

如上图,我想在RecycleView 上画一条中心线,然后在滚动时获取中心项(以及向左或向右移动)
这是我尝试画一个水平的RecycleView:

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

有什么方法可以知道哪个项目移动到RecycleView 的中心?我怎样才能将RecycleView 向左或向右滚动一个位置?

更新:我尝试使用滚动侦听器来获取中间位置,但它不能用作方面。

  @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();
        }

并得到结果:

我只想在蓝色Rect时更改所选项目(将文本设为白色)

【问题讨论】:

  • 你能告诉我什么包含list_item_padding布局吗?你能告诉我吗
  • @MaheshSuthar 这只是一个项目宽度相等的空布局
  • 对不起,它不起作用
  • 你能告诉我当用户点击日期并且日期将在中心时如何工作吗??
  • @MaheshSuthar 你试过什么?如果它不起作用,您应该创建另一个问题。试着仔细阅读 TranHieu 的回答,算法就在这上面。我使用 TranHieu 的答案并根据我的需要进行定制。

标签: android android-recyclerview scroll horizontal-scrolling


【解决方案1】:

我做了这样的东西。我可以做你需要的。 首先,这就是我的算法工作方式

这是我的 recyclerView 适配器

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);
    }
}}

这是最重要的算法:

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);
}

这是我的结果:

看看这个视频link,这是我的应用演示

【讨论】:

  • 您的代码有问题。 paddingDate 永远不会改变。所以公式(allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate等于allPixelsDate/itemWidthDate,返回错误的选择位置
  • 好吧,我认为第一个 Item 在 recyclerView 中的位置(不是填充的)是 0,它的 Item 是数字 1。这意味着如果你想获得正确的数字 Item 你必须 +1 .我在 setDateValue() 做到了。感谢您的回应
  • 我的项目宽度是100dp,我的中间项目宽度是200dp。我如何用不同的项目宽度来实现这个?
  • @hema18 在我的onBindViewHolder 中我有一个名为selectedItem 的变量。看看吧。它将决定哪个项目位于中心。
  • 截图来自iOS设备?这有点令人困惑。
【解决方案2】:

有时需要将整个示例代码块放在一起,因为我们可能会遗漏一些东西。这是我所拥有的,请随时纠正任何内容,因为我可能在某个地方犯了一些小错误。是的,这个答案是@tranhieu 答案的扩展。谢谢@tranhieu。

MainActivity.java

package com.test;

import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();

    public float firstItemWidthDate;
    public float paddingDate;
    public float itemWidthDate;
    public int allPixelsDate;
    public int finalWidthDate;
    private DateAdapter dateAdapter;
    private ArrayList<LabelerDate> labelerDates = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getRecyclerviewDate();

    }


    public void getRecyclerviewDate() {
        final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
        if (recyclerViewDate != null) {
            recyclerViewDate.postDelayed(new Runnable() {
                @Override
                public void run() {
                    setDateValue();
                }
            }, 300);
            recyclerViewDate.postDelayed(new Runnable() {
                @Override
                public void run() {
                    recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
                    setDateValue();
                }
            }, 5000);
        }
        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<>();
                }
                genLabelerDate();
                dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
                recyclerViewDate.setAdapter(dateAdapter);
                dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
                return true;
            }
        });
    }

    private void genLabelerDate() {
        for (int i = 0; i < 32; i++) {
            LabelerDate labelerDate = new LabelerDate();
            labelerDate.setNumber(Integer.toString(i));
            labelerDates.add(labelerDate);

            if (i == 0 || i == 31) {
                labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
            } else {
                labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
            }
        }
    }
/* 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);
        }
        setDateValue();
    }

    //
    private void setDateValue() {
        int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
        int setColorDate = expectedPositionDateColor + 1;
//        set color here
        dateAdapter.setSelecteditem(setColorDate);
    }


    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.item,
                    parent, false);
                return new DateViewHolder(view);
            } else {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
                    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) {
                holder.tvDate.setText(labelerDate.getNumber());
                holder.tvDate.setVisibility(View.VISIBLE);

                Log.d(TAG, "default " + position + ", selected " + selectedItem);
                if (position == selectedItem) {
                    Log.d(TAG, "center" + position);
                    holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
                    holder.tvDate.setTextSize(35);

                } else {
                    holder.tvDate.setTextColor(Color.WHITE);
                    holder.tvDate.setTextSize(18);
                }
            } else {
                holder.tvDate.setVisibility(View.INVISIBLE);
            }
        }

        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.getType() == VIEW_TYPE_PADDING) {
                return VIEW_TYPE_PADDING;
            } else {
                return VIEW_TYPE_ITEM;
            }

        }


        public class DateViewHolder extends RecyclerView.ViewHolder {
            public TextView tvDate;

            public DateViewHolder(View itemView) {
                super(itemView);
                tvDate = (TextView) itemView.findViewById(R.id.txt_date);
            }
        }
    }

    private class LabelerDate {
        private int type;
        private String number;

        public String getNumber() {
            return number;
        }

        public void setNumber(String number) {
            this.number = number;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_tasks_date"
            android:layout_width="match_parent"
            android:layout_height="48dp" />

        <ImageView
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center"
            android:layout_marginTop="48dp"
            android:src="@android:drawable/ic_dialog_info" />
    </FrameLayout>

</LinearLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical" android:layout_width="wrap_content"
              android:layout_height="match_parent">

    <TextView
        android:id="@+id/txt_date"
        android:layout_width="@dimen/item_dob_width"
        android:layout_height="48dp"
        android:text="32"
        android:textColor="@android:color/white"
        android:background="@android:color/darker_gray"
        android:textSize="28sp"
        android:gravity="center"/>

</LinearLayout>

dimens.xml

<resources>
    <dimen name="item_dob_width">100dp</dimen>
</resources>

【讨论】:

【解决方案3】:

哦,孩子。我一直在寻找这个答案将近一个星期,然后找到了解决方案。自定义LayoutManagers?没有。ItemDecorator?没有。

这是最简单的方法:

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:paddingStart="150dp"
    android:paddingEnd="150dp"
    android:clipToPadding="false" />

关键部分是:

    android:paddingStart="150dp"
    android:paddingEnd="150dp"
    android:clipToPadding="false"

然后只需将SnapHelper 分配给您的RecylcerView:

val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)

就是这样。最简单最完美的解决问题的方法

【讨论】:

  • 此解决方案仅适用于RecyclerViewer 宽度以及第一个和最后一个项目的宽度使得150dp = RV.width * 0.5f - item.width * 0.5f 如果项目宽度相对于RecyclerView 第一个和最后一个项目将发生变化始终未对齐,因为这依赖于使用 RV 及其项目的静态宽度,并希望它们始终保持固定。由于完全缺乏灵活性,这不是一个很好的解决方案。
【解决方案4】:

我在这里使用了 SnapHelper:

    // init snaphelper        
    SnapHelper snapHelper = new LinearSnapHelper();
    snapHelper.attachToRecyclerView(recyclerView)

    // init layout manager
    LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity);
    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
    recyclerView.setLayoutManager(layoutManager);

    // init adapter
    adatper.setSnapHelper(snapHelper);
    adatper.setLayoutManager(layoutManager);

    adatper.initAdapter(new Float((DisplayHelper.getDisplayWidth(mainActivity) / 2) - (fooViewWidth / 2)).intValue());
    recyclerView.setAdapter(adatper);

正如 TranHieu 所说,插入 2 个填充项(在开始位置和结束位置)的解决方案很好。

我不喜欢使用 ViewTreeObserver,因为代码的可读性很差。使用这种技术,如果项目被回收,您还必须管理它们的重绘。

如果您使用自定义视图类,您可以将其宽度直接设置到这些类中。

例如这是我的填充类

/**
 * Created by firegloves on 25/09/15.
 */
@EViewGroup(R.layout.view_padding)
public class PaddingView extends FooView {

    Context mCtx;

    public PaddingView(Context context) {
        super(context);
        mCtx = context;
    }


    public void setWidth(int width) {
        setLayoutParams(new LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
    }

}

在我的适配器中,我存储了所需的填充项宽度,即等于 (displayWidth / 2) - (realItemWidth / 2)

这是我的适配器,不要看方法不匹配RecyclerView.Adapter,注意initAdapter方法和onCreateItemView方法

@EBean
public class FooAdapterRecycler extends RecyclerViewAdapterBase<Foo, FooView> {

    private final int TYPE_PADDING_VIEW = 0;
    private final int TYPE_REAL_VIEW = 1;

    @RootContext
    Context ctx;
    @Bean(Finder.class)
    IFinder finder;

    SnapHelper snapHelper;
    RecyclerView.LayoutManager layoutManager;

    private int paddingWidth = 0;

    /**
     * preleva i dati dal finder
     */
    public void initAdapter(int paddingWidth) {

        /*******************************
         * THIS CODE IS THE IMPORTANT ONE
         ******************************/

        this.paddingWidth = paddingWidth;

        // add 1 item for initial space
        mItems = new ArrayList<>();
        Foo foo = new Foo();
        mItems.add(foo);

        // get real items from finder
        mItems.addAll(finder.findAll());

        // add 1 item for final space
        mItems = new ArrayList<>();
        Foo foo2 = new Foo();
        mItems.add(foo2);

    }


    @Override
    public int getItemViewType(int position) {
        if (position == 0 || position == getItemCount()-1) {
            return TYPE_PADDING_VIEW;
        } else {
            return TYPE_REAL_VIEW;
        }
    }

    @Override
    protected FooView onCreateItemView(ViewGroup parent, int viewType) {

        /*******************************
         * THIS CODE IS THE IMPORTANT ONE
         ******************************/

        if (viewType == TYPE_PADDING_VIEW) {
            PaddingView view = PaddingView_.build(ctx);
            view.setWidth(paddingWidth);
            return view;
        } else {
            return FooView_.build(ctx);
        }
    }

    public void setSnapHelper(SnapHelper snapHelper) {
        this.snapHelper = snapHelper;
    }

    public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
        this.layoutManager = layoutManager;
    }
}

我正在使用 AndroidAnnotations 库,但这不是必需的

希望有帮助

【讨论】:

  • 您能否详细说明您的答案?就像你如何分辨什么是中心项目并改变它的文本颜色?如果没有完全对齐,你如何初始设置捕捉?
【解决方案5】:

使用 SNAPHELPER - 更顺畅的解决方案

这是另一个使用 SnapHelper 的解决方案。从@TranHieu 的回答开始:

https://stackoverflow.com/a/34647005/3944251

这里由@sector11 压缩:

https://stackoverflow.com/a/38411582/3944251

我编写了以下代码,它也基于上述两个答案,但它更简单,并且使用 android 支持库 24.2.0 中提供的 SnapHelper 提供了更流畅的解决方案。

这里有 MainActivity 类。其余与@sector11 的回答相同。

import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSnapHelper;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    public float firstItemWidthDate;
    public float itemWidthDate;
    public int allPixelsDate;
    public int finalWidthDate;
    private DateAdapter dateAdapter;
    private ArrayList<LabelerDate> labelerDates;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        labelerDates = new ArrayList<>();
        getRecyclerviewDate();

    }


    public void getRecyclerviewDate() {
        final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
        recyclerViewDate.postDelayed(new Runnable() {
            @Override
            public void run() {
                //recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
                setDateValue();
            }
        }, 300);
        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);
                firstItemWidthDate = (finalWidthDate - itemWidthDate) / 2;
                allPixelsDate = 0;

                final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
                dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
                recyclerViewDate.setLayoutManager(dateLayoutManager);

                /* Create a LinearSnapHelper and attach the recyclerView to it. */
                final LinearSnapHelper snapHelper = new LinearSnapHelper();
                snapHelper.attachToRecyclerView(recyclerViewDate);

                recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        allPixelsDate += dx;
                        recyclerView.post(new Runnable() {
                            public void run() {
                                setDateValue();
                            }
                        });
                    }
                });

                genLabelerDate();
                dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
                recyclerViewDate.setAdapter(dateAdapter);
                dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
                return true;
            }
        });
    }

    private void genLabelerDate() {
        for (int i = 0; i < 32; i++) {
            LabelerDate labelerDate = new LabelerDate();
            labelerDate.setNumber(Integer.toString(i));
            labelerDates.add(labelerDate);

            if (i == 0 || i == 31) {
                labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
            } else {
                labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
            }
        }
    }

    //
    private void setDateValue() {
        int expectedPositionDateColor = Math.round(allPixelsDate / itemWidthDate);
        int setColorDate = expectedPositionDateColor + 1;
//        set color here
        dateAdapter.setSelecteditem(setColorDate);
    }


    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 DateAdapter.DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            if (viewType == VIEW_TYPE_PADDING) {
                RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
                layoutParams.width = paddingWidthDate;
                view.setLayoutParams(layoutParams);
            }
            return new DateViewHolder(view);
        }

        @Override
        public void onBindViewHolder(DateAdapter.DateViewHolder holder, int position) {
            LabelerDate labelerDate = dateDataList.get(position);
            if (getItemViewType(position) == VIEW_TYPE_ITEM) {
                holder.tvDate.setText(labelerDate.getNumber());
                holder.tvDate.setVisibility(View.VISIBLE);

                Log.d(TAG, "default " + position + ", selected " + selectedItem);
                if (position == selectedItem) {
                    Log.d(TAG, "center" + position);
                    holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
                    holder.tvDate.setTextSize(35);

                } else {
                    holder.tvDate.setTextColor(Color.WHITE);
                    holder.tvDate.setTextSize(18);
                }
            } else {
                holder.tvDate.setVisibility(View.INVISIBLE);
            }
        }

        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.getType() == VIEW_TYPE_PADDING) {
                return VIEW_TYPE_PADDING;
            } else {
                return VIEW_TYPE_ITEM;
            }

        }


        public class DateViewHolder extends RecyclerView.ViewHolder {
            public TextView tvDate;

            public DateViewHolder(View itemView) {
                super(itemView);
                tvDate = (TextView) itemView.findViewById(R.id.txt_date);
            }
        }
    }

    private class LabelerDate {
        private int type;
        private String number;

        public String getNumber() {
            return number;
        }

        public void setNumber(String number) {
            this.number = number;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }
    }
}

【讨论】:

  • 但是第一项从中心开始。所以,最后一项。如何使第一项从左开始,最后一项从右开始?
  • @Dionis,什么是“item_db_width”?
  • 我不知道要为“item_db_width”提供什么。我只给它20。并且中心项目在初始加载时被选中。手动滚动时,中心项目未被选中。
  • @Manikandan item_dob_width 是@sector11 的答案中item.xml 中定义的值:stackoverflow.com/a/38411582/3944251。从他的答案中复制所有.xml 文件并从我的答案中复制MyActivity 代码以了解其工作原理。
【解决方案6】:

正如另一个答案中提到的,没有直接的方法可以做到这一点。

这可能是您实现问题中描述的方法。

  1. 了解屏幕上可见的项目数。
  2. 每次滚动视图时以编程方式选择中间项。
  3. 在recyclerview 的中间项目上保留部分透明的图像作为覆盖。 (您需要根据回收站视图的宽度或屏幕宽度以及您选择放置的叠加图像的宽度来计算坐标。
  4. 每次滚动时,都会在回收站视图下方的文本视图中刷新所选值。

图像叠加层必须以一种看起来相互连接的方式放置,并且作为一个单独的控件。

【讨论】:

  • 谢谢,但我不太明白第 3 步和第 4 步。您能提供一些代码或示例吗?
  • 第 3 点:我的意思是添加一个具有该形状透明图像的重叠 ImageView。示例代码:stackoverflow.com/questions/7754092/…
  • 第 4 点:我的意思是每次用户滚动并且中心值发生变化时,以编程方式选择它,并将所选日期放在 TextView 中,这将是在 gif 中看到的 recyclerview 下方的另一个视图你发布了。我希望这里不需要代码示例。它只是在recyclerview中获取选定的值并将其放在文本视图中格式化后。
【解决方案7】:

您可以使用LinearSnapHelper

像

一样附加到你的 recyclerView
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(this)

然后,要获得中心视图,请使用 snapHelper.findSnapView(horizontalScrollView.layoutManager)?

【讨论】:

    【解决方案8】:

    对于此功能,请使用 EcoGallery 库: https://github.com/falnatsheh/EcoGallery

    【讨论】:

    • 截至目前 - 请不要将此作为解决方案,它已在 7 年前更新。
    【解决方案9】:

    起初,我需要类似的东西,而不是这个。但我能够根据自己的需要调整@TranHieu 解决方案,因此我投票赞成他的解决方案。

    我想创建全屏水平回收视图,在用户滚动后将 scrollPosition 更新为 mostVisibleItem。

    设置:

    private void setUpScrolling() {
        mRecyclerVIew.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mRecyclerVIew.getViewTreeObserver().removeOnPreDrawListener(this);
                CustomScrollListener listener = (CustomScrollListener) mScrollListener;
                listener.width = mRecyclerVIew.getMeasuredWidth();
                listener.dx = 0;
                return true;
            }
        });
    }
    

    听众:

    private class CustomScrollListener extends OnScrollListener {
        private int mLastDx = 0;
        int width = 0;
        int dx = 0;
    
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                if (mLastDx != dx) {
                    scrollToMostVisibleItem();
                } else {
                    dx = 0;
                    mLastDx = 0;
                }
            }
            super.onScrollStateChanged(recyclerView, newState);
        }
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            this.dx += dx;
        }
    
        private void scrollToMostVisibleItem() {
            int direction = (dx > 0) ? 1 : -1;
            dx = Math.abs(dx);
            int shiftCount = Math.round(dx / width);
            int pixelShift = dx % width;
            if (pixelShift > width / 2) {
                shiftCount++;
            }
    
            float targetScrollPixels = shiftCount * width;
            float finalScrollPixels = (targetScrollPixels - dx) * direction;
            if (finalScrollPixels != 0) {
                mRecyclerVIew.smoothScrollBy((int) finalScrollPixels, 0);
                mLastDx = (int) finalScrollPixels;
                dx = 0;
            }
        }
    }
    

    【讨论】:

    • 什么是 mScrollListener ?
    【解决方案10】:

    在我的案例中,我使用了另一种方法。

    您可以在这里找到详细信息:RecyclerView - How highlight central visible item during scroll1

    在我看来,我的解决方案比其他人更容易。

    【讨论】:

      【解决方案11】:

      如果有人正在寻找更通用的实现,这是我根据这个线程的答案编写的代码:

      添加CenterLinearSnapHelper

      public class CenterLinearSnapHelper extends LinearSnapHelper {
      
          //Constants
          public static final String TAG = CenterLinearSnapHelper.class.getSimpleName();
      
          //Attributes
          private Context context;
          private float itemWidth;
          private OnPaddingComputationListener listener;
      
          //Constructors
      
          /**
           * A linear snap helper which helps centering the items in a recyclerview.
           *
           * @param itemWidth The (fixed) width of a child view in pixels.
           */
          public CenterLinearSnapHelper(float itemWidth) {
              this.itemWidth = itemWidth;
          }
      
          public void attachToRecyclerView(@Nullable RecyclerView recyclerView,
                                           @NonNull OnPaddingComputationListener listener) throws IllegalStateException {
              this.listener = listener;
      
              //Calculates the padding for the first and end item
              calculatePadding(recyclerView);
      
              //Create a LinearSnapHelper and attach the recyclerView to it.
              attachToRecyclerView(recyclerView);
          }
      
          public float getItemWidth() {
              return itemWidth;
          }
      
          private void calculatePadding(RecyclerView recyclerView) {
              if (recyclerView == null)
                  return;
      
              ViewTreeObserver observer = recyclerView.getViewTreeObserver();
              observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
      
                  @Override
                  public boolean onPreDraw() {
                      recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
                      int finalWidth = recyclerView.getMeasuredWidth();
                      float padding = (finalWidth - itemWidth) / 2;
                      listener.onPadding(padding, finalWidth);
                      return true;
                  }
              });
          }
      
          public interface OnPaddingComputationListener {
              void onPadding(float padding, int finalWidth);
          }
      
      }
      

      在您创建 RecyclerView 的 Activity/Fragment 中:

      float itemWidth = getResources().getDimension(R.dimen.favorite_room_width);
      CenterLinearSnapHelper snapHelper = new CenterLinearSnapHelper(itemWidth);
      snapHelper.attachToRecyclerView(binding.listFavorites, (padding, finalWidth) -> {
          //Set the adapter
          roomAdapter = new RoomAdapter(requireContext(), rooms);
          roomAdapter.addPaddingItems((int) padding);
          roomAdapter.setOnToggleClickListener(FavoritesFragment.this);
          binding.listFavorites.setAdapter(roomAdapter);
      });
      

      在您的适配器中:

      public void addPaddingItems(int padding) {
          if (padding < 0)
              throw new IllegalStateException("Padding cannot be smaller than 0");
      
          this.padding = padding;
          //Add 2 new items as the first and last
          //NOTE: If you update your existing dataset (e.g add new items), you should redo the calculation!
          rooms.add(0, new Room("First"));
          rooms.add(rooms.size(), new Room("Last"));
      }
      
      
      @Override
      public int getItemViewType(int position) {
          if (padding >= 0 && (position == 0 || position == rooms.size() - 1)) {
              return VIEW_TYPE_PADDING;
          }
      
          return VIEW_TYPE_ITEM;
      }
      
      @NonNull
      @Override
      public RoomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          ViewFavoriteRoomBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_favorite_room, parent, false);
      
          if (viewType == VIEW_TYPE_PADDING) {
              RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) binding.getRoot().getLayoutParams();
              layoutParams.width = padding;
              binding.getRoot().setLayoutParams(layoutParams);
          }
      
          RoomViewHolder viewHolder = new RoomViewHolder(context, binding, onToggleClickListener);
          viewHolder.getRecyclerView().setRecycledViewPool(viewPool);
          return viewHolder;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-26
        • 2019-03-04
        • 1970-01-01
        相关资源
        最近更新 更多