前言
最近在做一个音乐列表,希望正在播放的歌曲始终在列表中居中显示,类似下面的样子。 切换上一曲下一曲时始终能保持当前的歌曲始终居中。RecyclerView有两个函数smoothScrollToPosition和scrollToPosition都能滚动到指定的位置,但是并不能保持居中,自己做位置记忆的话又很麻烦,自己尝试了半天没有好方法,最终参考了一位大佬的文章。
https://blog.csdn.net/iblade/article/details/90449089
自定义布局管理器
继承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);
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);
int max = recyclerView.computeVerticalScrollRange();
int offset = recyclerView.computeVerticalScrollOffset();
}
|