android RecylerView使用問題總結

一、Fragment切換致使RecylerView自動上滑問題android

問題描述服務器

該問題主要存在於Fragment可見狀態變化時(通常是生命週期更新或者Fragment之間相互切換):網絡

① RecylerView佈局中存在動態設置高度的ItemView;ide

② 存在使用ViewPager作輪播組件的時候;oop

 

解決方案:佈局

在RecyclerView的父佈局中設置post

android:descendantFocusability="blocksDescendants"

或者設置以下:this

android:focusable="true"
android:focusableInTouchMode="true"

 

二、RecylerView中嵌套ViewPager事件衝突url

問題描述:spa

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(目標) 模式的問題。

 

三、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);

    }

 

四、垂直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);

 

五、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));

 

六、Handler或者定時器問題

問題描述:

定時器什麼時候移除,如何移除?

解決方案:

因爲RecyclerView是能夠複用的,所以ItemView必然會onAttachToWindow和onDetachFromWindow,所以咱們需藥在onDetachFromWindow終止這些定時器。可是這裏咱們可能遇到某些低版本handler.removeCallbacks失效的問題,所以咱們須要在Runnable中設置一個變量,相似結束線程的方式終止定時器循環。

此外,禁止使用以下方法,該方法可能形成系統中的消息、事件丟失,從而形成下拉刷新控件下拉以後沒法恢復到初始狀態。

removeCallbacksAndMessages
相關文章
相關標籤/搜索