1. 程式人生 > >[Android]使用VLayout,你可能遇到的問題

[Android]使用VLayout,你可能遇到的問題

概述

VirtualLayout是一個針對RecyclerView的LayoutManager擴充套件, 主要提供一整套佈局方案和佈局間的元件複用的問題。

設計思路

  • 通過定製化的LayoutManager,接管整個RecyclerView的佈局邏輯;
  • LayoutManager管理了一系列LayoutHelper,LayoutHelper負責具體佈局邏輯實現的地方;
  • 每一個LayoutHelper負責頁面某一個範圍內的元件佈局;
  • 不同的LayoutHelper可以做不同的佈局邏輯,因此可以在一個RecyclerView頁面裡提供異構的佈局結構,這就能比系統自帶的LinearLayoutManager、GridLayoutManager等提供更加豐富的能力。 同時支援擴充套件LayoutHelper來提供更多的佈局能力。

主要功能

  • 預設通用佈局實現,解耦所有的View和佈局之間的關係: Linear, Grid, 吸頂, 浮動, 固定位置等。
    • LinearLayoutHelper: 線性佈局
    • GridLayoutHelper: Grid佈局, 支援橫向的colspan
    • FixLayoutHelper: 固定佈局,始終在螢幕固定位置顯示
    • ScrollFixLayoutHelper: 固定佈局,但之後當頁面滑動到該圖片區域才顯示, 可以用來做返回頂部或其他書籤等
    • FloatLayoutHelper: 浮動佈局,可以固定顯示在螢幕上,但使用者可以拖拽其位置
    • ColumnLayoutHelper: 欄格佈局,都佈局在一排,可以配置不同列之間的寬度比值
    • SingleLayoutHelper: 通欄佈局,只會顯示一個元件View
    • OnePlusNLayoutHelper: 一拖N佈局,可以配置1-5個子元素
    • StickyLayoutHelper: stikcy佈局, 可以配置吸頂或者吸底
    • StaggeredGridLayoutHelper: 瀑布流佈局,可配置間隔高度/寬度
  • 上述預設實現裡可以大致分為兩類:一是非fix型別佈局,像線性、Grid、欄格等,它們的特點是佈局在整個頁面流裡,隨頁面滾動而滾動;另一類就是fix型別的佈局,它們的子節點往往不隨頁面滾動而滾動。
  • 所有除佈局外的元件複用,VirtualLayout將用來管理大的模組佈局組合,擴充套件了RecyclerView,使得同一RecyclerView內的元件可以複用,減少View的建立和銷燬過程。

以上是VLayout 在 GitHub 上的介紹

RecyclerView 焦點

如果發現你的RecyclerView 在滑動的過程中經常跳屏(暫且稱它為跳屏,現象是總是跳到某個item),那麼你可能遇到RecyclerView與子item某控制元件的焦點衝突問題。處理這類問題的方法有很多,例如你可以在你的xml中使用android:descendantFocusability,這裡就不贅述了。

橫向滑動元件

在使用VLayout的過程中,你會發現根本找不到一個橫向線性佈局的Helper(LinearLayoutHelper不支援橫向)

GitHub issues #9

那麼我們按照VLayout設計者的建議,自行巢狀一個橫向的RecyclerView。建立類HorizontalAdapter 並繼承DelegateAdapter.Adapter<HorizontalViewHolder>

public class HorizontalAdapter extends DelegateAdapter.Adapter<HorizontalViewHolder> {

    private Context mContext;

    private LayoutHelper mLayoutHelper;

    public HorizontalAdapter(Context context, LayoutHelper layoutHelper) {
        this.mContext = context;
        this.mLayoutHelper = layoutHelper;
    }

    @Override
    public LayoutHelper onCreateLayoutHelper() {
        return mLayoutHelper;
    }

    @Override
    public HorizontalViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == 4) {
            return new HorizontalViewHolder(
                    LayoutInflater.from(mContext).inflate(R.layout.layout_recyclerview, parent, false));
        }
        return null;
    }

    @Override
    public void onBindViewHolder(NormalViewHolder holder, int position) {
        if (holder.itemView instanceof RecyclerView) {
            RecyclerView recyclerView = (RecyclerView) holder.itemView;
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext);
            linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
            recyclerView.setLayoutManager(linearLayoutManager);
            recyclerView.setAdapter(new HotItemAdapter(mContext, list,listener));
        }
    }

    @Override
    public int getItemViewType(int position) {
        return 4;
    }

    @Override
    public int getItemCount() {
        return 1;
    }
}

至此,初步的Adapter已經碼好了。那麼問題來了

橫向RecyclerView 狀態重置。 這句話什麼意思,就是當你的橫向RecyclerView滑出螢幕外被複用時,之前對橫向RecyclerView或其子item做的操作都將重置。例如橫向RecyclerView滑動過的距離,當你滑出螢幕外後再滑回來,橫向RecyclerView又回到第一個item。解決思路: 當橫向RecyclerView 將被複用時,記錄RecyclerView的狀態。當橫向RecyclerView 即將出現在螢幕內時,恢復其狀態。 我們在HorizontalAdapter中重寫兩個方法onViewDetachedFromWindowonViewAttachedToWindow,以上面滑動距離為例

    @Override
    public void onViewDetachedFromWindow(NormalViewHolder holder) {
        if (holder.itemView instanceof RecyclerView) {
            RecyclerView recyclerView = ((RecyclerView) holder.itemView);
            LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
            position = manager.findFirstVisibleItemPosition();
            View view = manager.findViewByPosition(position);
            ViewGroup.MarginLayoutParams lp = 
                              (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            if (view != null) {
                xOffset = view.getLeft() - lp.leftMargin; //如果你設定了margin則減去
            }
        }
        super.onViewDetachedFromWindow(holder);
    }

    @Override
    public void onViewAttachedToWindow(NormalViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        if (holder.itemView instanceof RecyclerView) {
            RecyclerView recyclerView = ((RecyclerView) holder.itemView);
            LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
            manager.scrollToPositionWithOffset(position, xOffset);
        }
    }

如果你還內嵌了ViewPager(輪播?),同樣會出現元件的狀態問題。因為VLayout干預不到這一層,所以我們還是得自己解決,解決方案同上

共用複用池 當我們需要巢狀RecyclerView時,我們讓子RecyclerView與父RecyclerView共用一個複用池

public HorizontalAdapter(Context context, LayoutHelper layoutHelper, RecyclerView.RecycledViewPool viewPool) {
        this.mContext = context;
        this.mLayoutHelper = layoutHelper;
        this.viewPool = viewPool;
    }

    @Override
    public void onBindViewHolder(NormalViewHolder holder, int position) {
        if (holder.itemView instanceof RecyclerView) {
            RecyclerView recyclerView = (RecyclerView) holder.itemView;
            recyclerView.setRecycledViewPool(viewPool);
            ...
       }
    }

如果喜歡,就點點贊

作者:Vane_Subin 連結:https://www.jianshu.com/p/cb4fc4f01cf9 來源:簡書 簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。