使用RecyclerView及相关架构组件实现列表数据展示
参考资料
安卓开发官方文档:
IDE
AndroidStudio
相关组件概述
LiveData
LiveData是一种可观察的数据存储器类。具有生命周期感知能力,它遵循其他应用组件(如Activity、Fragment或Service)的生命周期。这种感知能力可确保LiveData仅更新处于活跃生命周期状态(STARTED/RESUMED)的应用组件观察者。更加高效与准确。具体特性与使用场景请参考官方文档中的内容。
ViewModel
ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转的配置更改后继续留存
ViewModel的存在是为了从Activity和Fragment之类的界面控制器逻辑中分离出视图数据的所有权,实现界面显示与数据准备工作的分离。用于辅助界面控制器,负责为界面准备数据。并且可以避免视图控制器被销毁或者重新创建,设备发生配置更改(如屏幕旋转)等情况下,视图数据丢失的问题。具体特性与使用场景请参考官方文档中的内容。
分页库
通过分页库功能可以一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用。
官方文档示例中,分页库显示数据主要用到以下组件:
PagedList
分页库的关键组件是PagedList类,用于加载应用数据块或者页面。随着所需数据的增多,系统会将其分页到现有的PagedList对象中。如果任何已加载的数据发生更改,会从LiveData或基于Rxjava2的对象向可观察数据存储器发出一个新的PagedList实例。随着PagedList对象的生成,应用界面会呈现其内容,同时还会考虑界面控件的生命周期。
数据源(DataSource)
DataSource对象可以为PagedList提供数据来源,通过该对象加载应用数据的最新快照,包括从远程服务器获取数据或者从本地数据库获取数据。数据源的对象可以通过一个自定义的数据源工厂(DataSourceFactory)提供
RecyclerView
PagedList类使用PagedListAdapter适配器将数据逐项加载到RecyclerView。这些类共同作用,在内容加载时抓取和显示内容,预取不在界面可视范围内的内容以及针对内容更改添加动画
声明依赖项
使用以上架构组件的功能之前,需要在项目中添加其引用声明。包括生命周期组件、Paging组件,如果数据来源为数据库还可以添加Room组件。具体参考官方文档–声明依赖项
搭建列表显示模块代码框架
创建列表项的视图布局文件
创建一个Fragment视图作为列表项的Layout布局资源,并在RecyclerView中声明其为列表项布局文件。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/myRecyclerview"
<!--其他属性-->
tools:listitem="@layout/fragment_list_item" />
构建适配器PagedListAdapter
首先需要以PagedListAdapter为基类,自定义列表数据显示的适配器类。并重写以下方法:
- onCreateViewHolder----以列表项fragment_list_item为模板,返回列表项的一个新的视图对象
- onBindViewHolder—为列表中每一个视图对象绑定数据并显示
并自定义一个扩展自RecyclerView.ViewHolder的ViewHolder类,该类是与列表项布局fragment_list_item对应的。在ViewHolder中对列表项中的每一个视图控件做出声明并获取控件对象。
另外每一个ViewHolder中的视图组件的数据来自于一个自定义数据对象ItemInfo
public class ItemInfo
{
//字符串,用于在textView中显示
public String str;
//图片链接,用于显示图片
public String imgUrl;
//一个唯一标识,可以作为列表项的键
public String key;
}
整体代码大致如下:
public class MyListAdapter extends PagedListAdapter<ItemInfo,MyListAdapter.ViewHolder> {
public MyListAdapter()
{
super(DIFF_CALLBACK);
}
private static DiffUtil.ItemCallback<ItemInfo> DIFF_CALLBACK =
new DiffUtil.ItemCallback<ItemInfo>() {
@Override
public boolean areItemsTheSame(@NonNull ItemInfo oldItem, @NonNull ItemInfo newItem) {
// The ID property identifies when items are the same.
return oldItem.key == newItem.key;
}
@Override
public boolean areContentsTheSame(@NonNull ItemInfo oldItem, @NonNull ItemInfo newItem) {
// Don't use the "==" operator here. Either implement and use .equals(),
// or write custom data comparison logic here.
return oldItem.key.equals(newItem.key);
}
};
@NonNull
@Override
public MyListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_list_item,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyListAdapter.ViewHolder holder, int position) {
try {
ItemInfo _itemInfo=getItem(position);
holder._textView.setText(_itemInfo.str);
Glide.with(holder.mView).load(_itemInfo.imgUrl).into(holder._imageView);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public class ViewHolder extends RecyclerView.ViewHolder
{
public final ImageView _imageView;
public final TextView _textView;
public final View mView;
public ViewHolder(@NonNull View itemView)
{
super(itemView);
_textView=(TextView)itemView.findViewById(R.id.idOfTextView);
_imageView=(ImageView)itemView.findViewById(R.id.idOfImageView);
mView=itemView;
}
}
}
通过以上代码,可以构建一个自定义的RecyclerView的适配器类,将列表视图的每一项进行数据适配
构建数据源DataSource
DataSource类是提供分页数据快照功能的一个基类。它的子类有以下三个
关于以上三种子类的详细区分可以查看链接内容。
下面以ItemKeyedDataSource为基类扩展一个自定义数据源类MyDataSource。
public class MyDataSource extends ItemKeyedDataSource<String, ItemInfo> {
private int pageIndex=1;
@Override
public void loadInitial(@NonNull LoadInitialParams<String> params, @NonNull LoadInitialCallback<ItemInfo> callback) {
fetchItems(params.requestedInitialKey,params.requestedLoadSize,pageIndex,callback);
}
private void fetchItems(String requestedInitialKey, int requestedLoadSize,int pageIndex,LoadCallback<ItemInfo> callback)
{
try
{
//在此处写入获取当前页列表数据的代码,可以从数据库中获取,或者从后端服务器API获取
//List<ItemInfo> itemInfoList=*******
//callback.onResult(itemInfoList);//通过回调返回列表数据
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public void loadAfter(@NonNull LoadParams<String> params, @NonNull LoadCallback<ItemInfo> callback) {
try {
pageIndex=pageIndex+1;
fetchItems(params.key, params.requestedLoadSize,pageIndex,callback);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void loadBefore(@NonNull LoadParams<String> params, @NonNull LoadCallback<ItemInfo> callback) {
}
@NonNull
@Override
public String getKey(@NonNull ItemInfo item) {
return item.key;
}
}
通过以上代码可以构建一个自定义的数据源类,用于获取某一页的数据快照。
创建完MyDataSource之后,可以进一步创建一个工厂类MyDataSourceFactory,通过工厂类获得MyDataSource的对象。
public class MyDataSourceFactory extends MyDataSource.Factory<String, ItemInfo> {
private MutableLiveData<MyDataSource> sourceLiveData=new MutableLiveData<>();
private MyDataSource myDataSource;
@NonNull
@Override
public DataSource<String, ItemInfo> create() {
myDataSource=new MyDataSource();
sourceLiveData.postValue(myDataSource);
return myDataSource;
}
}
构建ViewModel
自定义一个MyViewModel类,扩展自ViewModel基类。
public class MyViewModel extends ViewModel {
public final LiveData currentItemInfoList;
public MyViewModel()
{
PagedList.Config config=new PagedList.Config.Builder().setPageSize(20).setInitialLoadSizeHint(20).setPrefetchDistance(5).build();
currentItemInfoList=new LivePagedListBuilder(new MyDataSourceFactory(),config).build();
}
}
可以进一步创建一个ViewModel工厂,用于提供MyViewModel对象。
public class MyViewModelFactory implements ViewModelProvider.Factory {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
T t = (T) new MyViewModel();
return t;
}
}
组装以上模块
完成以上模块的构建之后,就可以在界面控制器中应用这些模块,实现完整的包括列表数据获取、适配与显示等功能。以一个Activity控制器为例:
public class MyActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private MyListAdapter adapter=new MyListAdapter();
private MyViewModel viewModel;
private MyViewModelFactory viewModelFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
viewModelFactory=new MyViewModelFactory();
try {
recyclerView=(RecyclerView)findViewById(R.id.myRecyclerview);
layoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
viewModel=new ViewModelProvider(this,viewModelFactory).get(MyViewModel.class);
Observer<PagedList<ItemInfo>> listObserver= itemList->submitListForAdapter(itemList);
viewModel.currentItemInfoList.observe(this,listObserver);
}
catch (Exception e)
{
}
}
public void submitListForAdapter(PagedList<ItemInfo> itemList)
{
adapter.submitList((PagedList<ItemInfo>) itemList);
//其他自定义逻辑
}
}
通过以上的工作,可以实现使用RecyclerView及相关架构组件实现列表数据展示的这一目标。当然只是实现基本的功能与流程。对于具体的业务需求和不同的使用场景。可以视情况,在自定义的DataSource,Adapter和界面控制器中添加更多的业务逻辑代码。实现更丰富的数据展示和更复杂更完善的功能
额外说明
以上的内容中可能会用到一些组件、框架。
另外还有一些未明确解释的概念。
具体情况可以查看以下相关资料深入了解。