RecyclerView中在ImageView右侧显示自定义Toast的问题排查
解决RecyclerView指定项ImageView旁显示Toast并随滚动保持位置的问题
我来帮你搞定这个Toast位置跑偏、还不跟着RecyclerView滚动的问题!先拆解下当前代码的问题根源:
- 坐标系统不匹配:你用
holder.imgeView.getRight()获取的是ImageView在它所在item布局里的局部坐标,但Toast的setGravity(Gravity.TOP | Gravity.LEFT)是基于整个屏幕的左上角计算偏移量的,两者坐标系不匹配,自然会显示到错误位置。 - 时机不对:
onBindViewHolder是在item数据绑定阶段执行的,这时候item视图还没被RecyclerView布局到正确的屏幕位置,获取的坐标值并不准确。 - Toast的特性限制:Toast是系统级的独立窗口,它不依附于任何应用内视图,所以RecyclerView滚动时,它没法跟着item一起移动。
接下来给你两种解决方案,按需选择:
方案一:修正Toast的坐标计算(仅解决首次位置问题)
如果只需要Toast第一次显示在正确位置,不需要跟随滚动,可以这样修改onBindViewHolder代码:
@Override public void onBindViewHolder(ViewHolderLTS holder, int position) { if (position == 10){ // 用post延迟执行,确保item已完成布局,能获取到正确的屏幕坐标 holder.imgeView.post(new Runnable() { @Override public void run() { // 获取ImageView在屏幕上的绝对坐标 int[] screenLocation = new int[2]; holder.imgeView.getLocationOnScreen(screenLocation); int imgScreenX = screenLocation[0]; int imgScreenY = screenLocation[1]; // 计算Toast的偏移量:ImageView右侧,垂直居中 int toastOffsetX = imgScreenX + holder.imgeView.getWidth(); int toastOffsetY = imgScreenY + holder.imgeView.getHeight() / 2; Toast toast = new Toast(getContext()); // 基于屏幕左上角设置偏移 toast.setGravity(Gravity.TOP | Gravity.LEFT, toastOffsetX, toastOffsetY); toast.setDuration(Toast.LENGTH_LONG); toast.setView(viewToast); toast.show(); } }); } }
方案二:用PopupWindow替代Toast(解决位置+跟随滚动问题)
如果需要提示跟着item一起滚动,推荐用PopupWindow替代Toast,它可以依附于指定视图,滚动时会自动跟随。
步骤1:在Adapter中添加PopupWindow变量
private PopupWindow customToastPopup;
步骤2:修改onBindViewHolder方法
@Override public void onBindViewHolder(ViewHolderLTS holder, int position) { if (position == 10){ holder.imgeView.post(new Runnable() { @Override public void run() { // 初始化PopupWindow(复用避免重复创建) if (customToastPopup == null) { customToastPopup = new PopupWindow(viewToast, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); customToastPopup.setOutsideTouchable(true); // 点击外部关闭 customToastPopup.setFocusable(false); // 不抢占焦点 } // 计算显示位置:ImageView右侧,垂直居中 int offsetX = holder.imgeView.getWidth(); // 向右偏移整个ImageView宽度 // 垂直居中:PopupWindow中点与ImageView中点对齐 int offsetY = -(viewToast.getHeight() / 2 - holder.imgeView.getHeight() / 2); // 以ImageView的左上角为基准显示PopupWindow customToastPopup.showAsDropDown(holder.imgeView, offsetX, offsetY); } }); } else { // 绑定其他item时,关闭正在显示的PopupWindow if (customToastPopup != null && customToastPopup.isShowing()) { customToastPopup.dismiss(); } } }
步骤3:添加RecyclerView滚动监听,处理item滚出屏幕的情况
在你的Activity/Fragment的RecyclerView初始化代码里添加滚动监听:
@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); recyclerViewLTS.setLayoutManager(layoutManager); adapterLTS.setArrayList(arrayListOfJSON); recyclerViewLTS.setAdapter(adapterLTS); // 添加滚动监听,当第10项滚出屏幕时关闭PopupWindow recyclerViewLTS.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager(); if (lm == null) return; int firstVisiblePos = lm.findFirstVisibleItemPosition(); int lastVisiblePos = lm.findLastVisibleItemPosition(); // 如果第10项不在可见范围内,关闭PopupWindow if (10 < firstVisiblePos || 10 > lastVisiblePos) { if (adapterLTS.customToastPopup != null && adapterLTS.customToastPopup.isShowing()) { adapterLTS.customToastPopup.dismiss(); } } } }); }
这样就能完美实现你要的效果:第10项的ImageView右侧显示自定义提示,滚动时提示会跟着item移动,item滚出屏幕时自动消失~
内容的提问来源于stack exchange,提问作者Deepak sahu
相关产品推荐
相关产品推荐

