安卓开发学习笔记—————《Anroid编程权威指南》第七章至第十九章 CriminalIntent完整开发记录
前言:由于CriminalIntent应用涉及内容较多,逐章记录不便于完整学习,故将全部的CriminalIntent开发过程记录于本篇文章之中。
应用名称:CriminalIntent
应用功能:该应用可详细记录各种办公室陋习。
——————————————第七章 UI fragment与fragment管理器——————————————
创建Crime类,Crime实例代表某种办公室陋习。
标题是一段描述性名称,标识ID是别是Crime实力的唯一元素。
public class Crime { private UUID mId; private String mTitle; private Date mDate; private boolean mSolved; public Crime() { mId = UUID.randomUUID(); //随机产生唯一ID值 mDate = new Date(); } public UUID getId(){ return mId; } public String getTitle() { return mTitle; } public void setTitle(String title) { mTitle = title; } public Date getDate() { return mDate; } public void setDate(Date date) { mDate = date; } public boolean isSolved() { return mSolved; } public void setSolved(boolean solved) { mSolved = solved; } }
activity托管UI fragment,在布局中必须为fragment的视图安排位置以及管理fragment实例的生命周期。fragment的生命周期方法由托管activity调用。
托管方式分为:(1)activity布局中添加fragment (2)activity代码中添加fragmnet
本项目通过代码的方式添加fragmnet,要在activity视图层级结构中为frament视图安排位置。
定义容器视图
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout>
CrimeFragment类是与模型及视图对象交互的控制器,用于显示特定crime的明细信息。
CrimeFragment视图布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" > <TextView style="?android:listSeparatorTextViewStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/crime_title_label" /> <EditText android:id="@+id/crime_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/crime_title_hint" /> <TextView style="?android:listSeparatorTextViewStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/crime_details_label" /> <Button android:id="@+id/crime_date" android:layout_width="match_parent" android:layout_height="wrap_content" /> <CheckBox android:id="@+id/crime_solved" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/crime_solved_label" /> </LinearLayout>
onCreateView()方法实例化fragment视图的布局,然后将实例化的View返回给托管activity。fragment的视图调用LayoutInflater()方法并传入布局的资源ID生成。第二个参数是视图的父视图,需要父视图来正确配置组件。第三个参数告诉布局生成器是否将生成的视图添加给父视图,以代码的方式添加视图,传入false。
public class CrimeFragment extends Fragment { private Crime mCrime; private EditText mTitleField; private Button mDateButton; private CheckBox mSolvedCheckBox; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCrime = new Crime(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_crime,container,false); mTitleField = (EditText)v.findViewById(R.id.crime_title); mTitleField.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //This space intentionally left blank } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mCrime.setTitle(s.toString()); } @Override public void afterTextChanged(Editable s) { //This one too } }); mDateButton=(Button)v.findViewById(R.id.crime_date); mDateButton.setText(mCrime.getDate().toString()); mDateButton.setEnabled(false); mSolvedCheckBox=(CheckBox)v.findViewById(R.id.crime_solved); mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { } }); return v; } }
向FragmentManager 添加UI fragment
FragmentManager类负责管理fragment并将它们的视图添加到activity的视图层级结构中。
FragmentManager使用FrameLayout组件的资源ID识别CrimeFragment,如要向activity添加多个fragment,需要分别为每个fragment创建具体不同ID的不同容器。
容器视图资源ID的作用:1.告诉FragmentManager,fragment视图应该出现在activity视图的什么位置。2.唯一标识FragmentManger队列中的fragment。
public class CrimeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crime); FragmentManager fm = getSupportFragmentManager(); Fragment fragment = fm.findFragmentById(R.id.fragment_container); if(fragment == null) { fragment = new CrimeFragment(); fm.beginTransaction().add(R.id.fragment_container,fragment).commit(); } } }
FragmentManager使用FrameLayout组件的资源ID识别CrimeFragment,如果指定容器视图资源ID的fragment不存在,则fragment变量为空值。
——————————————第八章 使用RecyclerView显示列表——————————————
更新CriminalIntent应用以支持显示crime列表
CrimeLab,一个数据集中存储池
public class CrimeLab { private static CrimeLab sCrimeLab; private List<Crime> mCrimes; public static CrimeLab get(Context context) { if(sCrimeLab == null) { sCrimeLab = new CrimeLab(context); } return sCrimeLab; } 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); mCrimes.add(crime); } } public List<Crime> getCrimes() { return mCrimes; } public Crime getCrime(UUID id) { for(Crime crime : mCrimes) { if(crime.getId().equals(id)) { return crime; } } return null; } }
复用CrimeActivity代码来创建CrimeListActivity类
public abstract class SingleFragmentActivity extends AppCompatActivity { protected abstract Fragment createFragment(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment); FragmentManager fm = getSupportFragmentManager(); Fragment fragment = fm.findFragmentById(R.id.fragment_container); if(fragment == null) { fragment = createFragment(); fm.beginTransaction().add(R.id.fragment_container,fragment).commit(); } } }
修改CrimeActivity
public class CrimeActivity extends SingleFragmentActivity { @Override protected Fragment createFragment() { return new CrimeFragment(); } }
新建两个控制类
CrimeListActivity
public class CrimeListActivity extends SingleFragmentActivity { @Override protected Fragment createFragment() { return new CrimeListFragment(); } }
CrimeListFragment
public class CrimeListFragment extends Fragment { //Nothing yet }
RecyclerView:用于回收和定位屏幕上的View。
View Holder:容纳View视图。
Adapter:创建必要的ViewHolder,绑定ViewHolder至模型层数据。
CrimeListFragment布局文件fragment_crime_list.xml
<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>
列表项视图list_item_crime.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="8dp"> <TextView android:id="@+id/crime_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Crime Title"/> <TextView android:id="@+id/crime_date" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Crime Date"/> </LinearLayout>
CrimeListFragment.java
public class CrimeListFragment extends Fragment { private RecyclerView mCrimeRecyclerView; private CrimeAdapter mAdapter; @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=(RecyclerView)view.findViewById(R.id.crime_recycler_view); mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); updateUI(); return view; } private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ private TextView mTitleTextView; private TextView mDateTextView; private Crime mCrime; public CrimeHolder(LayoutInflater inflater, ViewGroup parent) { super(inflater.inflate(R.layout.list_item_crime,parent,false)); itemView.setOnClickListener(this); mTitleTextView=(TextView)itemView.findViewById(R.id.crime_title); mDateTextView=(TextView)itemView.findViewById(R.id.crime_date); } @Override public void onClick(View v) { Toast.makeText(getActivity(),mCrime.getTitle()+" cliced!",Toast.LENGTH_SHORT).show(); } public void bind(Crime crime){ mCrime = crime; mTitleTextView.setText(mCrime.getTitle()); mDateTextView.setText(mCrime.getDate().toString()); } } private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> { private List<Crime> mCrimes; public CrimeAdapter(List<Crime> crimes) { mCrimes =crimes; } @NonNull @Override public CrimeHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater layoutInflater =LayoutInflater.from(getActivity()); return new CrimeHolder(layoutInflater,parent); } @Override public void onBindViewHolder(@NonNull CrimeHolder holder, int position) { Crime crime = mCrimes.get(position); holder.bind(crime); } @Override public int getItemCount() { return mCrimes.size(); } } private void updateUI() { CrimeLab crimeLab = CrimeLab.get(getActivity()); List<Crime> crimes =crimeLab.getCrimes(); mAdapter = new CrimeAdapter(crimes); mCrimeRecyclerView.setAdapter(mAdapter); } }
——————————————第九章 使用布局与组件创建用户界面——————————————
本章主要讲了ConstraintLayout的使用,更具体的使用参考 “ConstraintLayout完全解析(郭霖)"。
———————————————第十章 使用fragment argument———————————————
从fragment中启动Activity
CrimeListFragment.java
public class CrimeListFragment extends Fragment { private RecyclerView mCrimeRecyclerView; private CrimeAdapter mAdapter; @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=(RecyclerView)view.findViewById(R.id.crime_recycler_view); mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); updateUI(); return view; } private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ private TextView mTitleTextView; private TextView mDateTextView; private ImageView mSolvedImageView; private Crime mCrime; public CrimeHolder(LayoutInflater inflater, ViewGroup parent) { super(inflater.inflate(R.layout.list_item_crime,parent,false)); itemView.setOnClickListener(this); mTitleTextView=(TextView)itemView.findViewById(R.id.crime_title); mDateTextView=(TextView)itemView.findViewById(R.id.crime_date); mSolvedImageView=(ImageView)itemView.findViewById(R.id.crime_solved); } @Override public void onClick(View v) { Intent intent = CrimeActivity.newIntent(getActivity(),mCrime.getId()); startActivity(intent); } public void bind(Crime crime){ mCrime = crime; mTitleTextView.setText(mCrime.getTitle()); mDateTextView.setText(mCrime.getDate().toString()); mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE :View.GONE); } } private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> { private List<Crime> mCrimes; public CrimeAdapter(List<Crime> crimes) { mCrimes =crimes; } @NonNull @Override public CrimeHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater layoutInflater =LayoutInflater.from(getActivity()); return new CrimeHolder(layoutInflater,parent); } @Override public void onBindViewHolder(@NonNull CrimeHolder holder, int position) { Crime crime = mCrimes.get(position); holder.bind(crime); } @Override public int getItemCount() { return mCrimes.size(); } } @Override public void onResume() { super.onResume(); updateUI(); } private void updateUI() { CrimeLab crimeLab = CrimeLab.get(getActivity()); List<Crime> crimes =crimeLab.getCrimes(); if(mAdapter == null) { mAdapter = new CrimeAdapter(crimes); mCrimeRecyclerView.setAdapter(mAdapter); }else { mAdapter.notifyDataSetChanged(); } } }
CrimeActivity.java
public class CrimeActivity extends SingleFragmentActivity { private static final String EXTRA_CRIME_ID = "com.example.android.criminalintent.crime_id"; @Override protected Fragment createFragment() { //return new CrimeFragment(); UUID crimeId =(UUID)getIntent().getSerializableExtra(EXTRA_CRIME_ID); return CrimeFragment.newInstance(crimeId); } public static Intent newIntent(Context packageContext, UUID crimeId) { Intent intent = new Intent(packageContext,CrimeActivity.class); intent.putExtra(EXTRA_CRIME_ID,crimeId); return intent; } }
CrimeFragment.java
public class CrimeFragment extends Fragment { private static final String ARG_CRIME_ID ="crime_id"; private Crime mCrime; private EditText mTitleField; private Button mDateButton; private CheckBox mSolvedCheckBox; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); UUID crimeId = (UUID)getArguments().getSerializable(ARG_CRIME_ID); mCrime =CrimeLab.get(getActivity()).getCrime(crimeId); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_crime,container,false); mTitleField = (EditText)v.findViewById(R.id.crime_title); mTitleField.setText(mCrime.getTitle()); mTitleField.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //This space intentionally left blank } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mCrime.setTitle(s.toString()); } @Override public void afterTextChanged(Editable s) { //This one too } }); mDateButton=(Button)v.findViewById(R.id.crime_date); mDateButton.setText(mCrime.getDate().toString()); mDateButton.setEnabled(false); mSolvedCheckBox=(CheckBox)v.findViewById(R.id.crime_solved); mSolvedCheckBox.setChecked(mCrime.isSolved()); mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { } }); return v; } public static CrimeFragment newInstance(UUID crimeId) { Bundle args = new Bundle(); args.putSerializable(ARG_CRIME_ID,crimeId); CrimeFragment fragment = new CrimeFragment(); fragment.setArguments(args); return fragment; } }
附加argument给fragment
为了避免破坏fragment封装,添加newInstance()静态方法,使用该方法完成fragment实例及Bundle对象的创建,然后将argument放入bundle中,最后再附加给fragment。