0
碎片是什么
碎片是一种可以镶入活动中的UI片段,让程序更加合理的利用大屏空间。它和活动很像,可以认为碎片是迷你型的活动。在平板中使用广泛。
碎片的使用
例如向一个活动里面添加两个碎片平分空间就是建立两个布局,左侧布局和右侧布局
left_fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="button"/>
</LinearLayout>
right_fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#0361AB"
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 a right fragment"/>
</LinearLayout>
然后建立LeftFragment类和RightFragment类,二者都继承Fragment,并重写onCreate()方法,在这和方法里面通过LayoutInflater的inflate()方法将刚才定义的left_fragment布局和right_fragment动态加载进来。
最后修改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/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
使用了标签在布局中加载碎片,通过android:name属性来显示指明要添加的碎片类名。
动态添加碎片
上面的是在布局文件中添加碎片的方法,碎片最厉害的地方是在程序运行中动态的添加到活动中。根据具体情况来动态的添加碎片
根据上例继续完善,新建another_right.fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ffff00">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="这是另外一个右边的fragment"/>
</LinearLayout>
这个布局只是改了背景颜色和显示文字
然后新建AnotherRightFragment作为另一个右侧碎片
public class AnotherRightFragment extends Fragment
{
public view onCreate(LayoutInflater inflater,ViewGroup container,Bundle saveInstanceState)
{
View view=inflater.inflate(R.layout.another_right_fragment,container,false);
return view;
}
}
现在准备好了另一个碎片我们把它动态的加入到活动中。
修改activity.main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:id="@+id/right_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<!--
<fragment
android:id="@+id/fragment_right"
android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
-->
</LinearLayout>
把原来的右侧碎片换成了一个FrameLayout布局(所有的控件默认的摆在布局的坐上角)
然后我们向FrameLayout布局中添加内容从而实现动态的添加碎片的功能,修改MainActivity
public class MainActivity extends AppcompatActivity implements View.OnClickListener{
public 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()):
}
public void onClick(View v)
{
switch(v.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();
}
}
上述代码,给左侧碎片中的按钮注册了一个点击事件,调用 replaceFragment() 方法动态添加碎片。结合代码可看出,动态添加碎片主要分为 5 步。
-
创建待添加的碎片实例。
-
获取 FragmentManager,在活动中可以直接调用 getSupportFragmentManager() 方法得到。
-
开启一个事务,通过调用 beginTransaction() 方法开启。
-
向容器内添加或替换碎片,一般使用 replace() 方法实现,需要传入容器的 id 和待添加的碎 片实例。
-
提交事务,调用 commit() 方法来完成。
在碎片中模拟返回栈
在上一小节中,实现了向活动中动态添加碎片的功能,但通过点击按钮添加了一个碎片之后,按下 Back 键程序就会直接退出。该如何实现按下 Back 键可以回到上一个碎片呢?
FragmentTransaction 中提供了一个 addToBackStack() 方法,可以用于将一个事务添加到返回栈中,修改 Activity 中的代码如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
private void replaceFragment(Fragment fragment)
{
Fragment fragmentManager=getSupportFragmentManager();
FragmentTransaction reansaction=fragmentManager.beginTransaction();
transaction,replace(R.id.right_layout,fragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
碎片和活动的通信
虽然碎片镶嵌在活动中显示但是没有明显的方式直接进行通信。
为了方便碎片和活动之间进行通信,FragmentManager 提供了一个类似于 findViewById() 的方法,专门用于从布局文件中获取碎片的实例。在活动中调用碎片里的方法:
RightFragment rightFragment=(RightFragment)getSupportFragmentManager().findFragmentById(R.id.right_fragment);
在碎片中调用活动里的方法:通过调用 getActivity()方法来得到和当前碎片相关联的活动实例,代码如下:
MainActivity activity=(MainActivity)getActivity;
碎片的生命周期
每个活动在其生命周期内可能会有四种状态:运行状态、暂停 状态、停止状态和销毁状态。类似地,每个碎片在其生命周期内也可能会经历这几种状态,只不过在一些细小的地方会有部分区别。
运行状态
和他相关的活动是运行状态,这个碎片可见
暂停状态
和他相关的活动是暂停状态(一个未占满屏幕的活动成为栈顶),这个碎片可见
停止状态
1.和他相关的活动是停止状态,则这个碎片进入停止状态。
2.或者调用FragmentTransaction()的remove,replace()方法把碎片从活动移除,但在事务提交之前调用了addToBackStack()方法
总的来说进入停止状态的碎片对用户完全不可见。
销毁状态
1.碎片总是依附于活动而存在,当活动被销毁时,与它相关联的碎片会进入销毁的状态
2.或者调用FragmentTransaction()的remove,replace()方法把碎片从活动移除,但在事务提交之前没有调用addToBackStack()方法
和活动 Acitvity 相似,Fragment 类中也提供了一系列的回调方法,以覆盖碎片生命周期的每个环节。其中,活动中有的回调方法,碎片中几乎都有,不过碎片还提供了一些附加的回调方法,重点来看下这几个回调:
onAttach() 当碎片和活动建立关联的时候调用。
onCreateView() 为碎片创建视图(加载布局)时调用。
onActivityCreated() 确保与碎片相关联的活动一定已经创建完毕的时候调用。
onDestroyView() 当与碎片关联的视图被移除的时候调用。
onDetach() 当碎片和活动解除关联的时候调用。
动态加载布局技巧
使用限定符
现有个需求:在平板上使用双页模式,在手机上显示单页模式。那么怎样才能在运行时判断程序应该是使用双页模式还是单页模式呢?这就需要借助限定符(Qualifiers)来实现了。
通过一个例子来学习一下它的用法,修改项目中 activity_fragment.xml 的代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这里将多余的代码都删掉,只留下一个左侧碎片,并让它充满整个父布局。接着在 res 目录下新建 layout-large 文件夹,在这个文件夹下新建一个布局,也叫做 activity_fragment.xml, 代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="@+id/fragment_left"
android:name="com.wonderful.myfirstcode.inquiry_fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<fragment
android:id="@+id/fragment_right"
android:name="com.wonderful.myfirstcode.inquiry_fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"/>
</LinearLayout>
上面 layout/activity_fragment 布局只包含了一个碎片,即单页模式,而 layout-large/activity_fragment 布局包含了两个碎片,即双页模式。其中 large 就是一个限定符,那些屏幕被认为是 large 的设备就会自动加载 layout-large 文件夹下的布局,而小屏幕的设备则还是会加载 layout 文件夹下的布局。
然后将 Activity 中 replaceFragment() 方法注释掉。
Android 中一些常见的限定符可以参考下表:
使用最小宽度限定符
有时候希望可以更加灵活地为不同设备加载布局,不管它们是不是被系统认定为 “ large ”,这时就可以使用最小宽度限定符(Smallest-width Qualifier)了。
最小宽度限定符允许我们对屏幕的宽度指定一个最小指(以 dp 为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕宽度小于这个值的设备就加载另一个布局。
在 res 目录下新建 layout-sw600dp 文件夹,然后在这个文件夹下新建 activity_fragment .xml 布局,代码与上面 layout-large/activity_fragment 中的一样。
这就意味着,当程序运行在屏幕宽度大于 600dp 的设备上时,会加载 layout-sw600dp/ activity_fragment 布局,当程序运行在屏幕宽度小于 600dp 的设备上时,则仍然加载默认的 layout/activity_fragment 布局。