0
点赞
收藏
分享

微信扫一扫

RecyclerView选中的item保持在视图中间

茗越 2022-05-06 阅读 92

前言

最近在做一个音乐列表,希望正在播放的歌曲始终在列表中居中显示,类似下面的样子。
在这里插入图片描述
切换上一曲下一曲时始终能保持当前的歌曲始终居中。RecyclerView有两个函数smoothScrollToPosition和scrollToPosition都能滚动到指定的位置,但是并不能保持居中,自己做位置记忆的话又很麻烦,自己尝试了半天没有好方法,最终参考了一位大佬的文章。

自定义布局管理器

继承LinearLayoutManager,重新smoothScrollToPosition方法实现滚动到指定的position

public class CenterLayoutManager extends LinearLayoutManager {
    public CenterLayoutManager(Context context) {
        super(context);
    }

    public CenterLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public CenterLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        super.smoothScrollToPosition(recyclerView, state, position);
        RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private static class CenterSmoothScroller extends LinearSmoothScroller{

        public CenterSmoothScroller(Context context) {
            super(context);
        }
		// 正是该方法实现居中显示
        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }
    }
}

接下来我们在Adapter中通过一个变量记录下当前正在播放的歌曲的位置,需要时滚动到该位置即可。

public class PinAdapter extends RecyclerView.Adapter<PinAdapter.ViewHolder>{
    private static final String TAG = "PinAdapter";

    private Context context;
    private List<String> mList;
	// 记录当前歌曲位置
    public int mPosition = 0;

    public PinAdapter(Context context, List<String> mList) {
        this.context = context;
        this.mList = mList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.simple_text_layout,parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        if (position == mPosition){
            holder.textView.setTextColor(Color.GREEN);
        }else {
            holder.textView.setTextColor(Color.WHITE);
        }
        holder.textView.setText(mList.get(position));
    }

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

    class ViewHolder extends RecyclerView.ViewHolder{

        private TextView textView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.tv_simple_text);
        }
    }
}

设置适配器和布局管理器

        pinAdapter = new PinAdapter(this,list);
        recyclerView.setLayoutManager(new CenterLayoutManager(this));
        recyclerView.setAdapter(pinAdapter);
        // 为了让每一个item完整显示
        PagerSnapHelper helper = new PagerSnapHelper();
        helper.attachToRecyclerView(recyclerView);

PagerSnapHelper类是为了辅助让每一首歌都完整显示,不出现因为RecyclerView滚动时显示部分的情况,这个类是原生的可以直接使用。我们点击上一曲下一曲时就可以进行滚动并刷新了

            recyclerView.smoothScrollToPosition(pinAdapter.mPosition);
            pinAdapter.notifyDataSetChanged();

监听RecyclerView滚动

滑动歌曲时,我们希望在停止滑动一段时间后能回到正在播放的歌曲的位置,这时我们可以通过Handler来实现,停止滚动后发送一个延迟信息,比如3s后如果该信息没有被取消那么就回到播放位置,开始滑动时取消该消息。首先给RecyclerView添加一个监听器

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                // 停止滚动
                if (newState == RecyclerView.SCROLL_STATE_IDLE){
                    Message message = mHanler.obtainMessage(1);
                    mHanler.sendMessageDelayed(message,3000);
                }else {
                    mHanler.removeMessages(1);
                }
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }
        });

方法onScrollStateChanged是在RecyclerView停止滑动时被调用,一般来说有如下几种状态

//停止滚动
public static final int SCROLL_STATE_IDLE = 0;
 
//正在被外部拖拽,一般为用户正在用手指滚动
public static final int SCROLL_STATE_DRAGGING = 1;
 
//自动滚动开始
public static final int SCROLL_STATE_SETTLING = 2;

我们只需要在开始滚动时移除handler消息,停止滚动后创建消息即可。
onScrolled函数一般是用来计算滚动距离之类的,比如希望将RecyclerView和一个外部的滚动条联系起来,我们则可以通过该函数计算,dx是水平滚动的距离,dy是垂直滚动的距离,正负代表滚动的方向,比如dy>0时表示向上滚动,dy<0时表示向下滚动。还可以通过其他两个函数去计算它的总长度和偏移量。

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                // RecyclerView总长度
                int max = recyclerView.computeVerticalScrollRange();
                // 当前位置偏移量
                int offset = recyclerView.computeVerticalScrollOffset();
            }
举报

相关推荐

0 条评论