上接Android开发3、4周——Criminallntent项目
第5、6周,继续Criminalintent项目的开发。上次写到图形界面,用布局与组件创建了用户界面。这次开始使用ViewPager,对话框,工具栏和数据库。
使用ViewPager
布局示意图如图所示:
其中使用了CrimePagerActivity的新建Activity取代了CrimeActivity。CrimePagerActivity的布局由一个ViewPager组成。
ViewPager与PagerAdapter
ViewPager在某种程度上类似于RecyclerView。RecyclerView需借助于Adapter提供视图。同样地,ViewPager也需要PagerAdapter的支持。
FragmentStatePagerAdapter化繁为简,提供了两个有用的方法:getCount()和getItem(int)。调用getItem(int)方法,获取并显示crime数组中指定位置的crime时,它会返回配置过的CrimeFragment来完成显示任务。
代码如下:
mViewPager = (ViewPager) findViewById(R.id.activity_crime_pager_view_pager); mCrimes = CrimeLab.get(this).getCrimes(); FragmentManager fragmentManager = getSupportFragmentManager(); mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) { @Override public Fragment getItem(int position) { Crime crime = mCrimes.get(position); return CrimeFragment.newInstance(crime.getmId()); } @Override public int getCount() { return mCrimes.size(); } });
FragmentStatePagerAdapter与FragmentPagerAdapter
FragmentPagerAdapter是另外一种可用的PagerAdapter,其用法与FragmentStatePagerAdapter基本一致。唯一的区别在于:卸载不再需要的fragment时,各自采用的处理方法有所不同。
FragmentStatePagerAdapter会销毁不需要的fragment。事务提交后,activity的FragmentManager中的fragment会被彻底移除。
FragmentStatePagerAdapter类名中的“state”表明:在销毁fragment时,可在onSaveInstanceState(Bundle)方法中保存fragment的Bundle信息。用户切换回来时,保存的实例状态可用来恢复生成新的fragment。
如图所示:
相比之下,FragmentPagerAdapter有不同的做法。对于不再需要的fragment,FragmentPagerAdapter会选择调用事务的detach(Fragment)方法来处理它,而非remove(Fragment)方法。
也就是说,FragmentPagerAdapter只是销毁了fragment的视图,fragment实例还保留在FragmentManager中。因此,FragmentPagerAdapter创建的fragment永远不会被销毁。
如图所示:
对话框
对话框是AlertDialog类的一个实例。实际开发中,AlertDialog类是个常用的多用途Dialog子类。
要使用对话框要先使用AppCompat兼容库,要使用兼容库就要先添加为依赖库,这取决于项目创建的方式。
打开项目结构窗口(File → Project Structure...),选择app模块并点击Dependencies选项页。如果看不到AppCompat,请点击+按钮,然后从依赖项列表中选择appcompat-v7完成添加。
选择内容如图所示:
这里注意,使用的是android.support.v7.app.AlertDialog。
各个对象之间的关系如图所示:
fragment间的数据传递
CrimeFragment和DatePickerFragment之间的数据传递如图所示:
为了返回新日期给CrimeFragment,并更新模型层以及对应视图,需将日期打包为extra并附加到Intent上,然后调用CrimeFragment.onActivityResult(...)方法,并传入准备好的Intent参数。
如图所示:
要传递crime记录日期给DatePickerFragment,需将它保存在DatePickerFragment的argumentbundle中。这样,DatePickerFragment便可直接获取到它。
代码如下:
private static final String ARG_DATE = "date"; private DatePicker mDatePicker; public static DatePickerFragment newInstance(Date date) { Bundle args = new Bundle(); args.putSerializable(ARG_DATE,date); DatePickerFragment fragment = new DatePickerFragment(); fragment.setArguments(args); return fragment; }
工具栏
在上面已经添加过AppCompat依赖项,这里我们要完全整合AppCompat库。
先要使用AppCompat主题,在res/values/styles.xml中修改代码。
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
并且在res/values/string.xml中添加字符串资源。
<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>
在XML文件中定义菜单。在项目工具窗口中,右键单击res目录,选择New → Android resource file菜单项。在弹出的窗口界面,选择Menu资源类型,并命名资源文件为fragment_crime_list,点击OK按钮确认。
如图所示:
在新建的文件中,加入的代码如下:
xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_item_new_crime" android:icon="@drawable/ic_menu_add" android:title="@string/new_crime" app:showAsAction="ifRoom|withText"/>
层级导航的工作原理如图所示:
SQLite数据库
首先要定义Schema,创建CrimeDbSchema.java类,加入代码如下:
public class CrimeDbSchema { public static final class CrimeTable { public static final String NAME = "crimes"; public static final class Cols { public static final String UUID = "uuid"; public static final String TITLE = "title"; public static final String DATE = "date"; public static final String SOLVED = "solved"; public static final String SUSPECT = "suspect"; } } }
然后创建数据库,创建CrimeBaseHelper.java类,加入代码如下:
public class CrimeBaseHelper extends SQLiteOpenHelper { private static final int VERSION = 1; private static final String DATABASE_NAME = "crimeBase.db"; public CrimeBaseHelper(Context context) { super(context,DATABASE_NAME,null,VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table" + CrimeTable.NAME + "(" + "_id integer primary key autoincrement," + CrimeTable.Cols.UUID + "," + CrimeTable.Cols.TITLE + "," + CrimeTable.Cols.DATE + "," + CrimeTable.Cols.SOLVED + "," + CrimeTable.Cols.SUSPECT + ")"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
编写SQL创建初始代码的时候,需要导入Add import for ‘com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable’可选项。
如图所示:
然后Android Studio会自动导语句:
import database.CrimeDbSchema.CrimeDbSchema.CrimeTable;