一、News 实体类:
public class News {
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
二、新闻列表中子项的布局 news_item.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"/>
</LinearLayout>
这段代码也非常简单,只是在 LinearLayout 中放入了一个 TextView 用于显示新闻的标题。仔细观察 TextView,你会发现其中有几个属性是我们之前没有学过的。android:padding 表示给控件的周围加上补白,这样不至于让文本内容会紧靠在边缘上。android:singleLine 设置为 true 表示让这个TextView 只能单行显示。android:ellipsize 用于设定当文本内容超出控件宽度时,文本的缩略方式,这里指定成 end 表示在尾部进行缩略。
三、新闻列表的适配器 NewsAdapter:
public class NewsAdapter extends ArrayAdapter<News> {
private int resourceId;
public NewsAdapter(Context context, int textViewResourceId, List<News>objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
News news = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
} else {
view = convertView;
}
TextView newsTitleText =(TextView) view.findViewById(R.id.news_title);
newsTitleText.setText(news.getTitle());
return view;
}
}
四、新闻内容布局 news_content_frag.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="@+id/visibility_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible" >
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:textSize="20sp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:scaleType="fitXY"
android:src="@drawable/spilt_line" />
<TextView
android:id="@+id/news_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="15dp"
android:textSize="18sp" />
</LinearLayout>
<ImageView
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:scaleType="fitXY"
android:src="@drawable/spilt_line_vertical" />
</RelativeLayout>
新闻内容的布局主要可以分为两个部分,头部显示完整的新闻标题,正文部分显示新闻内容,中间使用一条细线分隔开。这里的细线是利用 ImageView 显示了一张很窄的图片来实现的,将 ImageView 的 android:scaleType 属性设置为 fitXY,表示让这张图片填充满整个控件的大小。
五、新闻内容碎片类 NewsContentFragment:
NewsContentFragment extends Fragment {
private View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.news_content_frag, container, false);
return view;
}
public void refresh(String newsTitle, String newsContent) {
View visibilityLayout = view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE);
TextView newsTitleText = (TextView) view.findViewById (R.id.news_title);
TextView newsContentText = (TextView) view.findViewById(R.id.news_content);
newsTitleText.setText(newsTitle); // 刷新新闻的标题
newsContentText.setText(newsContent); // 刷新新闻的内容
}
}
首先在 onCreateView() 方法里加载了我们刚刚创建的 news_content_frag 布局,这个没什么好解释的。接下来又提供了一个 refresh() 方法,这个方法就是用于将新闻的标题和内容显示在界面上的。可以看到,这里通过 findViewById() 方法分别获取到新闻的标题和内容控件,然后将方法传递进来的参数设置进去。
六、新闻内容布局 news_content.xml:
<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 布局的内容自动加了进来。
七、显示新闻内容的活动 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); // 刷新NewsContentFragment界面
}
}
可以看到,在 onCreate() 方法中我们通过 Intent 获取到了传入的新闻标题和新闻内容,然后调用 FragmentManager 的 findFragmentById() 方法得到了 NewsContentFragment 的实例,接着调用它的 refresh() 方法,并将新闻的标题和内容传入,就可以把这些数据显示出来了。注意这里我们还提供了一个 actionStart() 方法。
八、显示新闻列表的布局 news_title_frag.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/news_title_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</LinearLayout>
九、新闻标题碎片类 NewsTitleFragment:
public class NewsTitleFragment extends Fragment implements OnItemClickListener {
private ListView newsTitleListView;
private List<News> newsList;
private NewsAdapter adapter;
private boolean isTwoPane;
@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;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true; // 可以找到news_content_layout布局时,为双页模式
} else {
isTwoPane = false; // 找不到news_content_layout布局时,为单页模式
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
News news = newsList.get(position);
if (isTwoPane) {
// 如果是双页模式,则刷新NewsContentFragment中的内容
NewsContentFragment newsContentFragment = (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
} else {
// 如果是单页模式,则直接启动NewsContentActivity
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
}
private List<News> getNews() {
List<News> newsList = new ArrayList<News>();
News news1 = new News();
news1.setTitle("Succeed in College as a Learning Disabled Student");
news1.setContent("College freshmen will soon learn to live with a
roommate, adjust to a new social scene and survive less-than-stellar
dining hall food. Students with learning disabilities will face these
transitions while also grappling with a few more hurdles.");
newsList.add(news1);
News news2 = new News();
news2.setTitle("Google Android exec poached by China's Xiaomi");
news2.setContent("China's Xiaomi has poached a key Google executive
involved in the tech giant's Android phones, in a move seen as a coup
for the rapidly growing Chinese smartphone maker.");
newsList.add(news2);
return newsList;
}
}
根据碎片的生命周期,我们知道,onAttach() 方法会首先执行,因此在这里做了一些数据初始化的操作,比如调用 getNews() 方法获取几条模拟的新闻数据,以及完成 NewsAdapter 的创建。然后在 onCreateView() 方法中加载了 news_title_frag 布局,并给新闻列表的 ListView 注册了点击事件。接下来在 onActivityCreated() 方法中,我们通过是否能够找到一个 id 为 news_content_layout 的View 来判断当前是双页模式还是单页模式,这个 id 为 news_content_layout 的 View 只在双页模式中才会出现,在稍后的布局里你将会看到。然后在 ListView 的点击事件里我们就可以判断,如果当前是单页模式,就启动一个新的活动去显示新闻内容,如果当前是双页模式,就更新新闻内容碎片里的数据。
十、主布局 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>
新建 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>
可以看出,在双页模式下我们同时引入了两个碎片,并将新闻内容的碎片放在了一个 FrameLayout 布局下,而这个布局的id 正是 news_content_layout。因此,能够找到这个 id 的时候就是双页模式,否则就是单面模式。
十一、MainActivity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}