Android Leanback结构源码简析

Leanback页面构建主要类

  • BaseGridView 继承RecyclerView,重写所有焦点逻辑,Leanback页面根布局容器

  • HorizontalGridView 继承BaseGridView,提供水平布局能力

  • VerticalGridView 继承BaseGridView,提供垂直布局能力

  • ArrayObjectAdapter 数据适配器,继承ObjectAdapter,内部可包含数据和视图结构内容信息
两个构造方法:

    // 一般用于每行或列数据的创建
    /**
     * Constructs an adapter with the given {@link PresenterSelector}.
     */
    public ArrayObjectAdapter(PresenterSelector presenterSelector) {
        super(presenterSelector);
    }

    // 一般为ItemBridgeAdapter创建构造参数时使用
    /**
     * Constructs an adapter that uses the given {@link Presenter} for all items.
     */
    public ArrayObjectAdapter(Presenter presenter) {
        super(presenter);
    }

  • ListRow行视图提供者,Android原生封装好了,支持子视图焦点动效及行标题展示

  • Presenter 提供视图创建及数据绑定,类似RecyclerView.Adapter的功能,注意是类似,下面的ItemBridgeAdapter才是填充到BaseGridView中真正的Adapter。目前暂且称之为视图加载器
    /**
     * Creates a new {@link View}.
     */
    public abstract ViewHolder onCreateViewHolder(ViewGroup parent);

    /**
     * Binds a {@link View} to an item.
     */
    public abstract void onBindViewHolder(ViewHolder viewHolder, Object item);

  • PresenterSelector 根据不同的Item Object类型提供不同的Presenter对象,进行不同的布局视图创建和数据绑定,目前暂且称之为视图构造筛选器
    /**
     * Returns a presenter for the given item.
     */
    public abstract Presenter getPresenter(Object item);
    // 这个方法需要重写,根据item返回对应的presenter

    /**
     * Returns an array of all possible presenters.  The returned array should
     * not be modified.
     */
    public Presenter[] getPresenters() {
        return null;
    }
    // 这个方法需要返回所有的presenters数组,中途不可改变数据

  • ItemBridgeAdapter 填充至BaseGridView的适配器,继承RecyclerView.Adapter
主要有两个构造方法,需要传递一个ObjectAdapter

    public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
        setAdapter(adapter);
        mPresenterSelector = presenterSelector;
    }

    public ItemBridgeAdapter(ObjectAdapter adapter) {
        this(adapter, null);
    }

基本使用(以VerticalGridView垂直视图为例)

  1. 自定义Presenter,每一行的视图提供者
public class PresenterSample extends Presenter {
    // 创建BaseGridView中每一个Item的视图,如果使用ListRow则是创建每一行中的每一个Item视图
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent) {
        return new HolderSample(...ItemView...);
    }

    // 数据绑定,item即是外层ArrayObjectAdapter中包含的数据类型
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
        if (viewHolder instanceof HolderSample) {
            HolderSample holder = (HolderSample) viewHolder;
            if (item instanceof YourDataType) {
                bindDataWithViews()...
            }
        }
    }

    @Override
    public void onUnbindViewHolder(ViewHolder viewHolder) {
        releaseSource()...
    }

    // 自定义Holder,处理视图点击,焦点事件等
    static class HolderSample extends ViewHolder {
        HolderSample(View view) {
            super(view);
        }
    }
}

  1. 构造每一行的数据
  • ListRow方式构造
    // 构造一个ArrayObjectAdapter,填充一个Presenter
    ArrayObjectAdapter rowAdapter = new ArrayObjectAdapter(new PresenterSample());
    // 填充数据
    rowAdapter.add(...Data...);
    // 构造一个ListRow
    ListRow listRow = new ListRow(rowAdapter);
    
  • 普通方式构造
    // 构造一个指定数据类型对象
    CustomData data = new CustomData();
    
  1. 构造一个PresenterSelector
public class PresenterSelectorSample extends PresenterSelector {

    // 此处item就是外层ArrayObjectAdapter添加进来的数据,按添加索引排序
    @Override
    public Presenter getPresenter(Object item) {
        if (item.getClass == ListRow.class) {
            return new ListRowPresenter();
        } else if (item.getClass == CustomData.class) {
            return new PresenterCustom();
        } else {
            return ...
        }
    }

    @Override
    public Presenter[] getPresenters() {
        return mPresenters.toArray(new Presenter[]{});
    }

}
  1. 构造一个ArrayObjectAdapter,装载垂直视图每一行的数据
    // 构建一个自定义的PresenterSelectorSample
    PresenterSelectorSample presenterSelector = new PresenterSelectorSample();
    // 构建一个装载每行数据的ArrayObjectAdapter
    ArrayObjectAdapter verticalAdapter = new ArrayObjectAdapter(presenterSelector);
    // 填充数据
    verticalAdapter.add(listRow);
    verticalAdapter.add(CustomData);
  1. 构造一个ItemBridgeAdapter,填充给VerticalGridView
// 该Adapter只是作为一个桥梁作用,将每一行结构对应的presenter和data进行关联
ItemBridgeAdapter bridgeAdapter = new ItemBridgeAdapter(verticalAdapter);
VerticalGridView.setAdapter(bridgeAdapter);

至此,页面展示效果如下:


leanback1.png

源码分析

首先分析下非使用ListRow场景下视图的创建及数据绑定流程

  • 首先看下ArrayObjectAdapter,它内部有个ArrayList保存数据,同时其父类ObjectAdapter中有个mPresenterSelector变量保存当前adapter对应的presenterSelector
    
        public class ArrayObjectAdapter extends ObjectAdapter {
            ...
            // 当ArrayObjectAdapter作为行/列的数据提供者时(ListRow),缓存每行/列的每个子Item的数据
            // 当ArrayObjectAdapter作为ItemBridgeAdapter的构造参数时,缓存每行/列的数据对象
            private final List mItems = new ArrayList();
    
            public ArrayObjectAdapter(PresenterSelector presenterSelector) {
                super(presenterSelector);
            }
    
            public ArrayObjectAdapter(Presenter presenter) {
                super(presenter);
            }
    
            // 包含数据添加,删除,修改,查询方法
            public void add(Object item) {
                add(mItems.size(), item);
            }
            remove(...)
            move(...)
            replace(...)
            get(...)
            ...
        }    
    
    
        // 当ArrayObjectAdapter作为行/列的数据提供者时,缓存每行/列的视图数据提供者
        private PresenterSelector mPresenterSelector;// 缓存presenter
    
        // 提供get方法获取当前的presenter
        public final Presenter getPresenter(Object item) {
            if (mPresenterSelector == null) {
                throw new IllegalStateException("Presenter selector must not be null");
            }
            return mPresenterSelector.getPresenter(item);
        }
     
     
    • 主适配器ItemBridgeAdapter,看android命名应该是一个桥接的适配器,这也是整个模块中核心类之一
    
    
        // 缓存了构造传进来的ArrayObjectAdapter
        private ObjectAdapter mAdapter;
        // 缓存了PresenterSelector选择器,根据不同ViewType获取不同的Presenter进行不同的视图加载
        private PresenterSelector mPresenterSelector;
        // 焦点动效辅助类
        FocusHighlightHandler mFocusHighlight;
        // 缓存了根据PresenterSelector创建出来的各个不同的Presenter视图加载器
        private ArrayList mPresenters = new ArrayList();
    
    • 接着就按照正常使用RecyclerView的流程去分析ItemBridgeAdapter,首先是getItemViewType()方法。
    
        @Override
        public int getItemViewType(int position) {
            // mPresenterSelector可以直接调用setter主动赋值,如果没有赋值过,则会通过构造方法中的ArrayObjectAdapter.getPresenterSelector进行获取视图构造筛选器
            PresenterSelector presenterSelector = mPresenterSelector != null
                    ? mPresenterSelector : mAdapter.getPresenterSelector();
            // 这个Object就是构造传进来的ArrayObjectAdapter中的数据
            Object item = mAdapter.get(position);
            // 根据Object对象获取对应的presenter,这里是在自定义的PresenterSelector中进行分支判断处理
            Presenter presenter = presenterSelector.getPresenter(item);
            // 根据索引判断缓存中该Object是否有presenter对象
            int type = mPresenters.indexOf(presenter);
            if (type < 0) {
                // 不存在,将presenter加入缓存
                mPresenters.add(presenter);
                // 将索引值赋值给viewType
                type = mPresenters.indexOf(presenter);
                if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
                // 回调通知外部当前添加了presenter
                onAddPresenter(presenter, type);
                if (mAdapterListener != null) {
                    mAdapterListener.onAddPresenter(presenter, type);
                }
            }
            return type;
        }
    
    • 在这里我们先暂停,去看下PresenterSelector中创建Presenter对象部分
    
        // PresenterSelector是一个抽象类,需要我们自己实现以下两个方法
        public abstract class PresenterSelector {
    
            public abstract Presenter getPresenter(Object item);
    
    
            public Presenter[] getPresenters() {
                return null;
            }
        }
    
    // 这里以我们自己定义的一个Sample为例
    
    public class PresenterSelectorSample extends PresenterSelector {
        private List mPresenters = new ArrayList<>();
        private Map, Presenter> mPresenterCache = new HashMap<>();
        
        public void addPresenter(Class cls, Presenter presenter) {
            mPresenterCache.put(cls, presenter);
            if (!mPresenters.contains(presenter)) {
                mPresenters.add(presenter);
            }
        }
    
        @Override
        public Presenter getPresenter(Object item) {
            // 我们会调用addPresenter方法进行setter操作,此处通过map进行缓存
            // 注意:实际中还要进行class的重复冲突处理,例如有多个ListRow,但是每个ListRow中的Presenter视图展示效果不一样
            Class cls = item.getClass();
            // 然后通过class进行getter取操作
            Presenter presenter = mPresenterCache.get(cls);
            if (presenter != null) {
                return presenter;
            } else {
                return null;
            }
        }
    
        @Override
        public Presenter[] getPresenters() {
            // 返回所有的Presenters
            return mPresenters.toArray(new Presenter[]{});
        }
    }
    
    // sample code
    PresenterSelectorSample presenterSelector = new PresenterSelectorSample();
    presenterSelector.addPresenter(ListRow.class, new ListRowPresenter());
    presenterSelector.addPresenter(CustomDataObject.class, new CustomPresenter());
    ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenterSelector);
    adapter.add(new ListRow);
    adapter.add(new CustomDataObject());
    ItemBridgeAdapter bridgeAdapter = new ItemBridgeAdapter(adapter);
    
    // 1.这样ItemBridgeAdapter.getItemViewType中mAdapter.getPresenterSelector()取出来的就是我们构建的PresenterSelectorSample。
    // 2.然后mAdapter.get(position)就是我们上面adapter添加进去的数据。例如position=0时,取出来的就是一个ListRow对象。
    // 3.接着调用我们PresenterSelectorSample中的getPresenter(item)方法,会根据ListRow.class返回一个ListRowPresenter。同时缓存到ItemBridgeAdapter的mPresenters变量中。并且将ViewType用presenter在缓存池中的索引与之对应起来,方便后面onCreateViewHolder中的获取。
    
    • 此时,我们就可以理解了Presenter,PresenterSelector,ArrayObjectAdapter,ItemBridgeAdapter之间的关系。

    • 回到ItemBridgeAdapter,分析其onCreateViewHolder方法

    
        /**
         * {@link View.OnFocusChangeListener} that assigned in
         * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
         * {@link View.OnFocusChangeListener} after that.
         */
        @Override
        public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            // 首先取出presenter,前面也说了viewType已经通过缓存池中的索引关联了presenter
            Presenter presenter = mPresenters.get(viewType);
            // 我们熟悉的ViewHolder,注意,这个是我们presenter中自定义的viewHolder
            Presenter.ViewHolder presenterVh;
            // viewHolder中的view
            View view;
            if (mWrapper != null) {
                view = mWrapper.createWrapper(parent);
                presenterVh = presenter.onCreateViewHolder(parent);
                mWrapper.wrap(view, presenterVh.view);
            } else {// 一般走这里
                // 这里会去调用我们自定义Presenter中的onCreateViewHolder进行holder和view的创建
                presenterVh = presenter.onCreateViewHolder(parent);
                // 将试图赋值给viewHolder中的view
                view = presenterVh.view;
            }
            // 将我们presenter的viewHolder,presenter,view一起打包进行封装
            ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
            // 回调通知
            onCreate(viewHolder);
            if (mAdapterListener != null) {
                mAdapterListener.onCreate(viewHolder);
            }
            // 这个view就是我们presenter中的创建的视图
            View presenterView = viewHolder.mHolder.view;
            if (presenterView != null) {
                // 为我们presenter中的view设置focus监听,焦点变化时如果设置了FocusHighlight则会自动执行动效
                viewHolder.mFocusChangeListener.mChainedListener =
                        presenterView.getOnFocusChangeListener();
                presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
            }
            if (mFocusHighlight != null) {
                // 如果设置了FocusHighlight,在此焦点动效初始化
                mFocusHighlight.onInitializeView(view);
            }
            // 返回创建好的viewHolder(里面包含presenter,holder,view信息)
            return viewHolder;
        }
    
    • 接下去就是ItemBridgeAdapteronCreateViewHolder方法
    
        @Override
        public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
            // 这个holder包含了presenter,该presenter中对应的holder,view
            ViewHolder viewHolder = (ViewHolder) holder;
            // mItem是一个Object对象,也就是上面getItemViewType所说的ArrayObjectAdapter中的数据,例如sample中的CustomDataObject和ListRow
            viewHolder.mItem = mAdapter.get(position);
            // 调用对应presenter中的onBindViewHolder方法
            viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
            // 回调通知
            onBind(viewHolder);
            if (mAdapterListener != null) {
                mAdapterListener.onBind(viewHolder);
            }
        }
    
    • 抛开RecyclerView视图部分原理,此时视图创建和数据绑定都已经完成了,界面上已经可以展示了。

    接下来分析下Leanback中常用的ListRow的源码

    • ListRow继承Rowandroid封装好的行数据展示的一种抽象(并不是实际View的展示,leanback系统中view的创建都是在presenter层,对应ListRowPresenter),其结构如下:
    
    public class ListRow extends Row {
        // 这个adapter包含了该行中所有子Item的数据
        private final ObjectAdapter mAdapter;
        // 行标题文字,对应父类Row中的HeaderItem
        private CharSequence mContentDescription;
        ...
    }
    
    
    public class Row {
        // 行标题的结构体
        private HeaderItem mHeaderItem;
        ...
    }
    
    
    // 也是一种抽象,对应视图也是在RowHeaderPresenter中创建
    public class HeaderItem {
        private final long mId;
        private final String mName;
        private CharSequence mDescription;
        private CharSequence mContentDescription;
        ...
    }
    
    • 接下来看下视图部分ListRowPresenter,其继承RowPresenter,而RowPresenter继承PresenterPresenter在上面已经介绍过了,主要有这几个抽象方法:onCreateViewHolderonBindViewHolderonUnbindViewHolder

    • 首先看下ListRowPresenter内部的几个内部类:

    
    public abstract class RowPresenter extends Presenter {
        ...
        // RowPresenter这里不作重点分析,主要是对ViewHolder的抽象封装
        // 1.ContainerViewHolder,它内部持有一个ViewHolder
        // 2.ViewHolder
    }
    
    
    public class ListRowPresenter extends RowPresenter {
    
        // 行视图的ViewHolder
        public static class ViewHolder extends RowPresenter.ViewHolder {
            // 持有presenter
            final ListRowPresenter mListRowPresenter;
            // 这个其实就是展示水平列表视图的HorizontalGridView
            final HorizontalGridView mGridView;
            // 可以类比上面垂直视图案例中的ItemBridgeAdapter,作为桥梁关联mGridView中每个item视图创建者presenter
            ItemBridgeAdapter mItemBridgeAdapter;
            // 暂不分析
            final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
            // layout参数
            final int mPaddingTop;
            ...
    
            public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) {
                super(rootView);
                mGridView = gridView;
                mListRowPresenter = p;
                mPaddingTop = mGridView.getPaddingTop();
                ...
            }
            // 下面就是一些getter属性方法
            ...
        }
    
        // 选中的Position变化回调监听
        /**
         * A task on the ListRowPresenter.ViewHolder that can select an item by position in the
         * HorizontalGridView and perform an optional item task on it.
         */
        public static class SelectItemViewHolderTask extends Presenter.ViewHolderTask {
    
            private int mItemPosition;// 选中的position  
            private boolean mSmoothScroll = true;
            Presenter.ViewHolderTask mItemTask;// 缓存task
    
            /**
             * Sets task to run when the item is selected, null for no task.
             * @param itemTask Optional task to run when the item is selected, null for no task.
             */
            public void setItemTask(Presenter.ViewHolderTask itemTask) {
                // 设置task,等待时机执行run方法,如果没设置,run方法无效。本质上只是给外部提供一个监听选中position变化的回调
                mItemTask = itemTask;
            }
    
            // 主要是提供一些设置选中position,是否平滑滚动之类的方法
            ...
    
            // 关键是run方法
            @Override
            public void run(Presenter.ViewHolder holder) {
                if (holder instanceof ListRowPresenter.ViewHolder) {
                    // 获取到列表视图HorizontalGridView
                    HorizontalGridView gridView = ((ListRowPresenter.ViewHolder) holder).getGridView();
                    androidx.leanback.widget.ViewHolderTask task = null;
                    // 如果之前设置过task,则新创建
                    if (mItemTask != null) {
                        task = new androidx.leanback.widget.ViewHolderTask() {
                            final Presenter.ViewHolderTask itemTask = mItemTask;
                            @Override
                            public void run(RecyclerView.ViewHolder rvh) {
                                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) rvh;
                                itemTask.run(ibvh.getViewHolder());
                            }
                        };
                    }
                    // 设置选中的position,并将task和postition传递给BaseGridView,里面会执行task的run方法,再执行我们外部setter进来的Presenter.ViewHolderTask的run方法
                    if (isSmoothScroll()) {
                        gridView.setSelectedPositionSmooth(mItemPosition, task);
                    } else {
                        gridView.setSelectedPosition(mItemPosition, task);
                    }
                }
            }
        }
    
        // 对交互事件的处理
        class ListRowPresenterItemBridgeAdapter extends ItemBridgeAdapter {
            ListRowPresenter.ViewHolder mRowViewHolder;
    
            ListRowPresenterItemBridgeAdapter(ListRowPresenter.ViewHolder rowViewHolder) {
                mRowViewHolder = rowViewHolder;
            }
    
            @Override
            protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
                if (viewHolder.itemView instanceof ViewGroup) {
                    TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, true);
                }
                if (mShadowOverlayHelper != null) {
                    mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
                }
            }
    
            @Override
            public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
                // 监听点击事件
                // Only when having an OnItemClickListener, we will attach the OnClickListener.
                if (mRowViewHolder.getOnItemViewClickedListener() != null) {
                    viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
                                    mRowViewHolder.mGridView.getChildViewHolder(viewHolder.itemView);
                            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
                                mRowViewHolder.getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder,
                                        ibh.mItem, mRowViewHolder, (ListRow) mRowViewHolder.mRow);
                            }
                        }
                    });
                }
            }
    
            @Override
            public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
                // 移除点击事件
                if (mRowViewHolder.getOnItemViewClickedListener() != null) {
                    viewHolder.mHolder.view.setOnClickListener(null);
                }
            }
    
            @Override
            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
                applySelectLevelToChild(mRowViewHolder, viewHolder.itemView);
                // 设置是否持久化状态显示
                mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
            }
    
            @Override
            public void onAddPresenter(Presenter presenter, int type) {
                // 默认缓存池大小为24个,针对每种不同presenter可以设置不同的缓存空间
                mRowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews(
                        type, getRecycledPoolSize(presenter));
            }
        }
    
    }
    
    • 接下来就是真正的ListRowPresenter
    
    public class ListRowPresenter extends RowPresenter {
        // 行数,这个行数不是ListRow的行数,ListRow抽象上的单行,这个行数是指其内部HorizontalGridView的行数
        private int mNumRows = 1;
        // 行高度
        private int mRowHeight;
        // 展开行高度
        private int mExpandedRowHeight;
        private PresenterSelector mHoverCardPresenterSelector;
        // 缩放比例:FocusHighlight中定义的常量
        private int mFocusZoomFactor;
        // 是否使用聚焦高亮那种效果
        private boolean mUseFocusDimmer;
        // 是否支持阴影
        private boolean mShadowEnabled = true;
        private int mBrowseRowsFadingEdgeLength = -1;
        private boolean mRoundedCornersEnabled = true;
        private boolean mKeepChildForeground = true;
        // 缓存池
        private HashMap mRecycledPoolSize = new HashMap();
        ShadowOverlayHelper mShadowOverlayHelper;
        // 主要功能就是通过一层FrameLayout(ShadowOverlayContainer)包裹当前View实现阴影,高亮昏暗,圆角效果,api21以上才能使用
        private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper;
    
        private static int sSelectedRowTopPadding;
        private static int sExpandedSelectedRowTopPadding;
        private static int sExpandedRowNoHovercardBottomPadding;
    
        // 3个构造方法,主要是设置聚焦缩放等级和阴影效果
        public ListRowPresenter() {
            this(FocusHighlight.ZOOM_FACTOR_MEDIUM);
        }
    
        public ListRowPresenter(int focusZoomFactor) {
            this(focusZoomFactor, false);
        }
    
        public ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) {
            if (!FocusHighlightHelper.isValidZoomIndex(focusZoomFactor)) {
                throw new IllegalArgumentException("Unhandled zoom factor");
            }
            mFocusZoomFactor = focusZoomFactor;
            mUseFocusDimmer = useFocusDimmer;
        }
    
    
        // 接下来直接看onCreateViewHolder方法,在父类RowPresenter中
        @Override
        public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
            // 创建临时ViewHolder,这个holder只包含列表视图HorizontalGridView,不包含头部视图
            ViewHolder vh = createRowViewHolder(parent);
            vh.mInitialzed = false;// 标记未初始化过
            // 最终真正的ViewHolder
            Presenter.ViewHolder result;
            // 如果设置了Header标题视图,需要在外部添加布局和头部视图
            if (needsRowContainerView()) {
                RowContainerView containerView = new RowContainerView(parent.getContext());
                if (mHeaderPresenter != null) {
                    vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder)
                            mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view);
                }
                result = new ContainerViewHolder(containerView, vh);
            } else {// 没有设置头部标题
                result = vh;
            }
            // 初始化holder
            initializeRowViewHolder(vh);
            if (!vh.mInitialzed) {
                throw new RuntimeException("super.initializeRowViewHolder() must be called");
            }
            return result;
        }
    
    
        // 创建ViewHolder
        @Override
        protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
            initStatics(parent.getContext());
            // ListRowView其实就是一个布局封装,里面包含一个HorizontalGridView
            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 initializeRowViewHolder(RowPresenter.ViewHolder holder) {
            // 父类RowPresenter中只是设置clipChildren属性为false,因为设置true的话会影响缩放动效
            super.initializeRowViewHolder(holder);
            // 获取holder
            final ViewHolder rowViewHolder = (ViewHolder) holder;
            // ItemView的context
            Context context = holder.view.getContext();
            // 阴影效果相关,暂不分析,内部就是通过一层FrameLayout包裹当前的View实现阴影等效果
            if (mShadowOverlayHelper == null) {
                mShadowOverlayHelper = new ShadowOverlayHelper.Builder()
                        .needsOverlay(needsDefaultListSelectEffect())
                        .needsShadow(needsDefaultShadow())
                        .needsRoundedCorner(isUsingOutlineClipping(context)
                                && areChildRoundedCornersEnabled())
                        .preferZOrder(isUsingZOrder(context))
                        .keepForegroundDrawable(mKeepChildForeground)
                        .options(createShadowOverlayOptions())
                        .build(context);
                if (mShadowOverlayHelper.needsWrapper()) {
                    mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper(
                            mShadowOverlayHelper);
                }
            }
            // 构造桥接ItemBridgeAdapter
            rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder);
            // set wrapper if needed
            rowViewHolder.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper);
            mShadowOverlayHelper.prepareParentForShadow(rowViewHolder.mGridView);
            // ListRow默认会给设置Item的焦点缩放动效,下面动效部分单独分析
            FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter,
                    mFocusZoomFactor, mUseFocusDimmer);
            rowViewHolder.mGridView.setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
                    != ShadowOverlayHelper.SHADOW_DYNAMIC);
            // 通过BaseGridView监听焦点选中回调
            rowViewHolder.mGridView.setOnChildSelectedListener(
                    new OnChildSelectedListener() {
                @Override
                public void onChildSelected(ViewGroup parent, View view, int position, long id) {
                    selectChildView(rowViewHolder, view, true);
                }
            });
            // 通过BaseGridView监听按键事件
            rowViewHolder.mGridView.setOnUnhandledKeyListener(
                    new BaseGridView.OnUnhandledKeyListener() {
                    @Override
                    public boolean onUnhandledKey(KeyEvent event) {
                        return rowViewHolder.getOnKeyListener() != null
                                && rowViewHolder.getOnKeyListener().onKey(
                                        rowViewHolder.view, event.getKeyCode(), event);
                    }
                });
            // 设置HorizontalGridView的行数
            rowViewHolder.mGridView.setNumRows(mNumRows);
        }
    
    
        // 父类RowPresenter的初始化holder方法
        protected void initializeRowViewHolder(ViewHolder vh) {
            vh.mInitialzed = true;// 标记已经初始化过
            if (!isClippingChildren()) {
                // set clip children to false for slide transition
                if (vh.view instanceof ViewGroup) {
                    ((ViewGroup) vh.view).setClipChildren(false);
                }
                if (vh.mContainerViewHolder != null) {
                    ((ViewGroup) vh.mContainerViewHolder.view).setClipChildren(false);
                }
            }
        }
    
    
        // onBindRowViewHolder方法
        @Override
        protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
            super.onBindRowViewHolder(holder, item);
            // 获取holder
            ViewHolder vh = (ViewHolder) holder;
            // 获取到ListRow
            ListRow rowItem = (ListRow) item;
            // ListRow中的ObjectAdapter,设置到桥接的ItemBridgeAdapter中
            vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter());
            // 设置HorizontalGridView的adapter,而ItemBridgeAdapter的createRowViewHolder会调用我们ListRow中ObjectAdapter的自定义Presenter创建每一个子Item的视图,onBindRowViewHolder会将数据绑定
            vh.mGridView.setAdapter(vh.mItemBridgeAdapter);
            // 设置row描述信息
            vh.mGridView.setContentDescription(rowItem.getContentDescription());
        }
    
    }
    
    • 至此,ListRow的视图创建和数据绑定已经分析完了,其实内部子Item的视图创建和数据绑定是沿用ItemBridgeAdapter方式。

    • Leanback中的横竖列表展现形式都是通过这种PresenterBaseGridView之间的嵌套关系进行剥离。例如在多ViewType的形式下,一般我们写RecyclerView.Adapter是这样的:

    public class CutstomAdapter extends RecyclerView.Adapter {
        @NonNull
        @Override
        public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            if (viewType == Type1) {
                return VH1;// 不同行为对象
            } else if (viewType == Type2) {
                return VH2;// 不同行为对象
            } else ...
                ...// 不同行为对象...
        }
    
        @Override
        public void onBindViewHolder(@NonNull VH holder, int position) {
            if (holder instance VH1) {
                bind1();
            } else if (holder instance VH2) {
                bind2();
            } else ...
                ...
        }
    
        class VH1 extends Vh {}
        class VH2 extends Vh {}
        ...
    }
    
    • Leanback结构中对应的是ItemBridgeAdapter,其仅充当一个桥接作用,结构中增加定义了一个Presenter抽象,将onCreateViewHolderonBindViewHolder等行为抽离出去,让每个有不同样式的CustomPresenter自身去实现具体视图和数据行为,这样当需要增加新的样式和数据时,只需要往桥接类中添加对应的Presenter实现即可(往ArrayObjectAdapter中添加)。

    Leanback中焦点动效分析

    • 对于Leanback中使用原生展示控件,比如ListRow这种,其默认是会实现焦点缩放动效。上面分析ListRowPresenter时可以看到,其内部默认帮我们调用了FocusHighlightHelper.setupBrowseItemFocusHighlight()方法,在Item发生焦点变化时,焦点的监听回调中会通过Helper的方法实现缩放效果。

    • 首先看下FocusHighlightHelper这个类

    
    public class FocusHighlightHelper {
    
        // 是否可缩放
        static boolean isValidZoomIndex(int zoomIndex) {
            return zoomIndex == ZOOM_FACTOR_NONE || getResId(zoomIndex) > 0;
        }
    
        // 获取缩放比例
        static int getResId(int zoomIndex) {
            switch (zoomIndex) {
                case ZOOM_FACTOR_SMALL:
                    return R.fraction.lb_focus_zoom_factor_small;
                case ZOOM_FACTOR_XSMALL:
                    return R.fraction.lb_focus_zoom_factor_xsmall;
                ...
        // 具体值在res的value文件中定义缩放比例
        118%
        114%
        110%
        106%
        }
    
        // 绑定焦点动效
        public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex,
                boolean useDimmer) {
            // 这里我们只关注BrowseItemFocusHighlight,HeaderItemFocusHighlight类似
            adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex, useDimmer));
        }
    
        static class BrowseItemFocusHighlight implements FocusHighlightHandler {
            // 时长
            private static final int DURATION_MS = 150;
            // 缩放等级
            private int mScaleIndex;
            // 是否使用阴影
            private final boolean mUseDimmer;
    
            BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer) {
                if (!isValidZoomIndex(zoomIndex)) {
                    throw new IllegalArgumentException("Unhandled zoom index");
                }
                mScaleIndex = zoomIndex;
                mUseDimmer = useDimmer;
            }
            ...
    
            // 焦点变化监听回调
            @Override
            public void onItemFocused(View view, boolean hasFocus) {
                view.setSelected(hasFocus);
                // 第一个参数是否聚焦,第二个参数表示是否跳过动画执行过程直接展示结果
                getOrCreateAnimator(view).animateFocus(hasFocus, false);
            }
    
            // 初始化,如果绑定了动画,ItemBridgeAdapter的onCreateViewHolder中会调用
            @Override
            public void onInitializeView(View view) {
                getOrCreateAnimator(view).animateFocus(false, true);
            }
    
            // 创建或者获取动画对象
            private FocusAnimator getOrCreateAnimator(View view) {
                // 此处通过view的tag进行缓存,避免了频繁创建动画对象的开销,google的开发工程师这种对性能敏感度的思想非常值得学习
                FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
                if (animator == null) {
                    animator = new FocusAnimator(
                            view, getScale(view.getResources()), mUseDimmer, DURATION_MS);
                    view.setTag(R.id.lb_focus_animator, animator);
                }
                return animator;
            }
        }
    
        // 动画对象
        static class FocusAnimator implements TimeAnimator.TimeListener {
            private final View mView;
            private final int mDuration;
            // 支持阴影的FrameLayout,SDK_INT >= 21以上才支持,此处不详细分析了
            private final ShadowOverlayContainer mWrapper;
            private final float mScaleDiff;
            private float mFocusLevel = 0f;
            private float mFocusLevelStart;
            private float mFocusLevelDelta;
            private final TimeAnimator mAnimator = new TimeAnimator();
            private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
            // 颜色遮罩,就是那种聚焦高亮,非聚焦昏暗的效果层,内部通过canvas的drawRect方式实现,此处不详细分析了
            private final ColorOverlayDimmer mDimmer;
    
            void animateFocus(boolean select, boolean immediate) {
                // 先结束上一次动画
                endAnimation();
                final float end = select ? 1 : 0;
                if (immediate) {
                    // 不需要过程,直接setScale设置最终效果,结束
                    setFocusLevel(end);
                } else if (mFocusLevel != end) {
                    // 需要动画过程,开始执行动画
                    mFocusLevelStart = mFocusLevel;
                    mFocusLevelDelta = end - mFocusLevelStart;
                    mAnimator.start();
                }
            }
    
            FocusAnimator(View view, float scale, boolean useDimmer, int duration) {
                // 动画执行的view
                mView = view;
                // 动画时长
                mDuration = duration;
                // 动画缩放的比例差值
                mScaleDiff = scale - 1f;
                // 阴影和高亮效果
                if (view instanceof ShadowOverlayContainer) {
                    mWrapper = (ShadowOverlayContainer) view;
                } else {
                    mWrapper = null;
                }
                mAnimator.setTimeListener(this);
                if (useDimmer) {
                    mDimmer = ColorOverlayDimmer.createDefault(view.getContext());
                } else {
                    mDimmer = null;
                }
            }
    
            // 改变当前动画值
            void setFocusLevel(float level) {
                mFocusLevel = level;
                float scale = 1f + mScaleDiff * level;
                // 缩放
                mView.setScaleX(scale);
                mView.setScaleY(scale);
                // 阴影和高亮效果
                if (mWrapper != null) {
                    mWrapper.setShadowFocusLevel(level);
                } else {
                    ShadowOverlayHelper.setNoneWrapperShadowFocusLevel(mView, level);
                }
                if (mDimmer != null) {
                    // 改变高亮或者昏暗的透明度值
                    mDimmer.setActiveLevel(level);
                    int color = mDimmer.getPaint().getColor();
                    if (mWrapper != null) {
                        // 设置阴影
                        mWrapper.setOverlayColor(color);
                    } else {
                        // 取消阴影
                        ShadowOverlayHelper.setNoneWrapperOverlayColor(mView, color);
                    }
                }
            }
            ...
    
            void endAnimation() {
                mAnimator.end();
            }
    
            // 估值器
            @Override
            public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
                float fraction;
                if (totalTime >= mDuration) {
                    // 动画结束
                    fraction = 1;
                    mAnimator.end();
                } else {
                    // 计算当前动画执行进度
                    fraction = (float) (totalTime / (double) mDuration);
                }
                if (mInterpolator != null) {
                    // 有插值器的情况下计算的动画执行进度
                    fraction = mInterpolator.getInterpolation(fraction);
                }
                // 改变当前动画的值
                setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta);
            }
        }
    
    }
    
    • 下面我们看下是如何监听Item的焦点变化的
    
        @Override
        public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            ...
            if (presenterView != null) {
                // 设置焦点变化监听,这个Listener是每个ViewHolder中对应的,监听的是ViewHolder的ItemView
                viewHolder.mFocusChangeListener.mChainedListener =
                        presenterView.getOnFocusChangeListener();
                presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
            }
            if (mFocusHighlight != null) {
                // 这里会创建动画对象,并且缓存到view的tag中
                mFocusHighlight.onInitializeView(view);
            }
            return viewHolder;
        }
    
        // 焦点监听回调
        final class OnFocusChangeListener implements View.OnFocusChangeListener {
            // 这个内部的listener实在没搞懂干嘛用的,可能是为以后扩展准备的吧
            View.OnFocusChangeListener mChainedListener;
    
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                if (DEBUG) {
                    Log.v(TAG, "onFocusChange " + hasFocus + " " + view
                            + " mFocusHighlight" + mFocusHighlight);
                }
                if (mWrapper != null) {
                    view = (View) view.getParent();
                }
                if (mFocusHighlight != null) {
                    // 看到了,这里就会执行BrowseItemFocusHighlight的onItemFocused方法
                    mFocusHighlight.onItemFocused(view, hasFocus);
                }
                if (mChainedListener != null) {
                    mChainedListener.onFocusChange(view, hasFocus);
                }
            }
        }
    
    • 至此,Leanback中焦点缩放动效也分析完了,里面其实就是监听焦点变化,执行相应的scale动画而已。不过里面为了节省频繁创建动画对象的性能开销,通过View.Tag缓存思想的确值得学习借鉴。
    • 结构部分已经分析完了,后面抽时间重点分析下BaseGridView对焦点及按键事件的处理。

    你可能感兴趣的:(Android Leanback结构源码简析)