碎片概述
碎片是一种可以嵌入在活动中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上使用较广。一个屏幕中包含一个活动较空,可以通过构建两个碎片,包含不同的活动,在同一个活动中引入这两个碎片,可实现屏幕的充分利用
碎片的使用
碎片在平板中使用较广,因此后面的demo都使用了平板模拟器
碎片的简单使用
在一个活动中添加两个碎片,并让两个碎片平分活动空间 left_fragment.xml设置了一个按钮 并居中显示
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:layout_gravity="center_horizontal"
android:text="Button"/>
</LinearLayout>
right_fragment.xml将该布局的背景设置为绿色,并放置一个TextView用于显示文本
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#ABE6AB"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="This is right fragment"/>
</LinearLayout>
LeftFragment继承Fragment 重写了onCreateView() 方法,在这个方法中通过LayoutInflater的inflater() 方法将left_fragment布局动态加载进来
public class LeftFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}
}
RightFragment
public class RightFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.right_fragment, container, false);
return view;
}
}
activity_main.xml使用<fragment> 中添加碎片,通过android:name指明想要添加的碎片类名,将类的包名也加入。
<LinearLayout 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="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"/>
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"/>
</LinearLayout>

动态添加碎片
根据具体情况动态的添加碎片,可以将程序的界面制定的更多样化 another_right_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#ffff00"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="This is another right fragment"/>
</LinearLayout>
AnotherRightFragment
public class AnotherRightFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.another_right_fragment, container, false);
return view;
}
}
activity_main.xml 将右侧碎片替换成FrameLayout,这是Android中最简单的布局,所有的控件都默认摆放在布局的左上角。由于这里仅需要在布局里放入一个碎片,不需要任何定位,因此使用该布局。
<LinearLayout 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="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/right_layout">
</FrameLayout>
</LinearLayout>
MainActivity首先给左侧碎片中的按钮添加点击事件,然后调用replaceFragment() 方法动态添加RightFragment这个碎片。当点击左侧按钮时调用replaceFragment() 方法将右侧的碎片替换成AnotherRightFragment,这主要分为5步
- 创建待添加的碎片实例
- 获取FragmentManager,在活动中可以直接通过调用
getSupportFragmentManager() 方法得到 - 开启一个事务,通过调用
beginTransaction() 方法开启 - 向容器内添加或替换碎片,一般使用
replace() 实现,需要传入容器的id和待添加碎片的实例 - 提交事务,调用
commit() 方法完成
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
replaceFragment(new RightFragment());
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
replaceFragment(new AnotherRightFragment());
break;
default:
break;
}
}
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.commit();
}
}
点击按钮后 
在碎片中模拟返回栈
实现按一下Back返回上一个碎片 FragmentTransaction中提供了一个addToBackStack() 方法,用于将一个事务添加到返回栈中,
transaction.addToBackStack(null);
在提交事务之前调用该方法,它可以接收一个名字用于描述返回栈的状态,一般传入null。
碎片在活动之间进行通信
活动和碎片都是各自存在于一个独立的类当中,如何在活动中调用碎片里的方法或碎片中调用活动的方法,FragmentManager提供了一个类似于findViewById() 的方法,专门用于从布局文件中获取碎片的实例。 活动中调用碎片中的方法
RightFragment rightFragment = (RightFragment) getSupportFragmentManager().findFragmentById(R.id.right_fragment);
调用FragmentManager中的findViewById() 方法 可以在活动中得到相应碎片的实例,这样就能轻松调用碎片里的方法了 碎片中调用活动中的方法
MainActivity activity = (MainActivity) getActivity();
当碎片中需要使用Context对象时,也可以使用getActivity() 方法,因为获取到活动本身就是一个 Context对象。 碎片于碎片之间也可以进行通信,通过活动作为中间媒介。
碎片的生命周期
碎片的状态和回调
- 运行状态
当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态。 - 暂停状态
当一个活动进入暂停状态时(由于另一个未占满屏幕的活动被添加到了栈顶),与它相关联的可见碎片就会进入到暂停状态。 - 停止状态
当一个活动进入停止状态时,与它相关联的碎片就会进人到停止状态,或者通过调用FragmentTransaction的remove() 、 replace() 方 法将碎片从活动中移除,但如果在事务提交之前调用addToBackStack() 方法,这时的碎片也会进人到停止状态。总的来说,进入停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收。 - 销毁状态
碎片总是依附于活动而存在的,因此当活动被销毁时,与它相关联的碎片就会进人到销毁状态。或者通过调用FragmentTransaction 的remove() 、replace( ) 方法将碎片从活动中移除,但在事务提交之前并没有调用addToBackStack() 方法,这时的碎片也会进人到销毁状态。 同样地,Fragment 类中也提供了一系列的回调方法,以覆盖碎片生命周期的每个环节。其中,活动中有的回调方法,碎片中几乎都有,不过碎片还提供了一些附加的回调方法 - onAttach()
当碎片和活动建立关联的时候调用。 - onCreateView()
为碎片创建视图(加载布局)时调用。 - onActivityCreated()
确保与碎片相关联的活动一定 已经创建完毕的时候调用。 - onDestroyView()
当与碎片关联的视图被移除的时候调用。 - onDetach()
当碎片和活动解除关联的时候调用。

体验碎片的生命周期
RightFragment在每个回调方法中都加入了打印日志代码
public class RightFragment extends Fragment {
public static final String TAG = "RightFragment";
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
View view = inflater.inflate(R.layout.right_fragment, container, false);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach");
}
}
运行程序 ,当RightFragment第一次被加载进屏幕时会依次执行onAttach() ,onCreate() ,onCreateView() ,onActivityCreated() ,onStart() ,onResume() 方法  点击按钮后,由于AnotherRightFragment替换了RightFragment, 此时的RightFragment进人了停止状态,因此onPause() . onStop() 和onDestroyView() 方法会得到执行。当然如果在替换的时候没有调用addToBackStack() 方法, 此时的RightFragment 就会进人销毁状态,onDestroy() 和onDetach() 方法就会得到执行。  接着按下Back键,RightFragment 会重新回到屏幕。依次会执行onPause() . onStop() . onDest royView() . onDestroy() 和onDetach() 方法,最终将活动和碎片一起销毁。  另外值得一提的是,在碎片中你也是可以通过onSaveInstanceState() 方法来保存数据的,因为进入停止状态的碎片有可能在系统内存不足的时候被回收。保存下来的数据在onCreate() .onCreateView( ) 和onActivityCreated() 这 3个方法中你都可以重新得到,它们都含有一个Bundle类型的savedInstanceState参数。
动态加载布局的技巧
使用限定符
很多的平板应用都采用的是双页模式(程序会在左侧的面板上显示一个包含子项的列表,在右侧的面板上显示内容),因为平板电脑的屏幕足够 大,完全可以同时显示下两页的内容,但手机的屏幕一次就只能显示一页的内容, 因此两个页面需要分开显示。 使用限定符可在运行时判断程序应该使用单页还是多页
activity_main.xml只留下了左侧碎片,并让它充满整个布局,只包含了一个碎片,即单页模式
<LinearLayout 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="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"/>
</LinearLayout>
接着在res目录下新建layout-large文件夹,在这个文件夹下新建一个布局,也叫作activity_ main.xml, 该布局包含了两个碎片,即双页模式。其中large就是一个限定符,那些被屏幕认为是large的设备就会自动加载layout-large文件夹下的布局,而小屏幕的设备则还是会加载layout文件夹下的布局。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"/>
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"/>
</LinearLayout>
然后将MainActivity中replaceF ragment ()方法里的代码注释掉,并在平板模拟器上重新运行程序, 平板上  手机上 这样我们就实现了在程序运行时动态加载布局的功能。  
使用最小宽度限定符
最小宽度限定符允许我们对屏幕的宽度指定一个最小值 (以dp为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局, 屏幕宽度小于这个值的设备就加载另一个布局。
在res目录下新建layout-sw600dp文件夹,然后在这个文件夹下新建activity_main.xml 布局,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"/>
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"/>
</LinearLayout>
这就意味着,当程序运行在屏幕宽度大于600dp的设备上时,会加载layout-sw600dp/activity_ main布局,当程序运行在屏幕宽度小于600dp的设备上时,则仍然加载默认的layoutactivity_ main布局。
一个简易版的新闻应用
编写同时兼容手机和平板的应用程序 由于待会在编写新闻列表时会使用到RecyclerView,因此首先需要在app/build.gradle当中添加依赖库,如下所示:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
新建一个新闻实体类,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类的代码还是比较简单的,title 字段表示新闻标题,content 字段表示新闻内容。接着新建布局文件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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/visibility_layout"
android:orientation="vertical"
android:visibility="invisible">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/news_title"
android:gravity="center"
android:padding="10dp"
android:textSize="20sp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/news_content"
android:layout_weight="1"
android:padding="15dp"
android:textSize="18sp"/>
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#000"/>
</RelativeLayout>
新闻内容的布局主要可以分为两个部分,头部部分显示新闻标题,正文部分显示新闻内容,中间使用一条细线分隔开。这里的细线是利用View来实现的,将View的宽或高设置为1dp,再通过background属性给细线设置一下 颜色就可以了。这里我们把细线设置成黑色。 
android:padding
设置了内边距为10dp,padding为内边距;margin为外边距。
安卓的view是一块矩形区域,padding是内边距,就是view(里面的内容)永远都至少和边界有一段设定好的距离。margin是外边距,就是外面的view无法完全靠近这个view的边界,至少要间隔一段设置好的距离。
某个View指定为padding是针对该View里面的子View距离该View距离而言的。某个View指定为margin是针对该View本身距离别人或者父View而言的。
android:layout_alignParent
对于控件的 android:layout_alignParent 属性,只有在该布局的父布局也是RelativeLayout是才有用,此属性的含义为将控件边缘与父控件的边缘对齐
android:layout_alignParentLeft=“true” --将控件的左边缘和父控件的左边缘对齐 android:layout_alignParentTop=“true” --将控件的上边缘和父控件的上边缘对齐 android:layout_alignParentRight=“true” --将控件的右边缘和父控件的右边缘对齐 android:layout_alignParentBottom=“true” --将控件的底边缘和父控件的底边缘对齐
android:layout_centerInParent=“true” --将控件置于父控件的中心位置 android:layout_centerHorizontal=“true” --将控件置于水平方向的中心位置 android:layout_centerVertical=“true” --将控件置于垂直方向的中心位置
然后再新建一个NewsContentFragment类,继承自Fragment, 代码如下所示:
public class 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( ) 方法分别获取到新闻标题和内容的控件,然后将方法传递进来的参数设置进去。 这样我们就把新闻内容的碎片和布局都创建好了,但是它们都是在双页模式中使用的,如果想在单页模式中使用的话,我们还需要再创建一个活动。右击com.example.fragmentbestpractice包→New→Activity→ Empty Activity, 新建一个NewsContentActity, 并将布局名指定成news_ content, 然后修改news_ content.xml 中的代码,如下所示:
<LinearLayout 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="match_parent"
tools:context=".NewsContentActivity"
android:orientation="vertical">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_content_fragment"
android:name="com.example.fragmentbestpractice.NewsContentFragment"/>
</LinearLayout>
这里我们充分发挥了代码的复用性,直接在布局中引入了NewsContentFragment, 这样也就相当于把news_ content. frag 布局的内容自动加了进来。 然后修改NewsContentActivity中的代码
public class NewsContentActivity extends AppCompatActivity {
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);
setContentView(R.layout.news_content);
String newsTitle = getIntent().getStringExtra("news_title");
String newsContent = getIntent().getStringExtra("news_content");
NewsContentFragment newsContentFragment = (NewsContentFragment)getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTitle, newsContent);
}
}
可以看到,在onCreate( ) 方法中我们通过Intent 获取到了传人的新闻标题和新闻内容,然后调用FragmentManager的findFragmentById( ) 方法得到了NewsContentFragment的实例,接着调用它的refresh() 方法,并将新闻的标题和内容传入,就可以把这些数据显示出来了。 接下来还需要再创建一个用于显示新闻列表的布局,新建news_ title_ frag.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_title_recycler_view"/>
</LinearLayout>
这个布局的代码就非常简单了,里面只有一个用于显示新闻列表的RecyclerView。既然要用到RecyclerView,那么就必定少不了子项的布局。新建news_item.xml 作为RecyclerView子项的布局
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/news_title"
android:singleLine="true"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"/>
子项的布局也非常简单,只有一个TextView。android: padding表示给控件的周围加上补白,这样不至于让文本内容会紧靠在边缘上。
android: singleLine
设置为true表示让这个TextView只能单行显示。
android:ellipsize
用于设定当文本内容超出控件宽度时,文本的缩略方式,这里指定成end表示在尾部进行缩略。
既然新闻列表和子项的布局都已经创建好了,那么接下来我们就需要一个用于展示新闻列表的地方。这里新建NewsTitleF ragment作为展示新闻列表的碎片
public class NewsTitleFragment extends Fragment {
private boolean isTwoPane;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
}else {
isTwoPane = false;
}
}
}
可以看到,NewsTitleFragment 中并没有多少代码,在onCreateView() 方法中加载了news_title_frag 布局,这个没什么好说的。我们注意看一下onActivityCreated() 方法,这个方法通过在活动中能否找到一个id 为news_ content_ layout 的View来判断当前是双页模式还是单页模式,因此我们需要让这个id为news_ content_layout 的View只在双页模式中才会出现。 activity_main.xml
<FrameLayout 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:id="@+id/news_title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_title_fragment"
android:name="com.example.fragmentbestpractice.NewsTitleFragment"/>
</FrameLayout>
上述代码表示,在单页模式下,只会加载一个新闻标题的碎片。 然后新建layout-sw600dp文件夹,在这个文件夹下再新建一一个activity_ main.xml 文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.fragmentbestpractice.NewsTitleFragment"
android:id="@+id/news_title_fragment"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:id="@+id/news_content_layout">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_content_fragment"
android:name="com.example.fragmentbestpractice.NewsContentFragment"/>
</FrameLayout>
</LinearLayout>
可以看出,在双页模式下我们同时引入了两个碎片,并将新闻内容的碎片放在了一个Frame-Layout布局下,而这个布局的id正是news_ content layout。 因此,能够找到这个id的时候就是双页模式,否则就是单面模式。 我们在NewsTitleFragment中新建一个内部类NewsAdapter来作为RecyclerView的适配器
public class NewsTitleFragment extends Fragment {
private boolean isTwoPane;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
}else {
isTwoPane = false;
}
}
class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
private List<News> mNewsList;
class ViewHolder extends RecyclerView.ViewHolder {
TextView newsTitleText;
public ViewHolder(View view) {
super(view);
newsTitleText = (TextView) view.findViewById(R.id.news_title);
}
}
public NewsAdapter(List<News> newsList) {
mNewsList = newsList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
News news = mNewsList.get(holder.getAdapterPosition());
if(isTwoPane) {
NewsContentFragment newsContentFragment = (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
}else {
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
}
});
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
News news = mNewsList.get(position);
holder.newsTitleText.setText(news.getTitle());
}
@Override
public int getItemCount() {
return mNewsList.size();
}
}
}
之前我们都是将适配器写成一个独立的类,其实也是可以写成内部类的,这里写成内部类的好处就是可以直接访问NewsTitleF ragment的变量,比如isTwoPane。 观察一下onCreateViewHolder( ) 方法中注册的点击事件,首先获取到了点击项的News实例,然后通过isTwoPane变量来判断当前是单页还是双页模式,如果是单页模式,就启动一个新的活动去显示新闻内容,如果是双页模式,就更新新闻内容碎片里的数据。现在还剩最后一步收尾工作,就是向RecyclerView 中填充数据了。修改NewsTitle-Fragment中的代码
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container, false);
RecyclerView newsTitleRecyclerView = (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
newsTitleRecyclerView.setLayoutManager(layoutManager);
NewsAdapter adapter = new NewsAdapter(getNews());
newsTitleRecyclerView.setAdapter(adapter);
return view;
}
private List<News> getNews() {
List<News> newsList = new ArrayList<>();
for(int i = 1; i <= 50; i++) {
News news = new News();
news.setTitle("This is new title" + i);
news.setContent(getRandomLengthContent("This is news content" + i + "."));
newsList.add(news);
}
return newsList;
}
private String getRandomLengthContent(String contnet) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for(int i = 0; i < length; i++) {
builder.append(contnet);
}
return builder.toString();
}
可以看到,onCreateView( ) 方法中添加了RecyclerView标准的使用方法。另外,这里调用了getNews () 方法来初始化50条模拟新闻数据,同样使用了一个getRandomLengthContent() 方法来随机生成新闻内容的长度,以保证每条新闻的内容差距比较大   
|