赞
踩
单例与数据集中存储
单例是特殊的Java类,在创建实例时,一个单例类仅允许创建一个实例。
应用能在内存里活多久,单例就能活多久。因此将对象列表保存在单例里的话,就能随时获取crime数据,不管activity和fragment周期怎么变化。使用单例时还应该注意一点:Android从内存中清除应用时,单例对象也会随之消失。后文会对单例进一步介绍。
RecyclerView、ViewHolder和Adapter
RecyclerView是ViewGroup的子类,每一个列表项都是作为一个View子对象来显示的。这些View子对象可简单可复杂,这取决于列表项要显示些什么。
一次为所有的列表项创建View很容易搞垮应用。RecyclerView只创建刚好可以充满屏幕的N个View,而不是一次创建所有的。用户滑动切换视图时,上一个视图会回收利用。顾名思义,RecyclerView就是回收再利用,循环往复。
RecyclerView的任务仅限于回收和定位屏幕上的View。列表项View能够显示数据还离不开另外两个类的支持:ViewHolder子类和Adapter子类。
ViewHolder只做一件事:容纳View视图。定义每个列表项中包含的组件并将它们实例化,并定义点击事件等。
RecyclerView不能自己创建ViewHolder,而是通过Adapter来完成的。Adapter是一个控制器对象,从模型层获取数据,然后供给RecyclerView显示,是沟通的桥梁。
Adapter负责:
1.创建必要的ViewHoler;
2.绑定ViewHolder至模型层数据。
RecyclerView需要显示视图对象时,就会去找它的Adapter。
首先,调用Adapter的getItemCount()方法,RecyclerView会询问数组列表中包含多少个对象。
接着,RecyclerView调用Adapter的onCreateViewHolder(ViewGroup, int)方法创建ViewHolder及其要显示的视图。
最后,RecyclerView会传入ViewHolder及其位置,调用onBindViewHolder(ViewHolder, int)方法。Adapter会找到目标位置的数据并将其绑定在ViewHolder视图上。所谓绑定,就是使用模型数据填充视图。
LayoutManager
RecyclerView类不会亲自摆放屏幕上的列表项。实际上,摆放的任务被委托给了LayoutManager。除了在屏幕上的摆放,LayoutManager还负责定义屏幕滚动行为。除了一些Android操作系统内置版实现,LayoutManager还有很过第三方库实现版本。LinearLayoutManager类使用竖直列表的形式展示列表项。后续还会使用GridLayoutManager类,以网格的形式展示列表项。
ListView和GridView
Android操作系统核心库包含ListView、GridView和Adapter这3个类。在Android 5.0之前,创建列表项或网各项都应该优先使用这些类。
这列类的API与RecyclerView的API非常相似。ListView和GridView不关系具体的展示项,只负责展示项的滚动。Adapter负责创建列表项的所有视图。不过,使用ListView和GrisView时不一定非要使用ViewHolder模式(虽然可以并且应该使用)。
现在已经被RecyclerView替代,举例说明替代理由:
1.ListView的API不支持创建水平滚动的ListView,因此需要许多额外的定制工作。使用RecyclerView时,虽然创建定制布局和滚动行为也需要额外的工作,但RecyclerView天生支持拓展,所以使用体验还不错。
2.RecyclerView还有支持列表项动画效果的优点。如果要让ListView和GridView支持添加和删除列表项的动画效果,实现起来既复杂又容易出错;
挑战练习:RecyclerView ViewType
请在RecyclerView中创建两类列表项:一般性crime,以及需警方介入的crime。要完成这个挑战,你需要用到RecyclerView. Adapter的视图类别功能(view type)。在Crime对象里,再添加一个mRequiresPolice实例变量,使用它并借助getltemViewType(int)方法(developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)), 确定该加载哪个视图到CrimeAdapter。
在onCreateViewHolder(ViewGroup, int)方法里,基于getltemViewType(int)方法返回的viewType值,需要返回不同的ViewHolder。如果是一般性crime,就仍然使用原始布局;如果是需警方介人的crime,就使用一个带联系警方按钮的新布局
思路:
模型层需要在Crime对象里添加一个mRequiresPolice实例变量,用于控制是否需要显示特定的视图;视图层需要新定义一个列表项视图,其中包括报警的按钮;然后控制层对应创建新添列表项的ViewHolder,ViewHolder的选择机制在onCreateViewHolder(ViewGroup, int)方法里完成。
更改Crime对象:
Crime.java
- ……
- private int mRequiresPolice;
- ……
- public int getRequiresPolice() {
- return mRequiresPolice;
- }
-
- public void setRequiresPolice(int requiresPolice) {
- mRequiresPolice = requiresPolice;
- }
- ……
还要编辑CrimeLab在构造Crime数组时,设定好哪些Crime对象需要报警:
CrimeLab.java
- ……
- private CrimeLab(Context context) {
- // 单例类使用私有的构造方法,使得外部无法直接实例化单例类
- mCrimes = new ArrayList<>();
- for (int i = 0; i < 100; i++) {
- Crime crime = new Crime();
- crime.setTitle("Crime #" + i);
- crime.setSolved(i % 2 == 0);
- // 初始化哪些Crime对象需要报警
- crime.setRequiresPolice(i % 2);
- mCrimes.add(crime);
- }
- }
- ……
新建列表项布局文件:
list_item_crime_requires_police.xml
- <?xml version="1.0" encoding="utf-8"?>
- <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/crime_title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:text="Crime 标题"
- app:layout_constraintEnd_toStartOf="@+id/call_police_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <TextView
- android:id="@+id/crime_date"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:layout_marginBottom="8dp"
- android:text="Crime 日期"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/call_police_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/crime_title" />
-
- <Button
- android:id="@+id/call_police_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:layout_marginBottom="8dp"
- android:text="一键报警"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- </android.support.constraint.ConstraintLayout>

接下来在CrimeListFragment中改动,新增CrimeRequiresPoliceHolder,修改CrimeAdapter:
CrimeListFragment.java
- ……
-
- private class CrimeRequiresPoliceHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
-
- private TextView mTitleTextView;
- private TextView mDateTextView;
- private Button mCallPoliceButton;
- private Crime mCrime;
-
- public CrimeRequiresPoliceHolder(LayoutInflater inflater, ViewGroup parent) {
- super(inflater.inflate(R.layout.list_item_crime_requires_police, parent, false));
- itemView.setOnClickListener(this);
- mTitleTextView = itemView.findViewById(R.id.crime_title);
- mDateTextView = itemView.findViewById(R.id.crime_date);
- mCallPoliceButton = itemView.findViewById(R.id.call_police_button);
- mCallPoliceButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Toast.makeText(getActivity(), "我要报警啦!", Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- public void bind(Crime crime) {
- mCrime = crime;
- mTitleTextView.setText(mCrime.getTitle());
- mDateTextView.setText(mCrime.getDate().toString());
- }
-
- @Override
- public void onClick(View v) {
- Toast.makeText(getActivity(), mCrime.getTitle() + "Clicked!", Toast.LENGTH_SHORT).show();
- }
- }
- ……
- private class CrimeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
- private List<Crime> mCrimes;
-
- public CrimeAdapter(List<Crime> crimes) {
- mCrimes = crimes;
- }
-
- @NonNull
- @Override
- public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
- if (viewType != 0) return new CrimeRequiresPoliceHolder(layoutInflater, parent);
- return new CrimeHolder(layoutInflater, parent);
- }
-
- @Override
- public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
- Crime crime = mCrimes.get(position);
- if (crime.getRequiresPolice() != 0) {
- CrimeRequiresPoliceHolder crimeRequiresPoliceHolder = (CrimeRequiresPoliceHolder) holder;
- crimeRequiresPoliceHolder.bind(crime);
- }else {
- CrimeHolder crimeHolder = (CrimeHolder) holder;
- crimeHolder.bind(crime);
- }
- }
-
- @Override
- public int getItemCount() {
- return mCrimes.size();
- }
-
- @Override
- public int getItemViewType(int position) {
- return mCrimes.get(position).getRequiresPolice();
- }
- }
- ……

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。