Fragment

Fragment

可以兼容大小尺寸,例如:手机和平板,让布局能适应手机和平板,而不用维护两个,浪费资源和人力。

Fragment是一种可以嵌入在Activity当中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用得非常广泛。

简单用法

左侧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Button"
/>

</LinearLayout>
class LeftFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.left_fragment, container, false)
}

}

右侧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
right_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#00ff00"
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="24sp"
android:text="This is right fragment"
/>

</LinearLayout>
class RightFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.right_fragment, container, false)
}

}

activity_main.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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:id="@+id/leftFrag"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />

<fragment
android:id="@+id/rightFrag"
android:name="com.example.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />

</LinearLayout>

FragmentManager

使用 FragmentManager 来管理 Fragment 的添加、移除和替换操作。以下是如何使用 FragmentManager 替换一个 Fragment 的基本步骤:

1
2
3
4
5
val fragmentManager = supportFragmentManager // 或者使用 childFragmentManager,取决于所在的容器类型
val newFragment = NewFragment()
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.fragmentContainer, newFragment) // R.id.fragmentContainer 是你的容器视图的 ID
transaction.commit()

对于提交事务的方法,还有一个 commitAllowingStateLoss(),它允许在一些特定情况下提交事务,即使可能存在状态丢失的风险。通常情况下,使用 commit() 来提交事务是更安全的方式,因为它会确保在状态变化(如设备旋转)时不会丢失事务的状态。

然而,有时候在异步操作或其他情况下,如果使用 commit() 来提交事务可能会导致 IllegalStateException,此时可以考虑使用 commitAllowingStateLoss() 来避免崩溃,但要注意潜在的状态丢失问题。

Activity动态添加Fragment

另一个右侧,点击按钮动态变化右侧的fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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="24sp"
android:text="This is another right fragment"
/>

</LinearLayout>
class AnotherRightFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.another_right_fragment, container, false)
}

}

修改activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<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:id="@+id/leftFrag"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />

<FrameLayout
android:id="@+id/rightLayout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
</FrameLayout>

</LinearLayout>

将右侧Fragment替换成了一个FrameLayout。这是Android中最简单的一种布局,所有的控件默认都会摆放在布局的左上角。由于这里仅需要在布局里放入一个Fragment,不需要任何定位,因此非常适合使用FrameLayout。

MainActivity点击按钮动态添加另一个右侧fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
replaceFragment(AnotherRightFragment())
}
replaceFragment(RightFragment())
}

private fun replaceFragment(fragment: Fragment) {
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.rightLayout, fragment)
transaction.commit()
}

}

动态添加Fragment主要分为5步:

(1) 创建待添加Fragment的实例。

(2) 获取FragmentManager,在Activity中可以直接调用getSupportFragmentManager()方法获取。

(3) 开启一个事务,通过调用beginTransaction()方法开启。

(4) 向容器内添加或替换Fragment,一般使用replace()方法实现,需要传入容器的id和待添加的Fragment实例。

(5) 提交事务,调用commit()方法来完成。

Fragment中实现返回栈

通过点击按钮添加了一个Fragment之后,这时按下Back键程序就会直接退出。如果我们想实现类似于返回栈的效果,按下Back键可以回到上一个Fragment,该如何实现呢?

FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中。修改MainActivity中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MainActivity : AppCompatActivity() {

...

private fun replaceFragment(fragment: Fragment) {
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.rightLayout, fragment)
transaction.addToBackStack(null)
transaction.commit()
}

}

现在重新运行程序,并点击按钮将AnotherRightFragment添加到Activity中,然后按下Back键,你会发现程序并没有退出,而是回到了RightFragment界面。继续按下Back键,RightFragment界面也会消失,再次按下Back键,程序才会退出。

Fragment和Activity交互

如果想要在Activity中调用Fragment里的方法,或者在Fragment中调用Activity里的方法,应该如何实现呢?

Activity中调用Fragment中的方法:

1
2
3
4
val fragment = supportFragmentManager.findFragmentById(R.id.leftFrag) as LeftFragment

//推荐
val fragment = leftFrag as LeftFragment

Fragment中调用Activity中的方法:

1
2
3
if (activity != null) {
val mainActivity = activity as MainActivity
}

Activity和Fragment间数据传递

Activity向Fragment传递数据

使用Bundle

  • Bundle用来存储数据
  • setArguments()方法将数据集绑定到Fragment中

**Activity**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
BlankFragment1 fragment = new BlankFragment1();

//创建资源绑定器bundle
Bundle bundle = new Bundle();
//添加数据到资源绑定器
bundle.putString("name","张三");
//将资源绑定器添加到fragment对象中
fragment.setArguments(bundle);

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.contain1,fragment);
fragmentTransaction.commit();
}
}

Fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BlankFragment1 extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//getArguments获取该fragment对象中的bundle
Bundle bundle = getArguments();
//获取bundle中存放的数据
Log.d("ioc", bundle.getString("name"));
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_blank1, container, false);
}
}

Fragment向Activity传递数据

方法1:使用Java接口

  • 声明一个接口,使用接口实现Activity和Fragment之间的数据传输
  • 接口的声明在Fragment中
  • 接口的实现在Activity中

接口:

1
2
3
4
5
6
public interface IFragmentCallback {
//Fragment调用该方法发送信息msg给Acticity,Activity中通过实现该方法来获得发来的信息msg
void sendMsgToActivity(String msg);
//Activity实现该方法将信息作为返回值,Fragment调用该方法获取到的返回值即Activity传来的信息
String getMsgFromActivity();
}

Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BlankFragment1 extends Fragment {
//Fragment中要有IFragmentCallback接口属性,这里只有一个声明,具体实现在Activity中
private IFragmentCallback iFragmentCallback;
//接口对象的set方法,负责实例化接口
public void setiFragmentCallback(IFragmentCallback iFragmentCallback) {
this.iFragmentCallback = iFragmentCallback;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//调用iFragmentCallback的方法向Activity传参数,Activity实例化IFragmentCallback接口时会在sendMsgToActivity方法内对传参数进行操作
iFragmentCallback.sendMsgToActivity("你好,我来自Fragment");
//Activity实例化IFragmentCallback接口时在getMsgFromActivity方法内返回一个参数,Fragment调用该方法就会取得该参数,并进行相应操作
String msg = iFragmentCallback.getMsgFromActivity();
Log.d("ioc",msg);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_blank1, container, false);
}
}

Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
BlankFragment1 fragment = new BlankFragment1();

//Activity中通过匿名内部类的方式创建接口的实现类
IFragmentCallback iFragmentCallback = new IFragmentCallback() {
@Override
public void sendMsgToActivity(String msg) {
//接收到Fragment发来的msg数据并输出
Log.d("ioc",msg);
}

@Override
public String getMsgFromActivity() {
//向Fragment发送数据
return "我来自Activity";
}
};
/*fragment对象中有一个属性iFragmentCallback,通过给属性赋值给fragment对象的iFragmentCallback接口属性实例化,
这样Activity和Fragment就有了共同的IFragmentCallback对象,通过这个对象取得了联系(观察者模式)*/
fragment.setiFragmentCallback(iFragmentCallback);
//动态创建fragment,显示该fragment到contain1的标签内
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.contain1,fragment);
fragmentTransaction.commit();
}
}

Fragment之间互传数据

Fragment之间无法直接互传数据,需要一个Activity作为中间桥梁辅助两个Fragment之间数据传递

也就是Fragment向Activity传递数据,Activity又将数据传递给Fragment的组合。

例如点击某个列表项,跳转到对应的界面,并显示信息列表项的信息。

Fragment 1

1
2
3
4
5
6
7
8
9
10
11
12
13
iFragmentCallback iFragmentCallback;//属性
listview1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//获取到童虎点击的列表项内的TextView控件
TextView view1 = view.findViewById(R.id.text1);
//获取到TextView控件内的值
String name = view1.getText().toString();
Log.e("mylog","fragment1发送数据:"+name+"给Activity");
//通过调用sendMsgToActivity方法发送数据给Activity
iFragmentCallback.sendMsgToActivity(name);
}
});

Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Activity中实例化接口
iFragmentCallback iFragmentCallback = new iFragmentCallback() {
//通过实现接口的sendMsgToActivity方法得到fragment1传来的值msg(因为fragment1中调用该方法并传入一个参数msg)
@Override
public void sendMsgToActivity(String msg) {
//创建资源绑定器bundle,用于Activity向fragment发送数据
Bundle bundle = new Bundle();
Log.e("mylog","Activity得到fragment1传来的信息:"+msg);
//添加数据到资源绑定器
bundle.putString("name",msg);
//将资源绑定器添加到fragment对象中
Fragment4 fragment4 = new Fragment4();
Log.e("mylog","Activity使用buddle将信息发送给fragment4");
fragment4.setArguments(bundle);
//动态创建fragment4并跳转到fragment4
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame,fragment4);
fragmentTransaction.commit();
}
};
//实例化的接口对象传给fragment1的属性上(观察者模式)
fragment1.iFragmentCallback = iFragmentCallback;

Fragment 2

1
2
3
4
5
6
7
8
9
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_4, container, false);
TextView dialog = view.findViewById(R.id.thedialog);
Bundle bundle = getArguments();
Log.e("mylog","fragment4得到Activity发来的数据:"+bundle.getString("name"));
dialog.setText("与"+bundle.getString("name")+"的会话窗口");
return view;
}

Fragment生命周期

  1. onAttach()
    1. Fragment 被附加到其 Activity 时调用。
    2. 可以使用 onAttach() 方法获取对宿主 Activity 的引用。
  2. onCreate()
    1. Fragment 被创建时调用。
    2. 可以进行初始化操作,但不能直接与用户界面交互。
  3. onCreateView()
    1. Fragment 创建视图时调用。
    2. 应该在这里创建 Fragment 的用户界面,并返回该界面的根视图。
  4. onActivityCreated()
    1. 在与 Fragment 关联的 ActivityonCreate() 完成后调用。
    2. 可以在这里执行与 Activity 相关的操作。
  5. onStart()
    1. Fragment 可见时调用。
    2. 可以在这里执行与 UI 交互相关的操作。
  6. onResume()
    1. Fragment 处于活动状态时调用。
    2. 可以在这里开始耗时操作或者监听用户输入。
  7. onPause()
    1. Fragment 即将失去焦点或者被替换时调用。
    2. 应该停止与用户界面交互的操作。
  8. onStop()
    1. Fragment 不再可见时调用。
    2. 应该停止与 Activity 的交互。
  9. onDestroyView()
    1. Fragment 的视图被销毁时调用。
    2. 在这里进行释放资源的操作。
  10. onDestroy()
    1. Fragment 被销毁时调用。
    2. 在这里进行清理工作。
  11. onDetach()
    1. Fragment 与其宿主 Activity 分离时调用。
    2. 可以在这里释放对宿主 Activity 的引用。

img


Fragment
http://example.com/2024/02/25/Fragment/
作者
zlw
发布于
2024年2月25日
许可协议