Android开发学习教程(26)- Android Fragment(碎片)

137 阅读8分钟

—— 尽吾志也,而不能至者,所以无悔矣

什么是单窗格或多窗格布局?

面板或窗格代表用户界面的一部分 。术语窗格是一个通用术语,用于描述根据实际可用空间将多个视图组合成一个复合视图的概念。

如果没有足够的可用空间,则仅显示一个面板。这通常称为单窗格布局。

Android开发学习教程(26)- Android Fragment(碎片)

如果有更多可用空间,可以显示多个面板。

Android开发学习教程(26)- Android Fragment(碎片)

什么是Fragment?

Fragment是 Android 中的一个组件,Fragment依赖于Activity运行,但有自己的生命周期,通常有自己的用户界面。

Android设备存在各种屏幕尺寸和密度。Fragment简化了组件在不同布局及其逻辑中的重用。你可以为手机构建单窗格布局,为平板电脑构建多窗格布局。你还可以使用Fragment来支持智能手机上横向和纵向的不同布局。

因为可以从Activity中动态添加和删除Fragment。所以Fragment的使用允许设计非常灵活的用户界面。

典型示例是Activity中的项目列表。在平板电脑上,如果单击项目,你会立即在右侧的同一屏幕上看到详细信息。而在手机上,你需要跳转到新的详细信息屏幕。这在下图中进行了描述。

Android开发学习教程(26)- Android Fragment(碎片)

Fragment的用法

有两种方法可以将Fragmtn添加到Activity中:动态添加和静态添加。

静态添加

要静态添加Fragment,只需将Fragment嵌入到活动的 xml 布局文件中:

12345678910111213141516171819202122232425262728<?``xml version``=``"1.0" encoding``=``"utf-8"``?>``<``LinearLayout xmlns:android``=``"http://schemas.android.com/apk/res/android"``    ``xmlns:tools``=``"http://schemas.android.com/tools"``    ``android:layout_width``=``"fill_parent"``    ``android:layout_height``=``"fill_parent"``    ``android:baselineAligned``=``"false"``    ``android:orientation``=``"horizontal"``>``    ``<``fragment``        ``android:id``=``"@+id/listFragment"``        ``class``=``"com.example.myapplication1.MyListFragment"``        ``android:layout_width``=``"0dp"``        ``android:layout_height``=``"match_parent"``        ``android:layout_weight``=``"1"``        ``android:tag``=``"listFragment"``        ``tools:layout``=``"@layout/fragment_rsslist_overview" />``    ``<``fragment``        ``android:id``=``"@+id/detailFragment"``        ``class``=``"com.example.myapplication1.DetailFragment"``        ``android:layout_width``=``"0dp"``        ``android:layout_height``=``"match_parent"``        ``android:layout_weight``=``"2"``        ``android:tag``=``"detailFragment"``        ``tools:layout``=``"@layout/fragment_rssitem_detail" />``</``LinearLayout``>
1234567891011121314151617181920import android.os.Bundle;``import androidx.appcompat.app.AppCompatActivity;``public class MainActivityByXml ``extends AppCompatActivity ``implements MyListFragment.OnItemSelectedListener {``  ``@Override``  ``protected void onCreate(Bundle savedInstanceState) {``      ``super``.onCreate(savedInstanceState);``      ``setContentView(R.layout.activity_main_byxml);``  ``}``  ``@Override``  ``public void onRssItemSelected(String str) {``      ``// DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.detailFragment);``      ``DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentByTag(``"detailFragment"``);``      ``fragment.setText(str);``  ``}``}

动态添加

动态添加允许你在Activity中添加、删除和替换片段。

1234567891011121314151617181920<?``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``=``"horizontal"``>``    ``<``FrameLayout``        ``android:id``=``"@+id/listcontainer"``        ``android:layout_width``=``"0dp"``        ``android:layout_height``=``"match_parent"``        ``android:layout_weight``=``"1" />``    ``<``FrameLayout``        ``android:id``=``"@+id/detailscontainer"``        ``android:layout_width``=``"0dp"``        ``android:layout_height``=``"match_parent"``        ``android:layout_weight``=``"1" />``</``LinearLayout``>
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647import android.os.Bundle;``import androidx.appcompat.app.AppCompatActivity;``import androidx.fragment.app.FragmentManager;``import androidx.fragment.app.FragmentTransaction;``public class MainActivityByManager ``extends AppCompatActivity ``implements MyListFragment.OnItemSelectedListener {``    ``private FragmentManager fragmentManager;``    ``@Override``    ``protected void onCreate(Bundle savedInstanceState) {``        ``super``.onCreate(savedInstanceState);``        ``setContentView(R.layout.activity_main_bymanager);``        ``instanceFragmentManager();``    ``}``    ``private void instanceFragmentManager() {``        ``// 获取FragmentManger``        ``fragmentManager = getSupportFragmentManager();``        ``// 把Fragment add到FrameLayout容器中,并设置此add进来的Fragment的Tag为MyListFragment``        ``FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();``        ``fragmentTransaction.add(R.id.listcontainer, ``new MyListFragment(), ``"MyListFragment"``);``        ``fragmentTransaction.add(R.id.detailscontainer, ``new DetailFragment(), ``"DetailFragment"``);``        ``fragmentTransaction.commit();``//        // replace Fragment``//        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();``//        fragmentTransaction.replace(R.id.detailscontainer, new DetailFragment());``//        fragmentTransaction.commit();``//``//        // remove Fragment``//        Fragment fragment = fragmentManager.findFragmentById(R.id.detailscontainer);``//        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();``//        fragmentTransaction.remove(fragment);``//        fragmentTransaction.commit();``    ``}``    ``@Override``    ``public void onRssItemSelected(String str) {``        ``// 使用 findFragmentById 或者 findFragmentByTag都行,建议还是使用 findFragmentByTag,避免逻辑层与视图层耦合``        ``DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.detailscontainer);``//        DetailFragment fragment = (DetailFragment) getSupportFragmentManager().findFragmentByTag("DetailFragment");``        ``fragment.setText(str);``    ``}``}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253import android.content.Context;``import android.os.Bundle;``import android.view.LayoutInflater;``import android.view.View;``import android.view.ViewGroup;``import android.widget.Button;``import androidx.fragment.app.Fragment;``public class MyListFragment ``extends Fragment ``implements View.OnClickListener {``    ``private OnItemSelectedListener listener;``    ``@Override``    ``public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {``        ``View view = inflater.inflate(R.layout.fragment_rsslist_overview, container, ``false``);``        ``view.findViewById(R.id.button1).setOnClickListener(``this``);``        ``view.findViewById(R.id.button2).setOnClickListener(``this``);``        ``view.findViewById(R.id.button3).setOnClickListener(``this``);``        ``view.findViewById(R.id.button4).setOnClickListener(``this``);``        ``view.findViewById(R.id.button5).setOnClickListener(``this``);``        ``return view;``    ``}``    ``@Override``    ``public void onClick(View v) {``        ``updateDetail(((Button) v).getText().toString());``    ``}``    ``public interface OnItemSelectedListener {``        ``void onRssItemSelected(String str);``    ``}``    ``@Override``    ``public void onAttach(Context context) {``        ``super``.onAttach(context);``        ``if (context ``instanceof OnItemSelectedListener) {``            ``listener = (OnItemSelectedListener) context;``        ``} ``else {``            ``throw new ClassCastException(context.toString() + ``" must implemenet MyListFragment.OnItemSelectedListener"``);``        ``}``    ``}``    ``@Override``    ``public void onDetach() {``        ``super``.onDetach();``        ``listener = ``null``;``    ``}``    ``private void updateDetail(String str) {``        ``listener.onRssItemSelected(str);``    ``}``}
123456789101112131415161718192021import android.os.Bundle;``import android.view.LayoutInflater;``import android.view.View;``import android.view.ViewGroup;``import android.widget.TextView;``import androidx.fragment.app.Fragment;``public class DetailFragment ``extends Fragment {``    ``@Override``    ``public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {``        ``View view = inflater.inflate(R.layout.fragment_rssitem_detail, container, ``false``);``        ``return view;``    ``}``    ``public void setText(String text) {``        ``TextView view = (TextView) getView().findViewById(R.id.detailsText);``        ``view.setText(text);``    ``}``}

Android开发学习教程(26)- Android Fragment(碎片)

查找Fragment实例

通常我们需要在Activity中查找Fragment实例用于操作Fragment。有几种方法可以查找现有的Fragment实例:

  1. 通过ID查找即findFragmentById

  2. 通过Tag查找即findFragmentByTag

下面更详细地概述了每种方法。

按ID查找即findFragmentById

如果是通过静态添加的,这里的ID就表示静态xml中fragment标签的id,例如上面示例中的findFragmentById(R.id.detailFragment)。如果是通过动态添加的(大部分时候都是此方式),ID则表示Fragment被add上去的那个布局的ID,例如上面示例中的findFragmentById(R.id.detailscontainer)。

按Tag查找即findFragmentById

Tag可以理解为每一个Fragment的唯一标识符。如果是通过静态添加的,这里的Tag就表示静态xml中fragment标签的android:tag值,例如上面示例中的findFragmentByTag(“detailFragment”)。如果是通过动态添加的(大部分时候都是此方式),ID则表示Fragment被add上去时指定的Tag字符串,例如上面示例中的fragmentTransaction.add(R.id.detailscontainer, new DetailFragment(), “DetailFragment”)、findFragmentByTag(“DetailFragment”)。

Activity Fragment通信

Fragment通常应该只与它们的父Activity通信。通过父Activity来管理与其他Fragment的交互。Activity可以看做是每个Fragment之间交互的控制器。

Fragment和Activity可以通过三种方式进行通信:

  1. 通过Bundle传参 – Activity 可以构造一个片段并设置参数

  2. 通过调用Fragment成员方法 – Activity 可以调用Fragment实例上的方法

  3. 通过接口回调 – Fragment可以通过接口在Activity上触发侦听器事件

  4. 通过Bundle传参

在某些情况下,Fragment可能希望接受某些参数。一种常见的模式是创建一个静态newInstance方法来创建带有参数的Fragment:

12345678910111213public class DemoFragment ``extends Fragment {``    ``public static DemoFragment newInstance(``int someInt, String someTitle) {``        ``DemoFragment fragmentDemo = ``new DemoFragment();``        ``Bundle args = ``new Bundle();``        ``args.putInt(``"someInt"``, someInt);``        ``args.putString(``"someTitle"``, someTitle);``        ``fragmentDemo.setArguments(args);``        ``return fragmentDemo;``    ``}``    ``}

这会将某些参数设置到 Fragment 中,之后可以在 onCreate 中使用以下方法访问参数:

1234567891011public class DemoFragment ``extends Fragment {``   ``@Override``   ``public void onCreate(Bundle savedInstanceState) {``       ``super``.onCreate(savedInstanceState);``       ``int SomeInt = getArguments().getInt(``"someInt"``, ``0``);  ``       ``String someTitle = getArguments().getString(``"someTitle"``, ``""``);   ``   ``}``   ``}

在 Activity 中动态加载片段:

12345FragmentTransaction ft = getSupportFragmentManager().beginTransaction();``DemoFragment fragmentDemo = DemoFragment.newInstance(``5``, ``"my title"``);``ft.replace(R.id.your_placeholder, fragmentDemo);``ft.commit();
  1. 通过调用Fragment成员方法

如上面示例中的fragment.setText(str)

  1. 通过接口回调

如上面示例中的onRssItemSelected回调

了解FragmentManager

FragmentManager负责Fragment的所有运行时管理,包括添加、删除、隐藏、显示或以其他方式在Fragment之间导航。重要的可用方法概述如下:

1234567addOnBackStackChangedListener:为片段返回堆栈的更改添加一个新的侦听器。``beginTransaction():创建一个新事务以在运行时更改片段。``findFragmentById(``int id):通过通常从活动 XML 布局中扩展的 id 查找片段。``findFragmentByTag(String tag):通常为运行时添加的片段按标签查找片段。``popBackStack():从 backstack 中删除一个片段。``executePendingTransactions():强制应用已提交的事务。

Fragment生命周期

上面我们说过,Fragment虽然依赖于Activity运行,但是Fragment有它自己的生命周期

123456789101112onAttach():当片段连接到活动时调用。``onCreate():被调用来进行片段的初始创建。``onCreateView():一旦 Fragment 应该膨胀视图,Android 就会调用它。``onViewCreated():在之后调用onCreateView()并确保片段的根视图是non-``null``. 任何视图设置都应该在这里进行。例如,查看查找、附加侦听器。``onActivityCreated():当宿主活动完成其onCreate()方法时调用。``onStart():一旦片段准备好显示在屏幕上,就会被调用。``onResume():分配“昂贵”的资源,例如注册位置、传感器更新等。``onPause():释放“昂贵”的资源。提交任何更改。``onDestroyView():当片段的视图被销毁时调用,但片段仍然保留在周围。``onDestroy():当片段不再使用时调用。``onDetach():当片段不再连接到活动时调用。

Android开发学习教程(26)- Android Fragment(碎片)

通常在onCreateView中设置视图,onCreate用于任何数据初始化,onActivityCreated用于设置只有在 Activity 完全创建后才能发生的事情。

这是一个如何使用各种片段生命周期事件的示例:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849public class SomeFragment ``extends Fragment {``    ``ThingsAdapter adapter;``    ``FragmentActivity listener;``        ``    ``// 此事件在创建Fragment或任何视图之前最先触发,当Fragment与Activity关联时,将调用onAttach方法。``    ``@Override``    ``public void onAttach(Context context) {``        ``super``.onAttach(context);``        ``if (context ``instanceof Activity){``            ``this``.listener = (FragmentActivity) context;``        ``}``    ``}``       ``    ``// 此事件在为Fragment创建视图之前触发,在创建或重新创建Fragment时,会调用onCreate方法。``    ``@Override``    ``public void onCreate(Bundle savedInstanceState) {``        ``super``.onCreate(savedInstanceState);``        ``ArrayList<Thing> things = ``new ArrayList<Thing>();``        ``adapter = ``new ThingsAdapter(getActivity(), things);``    ``}``    ``// 当Fragment应该动态地或通过XML布局创建其视图对象层次结构时,调用onCreateView方法。``    ``@Override``    ``public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {``        ``return inflater.inflate(R.layout.fragment_some, parent, ``false``);``    ``}``    ``    ``// 此事件在onCreateView()之后不久触发。只有从onCreateView()返回的视图非空时,才会调用onViewCreated()。任何视图设置都建议在这里进行。``    ``@Override``    ``public void onViewCreated(View view, Bundle savedInstanceState) {``        ``super``.onViewCreated(view, savedInstanceState);``        ``ListView lv = (ListView) view.findViewById(R.id.lvSome);``        ``lv.setAdapter(adapter);``    ``}``        ``    ``// 此方法在父Activity的onCreate()方法完成后调用。``    ``@Override``    ``public void onActivityCreated(Bundle savedInstanceState) {``        ``super``.onActivityCreated(savedInstanceState);``    ``}``    ``// 当Fragment与Activity失去关联时,将调用此方法。保存在onAttach中的任何引用都应在此处为null,以防止内存泄漏。``    ``@Override``    ``public void onDetach() {``        ``super``.onDetach();``        ``this``.listener = ``null``;``    ``}``}

**