Android开发 - (适配器)Adapter类中ArrayObjectAdapter实现类详细解析
简介
- 用于 Android TV 的 Leanback 库,用于绑定对象数组到 UI 组件
具体作用
-
ArrayObjectAdapter 是 RecyclerView 和 Adapter 系列中用于处理列表数据的一种适配器类型,主要用于 Android TV 的 Leanback 库中的 BrowseFragment、DetailFragment 和 PlaybackOverlayFragment 等组件。它简化了列表项的管理,支持多种类型的视图和数据项
管理数据列表
- ArrayObjectAdapter 用于将数据列表与 RecyclerView 绑定在一起。它允许将数据项(如电影、电视节目、应用程序等)添加到适配器中,并将这些数据项显示在用户界面上
支持不同类型的视图
- 你可以在同一个 ArrayObjectAdapter 实例中管理不同类型的数据项。它支持多种视图类型,并根据数据项的类型自动选择合适的视图进行显示
提供数据操作方法
- ArrayObjectAdapter 提供了多种方法来操作数据,如添加、删除、更新和替换数据项。这些操作会自动更新视图,以反映数据的变化
与 Presenter 结合使用
- ArrayObjectAdapter 结合 Presenter 使用,Presenter 负责将数据项呈现到视图中。Presenter 负责将数据项转换为视图的表示,并处理用户交互
参数、方法解析
-
ArrayObjectAdapter();
:使用默认的 PresenterSelector 创建 ArrayObjectAdapter 实例。这种构造方法通常用于简单的场景,其中每种数据项都使用相同的 PresenterArrayObjectAdapter adapter = new ArrayObjectAdapter();
-
ArrayObjectAdapter(Presenter presenter);
:构造方法;使用指定的 Presenter 创建 ArrayObjectAdapter 实例。适配器将使用这个 Presenter 来展示数据项ArrayObjectAdapter adapter = new ArrayObjectAdapter(new MoviePresenter());
- 参数解析:
- presenter:用于展示数据项的 Presenter 对象。Presenter 对象负责将数据项转换为视图并处理用户交互
- 参数解析:
-
ArrayObjectAdapter(PresenterSelector presenterSelector);
:构造方法;使用 PresenterSelector 创建 ArrayObjectAdapter 实例。适配器将根据数据项的类型动态选择合适的 Presenter 来展示数据ArrayObjectAdapter adapter = new ArrayObjectAdapter(new CustomPresenterSelector());
- 参数解析:
- presenterSelector:用于选择 Presenter 的 PresenterSelector 对象。PresenterSelector 对象 根据数据项的类型选择合适的 Presenter
- 参数解析:
-
ArrayObjectAdapter(Presenter presenter, int initialCapacity);
:构造方法;用指定的 Presenter 和初始容量创建 ArrayObjectAdapter 实例。适配器将使用这个 Presenter 来展示数据,并初始化内部数据存储的容量ArrayObjectAdapter adapter = new ArrayObjectAdapter(new MoviePresenter(), 20);
- 参数解析:
- presenter:用于展示数据项的 Presenter 对象
- initialCapacity:初始容量。指定适配器内部数据存储的初始容量,有助于优化性能
- 参数解析:
-
ArrayObjectAdapter(PresenterSelector presenterSelector, int initialCapacity);
:构造方法;使用 PresenterSelector 和初始容量创建 ArrayObjectAdapter 实例。适配器将根据数据项的类型动态选择合适的 Presenter 来展示数据,并初始化内部数据存储的容量
ArrayObjectAdapter adapter = new ArrayObjectAdapter(new CustomPresenterSelector(), 20);
-
参数解析:
- presenterSelector:用于选择 Presenter 的 PresenterSelector 对象
- initialCapacity:初始容量。指定适配器内部数据存储的初始容量
-
adapter.add(Object item);
:向适配器中添加一个数据项
adapter.add(new Movie("Inception", "A mind-bending thriller"));
-
参数解析:
- item:要添加的数据项。通常是一个对象(如 Movie 实例)
-
adapter.add(int index, Object item);
:在指定索引位置插入一个数据项adapter.add(0, new Movie("The Matrix", "A sci-fi classic"));
- 参数解析:
- index:插入位置的索引,从 0 开始
- item:要插入的数据项
- 参数解析:
-
addAll(Collection<?> items);
:向适配器中添加一个数据项集合List<Movie> movies = new ArrayList<Movie>(); movies.add(new Movie("Inception", "A mind-bending thriller")); movies.add(new Movie("The Matrix", "A sci-fi classic")); adapter.addAll(movies);
- 参数解析:
- items:要添加的数据项集合。通常是一个 Collection(如 List<Movie>)
- 参数解析:
-
adapter.addAll(int index, Collection<?> items);
:在指定索引位置插入一个数据项集合List<Movie> movies = new ArrayList<Movie>(); movies.add(new Movie("Inception", "A mind-bending thriller")); movies.add(new Movie("The Matrix", "A sci-fi classic")); adapter.addAll(0, movies);
- 参数解析:
- index:插入位置的起始索引,从 0 开始
- items:要插入的数据项集合
- 参数解析:
-
adapter.replace(int index, Object item);
:替换指定索引位置的数据项
adapter.replace(0, new Movie("Interstellar", "A journey through space"));
-
参数解析:
- index:要替换的位置的索引,从 0 开始
- item:新的数据项,将替换旧的数据项
-
adapter.remove(Object item);
:移除指定的数据项adapter.remove(movie);
- 参数解析:
- item:要移除的数据项
- 参数解析:
-
adapter.removeItems(int position, int count);
:从指定位置开始移除多个数据项adapter.removeItems(0, 2); //从第一个位置开始计数移除2项数据项
- 参数解析:
- position:起始位置的索引,从 0 开始
- count: 要移除的数据项数量
- 参数解析:
-
adapter.clear();
:清除所有数据项 -
adapter.get(int position);
:获取指定位置的数据项Movie movie = (Movie) adapter.get(0);
- 参数解析:
- position:位置的索引,从 0 开始
- 参数解析:
-
adapter.size();
:返回适配器中数据项的数量
int count = adapter.size();
adapter.indexOf(Object item);
:返回指定数据项的索引
int index = adapter.indexOf(movie);
-
参数解析:
- item:要查找索引的数据项
-
adapter.notifyArrayItemRangeChanged(int positionStart, int itemCount);
:通知适配器在指定范围内的数据项发生了变化
adapter.notifyArrayItemRangeChanged(0, 2);
-
参数解析:
- positionStart:起始位置的索引,从 0 开始
- itemCount:变化的数据项数量
-
adapter.notifyChanged();
:通知适配器数据发生了变化
使用环境解析
-
ArrayObjectAdapter 是 Leanback 库中的一个核心类,用于在 TV 应用中管理和显示数据项。它常用于以下场景:
BrowseFragment 场景
-
BrowseFragment 是一个常见的 TV 应用布局,用于展示分类的内容列表。ArrayObjectAdapter 用于管理各个分类下的内容
-
示例代码:
import android.os.Bundle; import androidx.fragment.app.FragmentActivity; import androidx.leanback.app.BrowseFragment; import androidx.leanback.widget.ArrayObjectAdapter; import androidx.leanback.widget.HeaderItem; import androidx.leanback.widget.ListRow; import androidx.leanback.widget.ListRowPresenter; import androidx.leanback.widget.Presenter; import android.widget.TextView; import android.view.ViewGroup; public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查是否有已保存的实例状态 if (savedInstanceState == null) { // 没有已保存的实例状态时,加载 MyBrowseFragment getSupportFragmentManager().beginTransaction() .replace(R.id.main_browse_fragment, new MyBrowseFragment()) .commitNow(); } } // 自定义 BrowseFragment 类,显示电影分类和列表 public static class MyBrowseFragment extends BrowseFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 创建 ArrayObjectAdapter,用于管理 ListRow 数据项 ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); // 创建第一个分类 HeaderItem headerItem1 = new HeaderItem(0, "Category 1"); // 创建头部项,参数为ID和标题 ArrayObjectAdapter category1Adapter = new ArrayObjectAdapter(new MoviePresenter()); // 创建分类适配器,使用自定义的 MoviePresenter category1Adapter.add(new Movie("Inception", "A mind-bending thriller")); // 添加电影数据到分类适配器 category1Adapter.add(new Movie("The Matrix", "A sci-fi classic")); rowsAdapter.add(new ListRow(headerItem1, category1Adapter)); // 将分类适配器作为 ListRow 添加到主适配器 // 创建第二个分类 HeaderItem headerItem2 = new HeaderItem(1, "Category 2"); ArrayObjectAdapter category2Adapter = new ArrayObjectAdapter(new MoviePresenter()); category2Adapter.add(new Movie("Interstellar", "A journey through space")); category2Adapter.add(new Movie("The Dark Knight", "A superhero thriller")); rowsAdapter.add(new ListRow(headerItem2, category2Adapter)); // 设置 BrowseFragment 的适配器 setAdapter(rowsAdapter); } } // 电影类,包含标题和描述 public static class Movie { private String title; private String description; public Movie(String title, String description) { this.title = title; this.description = description; } // Getter 方法 public String getTitle() { return title; } public String getDescription() { return description; } } // 电影 Presenter 类,用于展示电影数据 public static class MoviePresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { // 创建 TextView 作为 ViewHolder 的视图 TextView textView = new TextView(parent.getContext()); return new ViewHolder(textView); } @Override public void onBindViewHolder(ViewHolder viewHolder, Object item) { // 绑定电影数据到 ViewHolder Movie movie = (Movie) item; // 强制转换 item 为 Movie TextView textView = (TextView) viewHolder.view; textView.setText(movie.getTitle()); // 设置 TextView 显示电影标题 } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // 清理资源,例如移除监听器 TextView textView = (TextView) viewHolder.view; textView.setOnClickListener(null); // 移除点击监听器 } } }
DetailsFragment 场景
-
DetailsFragment 用于展示选中项的详细信息。ArrayObjectAdapter 可以用于管理和展示详细信息的不同部分
-
示例代码:
import android.os.Bundle; import androidx.fragment.app.FragmentActivity; import androidx.leanback.app.DetailsFragment; import androidx.leanback.widget.ArrayObjectAdapter; import androidx.leanback.widget.ClassPresenterSelector; import androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter; import androidx.leanback.widget.ListRow; import androidx.leanback.widget.ListRowPresenter; import androidx.leanback.widget.Presenter; import androidx.leanback.widget.DetailsOverviewRow; import android.widget.TextView; import android.view.ViewGroup; public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查是否有已保存的实例状态 if (savedInstanceState == null) { // 没有已保存的实例状态时,加载 MyDetailsFragment getSupportFragmentManager().beginTransaction() .replace(R.id.main_details_fragment, new MyDetailsFragment()) .commitNow(); } } // 自定义 DetailsFragment 类,显示电影详情和相关内容 public static class MyDetailsFragment extends DetailsFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 创建 Presenter 选择器,用于管理不同类型的数据行 ClassPresenterSelector selector = new ClassPresenterSelector(); selector.addClassPresenter(DetailsOverviewRow.class, new FullWidthDetailsOverviewRowPresenter(new MovieDetailsPresenter())); // 添加详情 Presenter selector.addClassPresenter(ListRow.class, new ListRowPresenter()); // 添加列表 Presenter // 创建 ArrayObjectAdapter,用于管理数据行 ArrayObjectAdapter adapter = new ArrayObjectAdapter(selector); // 创建详情行 Movie movie = new Movie("Inception", "A mind-bending thriller"); DetailsOverviewRow detailsOverviewRow = new DetailsOverviewRow(movie); adapter.add(detailsOverviewRow); // 创建相关内容行 ArrayObjectAdapter relatedAdapter = new ArrayObjectAdapter(new MoviePresenter()); relatedAdapter.add(new Movie("The Matrix", "A sci-fi classic")); relatedAdapter.add(new Movie("Interstellar", "A journey through space")); HeaderItem headerItem = new HeaderItem(0, "Related Content"); adapter.add(new ListRow(headerItem, relatedAdapter)); // 设置 DetailsFragment 的适配器 setAdapter(adapter); } } // 电影类,包含标题和描述 public static class Movie { private String title; private String description; public Movie(String title, String description) { this.title = title; this.description = description; } // Getter 方法 public String getTitle() { return title; } public String getDescription() { return description; } } // 电影详情 Presenter 类,用于展示电影详细信息 public static class MovieDetailsPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { // 创建 TextView 作为 ViewHolder 的视图 TextView textView = new TextView(parent.getContext()); return new ViewHolder(textView); } @Override public void onBindViewHolder(ViewHolder viewHolder, Object item) { // 绑定电影详细信息到 ViewHolder Movie movie = (Movie) item; // 强制转换 item 为 Movie TextView textView = (TextView) viewHolder.view; textView.setText(movie.getDescription()); // 设置 TextView 显示电影描述 } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // 清理资源,例如移除监听器 TextView textView = (TextView) viewHolder.view; textView.setOnClickListener(null); // 移除点击监听器 } } // 电影 Presenter 类,用于展示电影数据 public static class MoviePresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { // 创建 TextView 作为 ViewHolder 的视图 TextView textView = new TextView(parent.getContext()); return new ViewHolder(textView); } @Override public void onBindViewHolder(ViewHolder viewHolder, Object item) { // 绑定电影数据到 ViewHolder Movie movie = (Movie) item; // 强制转换 item 为 Movie TextView textView = (TextView) viewHolder.view; textView.setText(movie.getTitle()); // 设置 TextView 显示电影标题 } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // 清理资源,例如移除监听器 TextView textView = (TextView) viewHolder.view; textView.setOnClickListener(null); // 移除点击监听器 } } }
VerticalGridFragment 场景
-
VerticalGridFragment 用于展示一个垂直网格布局的内容列表。ArrayObjectAdapter 用于管理网格中的内容
-
示例代码:
import android.os.Bundle; import androidx.fragment.app.FragmentActivity; import androidx.leanback.app.VerticalGridFragment; import androidx.leanback.widget.ArrayObjectAdapter; import androidx.leanback.widget.GridItemPresenter; import androidx.leanback.widget.Presenter; import androidx.leanback.widget.VerticalGridPresenter; import android.widget.TextView; import android.view.ViewGroup; public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查是否有已保存的实例状态 if (savedInstanceState == null) { // 没有已保存的实例状态时,加载 MyVerticalGridFragment getSupportFragmentManager().beginTransaction() .replace(R.id.main_vertical_grid_fragment, new MyVerticalGridFragment()) .commitNow(); } } // 自定义 VerticalGridFragment 类,显示电影网格 public static class MyVerticalGridFragment extends VerticalGridFragment { private static final int NUM_COLUMNS = 3; // 网格列数 @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 设置 VerticalGridFragment 的标题 setTitle("Movies"); // 创建 VerticalGridPresenter,设置焦点高亮效果 setGridPresenter(new VerticalGridPresenter(FocusHighlight.ZOOM_FACTOR_LARGE, false)); // 设置网格的列数 setNumberOfColumns(NUM_COLUMNS); // 创建 ArrayObjectAdapter,用于管理网格数据项 ArrayObjectAdapter adapter = new ArrayObjectAdapter(new GridItemPresenter()); // 添加数据项到网格适配器 adapter.add(new Movie("Inception", "A mind-bending thriller")); adapter.add(new Movie("The Matrix", "A sci-fi classic")); adapter.add(new Movie("Interstellar", "A journey through space")); // 设置 VerticalGridFragment 的适配器 setAdapter(adapter); } } // 电影类,包含标题和描述 public static class Movie { private String title; private String description; public Movie(String title, String description) { this.title = title; this.description = description; } // Getter 方法 public String getTitle() { return title; } public String getDescription() { return description; } } // 网格项 Presenter 类,用于展示网格项数据 public static class GridItemPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { // 创建 TextView 作为 ViewHolder 的视图 TextView textView = new TextView(parent.getContext()); // 设置 TextView 的大小 textView.setLayoutParams(new ViewGroup.LayoutParams(300, 300)); // 使 TextView 可获取焦点 textView.setFocusable(true); // 使 TextView 在触摸模式下可获取焦点 textView.setFocusableInTouchMode(true); // 返回包含 TextView 的 ViewHolder return new ViewHolder(textView); } @Override public void onBindViewHolder(ViewHolder viewHolder, Object item) { // 绑定网格项数据到 ViewHolder Movie movie = (Movie) item; // 强制转换 item 为 Movie TextView textView = (TextView) viewHolder.view; // 设置 TextView 显示电影标题 textView.setText(movie.getTitle()); } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // 清理资源,例如移除监听器 TextView textView = (TextView) viewHolder.view; // 移除点击监听器 textView.setOnClickListener(null); } } }
-
完整代码示例
-
使用 BrowseFragment 显示多个电影分类:
-
创建主布局文件:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" <!-- 这是 Fragment 的容器 --> android:layout_width="match_parent" android:layout_height="match_parent"/>
-
自定义 BrowseFragment类:
MyBrowseFragment.java
//自定义 BrowseFragment 类,使用 ArrayObjectAdapter 来管理分类和电影数据 import android.os.Bundle; import androidx.leanback.app.BrowseFragment; import androidx.leanback.widget.ArrayObjectAdapter; import androidx.leanback.widget.HeaderItem; import androidx.leanback.widget.ListRow; import androidx.leanback.widget.ListRowPresenter; import androidx.leanback.widget.Presenter; import android.view.ViewGroup; import android.widget.TextView; public class MyBrowseFragment extends BrowseFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 创建一个 ArrayObjectAdapter,用于管理不同的 ListRow ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); // 创建第一个电影分类(例如“热门电影”) HeaderItem headerItem1 = new HeaderItem(0, "Popular Movies"); // 创建一个 ArrayObjectAdapter 来管理这个分类中的电影项 ArrayObjectAdapter category1Adapter = new ArrayObjectAdapter(new MoviePresenter()); // 向适配器中添加电影项 category1Adapter.add(new Movie("Inception", "A mind-bending thriller")); category1Adapter.add(new Movie("The Matrix", "A sci-fi classic")); // 创建一个 ListRow,将标题和适配器组合起来,并添加到主适配器中 rowsAdapter.add(new ListRow(headerItem1, category1Adapter)); // 创建第二个电影分类(例如“新片上映”) HeaderItem headerItem2 = new HeaderItem(1, "New Releases"); ArrayObjectAdapter category2Adapter = new ArrayObjectAdapter(new MoviePresenter()); category2Adapter.add(new Movie("Interstellar", "A journey through space")); category2Adapter.add(new Movie("The Dark Knight", "A superhero thriller")); rowsAdapter.add(new ListRow(headerItem2, category2Adapter)); // 设置 BrowseFragment 的适配器,这样它就知道如何显示数据 setAdapter(rowsAdapter); } // 电影类,包含电影的标题和描述 public static class Movie { private String title; private String description; public Movie(String title, String description) { this.title = title; this.description = description; } // 获取电影标题 public String getTitle() { return title; } // 获取电影描述 public String getDescription() { return description; } } // 电影 Presenter 类,用于将电影数据显示到屏幕上 public static class MoviePresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { // 创建并初始化 TextView 作为 ViewHolder 的视图 TextView textView = new TextView(parent.getContext()); textView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, // 宽度填满父容器 ViewGroup.LayoutParams.WRAP_CONTENT)); // 高度根据内容自动调整 textView.setPadding(16, 16, 16, 16); // 设置 TextView 的内边距 textView.setTextSize(18); // 设置文字大小 return new ViewHolder(textView); // 返回 ViewHolder 对象 } @Override public void onBindViewHolder(ViewHolder viewHolder, Object item) { // 将电影数据绑定到 ViewHolder 的视图上 Movie movie = (Movie) item; // 将 item 转换为 Movie 对象 TextView textView = (TextView) viewHolder.view; textView.setText(movie.getTitle()); // 设置 TextView 的内容为电影标题 } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // 在 ViewHolder 被销毁或复用时进行资源清理 TextView textView = (TextView) viewHolder.view; textView.setOnClickListener(null); // 移除任何可能存在的点击监听器 } } }
-
启动类
import android.os.Bundle; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentTransaction; public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 设置活动布局文件 if (savedInstanceState == null) { // 如果没有已保存的实例状态,动态加载 MyBrowseFragment FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.fragment_container, new MyBrowseFragment()); // 替换容器中的 Fragment transaction.commit(); // 提交事务,完成 Fragment 的替换 } } }
-