RecyclerView显示 列表

来源:互联网 发布:c语言入门书 编辑:程序博客网 时间:2024/05/29 12:41

一,简介

1. RecyclerViewAdapterViewHolder的关系

我们需要CrimeListFragment向用户展示crime列表,这就要用到RecyclerView类。RecyclerViewViewGroup的子类,每一个列表项都是作为一个View子对象显示的。这些View子对象既可以是复杂的View对象,也可以是简单的View对象,这取决于我们对列表显示复杂度的需要

RecyclerView的任务仅限于回收和定位屏幕上的TextViewTextView能够显示数据还离不开另外两个类的支持:Adapter子类和ViewHolder子类。ViewHolder要做的事很少,我们首先讨论它。顾名思义,ViewHolder只做一件事:容纳View视图 




adapter
9-6进行了简化,实际上隐藏了一些信息。RecyclerView自己不创建ViewHolder。这个任务实际是由adapter来完成的。adapter是个控制器对象,从模型层获取数据,然后提供给RecyclerView显示,起到了沟通的桥梁作用。
adapter负责:
创建必要的ViewHolder
绑定ViewHolder至模型层数据。
要创建
adapter,首先要定义RecyclerView.Adapter子类。然后由它封装从CrimeLab获取的crime

RecyclerView需要显示视图对象时,就会去找它的adapter。图9-7展示了一个RecyclerView可能发起的会话。
首先,通过调用
adaptergetItemCount()方法,RecyclerView询问数组列表中包含多少个对象。
接 着 ,
RecyclerView 调 用adaptercreateViewHolder(ViewGroup, int)方 法 创 建ViewHolder以及ViewHolder要显示的视图。
最后,
RecyclerView会传入ViewHolder及其位置,调用onBindViewHolder(ViewHolder,int)方法。adapter会找到目标位置的数据并绑定ViewHolder的视图上。所谓绑定,就是使用模型数据填充视图。
整个过程执行完毕,
RecyclerView就能在屏幕上显示crime列表项了。需要注意的是,相对于onBindViewHolder(ViewHolder, int)方法,createViewHolder(ViewGroup, int)方法
的调用并不频繁。一旦创建了够用的
ViewHolderRecyclerView就会停止调用createViewHolder(...)方法。然后,通过回收利用旧的ViewHolder节约时间和内存。 



实质上,recycler类似于安卓内联的listview,只是其是支持库而已

添加支持库:

单击FileProject Structure....菜单项切换至项目结构窗口,选择左边的app模块,然后单击Dependencies选项页。单击+按钮弹出依赖库添加窗口。找到并选择recyclerview-v7支持库,单击OK按钮完成依赖库添加, 



例程:该例子是基于fragment显示的

1. RecyclerView的XML,Recycler界面用于容乃列表项:

<?xml version="1.0" encoding="utf-8"?><android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"              android:id="@+id/crime_recycler_view"              android:orientation="vertical"              android:layout_width="match_parent"              android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
2. 列表项的布局XML,用于设置每一项的布局:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="match_parent"              android:layout_height="wrap_content">    <TextView        android:id="@+id/crime_list_LabelText"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:textStyle="bold"        android:padding="4dp"        android:layout_toLeftOf="@+id/crime_list_CheckBox"        android:text="TextView"/>    <TextView        android:id="@+id/crime_list_DateText"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_toLeftOf="@+id/crime_list_CheckBox"        android:layout_below="@id/crime_list_LabelText"        android:padding="4dp"        android:text="TextView"/>    <CheckBox        android:id="@+id/crime_list_CheckBox"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:text="CheckBox"        android:padding="4dp"/></RelativeLayout>
3. Crime类,自定义类,表示每个元素的数据,模型里的单个数据结构:

//基本元素单位类public class Crime {    private UUID yID;    private String yTitle;    private Date yDate;    private  boolean CrimeFlag;    public Crime() {        yID = UUID.randomUUID();        yDate = new Date();    }    public UUID getyID() {        return yID;    }    public String getyTitle() {        return yTitle;    }    public void setyTitle(String yTitle) {        this.yTitle = yTitle;    }    public Date getyDate() {        return yDate;    }    public void setyDate(Date yDate) {        this.yDate = yDate;    }    public boolean getCrimeFlag() {        return CrimeFlag;    }    public void setCrimeFlag(boolean crimeFlag) {        CrimeFlag = crimeFlag;    }}
4. List类,模型本身:

//元素组合类public class CrimeListLab {    private static CrimeListLab yCrimeLab;    private List<Crime>yCrime;    private CrimeListLab(Context context)    {        yCrime = new ArrayList<>();        for(int i=0;i<100;i++){            Crime crime = new Crime();            crime.setyTitle("Crime #" + i);            crime.setCrimeFlag(i%2 == 0);            yCrime.add(crime);        }    }    public static CrimeListLab getyCrimeLab(Context context) {        if(null == yCrimeLab){            yCrimeLab = new CrimeListLab(context);        }        return yCrimeLab;    }    public List<Crime> getyCrime() {        return yCrime;    }    public Crime getCrime(UUID uuid) {        for (Crime crime:yCrime) {            if(crime.getyID().equals(uuid)){                return crime;            }        }        return null;    }}
5. fragment的内容,也是决定Recycler如何显示:

//控制RecyclerView的Fragmentpublic class CrimeListFragment extends Fragment{    //RecyclerView对象    private RecyclerView yCrimeRecyclerView;    //Adapter对象    private CrimeAdapter yCrimeAdapter;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        //绑定布局        View view = inflater.inflate(R.layout.fragment_crime_list, container, false);        //绑定并实例化Recycler        yCrimeRecyclerView = (RecyclerView) view               .findViewById(R.id.crime_recycler_view);        yCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));        //自定义的将Adapter与Recycler绑定的函数        updateUI();        return view;    }    //自定义的将Adapter与Recycler绑定的函数    public void updateUI(){        CrimeListLab crimelistlab = CrimeListLab.getyCrimeLab(getActivity());        List<Crime>crimeList = crimelistlab.getyCrime();        yCrimeAdapter = new CrimeAdapter(crimeList);        yCrimeRecyclerView.setAdapter(yCrimeAdapter);    }    //继承Holder的内部类定义,该类实例化布局内部组件资源    //此处还继承监听事件接口    public class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener{        private Crime yCrime;//用于接受Crime元素对象        private TextView yTitleLabel;        private TextView yTitleDate;        private CheckBox yCheckBox;        public CrimeHolder(View itemView) {            super(itemView);            itemView.setOnClickListener(this);//监听            //关联布局内的组件资源实例化            yTitleLabel = (TextView) itemView.findViewById(R.id.crime_list_LabelText);            yTitleDate = (TextView) itemView.findViewById(R.id.crime_list_DateText);            yCheckBox = (CheckBox) itemView.findViewById(R.id.crime_list_CheckBox);        }        //自定义方法,用于在Adapter的视图和数据绑定函数调用以实现数据绑定        public void bindCrime(Crime crime){            yCrime = crime;//通过每个传进来的Crime元素来获取数据            yTitleLabel.setText(yCrime.getyTitle());            yTitleDate.setText(yCrime.getyDate().toString());            yCheckBox.setChecked(yCrime.getCrimeFlag());        }        //监听事件的相应        @Override        public void onClick(View v){            Toast.makeText(getActivity(),                    yCrime.getyTitle() + " clicked!", Toast.LENGTH_SHORT)                    .show();        }    }    //决定项的布局,项的数目,并将项和数据模型绑定的Adpater类    public class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder>{        //用于接收存放List资源的对象        private List<Crime> yCrimelist;        //构造类,内部用于接受存放List资源        public CrimeAdapter(List<Crime>yCrimelist){//可能有内外部类同名隐患            this.yCrimelist = yCrimelist;        }        //创建项视图,关联项布局        @Override        public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {            LayoutInflater layoutInflater = LayoutInflater.from(getActivity());            //寻找项的布局文件            View view = layoutInflater.inflate(R.layout.fragment_crime_list_item,parent,false);            return new CrimeHolder(view);        }        //将视图和数据模型绑定        @Override        public void onBindViewHolder(CrimeHolder holder, int position) {            Crime crime = yCrimelist.get(position);//获取每个crime数据            holder.bindCrime(crime);//调用CrimeHolder的方法将数据传递并更新        }        //返回一共有多少项        @Override        public int getItemCount() {            return yCrimelist.size();        }    }}
6. 托管fragment的Activity

//在Activity建立fragmentmanager来托管xml布局,此时继承的是通用ActivityFragmentManager抽象类public class CrimeListActivity extends CrimeActivity {    @Override    protected Fragment createFragment(){        return new CrimeListFragment();    }}
7. 其他,托管fragment的Activity的超类:

//创建通用的ActivityFragmentManager的抽象类,防止重复代码public abstract class CrimeActivity extends FragmentActivity {    protected abstract Fragment createFragment();//抽象创建Fragment布局接口    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.crime_layout);        FragmentManager fm = getSupportFragmentManager();        Fragment yFragment = fm.findFragmentById(R.id.fragment_container);        if(null == yFragment){            yFragment = createFragment();//用抽象接口创建Fragment布局,可以根据需要创建不同接口            fm.beginTransaction()                    .add(R.id.fragment_container,yFragment)                    .commit();        }    }}






深入学习: ListViewGridView
Android操作系统核心库包含ListViewGridViewAdapter类。Android 5.0之前,创建列表项或网格项都应该优先使用这些类。
这些类的
APIRecyclerView非常相似。ListViewGridView不关心具体的展示项,只负责展示项的滚动。Adapter负责创建列表项的所有视图。不过,使用ListViewGridView不一定非要使用ViewHolder模式(虽然可以并且应该使用)。
过去传统的实现方式现已被
RecyclerView的实现方式取代,因为我们不用再费力地去调整ListViewGridView的工作行为了。举例来说,ListView API不支持创建水平滚动的ListView,我们需要许多额外的定制工作。
使用
RecyclerView时,虽然创建定制布局和滚动行为需要额外的工作,但RecyclerView天生支持拓展,所以使用体验还不错。
此外,
RecyclerView还有支持列表项动画效果的优点。如果要让ListViewGridView支持添加和删除列表项的动画效果,实施任务既复杂又容易出错;而对于天生支持动画特效的RecyclerView来说,对付这些任务简直是小菜一碟。
口吐莲花,不如直接秀代码。例如,如果
crime列表项要从位置0移动到位置5,下面这段代码就可以做到:

mRecyclerView.getAdapter().notifyItemMoved(0, 5);




深入学习:单例
Android开发实践中,经常会用到CrimeLab中使用过的单例模式。然而,单例使用不当的话,会导致应用难以维护,因此它也常遭人诟病。
Android开发常用到单例的一大原因是,它们比fragmentactivity活得久。例如,在设备旋转或是在fragmentactivity间跳转的场景下,单例不会受到影响,而旧的fragmentactivity已经不复存在了。
单例能方便地存储控制模型层对象。假设有个比CriminalIntent更为复杂的CriminalIntent应用,它的许多个activityfragment会修改crime数据。某个控制单元修改了crime数据之后,怎么保证发送给其他控制单元的是最新数据呢?如果CrimeLab掌控数据对象,所有的修改都由它来处理,是不是数据的一致性控制就容易多了?而且,在控制单元间流转时,我们还可以给每个crime添加ID标识,让控制单元使用ID标识从CrimeLab获取完整的crime数据。
再来谈谈单例的缺点。举个例子,虽然单例能存储数据,活得比控制单元长久,但这并不代表它能永存。在我们切换至其他应用,又逢
Android回收内存时,单例连同那些实例变量也就不复存在了。结论很明显:单例无法做到持久存储。(将文件写入磁盘或是发送到Web服务器是不
错的数据持久化存储方案。)
单例还不利于单元测试。例如,如果应用代码直接调用
CrimeLab对象的静态方法,测试时以模拟版本的CrimeLab代替实际CrimeLab实例就不太现实。实践中,Android开发人员会使用依赖注入工具解决这个问题。这个工具允许以单例模式使用对象,对象也可以按需替换。
单例使用很方便,因而很容易被滥用。在想用就用,想存就存之前,希望你能深思熟虑:数据究竟用在哪里?用在哪里能真正解决问题?假如不慎重对待这个问题,很可能后来人在查看你的单例代码时,就像打开了一个满是乱糟糟废品的抽屉:废电池、拉链扣、旧照片,等等。它们有什么存在的意义?再强调一次:请确保有充足的理由使用单例模式存储你的共享数据!
使用得当,单例就是拥有优秀架构的
Android应用中的关键部件













原创粉丝点击