1、Fragment切换导致RecylerView自动上滑问题
问题描述
该问题主要存在于Fragment可见状态变化时(一般是生命周期更新或者Fragment之间相互切换):
① RecylerView布局中存在动态设置高度的ItemView;
② 存在使用ViewPager做轮播组件的时候;
解决方案:
在RecyclerView的父布局中设置
android:descendantFocusability="blocksDescendants"
或者设置如下:
android:focusable="true"
android:focusableInTouchMode="true"
2、RecylerView中嵌套ViewPager事件冲突
问题描述:
ViewPager嵌套在RecyclerView中,存在无法捕获滑动事件的问题,解决方式如下,重写ViewPager的onTouchEvent事件
解决方案:
@Override
public boolean onTouchEvent(MotionEvent ev) {
final ViewParent parent = this.getParent();
switch (ev.getActionMasked()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
//解决ViewPager嵌套ViewPager导致无法滑动的问题
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
//解决ViewPager嵌套ViewPager导致无法滑动的问题
}
break;
}
return super.onTouchEvent(ev);
}
注意:以上方案还同样可以解决ViewPager+Fragment + RecylerView + ViewPager(目标) 模式的问题。
3、TabLayout初始选项位置问题
TabLayout如果是MODE_SCROLLABLE模式,要嵌入到RecyclerView中,渲染时并没有被add到RecylerView中(getWindowToken是空的),此时选中最后的几项,可能没有自动滑入可见区域,造成选中区域被遮挡。
解决方案:
使用主线程Looper队列处理该问题
public void setSelectedTab(int i){
if(i<0) return;
if(mTabLayout==null || mTabLayout.getTabCount()<=i) return;
final TabLayout.Tab tab = mTabLayout.getTabAt(i);
if(tab.isSelected()){
return;
}
mTabLayout.postDelayed(()->{
tab.select();
mTabLayout.setScrollPosition(i, 0, true);
},0);
}
4、垂直RecyclerView嵌套横向RecylerView位移复用问题。
问题描述:
一个被滑动了一段距离的横向RecylerView,存在复用位移问题,主要有三种现象。
①正常现象,状态保留完美(少数手机),能自动保存位移,自动复位。
②横向RecylerView位置自动复原位移到0的位置(多数手机)
③横向A滑动一段距离,滑动到某一位置A被D复用后,其位置被D继承了(明明没有滑动D,可D存在了位移)(多数手机)
解决方案:
方案一:参考:https://blog.csdn.net/anjoyandroid/article/details/75544381,其中使用ScrollListener,但是存在一个问题,有可能造成多个Listener监听问题。
方案二:在Recycler.Adapter存在2个生命周期方法如下:
onViewDetachedFromWindow(BaseViewHolder holder)和onViewAttachedToWindow(BaseViewHolder holder)
但是我们只使用前者,我们在onViewDetachedFromWindow之前的保存位置
@Override
public void onDetachedFromWindow(BaseViewHolder helper) {
super.onDetachedFromWindow(helper);
if(recyclerView==null || recyclerView.getAdapter()==null || recyclerView.getAdapter().getItemCount()<1) return;
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
final int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
dataEntity.scrollPosition = layoutManager.findFirstVisibleItemPosition();
View childView = layoutManager.findViewByPosition(firstVisibleItemPosition);
if (childView != null) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
dataEntity.scrollOffset = childView.getLeft();
if(layoutParams!=null){
dataEntity.scrollOffset = childView.getLeft() - layoutParams.rightMargin;
}
}
NCFLog.e("scrollPosition","brandItem="+dataEntity.getSort()+",scrollOffset="+ dataEntity.scrollOffset+" ,scrollPosition="+dataEntity.scrollPosition);
}
然后下次再初始化的时候还原
if(childRecyclerViewAdapter.getItemCount()==0) return;
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if(childRecyclerViewAdapter.getItemCount()<=2) {
layoutManager.scrollToPositionWithOffset(0,0);
return;
}
if(dataEntity.scrollPosition>=childRecyclerViewAdapter.getItemCount()){
dataEntity.scrollPosition = childRecyclerViewAdapter.getItemCount()-1;
dataEntity.scrollOffset = 0;
}
layoutManager.scrollToPositionWithOffset(dataEntity.scrollPosition, dataEntity.scrollOffset);
5、Glide图片大小自适应问题
问题描述
有时为了网络图片看起来不被压缩,一般设置ScaleType为fitCenter即可,但是这种方案要求服务器返回不同尺寸的图片才能适配各种手机。因此可以说,FitXY实际上更常见。一般情况下,在RecyclerView中图片控件的宽度是已知的,因此我们求出高度即可。
解决方案如下:
public class GlideTargetHeightAdapter extends SimpleTarget<Drawable> {
private final int reqWidth;
private final String url;
private ImageView targetView;
public static GlideTargetHeightAdapter helper(int reqWidth, ImageView iv, String url){
return new GlideTargetHeightAdapter(reqWidth,iv,url);
}
public void setPlaceHolder(int resId){
if(resId==0) return;
if(this.targetView!=null && this.targetView.getDrawable()==null){
this.targetView.setImageResource(resId);
}
}
public void setPlaceHolder(Drawable placeholder) {
if(this.targetView!=null && this.targetView.getDrawable()==null){
this.targetView.setImageDrawable(placeholder);
}
}
public GlideTargetHeightAdapter(int reqWidth, ImageView targetView, @NonNull String tag) {
super();
this.targetView = targetView;
this.reqWidth = reqWidth;
this.url = tag;
this.targetView.setImageResource(R.drawable.xsj_default_product_img);
}
public GlideTargetHeightAdapter(ImageView targetView, @NonNull String tag) {
this(-1,targetView,tag);
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
if(resource==null || this.targetView==null) return;
final float width = resource.getIntrinsicWidth() * 1f;
final float height = resource.getIntrinsicHeight() * 1f;
int imageWidth = this.reqWidth;
final ViewGroup.LayoutParams layoutParams = targetView.getLayoutParams();
float hspace = (this.targetView.getPaddingLeft() + this.targetView.getPaddingRight());
float vspace = (this.targetView.getPaddingTop() + this.targetView.getPaddingBottom());
float contentWidth = imageWidth - hspace;
float contentHeight = contentWidth * (height/width);
if(layoutParams!=null){
int oldHeight = layoutParams.height;
layoutParams.height = (int) ( contentHeight + vspace);
if(oldHeight!=layoutParams.height) {
this.targetView.setLayoutParams(layoutParams);
}
}
if(this.targetView.getScaleType()!= ImageView.ScaleType.FIT_XY){
this.targetView.setScaleType(ImageView.ScaleType.FIT_XY);
}
if(resource instanceof BitmapDrawable){
Bitmap createScaledBitmap = Bitmap.createScaledBitmap(((BitmapDrawable) resource).getBitmap(), (int)contentWidth, (int)contentHeight, false);
this.targetView.setImageBitmap(createScaledBitmap);
}else if(resource instanceof GifDrawable) {
NcfGlide.with(this.targetView).placeholder(R.drawable.xsj_default_product_img).load(this.url).into(this.targetView);
}
}
}
用法
final int widthPixels = mImageIv.getResources().getDisplayMetrics().widthPixels;
Glide.with(mImageIv).placeholder(R.drawable.icon_default_img).load(imageUrl).into(GlideTargetHeightAdapter.helper(widthPixels,mImageIv,imageUrl));
6、Handler或者定时器问题
问题描述:
定时器何时移除,如何移除?
解决方案:
由于RecyclerView是可以复用的,因此ItemView必然会onAttachToWindow和onDetachFromWindow,因此我们需药在onDetachFromWindow终止这些定时器。但是这里我们可能遇到某些低版本handler.removeCallbacks失效的问题,因此我们需要在Runnable中设置一个变量,类似结束线程的方式终止定时器循环。
此外,禁止使用如下方法,该方法可能造成系统中的消息、事件丢失,从而造成下拉刷新控件下拉之后无法恢复到初始状态。
removeCallbacksAndMessages