最近博主发现让RecyclerView滑动到某一位置并置顶的博客一大堆,抄的是完全一模一样。此外,虽然这些博客“解决”了这些问题,但这种解决方案过于浅显、粗暴,甚至都违背了开发思想。遂在此纠正这种错误。

RecyclerView提供了几种移动的方法

scrollToPosition

scrollTo

scrollBy

smoothScrollBy

smoothScrollToPosition

由于多数博客鱼龙混杂,本博客如果让你非常满意或解决了大家的根本性问题,希望多多支持在下方点赞和回复一下,举手之劳方便大家。

虽然里面有移动到指定位置的方法scrollToPosition(直接闪现至某一位位置)、smoothScrollToPosition(惯性滑动至某一位置)但是貌似都不尽人意,因为他们只保证能够展示出来,并不能保证在第一位。而此时如果你打开源码就会发现,原来全都是调用的LayoutManager移动方法,首先打开我们耳熟能详的LinearLayoutManager惊喜就在眼前

scrollToPosition

在scrollToPosition旁边有木有一个很像的方法

@Override

public void scrollToPosition(int position) {

mPendingScrollPosition = position;

mPendingScrollPositionOffset = INVALID_OFFSET;

if (mPendingSavedState != null) {

mPendingSavedState.invalidateAnchor();

}

requestLayout();

}

public void scrollToPositionWithOffset(int position, int offset) {

mPendingScrollPosition = position;

mPendingScrollPositionOffset = offset;

if (mPendingSavedState != null) {

mPendingSavedState.invalidateAnchor();

}

requestLayout();

}

当看到offset时也许就会明白:没错,这个就是item移动后相对父控件的偏移值,传入0就会有你想要的

smoothScrollToPosition

@Override

public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,

int position) {

LinearSmoothScroller linearSmoothScroller =

new LinearSmoothScroller(recyclerView.getContext());

linearSmoothScroller.setTargetPosition(position);

startSmoothScroll(linearSmoothScroller);

}

而smoothScrollToPosition原来仅仅是new了一个LinearSmoothScroller然后调用startSmoothScroll

我们只需要自定义一个LinearSmoothScroller,之前写的有点仓促,仔细看LinearSmoothScroller的源码发现,其实谷歌已经埋下了伏笔,既然纠正就纠正到底吧

public class TopSmoothScroller extends LinearSmoothScroller {

TopSmoothScroller(Context context) {

super(context);

}

@Override

protected int getHorizontalSnapPreference() {

return SNAP_TO_START;//具体见源码注释

}

@Override

protected int getVerticalSnapPreference() {

return SNAP_TO_START;//具体见源码注释

}

}

然后调用LinearLayoutManager的startSmoothScroll即可

final TopSmoothScroller mScroller = new TopSmoothScroller(getActivity());

mScroller.setTargetPosition(integer);

mManager.startSmoothScroll(mScroller);

是否恍然大悟:其实我们并不需要什么bd,也不需要修改LinearLayoutManager,仅仅需要几行代码即可解决。

多看看源码,多思考思考,你也可以。

真理往往掌握在少数人手中,你是不是其中一员呢?

对于一些不会或质疑的人,此处追加demo:

public class RvHuaDongActivity extends BaseActivity {

@BindView(R.id.rv_rvhuadong)

RecyclerView mRv;

private LinearLayoutManager mManager;

//此处等于setContentView

@Override

protected int getLayouRes() {

return R.layout.activity_rv_hua_dong;

}

//此处等于onCreate

@Override

protected void initData() {

Listlist = new ArrayList<>();

for (int i = 0; i < 100; i++) {

list.add("position" + i);

}

mManager = new LinearLayoutManager(this);

mRv.setLayoutManager(mManager);

mRv.setAdapter(new MyAdapter(list));

}

@Override

protected void setListener() {

}

@OnClick({R.id.tv_rvhuadong_GuanXing_1, R.id.tv_rvhuadong_GuanXing_2, R.id.tv_rvhuadong_GuanXing_3,

R.id.tv_rvhuadong_ShanXian_1, R.id.tv_rvhuadong_ShanXian_2, R.id.tv_rvhuadong_ShanXian_3})

public void onViewClicked(View view) {

switch (view.getId()) {

case R.id.tv_rvhuadong_GuanXing_1:

int position1 = (int) (Math.random() * 100);

Toast.makeText(this, "滑到:" + position1, Toast.LENGTH_SHORT).show();

LinearSmoothScroller s1 = new TopSmoothScroller(getActivity());

s1.setTargetPosition(position1);

mManager.startSmoothScroll(s1);

break;

case R.id.tv_rvhuadong_GuanXing_2:

LinearSmoothScroller s2 = new TopSmoothScroller(getActivity());

s2.setTargetPosition(20);

mManager.startSmoothScroll(s2);

break;

case R.id.tv_rvhuadong_GuanXing_3:

LinearSmoothScroller s3 = new TopSmoothScroller(getActivity());

s3.setTargetPosition(99);

mManager.startSmoothScroll(s3);

break;

case R.id.tv_rvhuadong_ShanXian_1:

int position2 = (int) (Math.random() * 100);

Toast.makeText(this, "闪到:" + position2, Toast.LENGTH_SHORT).show();

mManager.scrollToPositionWithOffset(position2, 0);

break;

case R.id.tv_rvhuadong_ShanXian_2:

mManager.scrollToPositionWithOffset(20, 0);

break;

case R.id.tv_rvhuadong_ShanXian_3:

mManager.scrollToPositionWithOffset(99, 0);

break;

}

}

private class MyAdapter extends RecyclerView.Adapter{

private final ListmList;

public MyAdapter(Listlist) {

mList = list;

}

@Override

public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

LinearLayout ll = new LinearLayout(getActivity());

ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

ll.setOrientation(LinearLayout.VERTICAL);

AppCompatTextView tv = new AppCompatTextView(RvHuaDongActivity.this);

tv.setTextSize(30);

tv.setBackgroundColor(0xffeeeeee);

ll.addView(tv);

RecyclerView rv = new RecyclerView(getActivity());

rv.setLayoutManager(new LinearLayoutManager(getActivity()));

rv.setNestedScrollingEnabled(true);

rv.setAdapter(new ItemAdapter(new ArrayList()));

ll.addView(rv, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

return new BaseViewHolder(ll);

}

@Override

public void onBindViewHolder(BaseViewHolder holder, int position) {

ViewGroup vg = (ViewGroup) holder.itemView;

TextView tv = (TextView) vg.getChildAt(0);

tv.setText(mList.get(position));

RecyclerView rv = (RecyclerView) vg.getChildAt(1);

ItemAdapter adapter = (ItemAdapter) rv.getAdapter();

adapter.mList.clear();

for (int i = 0; i < 6; i++) {

adapter.mList.add("item" + i);

}

adapter.notifyDataSetChanged();//在bind时确定好数据

}

@Override

public int getItemCount() {

return mList.size();

}

}

private class ItemAdapter extends RecyclerView.Adapter{

private final ListmList;

public ItemAdapter(Listlist) {

mList = list;

}

@Override

public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

AppCompatTextView tv = new AppCompatTextView(RvHuaDongActivity.this);

tv.setTextSize(30);

tv.setBackgroundColor(0xffeeeeee);

tv.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

return new BaseViewHolder(tv);

}

@Override

public void onBindViewHolder(BaseViewHolder holder, int position) {

TextView tv = (TextView) holder.itemView;

tv.setText(mList.get(position));

if (position >= mList.size() - 2) {

tv.getLayoutParams().height = 600;

} else {

tv.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;

}

tv.setLayoutParams(tv.getLayoutParams());

}

@Override

public int getItemCount() {

return mList.size();

}

}

public static class TopSmoothScroller extends LinearSmoothScroller {

TopSmoothScroller(Context context) {

super(context);

}

@Override

protected int getHorizontalSnapPreference() {

return SNAP_TO_START;//具体见源码注释

}

@Override

protected int getVerticalSnapPreference() {

return SNAP_TO_START;//具体见源码注释

}

}

}

activity_rv_hua_dong.xml

support包27.0.2,目前除了startSmoothScroll进行中然后立即调用scrollToPositionWithOffset(谁会这么做)位置会出现偏差 ,其他连续调用等操作均没有任何问题

注:

1.终于明白下面评论里的最后一个条目置顶的事了,对于类似rv嵌套rv置顶的问题(尤其是最后一个置顶):首先你的内层rv高度(如果是竖着的)必须是wrap,为了效率和复用内层rv要在bind初始化好基本数据LayoutManager、Adapter、setNestedScrollingEnabled(true);,在外层rv bind时必须确定好你内层rv的数据(我上面的例子都有写)。

2.置顶失败的举例:①rv嵌套内层的数据需要再次请求网络动态获取的②adapter里图片高度是网络请求决定的③rv设置数据用的是handler或子线程(这3个问题的根本原因是,滑到最后一个条目了了,你还在异步加载数据,rv发现滑不动自然就把最后一个条目停在最底下了,然后你才把数据返回来早就晚了)④用了一些支持header、footer的Adapter的,条目可能需要+1才能滑到

具体的使用场景及效果可以参考微信的通讯录

转载请注明出处:王能的博客https://blog.csdn.net/weimingjue/article/details/82805361

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐