安卓中,从api中获取数据并填充到listview、RecyclerView以及fragment的各个adapter适配器的定义以及绑定的基本步骤及相关问题。
listview
? listview是常见的列表视图。
定义listview
XML
定义<listview>
activity
定义listview类xml
的id获得数据
调用retrofit
将api中的数据存入数据类
retrofit
小白使用步骤:Retrofit2 实战(一、使用详解篇) - 掘金 (juejin.cn)
利用rxjava
将获得网络线程获得的数据提取到主线程使用
rxjava结合retrofit获得数据:Rxjava+Retrofit简单使用-CSDN博客
设置适配器
连接UI并赋予单个item数据,数据由外部传入
继承ArrayAdapter<T>
getView
中定义对单个Item中组件取值并赋值初始化
public class OrderAdapter extends ArrayAdapter<OrderCategories> {
public OrderAdapter(@NonNull Context context, int resource, @NonNull List<T> objects) {
super(context, resource, objects);
}
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
OrderCategories categorie=getItem(position);//得到当前项的Article实例
//为每一个子项加载设定的布局
View view= LayoutInflater.from(getContext()).inflate(R.layout.item_category,parent,false);//传入单个item的布局
//定义视图类并绑定组件
ImageView image =view.findViewById(R.id.category_image);
TextView name=view.findViewById(R.id.category_text);
//提取数据并赋值到视图类
String imgurl = categorie.getPicture();
Uri uri = Uri.parse(imgurl);
Glide.with(getContext())
.load(uri)
.apply(options2)
.into(image);
return view;
}
}
定义使用适配器
在主函数中list设置适配器来获得整个列表的数据
mAdapter = new OrderAdapter(this,R.layout.item_category,Ocategories);//赋予单项的布局
listView.setAdapter(mAdapter);
RecyclerView
列表? RecyclerView能实现复杂的listview,如横着滑动或者一行三个的布局。
定义及数据获取相同
适配器类定义
public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.ViewHolder> {
//1.适配器初始化,传入list数据
public FoodAdapter(List<Food> cardList) {
this.cardList = cardList;
}
//2.初始化view,获得view
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_order, parent, false);
//将view和上下文变为全局变量
mview=view;
mContext = parent.getContext();
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
//获得列表中的数据
Food card = cardList.get(position);
//从数据源中提取数据
/*以card为例*/
String imgurl = card.getPicture();
String name = card.getName();
String price = card.getPrice();
//将数据赋值到视图变量
Glide.with(mContext)
.load(uri)
.apply(options)
.into(holder.FoodImage);
}
@Override
public int getItemCount() {
return cardList.size();
}
//3.定义视图类并绑定xml组件
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView FoodImage;
public ViewHolder(@NonNull View itemView) {
super(itemView);
FoodImage = itemView.findViewById(R.id.food_imageView);
}
}
}
RecyclerView可以通过setLayoutManager实现多种布局
以每行三个item的布局为例
mRecyclerView = findViewById(R.id.recyclerview);
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
? viewpaper2+tablayout可以适配多个fragment,实现的就是导航栏切换页面的效果。
适配器类继承FragmentStateAdapter
public class ProjectItemDataAdapter extends FragmentStateAdapter {
List<PItemDataFragment> fragments;
//初始化
public ProjectItemDataAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<PItemDataFragment> fragments) {
super(fragmentManager, lifecycle);
this.fragments = fragments;
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
}
使用适配器并关联TabLayout和Viewpaper2
//实例化适配器
myAdapter=new KnowItemAdapter(fm,lc,chFragments);
//viewpaper类设置适配器
wxPager2.setAdapter(myAdapter);
//TabLayout和Viewpager2进行关联
new TabLayoutMediator(wxTab, wxPager2, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
//可进行对标题栏各单项进行操作
tab.setText(titles.get(position));
}
}).attach();
会遇到的问题
因为viewpaper2和fragment结合包含多个嵌套,会出现该fragment其实是外部的tablayout导航栏的某一页面,而其中的fragment中也包含list,所以也会进行list定义及数据获取。
Fragment no longer exists for key f0: index 0
错误
实现背景:在二级fragment中(即外部导航栏的某一页中的导航栏的某一页)用retrofit获取数据,填充列表
错误描述:外部的导航栏切换至其他页面再切换回本页面(一级)会闪退
外部原因:获得fragment出错
wxPager2.setSaveEnable(false)
,参考链接viewpager2 Fragment no longer exists for key f#0:-CSDN博客重写fragment的生命周期函数,输出各全局变量的值
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG,"KnowChapterFragment创建");
Log.v(TAG,"此时的cid为"+id);
Log.v(TAG,"此时的page为"+page);
}
发现,虽然切换到其他页面(外部导航栏),该fragment已经销毁(执行onDestory),但是全局变量的数据仍然还在,也就是说,retrofit获得的数据仍然存在全局变量list中。
@Override
public void onDestroy() {
Articledatas.clear();
page=1;
super.onDestroy();
}
当多个组件嵌套无从下手时,可以在形成过程中重写调试输出来获得变量值以求解。
Fragment has not been attached yet
onCreateView
中定义视图,在onViewCreated
中绑定组件并配置适配器;
以上举例的adapter是执行的是一次性数据显示;
api获得数据常常会传入页数来分批次获得大量的数据,所以对应的adapter也可以通过adapter.notifyDataSetChanged();
来实现列表数据更新
会达到一个数据闪现更新的效果
注:不需要重新new一个adapter和重写绑定数据列表,更新只需要在原本已经绑定以及数据列表数据更新的基础上,执行上诉语句即可。
如果多次new,可能会报错
if(page==1) //api网址传入的界面
{
if(getActivity()!=null)
{
adapter = new KnowledgeAdapter(getContext(),R.layout.item_acticle,Articledatas);
}
listView.setAdapter(adapter);
setAdapterClick();
}
else
{
if(setOverText())
{
adapter.notifyDataSetChanged(); //更新
loadMoreButton.setText("查看更多..."); //恢复按钮文字
}
}