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 视频缩放问题