Android编程权威指南(第二版)学习笔记(十七)—— 第17章 Master-Detail 用户界面
来源:互联网 发布:godaddy 转入阿里云 编辑:程序博客网 时间:2024/05/14 17:02
本章介绍了如何写一个双版面 fragment 的布局,并对符合要求的设备进行适配,还介绍了回调接口的使用。
GitHub 地址:
完成17章
完成17章并进行了完善
对平板设备来说,使用主从用户界面将会得到更好的体验,在这章我们将对其使用,传递数据的方式进行探究。
1. 增加布局灵活性
要实现双版面的布局,需要完成如下任务:
- 修改 SingleFragmentActivity,使其不再硬编码实例化布局
- 创建包含两个 fragment 容器的布局
- 修改 CrimeListActivity,实现在手机设备上实例化单版面布局,在平板设备上实例化双版面布局
1.1 修改抽象类 SingleFragmentActivity
在其中加入一个 protected 方法,返回 activity 需要的 ResId,这样对于继承 SingleFragmentActivity 的 activity 可以重写该函数以返回自己需要的 ResId。
@LayoutResprotected int getLayoutResId() { return R.layout.activity_fragment;}
1.2 使用别名资源
我们想让最小屏幕宽度 600dp 的设备使用双版面界面,其他的使用单版面界面,那么对于不同的设备,使用的布局就不同。要让不同的设备使用不同的布局资源,有两种方法:
- 让 res/layout/目录中的文件使用资源修饰符。如果想使用
activity_masterdetail.xml
布局文件, 就需要将activity_fragment.xml
的内容复制到res/layout/activity_masterdetail.xml
中,将activity_twopane.xml
的内容复制到res/layout-sw600dp/activity_masterdetail.xml
中。这样做最明显的缺点就是数据冗余,因为每个布局文件都要复制一份。 使用别名资源。别名资源是一种指向其他资源的特殊资源。它存放在 res/values/目录下,并按照约定定义在 refs.xml 文件中。比如在默认的 values 文件夹下面新建一个 refs.xml,然后写入代码:
<?xml version="1.0" encoding="utf-8"?><resources> <item name="activity_masterdetail" type="layout">@layout/activity_fragment</item></resources>
再新建一个最小宽度600dp 的 refs.xml(即在 values-sw600dp 目录下),写入双版面的 layout 资源:
<?xml version="1.0" encoding="utf-8"?><resources> <item name="activity_masterdetail" type="layout">@layout/activity_twopane</item></resources>
这样,在 CrimeListActivity 中只要引用
R.layout.activity_masterdetail
即可
2. Activity:Fragment 的托管者
为了保证 fragment 的独立性,即不需要了解其托管者的工作,但要想在 fragment 生命周期没有结束的时候传递数据出去,就要使用回调接口。
回调就相当于一个委托,首先 fragment 自己定义回调的接口,托管的 acitivity 来实现这个接口,接着 fragment 需要持有实现了自己定义接口的对象,以便自己可以实时调用。
对于一个回调接口而言,fragment 只要求实现这个接口的类在函数里要做的是什么,却不知道实现类到底会做什么,每个实现类有自己的方法来实现。
2.1 CrimeListFragment 的回调接口
对于 CrimeListFragment,其所能响应的就是点击列表中的某一项,那么它的回调接口定义如下:
public interface Callbacks { void onCrimeSelected(Crime crime);}
然后应该在需要托管的 Activity 中实现该接口,在这里是 CrimeListActivity:
// 省略 implement 以节约版面@Overridepublic void onCrimeSelected(Crime crime) { // 如果发现布局里没有包含详情 fragment 容器的 id, // 就启动单独的 activity 用于显示详情 if (findViewById(R.id.detail_fragment_container) == null) { Intent intent = CrimePagerActivity.newIntent(this, crime.getId()); startActivity(intent); } else { // 否则就将 detail 页面放到 fragment 容器中 Fragment newDetail = CrimeFragment.newInstance(crime.getId()); getSupportFragmentManager().beginTransaction() .replace(R.id.detail_fragment_container, newDetail) .commit(); }}
在 CrimeListFragment 中持有实现接口的 activity 的引用,然后在生命周期末去除引用以便内存的回收
// CrimeListFragmentprivate Callbacks mCallbacks;@Overridepublic void onAttach(Context context) { super.onAttach(context); mCallbacks = (Callbacks) context;}// 中间的函数……@Overridepublic void onDetach() { super.onDetach(); mCallbacks = null;}
最后修改 onClick 事件,调用 mCallbacks.onCrimeSelected(Crime crime) 即可。这样以后,在双版面视图中点击列表中的某一项,在详情版面中就会显示相应的信息。
但是有一个问题,那就是在详情页(CrimeFragment)更改信息,在列表页没有任何响应,因为 CrimeListFragment 不会暂停,所以也就不会刷新,所以下一步要在 CrimeFragment 中定义回调接口, 让托管 activity 去更新 CrimeListFragment。
2.2 CrimeFragment 的回调接口
首先定义回调接口,这里想让托管者做的就是在 Crime 详情进行更新时更新列表
// CrimeFragmentprivate Callbacks mCallbacks;public interface Callbacks { void onCrimeUpdated(Crime crime);}@Overridepublic void onAttach(Context context) { super.onAttach(context); mCallbacks = (Callbacks) context;}// 中间的函数……@Overridepublic void onDetach() { super.onDetach(); mCallbacks = null;}
在 CrimeListActivity 中实现该接口:
@Overridepublic void onCrimeUpdated(Crime crime) { CrimeListFragment listFragment = (CrimeListFragment) getSupportFragmentManager() .findFragmentById(R.id.fragment_container); listFragment.updateUI();}
由于只要托管 CrimeFragment 的 activity 都应该实现其回调接口,所以在 CrimePagerActivity 中提供一个空的接口实现
之后在每次数据发生更改时都调用 mCallbacks.onCrimeUpdated(mCrime);
即可。书上将更新模型层也放到了一起。
3. 挑战的后遗症:删除 Crime
还记得我们在 ToolBar 那一章加入的挑战吗,就是删除一个 Crime,对于 CriminalIntent 这个应用来说,双版面和单版面的删除操作应该有着不同的结果,但这些行为在书上没有定义,所以我们再自己想一种解决方案,以便确立如何写接下来的补充程序。
- 双版面的界面下,点击删除应该要让左边的列表中去掉删除的那一项,并且详情页也要改为已存在的某一项的详情,为了方便实现,我们在这里改为已存在的第一项。如果只有最后一项并且点击了删除,那么右边应该要变成空白。
- 单版面的界面下,点击删除就直接删去该条记录,然后结束 activity。
在这里我在 CrimeFragment 的 Callbacks 接口中加入了 onCrimeDelete(Crime crime) 方法与 onCrimeAllDeleted(Crime crime) 方法,在 CrimeListActivity 中实现如下:
@Overridepublic void onCrimeDeleted(Crime crime) { // 如果只是删除了一个,而还有其他的 Crime 的话, // 就相当于选中一个 Crime,这里传过来的应该是第一个 Crime onCrimeSelected(crime);}@Overridepublic void onCrimeAllDeleted(Crime crime) { // 如果全部删除,就直接将该 fragment 移去 CrimeFragment fragment = (CrimeFragment) getSupportFragmentManager() .findFragmentById(R.id.detail_fragment_container); if (fragment != null) { getSupportFragmentManager() .beginTransaction() .remove(fragment) .commit(); } // 并且更新列表页 onCrimeUpdated(crime);}
在 CrimePagerActivity 中也要实现这两个方法,但是对于这个 activity 来说只要进行 finish() 即可。
在删除按钮的选中监听中:
CrimeLab.get(getActivity()).deleteCrime(mCrime);if (CrimeLab.get(getActivity()).getCrimes().isEmpty()) { mCallbacks.onCrimeAllDeleted(mCrime);} else { mCrime = CrimeLab.get(getActivity()).getCrimes().get(0); mCallbacks.onCrimeDeleted(mCrime); // 这里相当于选中第一个 updateCrime(); // 这里面升级了数据层并且更新了列表}return true;
整个程序就此完成啦~
- Android编程权威指南(第二版)学习笔记(十七)—— 第17章 Master-Detail 用户界面
- Android编程权威指南(第二版)学习笔记(二十七)—— 第27章 broadcast intent
- Android编程权威指南(第二版)学习笔记(八)—— 第8章 使用布局与组件创建用户界面
- Android编程权威指南(第二版)学习笔记(五)—— 第5章 第二个 Activity
- Android编程权威指南(第二版)学习笔记(一)——第1章 Android开发初体验
- Android编程权威指南(第二版)学习笔记(四)—— 第4章 Android 应用的调试
- Android编程权威指南(第二版)学习笔记(十三)—— 第13章 工具栏(Toolbar)
- Android编程权威指南(第二版)学习笔记(九)—— 第9章 使用 RecyclerView 显示列表
- Android编程权威指南(第二版)学习笔记(十)—— 第10章 使用 fragment argument
- Android编程权威指南(第二版)学习笔记(十二)—— 第12章 对话框
- Android编程权威指南(第二版)学习笔记(十四)—— 第14章 SQLite 数据库
- Android编程权威指南(第二版)学习笔记(十五)—— 第15章 隐式 Intent
- Android编程权威指南(第二版)学习笔记(十六)—— 第16章 使用 intent 拍照
- Android编程权威指南(第二版)学习笔记(十八)—— 第18章 Assets
- Android编程权威指南(第二版)学习笔记(十九)—— 第19章 使用 SoundPool 播放音频
- Android编程权威指南(第二版)学习笔记(二十)—— 第20章 样式与主题
- Android编程权威指南(第二版)学习笔记(二十一)—— 第21章 XML drawable
- Android编程权威指南(第二版)学习笔记(二十五)—— 第25章 搜索
- PHP中DOM操作
- 职场注意事项
- [LeetCode]368. Largest Divisible Subset
- 前端浏览器兼容问题&hack
- 【C#】C#中的委托与事件
- Android编程权威指南(第二版)学习笔记(十七)—— 第17章 Master-Detail 用户界面
- 属性注入方式
- ubuntu下android-studio环境配置
- ffmpeg裁剪合并视频
- 自定义View冷知识之动态替换layout.xml中的View
- Qt QPainter类 画板的使用
- HTML+CSS学习笔记四
- React native 技术视频免费共享190集(持续更新中)
- 【攻略技巧】分分钟教你选出最适合自己的笔记本电脑!