创建存储数据的单例类
package com.huangfei.criminalintent;
import java.util.Date;
import java.util.UUID;
public class Crime {
private UUID mId;
private String mTitle;
private Date mDate;
private boolean mSolved;
public Crime(){
mId = UUID.randomUUID();
setDate(new Date());
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
public UUID getId() {
return mId;
}
public Date getDate() {
return mDate;
}
public void setDate(Date date) {
mDate = date;
}
public boolean isSolved() {
return mSolved;
}
public void setSolved(boolean solved) {
mSolved = solved;
}
@Override
public String toString() {
return mTitle;
}
}
package com.huangfei.criminalintent;
import java.util.ArrayList;
import java.util.UUID;
import android.content.Context;
/**
*
* @author huangfeihong
* CrimeLab类是单例模式。应用能在内存里存在多久,单例就能存在多久,因此将对象列表保存在单例里可保持crime数据的一直存在,
* 不管activity、fragment及它们的生命周期发生什么变化。
*/
public class CrimeLab {
private ArrayList<Crime> mCrimes;
private static CrimeLab sCrimeLab;
private Context mAppContext;
private CrimeLab(Context appContext){
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
for (int i = 0; i < 100; i++) {
Crime crime = new Crime();
crime.setTitle("Crime #" + i);
crime.setSolved(i % 2 == 0);
mCrimes.add(crime);
}
}
/**
* 在get(Context)方法里,我们并没有直接将Context参数传递给构造方法。该Context可能是一个Activity,也可能是另一个Context对象,
* 如Serivie。在应用的整个生命周期里,我们无法保证只要CrimeLab需要用到Context对象,Context就一定存在。
* 因此,为保证单例总是有Context可以使用,可调用getApplicationContext()方法,将不确定是否存在的Context对象替换成 application context。
* application context是针对应用的全局性Context。任何时候,只要应用层面的单例,就该一直使用application context。
*/
public static CrimeLab get(Context c){
if(sCrimeLab == null){
sCrimeLab = new CrimeLab(c.getApplicationContext());
}
return sCrimeLab;
}
public ArrayList<Crime> getCrimes() {
return mCrimes;
}
public Crime getCrime(UUID id){
for (Crime crime : mCrimes) {
if(crime.getId().equals(id))
return crime;
}
return null;
}
}
创建ListFragment
package com.huangfei.criminalintent;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
/**
*
* @author huangfeihong 无需覆盖onCreateView(...)方法或为CrimeListFragment生成布局。
* ListFragment类默认实现方法已经生成了一个全屏ListView布局。
*/
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";
private ArrayList<Crime> mCrimes;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();
/**
* android.R.layout.simple_list_item_1是Android
* SDK提供的预定义布局资源。该布局拥有一个TextView根元素。 也可指定其他布局,只要满足布局的根元素是TextView即可。
*
* 默认的ArrayAdapter<T>.getView(...)实现方法依赖于toString()方法。它首先生成布局视图,
* 然后找到指定位置的Crime对象并对其调用 toString()方法,最后得到字符串信息并传递给TextView。
*/
CrimeAdapter adapter = new CrimeAdapter(mCrimes);
setListAdapter(adapter);
}
/**
* 响应用户对列表项的点击事件
*/
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Crime crime = (Crime) getListAdapter().getItem(position);
Log.d(TAG, crime.getTitle() + "was clicked");
}
private class CrimeAdapter extends ArrayAdapter<Crime> {
public CrimeAdapter(ArrayList<Crime> crimes) {
/**
* 这里调用超类的构造方法来绑定Crime对象的数组列表。由于不打算使用预定义布局, 我们传入0作为布局参数。
*/
super(getActivity(), 0, crimes);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = getActivity().getLayoutInflater().inflate(
R.layout.list_item_crime, null);
Crime crime = getItem(position);
TextView titleTextView = (TextView) convertView
.findViewById(R.id.crime_list_item_titleTextView);
titleTextView.setText(crime.getTitle());
TextView dateTextView = (TextView) convertView
.findViewById(R.id.crime_list_item_dateTextView);
dateTextView.setText(crime.getDate().toLocaleString());
/**
* CheckBox默认是聚焦的。这意味着,点击列表项会被解读为切换CheckBox的状态,自然也就无法触发onListItemClick(...)方法了。
* 由于ListView的这种内部特点,出现在列表项布局内的任何可聚焦组件(如CheckBox或Button)都应设置为非聚焦状态(android:focusable="false"),
* 从而保证用户点击列表项后能够获得预期效果。
*/
CheckBox solvedCheckBox = (CheckBox) convertView
.findViewById(R.id.crime_list_item_solvedCheckBox);
solvedCheckBox.setChecked(crime.isSolved());
return convertView;
}
}
}
创建托管Fragment的Activity
普通托管Fragment的通用Activity
package com.huangfei.criminalintent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
/**
*
* @author huangfeihong
* 普通托管Fragment的通用Activity
*/
public abstract class SingleFragmentActivity extends FragmentActivity {
protected abstract Fragment createFragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
/**
* 因为使用了支持库及FragmentActivity类,因此这里调用的方法是getSupportFragmentManager()。如果不考虑Honeycomb以前版本的兼容性问题,
* 可直接继承Activity类并调用getFragmentManager()方法。
*
* FragmentManager类负责管理fragment并将它们的视图添加到activity的视图层级结构中。
* FragmentManager类具体管理的是:
* 1、fragment队列;
* 2、fragment事务的回退栈。
*/
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if(fragment == null){
fragment = createFragment();
/**
* 这段代码创建并提交一个fragment事务。
* fragment事务被用来添加、移除、附加、分离或替换fragment队列中的fragment。这是使用fragment
* 在运行时组装和重新组装用户界面的核心方式。FragmentManager管理着fragment事务的回退栈。
*
* add(容器视图资源ID, 新创建的fragment)
* 容器视图资源ID的作用:
* 1、告知FragmentManagerfragment视图应该出现在activity视图的什么地方;
* 2、是FragmentManager队列中fragment的唯一标识符,见第25行代码’
*/
fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
}
}
}
实现普通托管Fragment的通用Activity
public class CrimeListActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}
}
代码地址