Android TV 开发-->Leanback 中的 VerticalGridView

Posted Kevin_小飞象

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android TV 开发-->Leanback 中的 VerticalGridView相关的知识,希望对你有一定的参考价值。

LeanBack 是 Google 官方推出的 TV 端的功能库,里面包含了很多在 TV android 端开发常用的控件,本文重点介绍其对 RecyclerView 适配 TV 端做的封装:VerticalGridView 。

效果图

属性 & 方法

  • focusOutFront、focusOutEnd
    如果标题栏使用 HorizontalGridView 实现,内容区域使用 Fragment 里放的 VerticalGridView 实现,可能出现标题栏和内容区焦点切换不成功的问题,比如说,焦点不能从内容区切到标题栏这样的情况。这时使用 focusOutFront 和 focusOutEnd 属性能够解决问题,解决不同容器里焦点切换不成功的问题。

  • setHorizontalSpacing(),setVerticalSpacing()
    设置 VerticalGridView 的 Item 之间的间距。

  • setNumColumns()
    设置列数,默认 VerticalGridView 为一列,通过 setNumColumns 方法可以设置多列。但有个注意点,设置多行后要注意 position 的位置。

基本使用

1. 添加依赖

	implementation 'androidx.leanback:leanback:1.0.0'
    implementation 'androidx.leanback:leanback-preference:1.0.0'

2. 布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_gradient_home">

    <com.hkt.meetlauncher.view.AppVerticalGridView
        android:id="@+id/vg_app_installed"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="100dp"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:paddingStart="75dp"
        android:paddingTop="10dp"
        android:paddingEnd="75dp"
        android:paddingBottom="75dp"
        app:focusOutEnd="true"
        app:focusOutFront="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent" />

    <TextView
        android:id="@+id/tv_app_installed_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="70dp"
        android:layout_marginTop="60dp"
        android:text="已安装应用"
        android:textColor="@color/white"
        android:textSize="14dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_app_installed_tips"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginEnd="70dp"
        android:text="长按确定删除选中应用"
        android:textColor="@color/white"
        android:textSize="14dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@+id/tv_app_installed_name" />

</androidx.constraintlayout.widget.ConstraintLayout>

3. AppVerticalGridView.java

public class AppVerticalGridView extends VerticalGridView 
    private static final String TAG = "AppVerticalGridView";
    private int mNumColumns;

    public AppVerticalGridView(Context context) 
        this(context, null);
    

    public AppVerticalGridView(Context context, AttributeSet attrs) 
        this(context, attrs, 0);
    

    public AppVerticalGridView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
    

    public void setColumnNumbers(int numColumns) 
        this.mNumColumns = numColumns;
        setNumColumns(numColumns);
    

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) 
        if (event.getAction() == KeyEvent.ACTION_DOWN) 
            switch (event.getKeyCode()) 
                case KeyEvent.KEYCODE_BACK:
                    if (getSelectedPosition() > 0) 
                        setSelectedPosition(0);
                        return true;
                    
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:

                    if (mNumColumns == 0 || getAdapter() == null) 
                        break;
                    

                    int itemCount = getAdapter().getItemCount();
                    if (itemCount + 1 < mNumColumns || itemCount % mNumColumns == 0) //这2种情况不需要单独处理
                        break;
                    

                    int currentPosition = getSelectedPosition();
                    int lineNumbers = itemCount / mNumColumns + 1;
                    int currentLine;
                    if (currentPosition + 1 % mNumColumns == 0) 
                        currentLine = currentPosition / mNumColumns;
                     else 
                        currentLine = currentPosition/ mNumColumns + 1;
                    
                    if ((currentLine == lineNumbers - 1)//倒数第二行
                            && ((currentPosition + 1 + mNumColumns) > itemCount)) 
                        setSelectedPositionSmooth(itemCount - 1);
                        return true;
                    
                    break;
            

        
        return super.dispatchKeyEvent(event);
    

4. Bean 类

public class HomeAppModel 
    public String dataDir;
    public Drawable icon;
    public String id;
    public String name;
    public String launcherName;
    public String packageName;
    public int pageIndex;
    public int position;
    public boolean sysApp;

    public String getDataDir() 
        return this.dataDir;
    

    public Drawable getIcon() 
        return this.icon;
    

    public String getId() 
        return this.id;
    

    public String getName() 
        return this.name;
    

    public String getPackageName() 
        return this.packageName;
    

    public int getPageIndex() 
        return this.pageIndex;
    

    public int getPosition() 
        return this.position;
    

    public void setDataDir(String paramString) 
        this.dataDir = paramString;
    

    public void setIcon(Drawable paramDrawable) 
        this.icon = paramDrawable;
    

    public void setId(String paramString) 
        this.id = paramString;
    

    public void setName(String paramString) 
        this.name = paramString;
    

    public void setPackageName(String paramString) 
        this.packageName = paramString;
    

    public void setPageIndex(int paramInt) 
        this.pageIndex = paramInt;
    

    public void setPosition(int paramInt) 
        this.position = paramInt;
    

    public String toString() 
        return "AppBean [packageName=" + this.packageName + ", name=" + this.name + ", dataDir=" + this.dataDir + "]";
    

    public boolean isSysApp() 
        return sysApp;
    

    public void setSysApp(boolean sysApp) 
        this.sysApp = sysApp;
    

    public String getLauncherName() 
        return launcherName;
    

    public void setLauncherName(String launcherName) 
        this.launcherName = launcherName;
    

5. 创建 Presenter

public class HomePresenter extends Presenter 
    private Context mContext;

    @Override
    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) 
        if (mContext == null) 
            mContext = parent.getContext();
        
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_home_app, parent, false);
        view.setOnFocusChangeListener(new View.OnFocusChangeListener() 
            @Override
            public void onFocusChange(View v, boolean hasFocus) 
                v.findViewById(R.id.tv_app_name).setSelected(hasFocus);
            
        );
        return new ViewHolder(view);
    

    @Override
    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) 
        if (item instanceof HomeAppModel) 
            ViewHolder vh = (ViewHolder) viewHolder;
            if (((HomeAppModel) item).icon != null) 
                Bitmap bitmap = getBitmapFromDrawable(((HomeAppModel) item).icon);//适配Android 8.0
                RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
                drawable.setCornerRadius(DensityUtil.dip2px(mContext, 10));
                vh.mIvAppIcon.setImageDrawable(drawable);
            
            if (!TextUtils.isEmpty(((HomeAppModel) item).name)) 
                vh.mTvAppName.setText(((HomeAppModel) item).name);
            
        
    

    @Override
    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) 

    


    public static class ViewHolder extends Presenter.ViewHolder 

        private final ImageView mIvAppIcon;
        private final TextView mTvAppName;

        public ViewHolder(View view) 
            super(view);
            mIvAppIcon = view.findViewById(R.id.iv_app_icon);
            mTvAppName = view.findViewById(R.id.tv_app_name);
        
    

    private Bitmap getBitmapFromDrawable(@NonNull Drawable drawable) 
        final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(bmp);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bmp;
    

6. MyItemBridgeAdapter.java

public abstract class MyItemBridgeAdapter extends ItemBridgeAdapter 
    protected MyItemBridgeAdapter(ObjectAdapter adapter) 
        super(adapter, null);
    

    @Override
    protected void onBind(final ViewHolder viewHolder) 
        if (getOnItemViewClickedListener() != null) 
            viewHolder.itemView.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    getOnItemViewClickedListener().onItemClicked(v, viewHolder.getViewHolder(),
                            viewHolder.getItem());

                
            );
            viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() 
                @Override
                public boolean onLongClick(View v) 
                    if (getOnItemViewLongClickedListener() != null) 
                        return getOnItemViewLongClickedListener().onItemLongClicked(v, viewHolder.getViewHolder(),
                                viewHolder.getItem());
                    
                    return true;
                
            );
        
        if (getOnItemFocusChangedListener() != null) 
            viewHolder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() 
                @Override
                public void onFocusChange(View v, boolean hasFocus) 
                    getOnItemFocusChangedListener().onItemFocusChanged(v, viewHolder.getViewHolder(),
                            viewHolder.getItem(), hasFocus, viewHolder.getAdapterPosition());
                
            );
        
        super.onBind(viewHolder);
    

    @Override
    protected void onUnbind(ViewHolder viewHolder) 
        super.onUnbind(viewHolder);
        viewHolder.itemView.setOnClickListener(null);
        if (getOnItemFocusChangedListener() != null) 
            viewHolder.itemView.setOnFocusChangeListener(null);
        
    

    public abstract OnItemViewClickedListener getOnItemViewClickedListener();

    public OnItemViewLongClickedListener getOnItemViewLongClickedListener() 
        return null;
    Android TV 开发-->Leanback 中的 BrowseSupportFragment

如何使用 Leanback 库在 Android TV 中创建顶部导航栏

Android TV (Leanback Launcher) - 来自 android 开发者的软键盘不起作用

Android TV Leanback Exoplayer 视频缩放问题

Aandroid TV 基于Leanback支持最新MD设计的TV开发框架

简化 Android TV / Leanback 的登录