产品需求,点击标签变成选中态,且被选中标签 自动滑到屏幕中间,如图所示:

1.如何实现自动滑动到屏幕中间?

2.如何避免闪动?

3.滑动速度如何控制?

一,自动滑动到屏幕中间:

RecyclerView中最容易想到的方法是smoothScrollToPosition(int position),可是position该是多少呢?显然这个方法行不通。

设置滑动还要从LinearLayoutManager入手,重写之。

/*** Created by iblade.Wang on 2019/5/22 17:08*/
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);}@Overridepublic void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());smoothScroller.setTargetPosition(position);startSmoothScroll(smoothScroller);}private static class CenterSmoothScroller extends LinearSmoothScroller {CenterSmoothScroller(Context context) {super(context);}@Overridepublic int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);}}}

点击Item调用

centerLayoutManager.smoothScrollToPosition(recyclerView1, new RecyclerView.State(), position);

OK,第一个问题搞定;现在解决闪动的问题。

二,item闪动问题:

UI说“初版”的切换时 闪一下体验太不好,说实话我是真心看不出来,不是王婆卖瓜自卖自夸,而是该Demo场景不够显眼,因为它确实闪屏了。在另外一个RecyclerView 点赞功能 时候,想护犊子都不行,犊子确实太闪眼。场景是Item中一个ImageView有共四张图Loading,ErrorImg,GIF和CoverImg的切换,图片下方是点赞按钮,一点赞,图片快速闪一下。明白人一听就知道问题出在哪:adapter1.notifyItemChanged(position);

现在就要引入一个经常听,我却很少用的操作:Item的局部刷新。

/*** Notify any registered observers that the item at <code>position</code> has changed with* an optional payload object.** <p>This is an item change event, not a structural change event. It indicates that any* reflection of the data at <code>position</code> is out of date and should be updated.* The item at <code>position</code> retains the same identity.* </p>** <p>* Client can optionally pass a payload for partial change. These payloads will be merged* and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the* item is already represented by a ViewHolder and it will be rebound to the same* ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing* payloads on that item and prevent future payload until* {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume* that the payload will always be passed to onBindViewHolder(), e.g. when the view is not* attached, the payload will be simply dropped.** @param position Position of the item that has changed* @param payload Optional parameter, use null to identify a "full" update** @see #notifyItemRangeChanged(int, int)*/
public final void notifyItemChanged(int position, @Nullable Object payload) {mObservable.notifyItemRangeChanged(position, 1, payload);
}

没错。就是带上这个payLoad。

当然在Adapter中也要对应重写一个带payLoads的方法如下:

  /*** Called by RecyclerView to display the data at the specified position. This method* should update the contents of the {@link ViewHolder#itemView} to reflect the item at* the given position.* <p>* Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method* again if the position of the item changes in the data set unless the item itself is* invalidated or the new position cannot be determined. For this reason, you should only* use the <code>position</code> parameter while acquiring the related data item inside* this method and should not keep a copy of it. If you need the position of an item later* on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will* have the updated adapter position.* <p>* Partial bind vs full bind:* <p>* The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or* {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,* the ViewHolder is currently bound to old data and Adapter may run an efficient partial* update using the payload info.  If the payload is empty,  Adapter must run a full bind.* Adapter should not assume that the payload passed in notify methods will be received by* onBindViewHolder().  For example when the view is not attached to the screen, the* payload in notifyItemChange() will be simply dropped.** @param holder The ViewHolder which should be updated to represent the contents of the*               item at the given position in the data set.* @param position The position of the item within the adapter's data set.* @param payloads A non-null list of merged payloads. Can be empty list if requires full*                 update.*/public void onBindViewHolder(@NonNull VH holder, int position,@NonNull List<Object> payloads) {onBindViewHolder(holder, position);}

有人问了,为何传参是Object,接收却是List payloads,哪位路过大神 求评论区 解读。

测试结果是 传参后list的长度总为1.当然不传参长度为0.

代码如下:


public static final int UPDATE_STATE = 101;
public static final int UPDATE_NAME = 102;@Override
public void onBindViewHolder(@NonNull LabelHolder holder, int position, @NonNull List<Object> payloads) {//list为空时,必须调用两个参数的onBindViewHolder(@NonNull LabelHolder holder, int position)if (payloads.isEmpty()) {onBindViewHolder(holder, position);} else if (payloads.get(0) instanceof Integer) {int payLoad = (int) payloads.get(0);switch (payLoad) {case UPDATE_STATE:holder.textView.setSelected(list.get(position).isSelected());break;case UPDATE_NAME:holder.textView.setText(list.get(position).getName());break;default:break;}}
}

这样就能顺利解决闪动的问题。下面说说滑动速度控制。

三,如何做到真正的smooth:

smooth纵享丝滑方法名的都是骗人的,使用时往往都是“噔”一下就到position了,搞得UI和我们鸡飞狗跳。

废话少说。控制滑动速度,其实关键关键还在LinearLayoutManager中的LinearSmoothScroller,重写一个方法:

private static final float MILLISECONDS_PER_INCH = 25f;
/*** Calculates the scroll speed.** @param displayMetrics DisplayMetrics to be used for real dimension calculations* @return The time (in ms) it should take for each pixel. For instance, if returned value is* 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.*/
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}

默认是25f,上图中是我已经改为100f后效果,不多解释,看变量名就知道咋回事。


private static class CenterSmoothScroller extends LinearSmoothScroller {CenterSmoothScroller(Context context) {super(context);}@Overridepublic int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);}@Overrideprotected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {return 100f / displayMetrics.densityDpi;}
}

完整代码如下:

Activity:


public class CenterItemActivity extends AppCompatActivity {private RecyclerView recyclerView;private LabelAdapter adapter;private List<FilterBean> list = new ArrayList<>();private int lastLabelIndex;private CenterLayoutManager centerLayoutManager;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_center_item);init();}private void init() {recyclerView = findViewById(R.id.label_recycler_view);adapter = new LabelAdapter(list, this);centerLayoutManager = new CenterLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);recyclerView.setLayoutManager(centerLayoutManager);for (int i = 0; i < 20; i++) {FilterBean bean = new FilterBean();bean.setName("Label-" + i);list.add(bean);}recyclerView.setAdapter(adapter);adapter.setOnLabelClickListener(new LabelAdapter.OnLabelClickListener() {@Overridepublic void onClick(FilterBean bean, int position) {if (position != lastLabelIndex) {ToastUtil.show(CenterItemActivity.this, bean.getName());FilterBean lastBean = list.get(lastLabelIndex);lastBean.setSelected(false);adapter.notifyItemChanged(lastLabelIndex, LabelAdapter.UPDATE_STATE);centerLayoutManager.smoothScrollToPosition(recyclerView, new RecyclerView.State(), position);bean.setSelected(true);adapter.notifyItemChanged(position, LabelAdapter.UPDATE_STATE);}lastLabelIndex = position;}});}
}
/*** Created by iblade.Wang on 2019/5/22 17:08*/
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);}@Overridepublic void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int 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);}@Overridepublic int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);}@Overrideprotected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {return 100f / displayMetrics.densityDpi;}}
}
/*** Created by iblade.Wang on 2019/5/22 17:16*/
public class LabelAdapter extends RecyclerView.Adapter<LabelAdapter.LabelHolder> {private List<FilterBean> list;private Activity activity;private LayoutInflater inflater;public LabelAdapter(List<FilterBean> list, Activity activity) {this.list = list;this.activity = activity;inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);}@NonNull@Overridepublic LabelHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {return new LabelHolder(inflater.inflate(R.layout.item_pos_type, parent, false));}@Overridepublic void onBindViewHolder(@NonNull LabelHolder holder, int position1) {final int position = holder.getAdapterPosition();if (list != null && null != list.get(position)) {FilterBean bean = list.get(position);holder.textView.setSelected(bean.isSelected());holder.textView.setText(bean.getName());holder.textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (onLabelClickListener != null) {onLabelClickListener.onClick(bean, position);}}});}}public static final int UPDATE_STATE = 101;public static final int UPDATE_NAME = 102;@Overridepublic void onBindViewHolder(@NonNull LabelHolder holder, int position, @NonNull List<Object> payloads) {//list为空时,必须调用两个参数的onBindViewHolder(@NonNull LabelHolder holder, int position)if (payloads.isEmpty()) {onBindViewHolder(holder, position);} else if (payloads.get(0) instanceof Integer) {int payLoad = (int) payloads.get(0);switch (payLoad) {case UPDATE_STATE:holder.textView.setSelected(list.get(position).isSelected());break;case UPDATE_NAME:holder.textView.setText(list.get(position).getName());break;default:break;}}}public interface OnLabelClickListener {/*** 点击label** @param bean     点击label的对象* @param position 点击位置*/void onClick(FilterBean bean, int position);}private OnLabelClickListener onLabelClickListener;public void setOnLabelClickListener(OnLabelClickListener onLabelClickListener) {this.onLabelClickListener = onLabelClickListener;}@Overridepublic int getItemCount() {return null == list ? 0 : list.size();}final class LabelHolder extends RecyclerView.ViewHolder {private TextView textView;public LabelHolder(View itemView) {super(itemView);textView = itemView.findViewById(R.id.tv_type);}}
}

RecyclerView选中Item滚动到屏幕中间 / 指定位置相关推荐

  1. android item list居中,RecyclerView选中item居中显示

    使用RecyclerView时需要将选中的item在View中居中显示,RecyclerView的scrollToPosition(position)方法只会将position所对应的item滚动到屏 ...

  2. 【Nodejs】使用robotjs控制鼠标键盘 自动点击屏幕上指定位置的图标 实现连接wifi等操作

    每天上班开机挺麻烦,要手动连wifi:因此可以写一个很简单的自动执行脚本,执行 node xxxxxx.js 安装 robotjs npm i robotjs -g xxxxxx.js 以下数字自行根 ...

  3. js鼠标移动到指定位置_Python: pyautogui模块之鼠标控制

    文章背景:PyAutoGUI是一个纯Python的GUI自动化工具,其目的是可以用程序自动控制鼠标和键盘操作,利用它可以实现自动化任务.pyautogui模块中包含了一些函数,可以模拟鼠标移动.按键和 ...

  4. 用startSmoothScroll实现RecyclerView滚动到指定位置并置顶,含有动画。

    RecyclerView滚动到指定位置并置顶 RecyclerView本身提供了几个定位的方法,除了手动滑动的scrollTo,smootScrollTo和scrollBy,smoothScrollB ...

  5. RecyclerView滚动到指定位置

    滚动到指定位置 recyclerView.scrollToPosition(position); 平滑滚动到指定位置 recyclerView.smoothScrollToPosition(posit ...

  6. JS 指定元素滚动到屏幕中间

    点击元素,使该元素滚动到屏幕中间 <!DOCTYPE html> <html lang="en"><head><meta charset= ...

  7. RecyclerView滑动到指定位置,并置顶

    一般我们用 mRecycleview.smoothScrollToPosition(0)滑动到顶部,具有滚动效果,但是如果我们想滚动到任意指定位置,那么smoothScrollToPosition() ...

  8. RecyclerView跳转到指定位置,RecyclerView上下滑动监听,RecyclerView滑动速度

    1.RecyclerView跳转到指定位置 只需调用recycleview的置顶方法: recyclerView.scrollToPosition(15); 如果你需要让第15item在屏幕居中,只需 ...

  9. vue 监听滚动事件之菜单滚动吸顶点击滑动到指定位置点击高亮

    ###菜单滚动吸顶效果: ###html: <section class="switchModule" id="switchModule">< ...

最新文章

  1. 干货丨从基础知识到实际应用,一文了解「机器学习非凸优化技术」
  2. 函数调用栈的获取原理分析【转】
  3. 重温强化学习之无模型学习方法:蒙特卡洛方法
  4. 全球及中国商用壁挂式浴镜行业投资决策与需求前景预测报告2022版
  5. php 面向对象 示例,php中面向对象示例
  6. 中班音乐计算机反思,中班歌曲《不再麻烦好妈妈》活动反思
  7. [渝粤教育] 西南科技大学 政府经济学 在线考试复习资料
  8. 丘处机《摄生消息论》(转)
  9. “变味”的扫码点餐 不获取个人信息不能吃饭
  10. linux中xy是什么命令,Linux命令中chmod 777 以及drwxr-xr-x分别代表什么意思
  11. LVGL(2)Visual Studio模拟器使用
  12. java invokelater 以及invokeandwait
  13. Java过滤emoji表情,找出emoji的unicode范围。
  14. Lavas的简单入门
  15. 由对称性知定点一定在x轴上_圆锥曲线解答题的经典答案:由椭圆的对称性知,定点在x轴上?...
  16. matlab用fft对信号进行频谱分析,用fft对信号作频谱分析matlab程序
  17. UUID订单单号生成器
  18. 三大视角,聊聊我眼中的广告系统
  19. D. Masquerade strikes back(思维)
  20. VF2, VF3算法

热门文章

  1. 高效管理:10年资深项目经理,最喜欢用这两招
  2. 苹果cmsv10海螺6.0.1修复版影视免费模板
  3. 新注册的领英账号如何快速拓展人脉?置顶推荐
  4. 联想企业云盘高可用方案:为数据提供万无一失的保障
  5. Idea Spring-boot整合shiro安全框架
  6. 【转】寻找最好的笔记软件:海选篇 (v1.0)
  7. Python爬取英雄联盟官网图片
  8. C++ 偏微分数值计算库_蒙特卡罗计算积分 - 人工智能遇见磐创
  9. Matlab符号方程组化简
  10. 社会工程学三本_2020年全国三本大学排名 【理科文科】