创建一个Criminallntent应用,并且向Java包中编写以下类:
Crime: package com.bignerdranch.android.criminalintent; import java.util.Date; import java.util.UUID; public class Crime { private UUID mId;//UUID是Android框架里的java工具类,有了它方便产生唯一的ID值 private String mTitle; private Date mDate; private boolean mSolved; private String mSuspect; public Crime() { this(UUID.randomUUID());//随机产生一个ID值 } public Crime(UUID id) { mId = id; mDate = new Date(); } //自动生成的getter方法和setter方法 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; } public String getSuspect() { return mSuspect; } public void setSuspect(String suspect) { mSuspect = suspect; } public String getPhotoFilename() { return "IMG_" + getId().toString() + ".jpg"; } } CrimeFragment: package com.bignerdranch.android.criminalintent; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.provider.MediaStore; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.content.FileProvider; import android.text.Editable; import android.text.TextWatcher; import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import java.io.File; import java.util.Date; import java.util.List; import java.util.UUID; import static android.widget.CompoundButton.*; //让CrimeFragment继承Fragment类,这里继承的是Fragment(android.support.v4.app)是支持库版 public class CrimeFragment extends Fragment { private static final String ARG_CRIME_ID = "crime_id"; private static final String DIALOG_DATE = "DialogDate"; //设置目标fragment private static final int REQUEST_DATE = 0; private static final int REQUEST_CONTACT = 1; private static final int REQUEST_PHOTO = 2; private Crime mCrime; private File mPhotoFile; private EditText mTitleField; private Button mDateButton; private CheckBox mSolvedCheckbox; private Button mReportButton; private Button mSuspectButton; private ImageButton mPhotoButton; private ImageView mPhotoView; //编写newInstance(UUID)方法 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; } @Override public void onCreate(Bundle savedInstanceState) {//这是公用方法,托管fragment的activity要调用它们 super.onCreate(savedInstanceState); //获取rxtra数据并取得Crime对象 UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID); mCrime = CrimeLab.get(getActivity()).getCrime(crimeId); mPhotoFile = CrimeLab.get(getActivity()).getPhotoFile(mCrime); } @Override//配置视图和fragment的关联 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { //这里有三个参数,第一个是fragment的视图直接调用LayoutInflater inflater生成的 //第二个参数是视图的父视图,通常需要父视图来正确配置组件 //第三个参数告诉布局生成器是否将生成的视图添加给父视图 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) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mCrime.setTitle(s.toString()); } @Override public void afterTextChanged(Editable s) { } }); //禁用Button按钮,确保它不会影响用户的点击,长期处于灰色的状态,用户一看就知道是不可以按的。 mDateButton = (Button) v.findViewById(R.id.crime_date); updateDate(); mDateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FragmentManager manager = getFragmentManager(); DatePickerFragment dialog = DatePickerFragment .newInstance(mCrime.getDate()); dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE); dialog.show(manager, DIALOG_DATE); } }); mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved); mSolvedCheckbox.setChecked(mCrime.isSolved()); mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mCrime.setSolved(isChecked); } }); mReportButton = (Button) v.findViewById(R.id.crime_report); mReportButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Intent i = new Intent(Intent.ACTION_SEND); i.setType("text/plain"); i.putExtra(Intent.EXTRA_TEXT, getCrimeReport()); i.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.crime_report_subject)); i = Intent.createChooser(i, getString(R.string.send_report)); startActivity(i); } }); final Intent pickContact = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); mSuspectButton = (Button) v.findViewById(R.id.crime_suspect); mSuspectButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { startActivityForResult(pickContact, REQUEST_CONTACT); } }); if (mCrime.getSuspect() != null) { mSuspectButton.setText(mCrime.getSuspect()); } PackageManager packageManager = getActivity().getPackageManager(); if (packageManager.resolveActivity(pickContact, PackageManager.MATCH_DEFAULT_ONLY) == null) { mSuspectButton.setEnabled(false); } mPhotoButton = (ImageButton) v.findViewById(R.id.crime_camera); final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); boolean canTakePhoto = mPhotoFile != null && captureImage.resolveActivity(packageManager) != null; mPhotoButton.setEnabled(canTakePhoto); //按钮的监听器 mPhotoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = FileProvider.getUriForFile(getActivity(), "com.bignerdranch.android.criminalintent.fileprovider", mPhotoFile); captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri); List<ResolveInfo> cameraActivities = getActivity() .getPackageManager().queryIntentActivities(captureImage, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo activity : cameraActivities) { getActivity().grantUriPermission(activity.activityInfo.packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } startActivityForResult(captureImage, REQUEST_PHOTO); } }); mPhotoView = (ImageView) v.findViewById(R.id.crime_photo); updatePhotoView(); return v; } @Override public void onPause() { super.onPause(); CrimeLab.get(getActivity()) .updateCrime(mCrime); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK) { return; } if (requestCode == REQUEST_DATE) { Date date = (Date) data .getSerializableExtra(DatePickerFragment.EXTRA_DATE); mCrime.setDate(date); updateDate(); } else if (requestCode == REQUEST_CONTACT && data != null) { Uri contactUri = data.getData(); // 指明你希望返回的区域 // values for. String[] queryFields = new String[]{ ContactsContract.Contacts.DISPLAY_NAME }; // Perform your query - the contactUri is like a "where" // clause here Cursor c = getActivity().getContentResolver() .query(contactUri, queryFields, null, null, null); try { // Double-check that you actually got results if (c.getCount() == 0) { return; } // 清空第一列第一行的数据 // 嫌疑人的姓名 c.moveToFirst(); String suspect = c.getString(0); mCrime.setSuspect(suspect); mSuspectButton.setText(suspect); } finally { c.close(); } } else if (requestCode == REQUEST_PHOTO) { Uri uri = FileProvider.getUriForFile(getActivity(), "com.bignerdranch.android.criminalintent.fileprovider", mPhotoFile); getActivity().revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); updatePhotoView(); } } private void updateDate() { mDateButton.setText(mCrime.getDate().toString()); } //反馈案件信息 private String getCrimeReport() { String solvedString = null; if (mCrime.isSolved()) { solvedString = getString(R.string.crime_report_solved); } else { solvedString = getString(R.string.crime_report_unsolved); } String dateFormat = "EEE, MMM dd"; String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString(); String suspect = mCrime.getSuspect(); if (suspect == null) { suspect = getString(R.string.crime_report_no_suspect); } else { suspect = getString(R.string.crime_report_suspect, suspect); } String report = getString(R.string.crime_report, mCrime.getTitle(), dateString, solvedString, suspect); return report; } //更新照片视图 private void updatePhotoView() { if (mPhotoFile == null || !mPhotoFile.exists()) { mPhotoView.setImageDrawable(null); } else { Bitmap bitmap = PictureUtils.getScaledBitmap( mPhotoFile.getPath(), getActivity()); mPhotoView.setImageBitmap(bitmap); } } } CrimeLab: package com.bignerdranch.android.criminalintent; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.bignerdranch.android.criminalintent.database.CrimeBaseHelper; import com.bignerdranch.android.criminalintent.database.CrimeCursorWrapper; import com.bignerdranch.android.criminalintent.database.CrimeDbSchema; import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.UUID; import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*; import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.Cols.*; public class CrimeLab { private static CrimeLab sCrimeLab; //注意sCrimeLab变量的s前缀,表示static,看到此前缀就大致猜出它是一个静态变量 private Context mContext; private SQLiteDatabase mDatabase; //其他类创建CrimeLab对象的唯一方法是使用下面这个get()方法 public static CrimeLab get(Context context) { if (sCrimeLab == null) { sCrimeLab = new CrimeLab(context);//传入context对象 } return sCrimeLab; } private CrimeLab(Context context) { mContext = context.getApplicationContext(); mDatabase = new CrimeBaseHelper(mContext) .getWritableDatabase(); } public void addCrime(Crime c) { ContentValues values = getContentValues(c); mDatabase.insert(CrimeTable.NAME, null, values); } public List<Crime> getCrimes() { List<Crime> crimes = new ArrayList<>(); CrimeCursorWrapper cursor = queryCrimes(null, null); try { cursor.moveToFirst(); while (!cursor.isAfterLast()) { crimes.add(cursor.getCrime()); cursor.moveToNext(); } } finally { cursor.close(); } return crimes; } public Crime getCrime(UUID id) { CrimeCursorWrapper cursor = queryCrimes( CrimeTable.Cols.UUID + " = ?", new String[]{id.toString()} ); try { if (cursor.getCount() == 0) { return null; } cursor.moveToFirst(); return cursor.getCrime(); } finally { cursor.close(); } } public File getPhotoFile(Crime crime) { File filesDir = mContext.getFilesDir(); return new File(filesDir, crime.getPhotoFilename()); } public void updateCrime(Crime crime) { String uuidString = crime.getId().toString(); ContentValues values = getContentValues(crime); mDatabase.update(CrimeTable.NAME, values, CrimeTable.Cols.UUID + " = ?", new String[]{uuidString}); } private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) { Cursor cursor = mDatabase.query( CrimeTable.NAME, null, // Columns - null selects all columns whereClause, whereArgs, null, // groupBy null, // having null // orderBy ); return new CrimeCursorWrapper(cursor); } private static ContentValues getContentValues(Crime crime) { ContentValues values = new ContentValues(); values.put(UUID, crime.getId().toString()); values.put(TITLE, crime.getTitle()); values.put(DATE, crime.getDate().getTime()); values.put(SOLVED, crime.isSolved() ? 1 : 0); values.put(CrimeTable.Cols.SUSPECT, crime.getSuspect()); return values; } } CrimeListActivity: package com.bignerdranch.android.criminalintent; import android.support.v4.app.Fragment; //表单动作类 public class CrimeListActivity extends SingleFragmentActivity { @Override protected Fragment createFragment() { return new CrimeListFragment(); } } CrimeListFragment: package com.bignerdranch.android.criminalintent; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class CrimeListFragment extends Fragment { private static final String SAVED_SUBTITLE_VISIBLE = "subtitle"; private RecyclerView mCrimeRecyclerView; private CrimeAdapter mAdapter; private boolean mSubtitleVisible; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, 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())); if (savedInstanceState != null) { mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE); } updateUI(); return view; } @Override public void onResume() { super.onResume(); updateUI(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.fragment_crime_list, menu); MenuItem subtitleItem = menu.findItem(R.id.show_subtitle); if (mSubtitleVisible) { subtitleItem.setTitle(R.string.hide_subtitle); } else { subtitleItem.setTitle(R.string.show_subtitle); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.new_crime: Crime crime = new Crime(); CrimeLab.get(getActivity()).addCrime(crime); Intent intent = CrimePagerActivity .newIntent(getActivity(), crime.getId()); startActivity(intent); return true; case R.id.show_subtitle: mSubtitleVisible = !mSubtitleVisible; getActivity().invalidateOptionsMenu(); updateSubtitle(); return true; default: return super.onOptionsItemSelected(item); } } private void updateSubtitle() { CrimeLab crimeLab = CrimeLab.get(getActivity()); int crimeCount = crimeLab.getCrimes().size(); String subtitle = getString(R.string.subtitle_format, crimeCount); if (!mSubtitleVisible) { subtitle = null; } AppCompatActivity activity = (AppCompatActivity) getActivity(); activity.getSupportActionBar().setSubtitle(subtitle); } 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.setCrimes(crimes); mAdapter.notifyDataSetChanged(); } updateSubtitle(); } private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private Crime mCrime; private TextView mTitleTextView; private TextView mDateTextView; private ImageView mSolvedImageView;//初始化图片信息 //定义CrimeHolder内部类,它会实例化并使用list_item_crime布局 public CrimeHolder(LayoutInflater inflater, ViewGroup parent) { //实例化list_item_crime布局并传给super()方法,即ViewHolder的构造方法 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); //控制图片显示 } public void bind(Crime crime) { mCrime = crime; mTitleTextView.setText(mCrime.getTitle()); mDateTextView.setText(mCrime.getDate().toString()); mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE); } @Override public void onClick(View view) { Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId()); //传递Crime实例 startActivity(intent); } } private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> { private List<Crime> mCrimes; public CrimeAdapter(List<Crime> crimes) { mCrimes = crimes; } @Override public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); return new CrimeHolder(layoutInflater, parent); } @Override public void onBindViewHolder(CrimeHolder holder, int position) { Crime crime = mCrimes.get(position); holder.bind(crime); } @Override public int getItemCount() { return mCrimes.size(); } public void setCrimes(List<Crime> crimes) { mCrimes = crimes; } } } CrimePagerActivity: package com.bignerdranch.android.criminalintent; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import java.util.List; import java.util.UUID; public class CrimePagerActivity extends AppCompatActivity { private static final String EXTRA_CRIME_ID = "com.bignerdranch.android.criminalintent.crime_id"; private ViewPager mViewPager; private List<Crime> mCrimes; public static Intent newIntent(Context packageContext, UUID crimeId) { Intent intent = new Intent(packageContext, CrimePagerActivity.class); intent.putExtra(EXTRA_CRIME_ID, crimeId); return intent; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crime_pager); UUID crimeId = (UUID) getIntent() .getSerializableExtra(EXTRA_CRIME_ID); mViewPager = (ViewPager) findViewById(R.id.crime_view_pager); mCrimes = CrimeLab.get(this).getCrimes(); FragmentManager fragmentManager = getSupportFragmentManager(); //设置adapter为FragmentStatePagerAdapter的一个匿名实例 mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) { @Override public Fragment getItem(int position) { Crime crime = mCrimes.get(position); return CrimeFragment.newInstance(crime.getId()); } @Override public int getCount() { return mCrimes.size(); } }); for (int i = 0; i < mCrimes.size(); i++) { if (mCrimes.get(i).getId().equals(crimeId)) { mViewPager.setCurrentItem(i); break; } } } } DatePickerFragment: package com.bignerdranch.android.criminalintent; import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.widget.DatePicker; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; public class DatePickerFragment extends DialogFragment { public static final String EXTRA_DATE = "com.bignerdranch.android.criminalintent.date"; private static final String ARG_DATE = "date"; private DatePicker mDatePicker; //传递数据给DatePickerFragment public static DatePickerFragment newInstance(Date date) { Bundle args = new Bundle(); args.putSerializable(ARG_DATE, date); //获取 Date对象并初始化DatePicker DatePickerFragment fragment = new DatePickerFragment(); fragment.setArguments(args); return fragment; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Date date = (Date) getArguments().getSerializable(ARG_DATE); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int day = calendar.get(Calendar.DAY_OF_MONTH); View v = LayoutInflater.from(getActivity()) .inflate(R.layout.dialog_date, null); mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker); mDatePicker.init(year, month, day, null); return new AlertDialog.Builder(getActivity()) .setView(v) .setTitle(R.string.date_picker_title) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { int year = mDatePicker.getYear(); int month = mDatePicker.getMonth(); int day = mDatePicker.getDayOfMonth(); Date date = new GregorianCalendar(year, month, day).getTime(); sendResult(Activity.RESULT_OK, date); } }) .create(); } private void sendResult(int resultCode, Date date) { if (getTargetFragment() == null) { return; } Intent intent = new Intent(); intent.putExtra(EXTRA_DATE, date); getTargetFragment() .onActivityResult(getTargetRequestCode(), resultCode, intent); } } PictureUtils: package com.bignerdranch.android.criminalintent; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Point; public class PictureUtils { public static Bitmap getScaledBitmap(String path, Activity activity) { Point size = new Point(); activity.getWindowManager().getDefaultDisplay() .getSize(size); return getScaledBitmap(path, size.x, size.y); } public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) { // Read in the dimensions of the image on disk BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); float srcWidth = options.outWidth; float srcHeight = options.outHeight; // Figure out how much to scale down by int inSampleSize = 1; if (srcHeight > destHeight || srcWidth > destWidth) { float heightScale = srcHeight / destHeight; float widthScale = srcWidth / destWidth; inSampleSize = Math.round(heightScale > widthScale ? heightScale : widthScale); } options = new BitmapFactory.Options(); options.inSampleSize = inSampleSize; // Read in and create final bitmap return BitmapFactory.decodeFile(path, options); } } SingleFragmentActivity: package com.bignerdranch.android.criminalintent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; //几乎每次新建activity都要使用这样的代码,为了避免重复,我们将这段代码直接封装在抽象类里面 public abstract class SingleFragmentActivity extends AppCompatActivity { //添加一个通用的超类 protected abstract Fragment createFragment(); @Override protected void onCreate(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(); } } } 以上只是Java类的代码部分,还需要编写xml部件代码才能正常运行 下面是xml: 在src包中添加如下组件代码(后缀均为.xml): AndroidManifest: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.bignerdranch.android.criminalintent"> <uses-feature android:name="android.hardware.camera" android:required="false" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <!--配置主界面的crime列表,配置crimelistativity为launcher activity--> <activity android:name=".CrimeListActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!--嫌疑人姓名信息--> <activity android:name=".CrimePagerActivity" android:parentActivityName=".CrimeListActivity"> </activity> <!--提供商信息--> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.bignerdranch.android.criminalintent.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/files" /> </provider> </application> </manifest> activity_crime_pager: <!--动态嫌疑人页面的组件--> <?xml version="1.0" encoding="utf-8"?> <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/crime_view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> activity_fragment: <!--动态片段组件--> <FrameLayout android:id="@+id/fragment_container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" /> dialog_date: <!--日志数据组件--> <?xml version="1.0" encoding="utf-8"?> <DatePicker xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dialog_date_picker" android:layout_width="wrap_content" android:layout_height="wrap_content" android:calendarViewShown="false"/> fragment_crime: <!--日志数据组件--> <?xml version="1.0" encoding="utf-8"?> <DatePicker xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dialog_date_picker" android:layout_width="wrap_content" android:layout_height="wrap_content" android:calendarViewShown="false"/> fragment_crime_list: <?xml version="1.0" encoding="utf-8"?> <!--使用utf-8可以适应中文输入--> <!--配置CrimeListFragment的视图,使其和父视图--> <android.support.v7.widget.RecyclerView android:id="@+id/crime_recycler_view" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"/> list_item_crime: <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/crime_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="Crime Title" android:textColor="@android:color/black" android:textSize="18sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@+id/crime_solved" app:layout_constraintTop_toTopOf="parent"/> <!--以layout开头的属性都属于局部参数,用来向父组件做指示,告诉父布局如何安排自己--> <TextView android:id="@+id/crime_date" android:layout_width="0dp" android:layout_height="wrap_content" android:text="Crime Date" android:layout_marginStart="16dp" app:layout_constraintLeft_toLeftOf="parent" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/crime_title" app:layout_constraintRight_toLeftOf="@+id/crime_solved" android:layout_marginEnd="8dp"/> <ImageView android:id="@+id/crime_solved" android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@drawable/ic_solved" android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp" android:layout_marginEnd="16dp" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout> values: colors: <!--色彩控制组件--> <?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <!--浅蓝--> <color name="colorPrimaryDark">#303F9F</color> <!--深蓝--> <color name="colorAccent">#FF4081</color> <!--粉色--> </resources> strings: <!--包含Crime类实例中的信息--> <resources> <string name="app_name">CriminalIntent</string> <string name="crime_title_hint">Enter a title for the crime.</string> <string name="crime_title_label">Title</string> <string name="crime_details_label">Details</string> <string name="crime_solved_label">Solved</string> <string name="date_picker_title">Date of crime:</string> <!--为对话框添加字符串资源--> <string name="new_crime">New Crime</string> <string name="show_subtitle">Show Subtitle</string> <string name="hide_subtitle">Hide Subtitle</string> <string name="subtitle_format">%1$d crimes</string> <string name="crime_suspect_text">Choose Suspect</string> <string name="crime_report_text">Send Crime Report</string> <string name="crime_report">%1$s! The crime was discovered on %2$s. %3$s, and %4$s <!--字符串在屏幕上都能显示--> </string> <string name="crime_report_solved">The case is solved</string> <string name="crime_report_unsolved">The case is not solved</string> <string name="crime_report_no_suspect">there is no suspect.</string> <string name="crime_report_suspect">the suspect is %s.</string> <string name="crime_report_subject">CriminalIntent Crime Report</string> <string name="send_report">Send crime report via</string> </resources> styles: <resources> <!-- 应用主题组件--> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- 设置应用颜色风格--> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>
运行结果:
完成了记录主题时间、拍照、发送给联系人的功能,还可以设定问题是否被解决的一个勾选框。