1.Fragment简介
在Android3.0之前的版本,通常程序是在较小屏幕的设备上(手机等)。尽管手机屏幕尺寸、分辨率、屏幕密度等参数存在较大的差异,但是手机交互界面的操作习惯基本相同。例如,对于一个联系人管理程序,通常都会首先用一个窗口显示所有的联系人名称以及少数的联系人的详细信息(如联系人电话号码等)。然后当单击某一个联系人时会另外显示一个窗口列出该联系人的详细信息,当然,更进一步的操作还可能有修改、删除联系人等。不管与手机屏幕相关参数如何变化,在手机上的联系人管理程序除了界面风格略有差异外,操作的流程都和这一过程基本上类似。
尽管界面的使用布局很容易实现,但是对于同时适应手机的平板电脑的APK程序就比较麻烦。通常是为不同的界面风格提供不同的布局文件,然后利用Android的本地化特性在不同的环境使用不同的布局文件。这种做法虽然可行,也能达到复用,但如果这类界面过多,就会造成布局文件过多,对于后期的维护就显得格外麻烦。为了解决这个问题,就需要一种可以布局、共享以及控制的通用式系统。
其目的是为了解决不同屏幕分辩率的动态和灵活UI设计。大屏幕如平板小屏幕如手机,平板电脑的设计使得其有更多的空间来放更多的UI组件,而多出来的空间存放UI使其会产生更多的交互,从而诞生了fragments。
2.Fragment的生命周期
Fragment必须依赖于Activity而存在,因此Activity的生命周期会直接影响到Fragment的生命周期。如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。但是,当Activity在活动状态时,可以独立控制Fragment的状态,比如加上或者移除Fragment。当进行fragment transaction(转换)的时候,可以把Fragment放入Activity的back stack中,这样用户就可以返回操作。
由图1可以看到,Fragment比Activity多了几个额外的生命周期回调函数。
图1.Activity与Fragment生命周期的关系
- onAttach(Activity):当Fragment与Activity发生关联时调用,从该方法开始,就可以通过Fragment.getActivity方法获取与Fragment关联的窗口对象了,但在该方法中仍然无法操作Fragment中的控件。
- onCreateView(LayoutInflater,ViewGroup,Bundle):创建该Fragment的视图。
- onActivityCreate(Bundle):当Activity的onCreate方法返回时调用。
- onDestroyView():与onCreateView相对应,当该Fragment的视图被移除时调用。
- onDetach():与onAttach相对应,当Fragment与Activity关联被取消时调用。
3.Fragment的简单使用
1.静态地使用Fragment
在layout文件下分别创建fragment1.xml和fragment2.xml布局文件,然后在创建一个类Fragment1,这个类继承自Fragment,重写onCreateView()方法绑定fragment1.xml。重复此步,创建Fragment2。
package com.example.demo_test_for_fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class Fragment1 extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment1, container, false);
}
}
然后在activity_main.xml布局文件中,静态使用fragemnt1和fragment2。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:baselineAligned="false">
<fragment
android:layout_width="100dp"
android:layout_height="match_parent"
android:id="@+id/fragment1"
android:name="com.example.demo_test_for_fragment.Fragment1"/>
<fragment
android:layout_width="100dp"
android:layout_height="match_parent"
android:id="@+id/fragment2"
android:name="com.example.demo_test_for_fragment.Fragment2"/>
</LinearLayout>
2.动态调用
在activity_main.xml布局文件中,不静态使用fragment1和fragment2。在MainActivity的onCreate()方法根据屏幕的情况(竖屏或者横屏),使用不同的fragment。
package com.example.demo_test_for_fragment;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.Display;
public class MainActivity extends AppCompatActivity {
@SuppressLint("ResourceType")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Display display = getWindowManager().getDefaultDisplay();
if (display.getWidth() > display.getHeight()){//横屏采用fragment1
Fragment1 fragment1 = new Fragment1();
getFragmentManager().beginTransaction().replace(R.id.main_layout,fragment1).commit();
}else{//竖屏采用fragment2
Fragment2 fragment2 = new Fragment2();
getFragmentManager().beginTransaction().replace(R.id.main_layout,fragment2).commit();
}
}
}
3.Fragment之间的通信
Fragment的存在必须依附于Activity,FragmentActivity是继承自Activity的。Fragment之间通信的桥梁就是FragmentManager类,这个类是用来管理所有的Fragment的,所以可以找到任何一个所需要的Fragment类。另外,Activity一般都会包含多个Fragment,这时多个Fragment之间如何进行通信就是个非常重要的问题了。下面通过一个例子来演示一下,如何在一个Fragment中去访问另一个Fragment的视图。
在静态代码上做修改。
首先打开fragment2.xml,在这个布局里面添加一个按钮。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment2"
android:textSize="25sp"
android:textColor="#000000"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:text="get fragemnt1 text"/>
</LinearLayout>
在fragment1.xml,为TextView添加一个id
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment1"
android:textSize="25sp"
android:textColor="#000000"
android:id="@+id/fragment1_text"/>
</LinearLayout>
打开Fragment2.java,添加onActivityCreated方法,并处理按钮的单击事件。
package com.example.demo_test_for_fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
public class Fragment2 extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = getActivity().findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TextView textView = getActivity().findViewById(R.id.fragment1_text);
Toast.makeText(getActivity(),textView.getText(),Toast.LENGTH_SHORT).show();
}
});
}
}
运行效果如下图。如何实现的呢?主要通过getActivity方法可以让Fragment获取关联的Activity,然后再调用Activity的findViewById方法,就可以获取和这个Activity关联的其他Fragment的视图了。