8.1Android编程权威指南(第3版)————使用RecyclerView显示列表
定义抽象类
public abstract class SinglesFragmentActivity extends AppCompatActivity {
public abstract Fragment createFramgnet();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
//创建一个新的fragment事务,执行一个fragment添加操作,然后提交该事务
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
/**
* 为什么要获取的fragment可能有了呢?设备旋转或回收内存时,Android系统会销
毁CrimeActivity,而后重建时,会调用CrimeActivity.onCreate(Bundle)方法。activity被
销毁时,它的FragmentManager会将fragment队列保存下来。这样,activity重建时,新的
FragmentManager会首先获取保存的队列,然后重建fragment队列,从而恢复到原来的状态。
*/
if (fragment == null) {
fragment = createFramgnet();
fm.beginTransaction()
//参数一:容器视图资源ID 参数二:创建的fragment
.add(R.id.fragment_container, fragment)
.commit();
}
}
}
recyclerview xml文件
<?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:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
public class CrimeListFragment extends Fragment {
private RecyclerView mCrimeRecyclerView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
mCrimeRecyclerView = view.findViewById(R.id.crime_recycler_view);
/**
* 注意:没有LayoutManager的支持,不仅RecyclerView无法工作,还会导致应用崩溃
摆放的任务被委托给了LayoutManager。除了在屏幕上摆放列表项,LayoutManager还
负责定义屏幕滚动行为
*/
mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
private void updateUI() {
CrimeLab crimeLab = CrimeLab.get(this);
List<Crime> crimes = crimeLab.getCrimes();
CrimeAdapter crimeAdapter = new CrimeAdapter(getActivity(), crimes);
mCrimeRecyclerView.setAdapter(crimeAdapter);
}
}
适配器
public class CrimeAdapter extends RecyclerView.Adapter<CrimeViewHolder> {
private Context mContext;
private List<Crime> mCrimes;
public CrimeAdapter(Context context, List<Crime> crimes) {
this.mContext = context;
this.mCrimes = crimes;
}
@Override
public CrimeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
CrimeViewHolder crimeViewHolder = new CrimeViewHolder(layoutInflater, parent, mContext);
return crimeViewHolder;
}
@Override
public void onBindViewHolder(CrimeViewHolder holder, int position) {//主要作用是起显示的功能
Crime crime = mCrimes.get(position);
holder.bind(crime);
}
@Override
public int getItemCount() {
return mCrimes.size();
}
}
//容纳View视图
class CrimeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
Crime mCrime;
private TextView mTitleTextView;
private TextView mDateTextView;
Context mContext;
/**
* onCreateViewHolder(ViewGroup, int)方
* 法的调用并不频繁。一旦有了够用的ViewHolder,RecyclerView就会停止调用onCreate-
* ViewHolder(...)方法。随后,它会回收利用旧的ViewHolder以节约时间和内存
*/
public CrimeViewHolder(LayoutInflater inflater, ViewGroup parent, Context context) {
super(inflater.inflate(R.layout.list_item_crime, parent, false));
this.mContext = context;
mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
mDateTextView = (TextView) itemView.findViewById(R.id.crime_data);
itemView.setOnClickListener(this);
}
public void bind(Crime crime) {
mCrime = crime;
mTitleTextView.setText(mCrime.getTitle());
mDateTextView.setText(mCrime.getDate().toString());
}
@Override
public void onClick(View view) {
Toast.makeText(mContext,
mCrime.getTitle() + " clicked!", Toast.LENGTH_SHORT)
.show();
}
}
Demo下载地址:
https://download.****.net/download/weixin_43953649/10864739
ListView 和GridView
Android操作系统核心库包含ListView、GridView和Adapter这3个类。Android 5.0之前,创建列表项或网格项都应该优先使用这些类。
这些类的API与RecyclerView的API非常相似。ListView和GridView不关心具体的展示项,只负责展示项的滚动。Adapter负责创建列表项的所有视图。不过,使用ListView和GridView时不一定非要使用ViewHolder模式(虽然可以并且应该使用)。
过去传统的实现方式现已被RecyclerView的实现方式取代,因此不用再费力地调整ListView和GridView的工作行为了。举例来说,ListView API不支持创建水平滚动的ListView,因此需要许多额外的定制工作。使用RecyclerView时,虽然创建定制布局和滚动行为也需要额外的工作,但RecyclerView天生支持拓展,所以使用体验还不错。此外RecyclerView还有支持列表项动画效果的优点。如果要让ListView和GridView支持添加和删除列表项的动画效果,实现起来既复杂又容易出错;而对于天生支持动画特效的RecyclerView来说,对付这些任务简直是小菜一碟。。例如,如果crime列表项要从位置0移动到位置5,下面这段代码就可以做到。mRecyclerView.getAdapter().notifyItemMoved(0, 5);
单例
Android开发实践中,经常会用到CrimeLab中使用过的单例模式。然而,单例若使用不当,会导致应用难以维护,因此它也常遭人诟病。Android开发常用到单例的一大原因是,它们比fragment或activity活得久。例如,在设备旋转或是在fragment和activity间跳转的场景下,单例不会受到影响,而旧的fragment或activity已经不复存在了。
单例能方便地存储和控制模型对象。假设有一个比CriminalIntent更为复杂的应用,它的许多个activity和fragment会修改crime数据。某个控制单元修改了crime数据之后,怎么保证发送给其他控制单元的是最新数据呢?如果CrimeLab掌控数据对象,所有的修改都由它来处理,是不是控制数据的一致性就容易多了?而且,在控制单元间流转时,你还以给每个crime添加ID标识,让控制单元使用ID标识从CrimeLab获取完整的crime数据。再来谈谈单例的缺点。举个例子,虽然单例能存储数据,活得也比控制单元长,但这并不代表它能永存。在我们切换至其他应用,或逢Android回收内存时,单例连同那些实例变量也就不复存在了。结论很明显:单例无法做到持久存储。(将文件写入磁盘或是发送到Web服务器是不错的数据持久化存储方案。)单例还不利于单元测试。例如,如果应用代码直接调用CrimeLab对象的静态方法,测试时以模拟版本的CrimeLab代替实际CrimeLab实例就不太现实。实践中,Android开发人员会使用依赖注入工具解决这个问题。这个工具允许以单例模式使用对象,对象也可以按需替换。使用单例很方便,因而它很容易被滥用。在想用就用、想存就存之前,希望你能深思熟虑:
数据究竟用在哪里?用在哪里能真正解决问题?假如不慎重对待这个问题,很可能后来人在查看你的单例代码时,就像打开了一个乱糟糟的
废品抽屉,里面堆满了废电池、拉链扣、旧照片,等等。它们有什么存在的意义?再强调一次:请确保有充足的理由使用单例模式存储你的共享数据!若使用得当,单例就是架构优秀的Android应用中的关键部件。