美文网首页
LeanBack框架介绍

LeanBack框架介绍

作者: Young_Allen | 来源:发表于2021-05-26 14:31 被阅读0次

    Leanback库是Google开源的一个高效开发的支持库,它包含了一套完整的电视应用开发Api资源和组件。

    Leanback库是基于Model --> Presenter --> View 的样式设计的(MVP)

    Google官方DEMO: https://github.com/android/tv-samples

    标准页面

    以VerticalGridFragment页面为例:

    页面拆解:

    image
    VerticalGridFragment -->(
      VerticalGridPresenter -->(
        VerticalGridView -->(
          ItemBridgeAdapter{
            CursorObjectAdapter(
              CardPresenter --> ImageCardView,
              Object
            )
          }
        )
      )
    )
    

    界面背后的迷宫

    阶段一

    setAdapter流程:

    阶段二

    View Bind流程:

    //ItemBridgeAdapter.java
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
        Presenter presenter = mPresenters.get(viewType);
        Presenter.ViewHolder presenterVh;
        View view;
        if (mWrapper != null) {
            view = mWrapper.createWrapper(parent);
            presenterVh = presenter.onCreateViewHolder(parent);
            mWrapper.wrap(view, presenterVh.view);
        } else {
            presenterVh = presenter.onCreateViewHolder(parent);
            view = presenterVh.view;
        }
        ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
        onCreate(viewHolder);
        if (mAdapterListener != null) {
            mAdapterListener.onCreate(viewHolder);
        }
        View presenterView = viewHolder.mHolder.view;
        if (presenterView != null) {
            viewHolder.mFocusChangeListener.mChainedListener =
                    presenterView.getOnFocusChangeListener();
            presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
        }
        if (mFocusHighlight != null) {
            mFocusHighlight.onInitializeView(view);
        }
        return viewHolder;
    }
    
    //ItemBridgeAdapter.java
    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
        ViewHolder viewHolder = (ViewHolder) holder;
        viewHolder.mItem = mAdapter.get(position);
    
        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
    
        onBind(viewHolder);
        if (mAdapterListener != null) {
            mAdapterListener.onBind(viewHolder);
        }
    }
    

    在tryGetViewHolderForPositionByDeadline时根据position获取对应的Presenter的type,在调用CreatViewHolder时通过type创建对应的ViewHolder,可以理解为getItemViewType实现了position到ViewHolder的映射

    //ItemBridgeAdapter.java
    @Override
    public int getItemViewType(int position) {
        PresenterSelector presenterSelector = mPresenterSelector != null
                ? mPresenterSelector : mAdapter.getPresenterSelector();
        Object item = mAdapter.get(position);
        Presenter presenter = presenterSelector.getPresenter(item);
        int type = mPresenters.indexOf(presenter);
        if (type < 0) {
            mPresenters.add(presenter);
            type = mPresenters.indexOf(presenter);
            if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
            onAddPresenter(presenter, type);
            if (mAdapterListener != null) {
                mAdapterListener.onAddPresenter(presenter, type);
            }
        }
        return type;
    }
    

    在VerticalGridFragment页面有如下定义:

    //VerticalGridFragment.java
    private final CursorObjectAdapter mVideoCursorAdapter =
            new CursorObjectAdapter(new CardPresenter());
    
        //CardPresenter.java
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent) {
            ...
            ImageCardView cardView = new ImageCardView(parent.getContext()) {
                @Override
                public void setSelected(boolean selected) {
                    updateCardBackgroundColor(this, selected);
                    super.setSelected(selected);
                }
            };
    
            cardView.setFocusable(true);
            cardView.setFocusableInTouchMode(true);
            updateCardBackgroundColor(cardView, false);
            return new ViewHolder(cardView);
        }
    
        //CardPresenter.java
        @Override
        public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
            Video video = (Video) item;
    
            ImageCardView cardView = (ImageCardView) viewHolder.view;
            cardView.setTitleText(video.title);
            cardView.setContentText(video.studio);
    
            if (video.cardImageUrl != null) {
                // Set card size from dimension resources.
                Resources res = cardView.getResources();
                int width = res.getDimensionPixelSize(R.dimen.card_width);
                int height = res.getDimensionPixelSize(R.dimen.card_height);
                cardView.setMainImageDimensions(width, height);
    
                Glide.with(cardView.getContext())
                        .load(video.cardImageUrl)
                        .apply(RequestOptions.errorOf(mDefaultCardImage))
                        .into(cardView.getMainImageView());
            }
        }
    

    阶段三

    View Recycle流程:

     //CardPresenter.java
        @Override
        public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
            ImageCardView cardView = (ImageCardView) viewHolder.view;
            // Remove references to images so that the garbage collector can free up memory.
            cardView.setBadgeImage(null);
            cardView.setMainImage(null);
        }
    

    在View Recycling策略下,tryGetViewHolderForPositionByDeadline先通过postion查找对应的type,然后确定mAttachScrapCache、mChangeScrapCache、mCacheItem(和postion相关)、mRecyclerPool(和type相关)中是否有可复用的ViewHolder,减少重复创建带来的性能影响。

    以上分析可知:要了解当前页面View的Bind、Recycler整体流程,需要重点分析getItemViewType是如何实现position到ViewHolder映射的

    如何写一个基本页面:

    • Fragment
    • Presenter
    • PresenterSelecter
    • ItemBridgeAdapter

    复杂页面

    以复杂的十字交错页面为例:

    拆解如下:

    ItemBridgeAdapter {
      PresenterSelector{
        Presenter
      }
      ObjectAdapter{
        Object
      }
    }
    
    Presenter -- ViewHolder -- ObjectAdapter
    
    HeadersSupportFragment -->(
      VerticalGridView -->(
        ItemBridgeAdapter{
           IconHeaderItemPresenter --> HeaderView,
           ArrayObjectAdapter(
            ListRow(HeaderItem + CursorObjectAdapter)
           )
        }
      )
    )
    
    RowsSupportFragment --> ( 
      VerticalGridView --> (
        ItemBridgeAdapter {
          ListRowPresenter --> ListRowView,
          ListRowDataAdapter(
            ArrayObjectAdapter (
              ListRow (HeaderItem + CursorObjectAdapter(CardPresenter))
           )
         ) 
       }
     )
    )
    
    ListRowView -->(
      HorizontalGridView -->(
        ItemBridgeAdapter {
           CardPresenter --> ImageCardView,
           CursorObjectAdapter
        }
      )
    )
    

    阶段一

    左侧导航栏

    右侧内容页

    阶段二、三

    左侧导航栏

    根据标准页面的分析结论,同理可以分析getItemViewType中position到ViewHolder的映射关系:

    //ItemBridgeAdapter.java
    @Override
    public int getItemViewType(int position) {
        PresenterSelector presenterSelector = mPresenterSelector != null
                ? mPresenterSelector : mAdapter.getPresenterSelector();
        Object item = mAdapter.get(position);
        Presenter presenter = presenterSelector.getPresenter(item);
        int type = mPresenters.indexOf(presenter);
        if (type < 0) {
            mPresenters.add(presenter);
            type = mPresenters.indexOf(presenter);
            if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
            onAddPresenter(presenter, type);
            if (mAdapterListener != null) {
                mAdapterListener.onAddPresenter(presenter, type);
            }
        }
        return type;
    }
    

    而HeadersSupportFragment的PresenterSelector声明如下

    setHeaderPresenterSelector(new PresenterSelector() {
        @Override
        public Presenter getPresenter(Object o) {
            return new IconHeaderItemPresenter();
        }
    });
    

    因此HeadersSupportFragment页面的Bind、Recycler的流程就在IconHeaderItemPresenter内实现:

    //IconHeaderItemPresenter.java
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
        mUnselectedAlpha = viewGroup.getResources()
                .getFraction(R.fraction.lb_browse_header_unselect_alpha, 1, 1);
        LayoutInflater inflater = (LayoutInflater) viewGroup.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    
        View view = inflater.inflate(R.layout.icon_header_item, null);
        view.setAlpha(mUnselectedAlpha); // Initialize icons to be at half-opacity.
    
        return new ViewHolder(view);
    }
    
    @Override
    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
        HeaderItem headerItem = ((ListRow) item).getHeaderItem();
        View rootView = viewHolder.view;
        rootView.setFocusable(true);
    
        ImageView iconView = (ImageView) rootView.findViewById(R.id.header_icon);
        Drawable icon = rootView.getResources().getDrawable(R.drawable.android_header, null);
        iconView.setImageDrawable(icon);
    
        TextView label = (TextView) rootView.findViewById(R.id.header_label);
        label.setText(headerItem.getName());
    }
    
    @Override
    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
        // no op
    }
    

    右侧内容页

    同理,分析getItemViewType的position到ViewHolder的映射关系

    //MainFragment.java
    mCategoryRowAdapter = new ArrayObjectAdapter(new ListRowPresenter());
    setAdapter(mCategoryRowAdapter);
    

    因此右侧内容栏页面的Bind、Recycler的流程在ListRowPresenter内实现

    //ListRowPresenter.java
    @Override
    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
        initStatics(parent.getContext());
        ListRowView rowView = new ListRowView(parent.getContext());
        setupFadingEffect(rowView);
        if (mRowHeight != 0) {
            rowView.getGridView().setRowHeight(mRowHeight);
        }
        return new ViewHolder(rowView, rowView.getGridView(), this);
    }
    
    @Override
    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
        super.onBindRowViewHolder(holder, item);
        ViewHolder vh = (ViewHolder) holder;
        ListRow rowItem = (ListRow) item;
        vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter());
        vh.mGridView.setAdapter(vh.mItemBridgeAdapter);
        vh.mGridView.setContentDescription(rowItem.getContentDescription());
    }
    
    @Override
    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
        ViewHolder vh = (ViewHolder) holder;
        vh.mGridView.setAdapter(null);
        vh.mItemBridgeAdapter.clear();
        super.onUnbindRowViewHolder(holder);
    }
    

    createRowViewHolder创建了一个ListRowView。HorizontalGridView和VerticalGridView实现方式一致,仅在LayoutManager上有区别

    public final class ListRowView extends LinearLayout {
    
        private HorizontalGridView mGridView;
        ...
    }
    

    onBindRowViewHolder设置mGridView的Adapter, onUnbindRowViewHolder时释放。

    再看页面源数据部分:

    //MainFragment.java
    CursorObjectAdapter videoCursorAdapter =
            new CursorObjectAdapter(new CardPresenter());
    videoCursorAdapter.setMapper(new VideoCursorMapper());
    mVideoCursorAdapters.put(videoLoaderId, videoCursorAdapter);
    
    ListRow row = new ListRow(header, videoCursorAdapter);
    mCategoryRowAdapter.add(row);
    

    rowItem.getAdapter()返回的是videoCursorAdapter。

    此后的流程和标准页面分析的流程一样。因为在onBindRowViewHolder阶段,设置ListRowView中HorizontalGridView的Adapter为CursorObjectAdapter ,对应的Presenter为CardPresenter,因此getItemViewType中position到ViewHolder的映射时,HorizontalGridView的每个子View的Bind、Recycler的流程在CardPresenter内实现

    public class CardPresenter extends Presenter {
        ...
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent) {
            mDefaultBackgroundColor =
                ContextCompat.getColor(parent.getContext(), R.color.default_background);
            mSelectedBackgroundColor =
                    ContextCompat.getColor(parent.getContext(), R.color.selected_background);
            mDefaultCardImage = parent.getResources().getDrawable(R.drawable.movie, null);
    
            ImageCardView cardView = new ImageCardView(parent.getContext()) {
                @Override
                public void setSelected(boolean selected) {
                    updateCardBackgroundColor(this, selected);
                    super.setSelected(selected);
                }
            };
    
            cardView.setFocusable(true);
            cardView.setFocusableInTouchMode(true);
            updateCardBackgroundColor(cardView, false);
            return new ViewHolder(cardView);
        }
    
    @Override
    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
        Video video = (Video) item;
    
        ImageCardView cardView = (ImageCardView) viewHolder.view;
        cardView.setTitleText(video.title);
        cardView.setContentText(video.studio);
    
        if (video.cardImageUrl != null) {
            // Set card size from dimension resources.
            Resources res = cardView.getResources();
            int width = res.getDimensionPixelSize(R.dimen.card_width);
            int height = res.getDimensionPixelSize(R.dimen.card_height);
            cardView.setMainImageDimensions(width, height);
    
            Glide.with(cardView.getContext())
                    .load(video.cardImageUrl)
                    .apply(RequestOptions.errorOf(mDefaultCardImage))
                    .into(cardView.getMainImageView());
        }
    }
    
    @Override
    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
        ImageCardView cardView = (ImageCardView) viewHolder.view;
    
        // Remove references to images so that the garbage collector can free up memory.
        cardView.setBadgeImage(null);
        cardView.setMainImage(null);
    }
    

    以上分析可知:一个Row布局的Bind、Recycler流程,是先对RowPresnter的处理,然后对子Presnter的处理。其中都是通过getItemViewType实现position到ViewHolder映射的。

    如何写一个包含Row结构页面:

    • Fragment
    • Presenter
    • PresenterSelecter
    • ItemBridgeAdapter
    • RowPresenter
    • ArrayObjectAdapter

    相关文章

      网友评论

          本文标题:LeanBack框架介绍

          本文链接:https://www.haomeiwen.com/subject/yqprcltx.html