android RecylerView使用问题总结

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

 

转载于:https://my.oschina.net/ososchina/blog/3035549

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值