BRAVH流程簡讀

BRAVH源碼解讀

對於BRAVH基本流程的梳理 因此不會針對每個判斷每個功能都詳細追溯 只會作大體的瞭解,和一些關鍵點的詳解數組

BaseViewHolder

首先咱們來看一下BaseViewHolder的關鍵代碼,至關簡單,能夠看到主要是保存這個View,而後views是這個條目的控件的集合,以setText()爲例,根據id獲取到控件並添加到控件集合,而後view.setText,就醬 剩餘的是一些事件點擊的綁定和處理,後面會單獨將事件點擊這一塊bash

public BaseViewHolder(final View view) {
        super(view);
        this.views = new SparseArray<>();
        this.childClickViewIds = new LinkedHashSet<>();
        this.itemChildLongClickViewIds = new LinkedHashSet<>();
        this.nestViews = new HashSet<>();
        convertView = view;
    }
    
 public BaseViewHolder setText(@IdRes int viewId, CharSequence value) {
        TextView view = getView(viewId);
        view.setText(value);
        return this;
    }

  public <T extends View> T getView(@IdRes int viewId) {
        View view = views.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            views.put(viewId, view);
        }
        return (T) view;
    }
複製代碼

BaseQuickAdapter

這裏咱們打算按照着Recycler的生命週期來說解會更好理解整個流程是如何運做的ide

構造方法

初始化layoutIddata data不傳就默認是一個空的ArrayList佈局

public BaseQuickAdapter(@LayoutRes int layoutResId, @Nullable List<T> data) {
        this.mData = data == null ? new ArrayList<T>() : data;
        if (layoutResId != 0) {
            this.mLayoutResId = layoutResId;
        }
    }
複製代碼

onAttachedToRecyclerView

Adapter與RecyclerView關聯起來 這裏面主要是作表格佈局管理器的頭佈局和腳佈局自佔一行的適配fetch

@Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    int type = getItemViewType(position);
                    if (type == HEADER_VIEW && isHeaderViewAsFlow()) {
                        return 1;
                    }
                    if (type == FOOTER_VIEW && isFooterViewAsFlow()) {
                        return 1;
                    }
                    if (mSpanSizeLookup == null) {
                        return isFixedViewType(type) ? gridManager.getSpanCount() : 1;
                    } else {
                        return (isFixedViewType(type)) ? gridManager.getSpanCount() : mSpanSizeLookup.getSpanSize(gridManager,
                                position - getHeaderLayoutCount());
                    }
                }
            });
        }
    }
複製代碼

getItemCount

獲取Item數量動畫

public int getItemCount() {
        int count;
        if (getEmptyViewCount() == 1) {
            count = 1;
            if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
                count++;
            }
            if (mFootAndEmptyEnable && getFooterLayoutCount() != 0) {
                count++;
            }
        } else {
            count = getHeaderLayoutCount() + mData.size() + getFooterLayoutCount() + getLoadMoreViewCount();
        }
        return count;
    }
複製代碼
  • 是空佈局:
    • 根據是否有頭佈局和腳佈局來增長數量
  • 沒有空佈局:
    • 數量 = 頭佈局+數據條目數量 + 腳佈局+加載更多佈局

getItemViewType

獲取Item類型ui

@Override
    public int getItemViewType(int position) {
        if (getEmptyViewCount() == 1) {
            boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
            switch (position) {
                case 0:
                    if (header) {
                        return HEADER_VIEW;
                    } else {
                        return EMPTY_VIEW;
                    }
                case 1:
                    if (header) {
                        return EMPTY_VIEW;
                    } else {
                        return FOOTER_VIEW;
                    }
                case 2:
                    return FOOTER_VIEW;
                default:
                    return EMPTY_VIEW;
            }
        }
        int numHeaders = getHeaderLayoutCount();
        if (position < numHeaders) {
            return HEADER_VIEW;
        } else {
            int adjPosition = position - numHeaders;
            int adapterCount = mData.size();
            if (adjPosition < adapterCount) {
                return getDefItemViewType(adjPosition);
            } else {
                adjPosition = adjPosition - adapterCount;
                int numFooters = getFooterLayoutCount();
                if (adjPosition < numFooters) {
                    return FOOTER_VIEW;
                } else {
                    return LOADING_VIEW;
                }
            }
        }
    }
複製代碼
  1. 首先判斷是不是空佈局,若是是,則根據位置來判斷是空佈局、頭佈局、腳佈局的哪個
  2. 不是空佈局的狀況下
    1. 首先判斷是不是頭佈局,是就返回頭佈局
    2. 不然,拿到條目位置,而後和數據位置作比對,若是位置和數據大小一致,則判斷是不是加載佈局
    3. 若是位置是在數據列表中則調用getDefItemViewType()

getDefItemViewType()this

protected int getDefItemViewType(int position) {
        if (mMultiTypeDelegate != null) {
            return mMultiTypeDelegate.getDefItemViewType(mData, position);
        }
        return super.getItemViewType(position);
    }
複製代碼

中間的判斷是是否啓用多類型條目的,若是沒啓用,就直接返回默認的條目類型,就是0!!spa

多類型條目

如今來看一下多類型條目 MultiTypeDelegate.getDefItemViewType()code

public final int getDefItemViewType(List<T> data, int position) {
        T item = data.get(position);
        return item != null ? getItemType(item) : DEFAULT_VIEW_TYPE;
    }
複製代碼

能夠看到,作了判斷若是數據不爲空就會調用MultiTypeDelegate.getItemType() 而後咱們看到這個MultiTypeDelegate.getItemType()是一個抽象方法。

這裏咱們要說一下多類型條目的使用了 代碼以下:

public class MatchDataAdapter extends BaseQuickAdapter<MatchStatusBean.StatsBean, BaseViewHolder> {
    public MatchDataAdapter() {
        super(null);
        setMultiTypeDelegate(new MultiTypeDelegate<MatchStatusBean.StatsBean>() {
            @Override
            protected int getItemType(MatchStatusBean.StatsBean statsBean) {
                switch (Integer.parseInt(statsBean.type)) {
                    case 12:
//                        比分
                        return C.MATCH.ITEM_TYPE_MATCH_DATA_POINT;
                    case 14:
                        //球隊統計
                        return C.MATCH.ITEM_TYPE_MATCH_DATA_TEAM_COUNT;
                    case 13:
                        //全場最佳
                        return C.MATCH.ITEM_TYPE_MATCH_DATA_TEAM_BEST;
                    default:
                        break;
                }
                return 0;
            }
        });
        getMultiTypeDelegate()
		        .registerItemType(C.MATCH.ITEM_TYPE_MATCH_DATA_POINT, R.layout.item_match_data_point)
                .registerItemType(C.MATCH.ITEM_TYPE_MATCH_DATA_TEAM_COUNT, R.layout.item_match_data_count_list)
                .registerItemType(C.MATCH.ITEM_TYPE_MATCH_DATA_TEAM_BEST, R.layout.item_null);
    }

    @Override
    protected void convert(BaseViewHolder helper, MatchStatusBean.StatsBean item) {
        switch (helper.getItemViewType()) {
            //比分
            case C.MATCH.ITEM_TYPE_MATCH_DATA_POINT:
			具體邏輯操做。。。
                break;
            //球隊統計
            case C.MATCH.ITEM_TYPE_MATCH_DATA_TEAM_COUNT:
            具體邏輯操做。。。
            
                break;
            case C.MATCH.ITEM_TYPE_MATCH_DATA_TEAM_BEST:
           具體邏輯操做。。。

            default:
                break;

        }
    }
}
複製代碼

簡單說一下

  1. setMultiTypeDelegate()設置一個MultiTypeDelegate對象並實現它的getItemType()方法,根據本身數據具體狀況,來返回多類型條目的標識(這個也是上面咱們分析的那個抽象方法的具體實現了)追進去看,其實就是初始化了BaseQuickAdapter內的MultiTypeDelegate對象,也就是咱們剛剛getDefItemViewType()中所使用的那一個
public void setMultiTypeDelegate(MultiTypeDelegate<T> multiTypeDelegate) {
        mMultiTypeDelegate = multiTypeDelegate;
    }
複製代碼
  1. getMultiTypeDelegate().registerItemType()getMultiTypeDelegate顧名思義也就是獲取咱們剛剛初始化的那個MultiTypeDelegate對象,registerItemType()這裏先說一下,BRAVH提供了兩種模式來註冊多類型條目,而且兩種模式是不能同時使用的,這就是下面checkMode()的做用,防止你同時使用兩種方式來註冊多類型條目。 而後進入addItmType() : 能夠看到就是把多類型標識資源id都放進了layouts裏面,而這個layouts天然是在MultiTypeDelegate.getLayoutId()的時候被調用 ,這裏先留個概念,後面onCreateViewHolder的時候咱們會用到
public MultiTypeDelegate registerItemType(int type, @LayoutRes int layoutResId) {
        selfMode = true;
        checkMode(autoMode);
        addItemType(type, layoutResId);
        return this;
    }


 private void addItemType(int type, @LayoutRes int layoutResId) {
        if (this.layouts == null) {
            this.layouts = new SparseIntArray();
        }
        this.layouts.put(type, layoutResId);
    }
複製代碼
  1. 最後就是在helper.getItemViewType()作判斷來肯定具體條目的類型了,由於在上面重寫了MultiTypeDelegate.getItemType()因此根據咱們以前的分析MultiTypeDelegate!=null的狀況下,helper.getItemViewType()獲取到的就是咱們MultiTypeDelegate.getItemType()返回的值

onCreateViewHolder

加載ViewHolder的佈局

@Override
    public K onCreateViewHolder(ViewGroup parent, int viewType) {
        K baseViewHolder = null;
        this.mContext = parent.getContext();
        this.mLayoutInflater = LayoutInflater.from(mContext);
        switch (viewType) {
            case LOADING_VIEW:
                baseViewHolder = getLoadingView(parent);
                break;
            case HEADER_VIEW:
                baseViewHolder = createBaseViewHolder(mHeaderLayout);
                break;
            case EMPTY_VIEW:
                baseViewHolder = createBaseViewHolder(mEmptyLayout);
                break;
            case FOOTER_VIEW:
                baseViewHolder = createBaseViewHolder(mFooterLayout);
                break;
            default:
                baseViewHolder = onCreateDefViewHolder(parent, viewType);
                bindViewClickListener(baseViewHolder);
        }
        baseViewHolder.setAdapter(this);
        return baseViewHolder;

    }
複製代碼
  1. getLoadingView() : BRAVH給咱們內置提供了簡單的加載更多佈局,這個方法內部也是使用了createBaseViewHolder(),那麼咱們就來看看createBaseViewHolder()
  2. createBaseViewHolder : 這裏它主要是針對自定義ViewHolder所作的事情,咱們通常使用BaseViewHolder就足夠了,因此就只須要關注最後一行,其實他就是new BaseViewHolder(view)而已(關於BaseViewHolder,前面咱們已經大體分析了)
protected K createBaseViewHolder(View view) {
        Class temp = getClass();
        Class z = null;
        while (z == null && null != temp) {
            z = getInstancedGenericKClass(temp);
            temp = temp.getSuperclass();
        }
        K k = createGenericKInstance(z, view);
        return null != k ? k : (K) new BaseViewHolder(view);
    }
複製代碼
  1. onCreateDefViewHolder() : 當不是頭佈局、加載佈局、腳佈局、空佈局的狀況下(也就是正常的數據佈局),就會調用這個方法。進去一看,至關簡單,赫然看到了MultiTypeDelegate.getLayoutId(viewType),也就是咱們以前分析的根據條目類型獲取到具體的佈局資源,而後createBaseViewHolder()
protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
        int layoutId = mLayoutResId;
        if (mMultiTypeDelegate != null) {
            layoutId = mMultiTypeDelegate.getLayoutId(viewType);
        }
        return createBaseViewHolder(parent, layoutId);
    }
複製代碼
  1. bindViewClickListener() : 最後就是對BaseViewHolder的點擊事件的綁定而已,就不貼代碼了。

onBindViewHolder

將數據綁定到佈局上,以及一些邏輯的控制就寫這啦

@Override
    public void onBindViewHolder(K holder, int positions) {
        //Add up fetch logic, almost like load more, but simpler.
        autoUpFetch(positions);
        //Do not move position, need to change before LoadMoreView binding
        autoLoadMore(positions);
        int viewType = holder.getItemViewType();

        switch (viewType) {
            case 0:

                convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                break;
            case LOADING_VIEW:
                mLoadMoreView.convert(holder);
                break;
            case HEADER_VIEW:
                break;
            case EMPTY_VIEW:
                break;
            case FOOTER_VIEW:
                break;
            default:
                convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                break;
        }
    }

複製代碼
  1. autoUpFetch() : BRAVH提供給咱們下拉加載,判斷是否啓用,是否達到了咱們須要的加載位置,來調用咱們實現的加載邏輯
private void autoUpFetch(int positions) {
        if (!isUpFetchEnable() || isUpFetching()) {
            return;
        }
        if (positions <= mStartUpFetchPosition && mUpFetchListener != null) {
            mUpFetchListener.onUpFetch();
        }
    }
複製代碼
  1. autoLoadMore() : 上拉加載更多,根據咱們的位置和加載狀態來判斷是否調用咱們實現的加載更多的邏輯,就不細看了
  2. int viewType = holder.getItemViewType() : 上面咱們分析過getItemViewType(),默認就是返回0,convert() 就是咱們使用過程當中具體實現的邏輯了。 看一眼mLoadMoreView.convert(holder),發現裏面就是根據狀態來選擇佈局的顯示
public void convert(BaseViewHolder holder) {
        switch (mLoadMoreStatus) {
            case STATUS_LOADING:
                visibleLoading(holder, true);
                visibleLoadFail(holder, false);
                visibleLoadEnd(holder, false);
                break;
            case STATUS_FAIL:
                visibleLoading(holder, false);
                visibleLoadFail(holder, true);
                visibleLoadEnd(holder, false);
                break;
            case STATUS_END:
                visibleLoading(holder, false);
                visibleLoadFail(holder, false);
                visibleLoadEnd(holder, true);
                break;
            case STATUS_DEFAULT:
                visibleLoading(holder, false);
                visibleLoadFail(holder, false);
                visibleLoadEnd(holder, false);
                break;
        }
    }
複製代碼

onViewAttachedToWindow

當Item進入這個頁面的時候調用

Override
    public void onViewAttachedToWindow(K holder) {
        super.onViewAttachedToWindow(holder);
        int type = holder.getItemViewType();
        if (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW) {
            setFullSpan(holder);
        } else {
            addAnimation(holder);
        }
    }
複製代碼
  • setFullSpan(holder) : 當條目類型是空佈局、頭佈局、腳佈局、加載佈局的時候會觸發,進來之後發現了,原來就是針對StaggeredGridLayout作獨佔一行的適配
protected void setFullSpan(RecyclerView.ViewHolder holder) {
        if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
            StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder
                    .itemView.getLayoutParams();
            params.setFullSpan(true);
        }
    }

複製代碼
  • addAnimation() : 顧名思義,就是條目動畫的添加。進來看到,作了以下判斷:是否開啓動畫、是否初次進入才啓動動畫、而後判斷是否使用自定義動畫,默認動畫mSelectAnimation是一個淡入動畫。而後進來到真正啓用動畫的地方了
private void addAnimation(RecyclerView.ViewHolder holder) {
        if (mOpenAnimationEnable) {
            if (!mFirstOnlyEnable || holder.getLayoutPosition() > mLastPosition) {
                BaseAnimation animation = null;
                if (mCustomAnimation != null) {
                    animation = mCustomAnimation;
                } else {
                    animation = mSelectAnimation;
                }
                for (Animator anim : animation.getAnimators(holder.itemView)) {
                    startAnim(anim, holder.getLayoutPosition());
                }
                mLastPosition = holder.getLayoutPosition();
            }
        }
    }

複製代碼

在說動畫啓動以前,咱們須要先知道BaseAnimation這個接口,只有一個Animator[] getAnimators(View view)方法,而後咱們能夠借鑑一下BRAVH內置的AlphaInAnimation,發現只是new了一個動畫數組。

public class AlphaInAnimation implements BaseAnimation {

    private static final float DEFAULT_ALPHA_FROM = 0f;
    private final float mFrom;

    public AlphaInAnimation() {
        this(DEFAULT_ALPHA_FROM);
    }

    public AlphaInAnimation(float from) {
        mFrom = from;
    }

    @Override
    public Animator[] getAnimators(View view) {
        return new Animator[]{ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)};
    }
}
複製代碼

回過頭來看,也就是遍歷啓動這個動畫集合裏面的每個動畫,也就是咱們能夠給咱們的列表組合各類各樣的入場動畫。

for (Animator anim : animation.getAnimators(holder.itemView)) {
                    startAnim(anim, holder.getLayoutPosition());
                }

  protected void startAnim(Animator anim, int index) {
        anim.setDuration(mDuration).start();
        anim.setInterpolator(mInterpolator);
    }

複製代碼

onViewDetachedFromWindow

當Item離開這個頁面的時候調用

基本流程結束

來到這裏證實咱們的基本流程已經講解完了 相信帶着這套流程的理解,在具體功能展開窺探源碼也會更加如魚得水

相關文章
相關標籤/搜索