碎片(Fragment)是可以嵌入在活动中的UI片段, 能让程序更合理使用大屏幕的空间. 可理解成一个迷你型的活动. 同活动一样, 能包含布局, 有自己的生命周期.
目标: 在一个活动中添加两个碎片, 并让这两个碎片评分活动空间.
步骤:
第一, 新建左侧碎片布局和右侧碎片布局, 左侧布局放置一个居中显示的按钮, 右侧放置一个TextView, 用于显示一段文本.
第二, 新建左侧碎片类和右侧碎片类, 继承自 android.app.Fragment . 两个类的代码大同小异, 比如左侧碎片类:
public class LeftFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}
}
第三, 修改主活动的布局文件:
<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="me.zipstream.fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/right_fragment"
android:name="me.zipstream.fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
LinearLayout>
使用
标签在布局中添加碎片, 这里注意 android:name
属性用来显式指明要添加的碎片类名, 要把包名也加上.
再新建一个右侧碎片, 包含一个TextView, 显示不同的文字. 再新建一个右侧碎片类, 代码都一样.
修改 主活动的布局:
<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="me.zipstream.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/right_fragment"
android:name="me.zipstream.fragment.RightFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
FrameLayout>
LinearLayout>
将右侧碎片放在了一个 FrameLayout 中.
然后, 在主活动的代码中动态替换 FrameLayout 中的内容, 实现动态添加碎片:
public class MainActivity extends Activity 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);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
AnotherRightFragment fragment = new AnotherRightFragment();
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.commit();
break;
default:
break;
}
}
}
总结动态添加碎片的步骤:
1, 创建待添加碎片的实例.
2, 获取到 FragmentManager , 通过 getFragmentManager() 方法.
3, 开启一个事务, 通过 beginTransaction() 方法.
4, 向容器内加入碎片, 通过 replace() 方法. 注意, 传入的是 容器的id, 和待添加碎片的实例.
5, 提交事务.
模拟返回栈的效果, 按下Back键返回到上一个碎片而不是退出程序.
很简单, FragmentTransaction 提供了一个 addToBackStack() 方法.
只需要在加入碎片后添加一行代码, 就可以了.
transaction.addToBackStack(null);
调用 FragmentManager 的 findFragmentById() 方法, 可以在活动中得到碎片的实例:
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
调用 getActivity() 方法, 可以在碎片中得到和当前碎片相关联的活动的实例:
MainActivity activity = (MainActivity) getActivity();
另外, 当碎片中需要使用 Context 对象时, 也可以使用 getActivity() 方法.
那么, 碎片和碎片之间怎么通信呢?
很简单, 首先在一个碎片中得到与之相关联的活动的实例, 再通过这个活动获取另外一个碎片.
活动中有的回调方法, 碎片中都有, 但是碎片还提供了一些附加的回调方法:
* onAttach(): 碎片和活动建立关联时调用.
* onCreateView(): 为碎片创建视图(加载布局)时调用.
* onActivityCreate(): 确保与碎片相关联的活动一定创建完毕时调用.
* onDestroyView(): 与碎片相关联的视图被移除时调用.
* onDetach(): 碎片和活动解除关联时调用.
使程序在运行时能够根据分辨率或屏幕大小来决定加载哪个布局.
限定符(Qualifiers).
修改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="me.zipstream.fragment.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
LinearLayout>
在 res 目录下新建一个文件夹 layout-large
, 新建布局, 也叫activity_main
, 代码:
<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="me.zipstream.fragment.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/right_fragment"
android:name="me.zipstream.fragment.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" />
LinearLayout>
large
就是一个限定符. 那些屏幕被认为是large的设备就会自动加载 layout_large 文件夹下的布局, 而小屏幕设备还会加载 layout 文件夹下的布局.
android中常见的限定符:
按大小分:
* small: 小屏幕设备
* normal: 中等
* large: 大
* xlarge: 超大
按分辨率分:
* ldpi: 低分辨率(120dpi以下)
* mdpi: 中分辨率(120dpi 到 160dpi)
* hdpi: 高分辨率(160dpi 到 240dpi)
* xhdpi: 超高分辨率(240dpi 到 320dpi)
按方向分:
* land: 横屏设备
* port: 竖屏设备
最小宽度限定符(Smallest-width Qualifier).
对屏幕的宽度指定一个最小值(以dp为单位), 屏幕宽度大于这个值的设备就加载这个布局, 小于则加载另一个布局.
比如在res目录下新建一个layout-sw600dp
文件夹, 那么屏幕大于600dp的设备就会加载这个目录下的布局.小于则加载默认的布局.
最小宽度限定符是在Android3.2之后引入的.