转载请注明出处:http://blog.csdn.net/chengbao315/article/details/50962716
最近学习了郭霖《第一行代码》的4.5章节《碎片的最佳实践——一个简易版的新闻应用》,反复读了几遍代码,才将程序搞清楚,理解了作者的设计意图,现对代码做以梳理总结,仅供参考。
先分析一下项目的设计目标,目标是要实现一个兼容手机和平板的新闻应用,新闻应用包含两部分内容:新闻列表项和新闻内容。那么基本设计思路是,手机系统会根据最小宽度限定符自动确定显示为单页模式还是双页模式,在MainActivity代码中,通过对单页模式、双页模式的判断启用不同的显示。手机用户会在MainActivity中显示手机列表项,在另一个Activity中显示新闻内容;平板用户会在MainActivity左侧碎片显示新闻列表项,右侧碎片显示新闻内容。
了解了作者的设计思路,现在来读代码。准备工作是必不可少的,首先是一个新闻实体类News,包含title、content两个字段和get、set方法,看到这个类,你第一时间就应该想到这是一个List列表的泛型类,用来创建适配器。接着准备好新闻列表适配器NewsAdapter和新闻列表子项布局news_item.xml,这些代码比较简单,这里不解释(不理解参考《第一行代码》的3.5章节ListView控件)。
接下来创建新闻内容布局news_content_frag.xml,里面包含一个显示新闻标题和一个显示新闻内容的文本框。创建新闻内容碎片NewsContentFragment继承Fragment,在onCreateView()方法里加载刚刚编写的新闻内容布局,碎片还提供了更新新闻标题和内容的方法refresh()。最后创建在活动中使用的新闻内容布局news_content.xml,看代码:
注:代码源自郭霖《第一行代码》
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="@+id/news_content_fragment"
android:name="com.example.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
可以看到布局中引入了NewsContentFragment碎片,这样就可以把news_content_frag布局的内容加进来了。这是静态显示的典型用法,还记得当年大明湖畔《碎片的简单用法》吗?不记得去翻书(《第一行代码》的4.2.1章节)。但是往后看又蒙圈了,请看layout-sw600dp文件夹下activity_main.xml的代码:
注:代码源自郭霖《第一行代码》
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/news_title_fragment"
android:name="com.example.fragmentbestpractice.NewsTitleFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" >
<fragment
android:id="@+id/news_content_fragment"
android:name="com.example.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
在这个布局中同样引入了NewsContentFragment碎片,还引入了后面要讲的NewsTitleFragment碎片,为什么需要引入两次?这是本节比较难理解的地方,为了兼容手机和平板设备,在布局上代码肯定是会存在冗余的。在平板设备中显示的activity_main.xml布局,左右碎片分别加载了标题碎片和内容碎片的内容布局,而在手机设备中显示的activity_main.xml布局只加载标题碎片的内容布局,内容碎片的内容news_content.xml布局在另一个Activity中显示。下面请看显示新闻内容的活动NewsContentActivity的代码:
注:代码源自郭霖《第一行代码》
public class NewsContentActivity extends Activity {
public static void actionStart(Context context, String newsTitle,
String newsContent) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news_title", newsTitle);
intent.putExtra("news_content", newsContent);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.news_content);
String newsTitle = getIntent().getStringExtra("news_title");
String newsContent = getIntent().getStringExtra("news_content");
NewsContentFragment newsContentFragment = (NewsContentFragment) getFragmentManager()
.findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTitle, newsContent);
}
}
代码中提供一个静态方法用于传递参数和启动本活动,同时在onCreate()方法中更新新闻内容(不理解回去读《第一行代码》的4.2.4章节《碎片和活动之间进行通信》)。
下面是实现新闻列表的代码了,同新闻内容一样,先创建一个新闻列表布局news_title_frag.xml,包含一个ListView控件,用户显示新闻列表子项,然后创建新闻列表碎片NewsTitleFragment继承Fragment,这里先埋个伏笔,一会再讲!然后用静态显示方式在activity_main.xml布局中引入NewsTitleFragment碎片,把news_title_frag布局的内容加了进来,请看activity_main.xml代码:
注:代码源自郭霖《第一行代码》
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/news_title_fragment"
android:name="com.example.fragmentbestpractice.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
最后,看MainActivity代码,直接在onCreate()方法中显示布局activity_main.xml。
注:代码源自郭霖《第一行代码》
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
代码很简单是不是?这样理解了吗?但是等等,是不是觉得少了点什么?新闻内容?ListView的适配器设置?还有怎么区分手机和平板的?这个问题我之前留了伏笔的,现在来解决!请看NewsTitleFragment.java的代码:
注:代码源自郭霖《第一行代码》
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
newsList = getNews();
adapter = new NewsAdapter(activity, R.layout.news_item, newsList);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater
.inflate(R.layout.news_title_frag, container, false);
newsTitleListView = (ListView) view
.findViewById(R.id.news_title_list_view);
newsTitleListView.setAdapter(adapter);
newsTitleListView.setOnItemClickListener(this);
return view;
}
在onCreate()方法和onCreateView()方法中初始化了新闻内容,同时设置了ListView适配器,解决了前两个疑问,有木有?接着看:
注:代码源自郭霖《第一行代码》
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
} else {
isTwoPane = false;
}
}
在方法onActivityCreated()方法中,通过判断activity_main是否包含news_content_layout布局来判断是双页模式还是单页模式(即手机还是平板),之后再在列表项点击事件onItemClick()方法里实现不同的显示,比较好理解不解释了。
因为本文是对原书内容的学习理解,代码本身并没有做很细致解释,读者可以自行阅读原书。文章的最后,对着项目的结构图做一个总结:
(1)MainActivity.java:用于显示主布局activity_main.xml
(2)News.java:新闻实体类,用于构建新闻列表适配器
(3)NewsAdapter.java:新闻列表适配器
(4)NewsContentActivity.java:新闻内容活动,用于显示手机的新闻内容
(5)NewsContentFragment.java:新闻内容碎片
(6)NewsTitleFragment.java:新闻列表碎片
(7)activity_main.xml:主布局,layout目录下为手机布局,layout-sw600dp目录下为平板布局
(8)news_content_frag.xml:新闻内容布局
(9)news_content.xml:在NewsContentActivity中显示的新闻内容布局
(10)news_item.xml:新闻列表子项布局
(11)news_title_frag.xml:新闻列表布局