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 关联的 Activity 的 onCreate() 完成后调用。
    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日
许可协议