Fragment是一种可以嵌入在Activity中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间。Fragment不能够单独使用,需要嵌套在Activity中使用,一个Activity可以有多个Fragment
一个Fragment可以被多个Activity重用,可以在Activity运行时动态地添加或删除Fragment
生命周期方法 | 说明 |
---|---|
onAttach() | Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数 |
onCreate() | Fragment被创建时调用 |
onCreateView() | 创建绘制Fragment的View时调用 |
onActivityCreated() | 当Activity完成onCreate()时调用 |
onStart() | 当Fragment可见时调用 |
onResume() | 当Fragment可见且可交互时调用 |
onPause() | 当Fragment不可交互但可见时调用 |
onStop() | 当Fragment不可见时调用 |
onDestroyView() | 当Fragment的UI从视图结构中移除时调用 |
onDestroy() | 销毁Fragment时调用 |
onDetach() | 当Fragment和Activity解除关联时调用 |
a. 核心类
Fragment:Fragment的基类,任何创建的Fragment都需要继承该类
FragmentManager:管理和维护Fragment。它是抽象类,具体的实现类是FragmentManagerImpl
FragmentTransaction:对Fragment的添加、删除等操作都需要通过事务方式进行。他是抽象类,具体的实现类是BackStackRecord
b. 扩展子类
DialogFragment:对话框
ListFragment:列表
PreferenceFragment:选项设置
WebViewFragment:WebView界面
Fragment按照加载方式分为静态加载和动态加载
a. 自定义Fragment的xml布局文件
新建left_fragment_layout.xml和right_fragment_layout.xml文件
left_fragment_layout.xml:
<?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="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Button" />
</LinearLayout>
right_fragment_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<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="20sp"
android:text="this is Fragment" />
</LinearLayout>
b. 自定义Fragment类
继承Fragment类或其子类,同时实现onCreate()方法,在方法中,通过inflater.inflate加载a中定义的布局文件,接着返回其View
LeftFragment.java:
public class LeftFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment_layout, container,false); //加载布局文件并返回view
return view;
}
}
RigthFragment.java:
public class RigthFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.right_fragment_layout, container, false); //加载布局文件并返回view
return view;
}
}
c. Activity的静态xml中加载Fragment布局文件
在需要加载Fragment的Activity对应布局文件中的name属性设为全限定类名,即包名.fragment
activity_main.xml:
<?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"
android:orientation="horizontal"
tools:context="com.example.administrator.myapplication.MainActivity">
<LinearLayout
android:layout_width="100dp"
android:orientation="vertical"
android:layout_height="match_parent">
...
</LinearLayout>
<FrameLayout
android:id="@+id/shop_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment_id" //id随意取名
android:name="com.vivo.a11085273.secondfragmenttest.LeftFragment" //指定Fragment类,在Fragment类中加载xml布局文件
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/right_fragment_id"
android:name="com.vivo.a11085273.secondfragmenttest.RigthFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</FrameLayout>
</LinearLayout>
d. Activity.java加载Activity的布局文件
最后在Activity调用setContentView()加载Activity的布局文件
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
a. 自定义Fragment的xml布局文件
同静态加载
b. 自定义Fragment类
同静态加载
c. Activity的静态xml中加载Fragment布局文件
同静态加载,唯一区别:不指定的name属性
<?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"
android:orientation="horizontal"
tools:context="com.example.administrator.myapplication.MainActivity">
<LinearLayout
android:layout_width="100dp"
android:orientation="vertical"
android:layout_height="match_parent">
...
</LinearLayout>
<FrameLayout
android:id="@+id/right_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
d. Activity.java调用Fragment
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
replaceFragment(new RigthFragment());
}
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.commit();
}
}
动态加载的步骤:
transaction事务:
transaction.add():往Activity里面添加一个片段
transaction.remove():从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁
transaction.replace():使用另一个Fragment替换当前的,实际上是remove()然后add()的合体
transaction.hide():隐藏当前Fragment,仅不可见,不会销毁
transaction.show():显示之前隐藏的Fragment
transaction.attach():重建view视图,附加到UI上并显示
transaction.detach():会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护
a. Fragment的onCreateView()方法返回Fragment的UI布局,需要注意的是inflate()的第三个参数是false,因为在Fragment内部实现中,会把该布局添加到container中,如果设为true,那么就会重复做两次添加,则会抛如下异常:
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.
b. 如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据
可以在Fragment的onAttach()中通过getArguments()获得传进来的参数。如果要获取Activity对象,不建议调用getActivity(),而是在onAttach()中将Context对象强转为Activity对象
public class Fragment1 extends Fragment{
private static String ARG_PARAM = "param_key";
private String mParam;
private Activity mActivity;
public void onAttach(Context context) {
mActivity = (Activity) context;
mParam = getArguments().getString(ARG_PARAM); //获取参数
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_1, container, false);
TextView view = root.findViewById(R.id.text);
view.setText(mParam);
return root;
}
public static Fragment1 newInstance(String str) {
Fragment1 frag = new Fragment1();
Bundle bundle = new Bundle();
bundle.putString(ARG_PARAM, str);
fragment.setArguments(bundle); //设置参数
return fragment;
}
}
c. commit方法一定要在Activity.onSaveInstance()之前调用
不要把Fragment事务放在异步线程的回调中
d. 回退栈
如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment,一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity
使用方法:
FragmentTransaction.addToBackStack(String)
示例1:
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null); //添加进回退栈
transaction.commit();
}
将当前的事务添加到了回退栈,所以被替换掉的Fragment实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView
示例2:
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.hide(this);
transaction.add(R.id.right_layout, fragment);
transaction.addToBackStack(null); //添加进回退栈
transaction.commit();
}
如果不希望视图重绘,可以将原来的Fragment隐藏
Fragment可以通过getActivity得到当前绑定的Activity的实例
Activity可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例
a. Activity执行setArguments传入数据
public class MainActivity extends AppCompatActivity implements FragmentA.FragmentListener {
private Button button;
private FragmentA fragmentA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fragmentA = new FragmentA();
Bundle args = new Bundle();
args.putString("MainActivity","Hello");
//setArguments方法必须在fragment创建以后
fragmentA.setArguments(args); //setArguments传入数据
addFragment(fragmentA);
}
});
}
private void addFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.fragment_contain,fragment);
transaction.commit();
}
}
b. Fragment的onCreateView中调用getArguments读取数据
public class FragmentA extends Fragment {
private String name;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState){
View view =inflater.inflate(R.layout.avtivity_time,container,false);
TextView textView = (TextView)view.findViewById(R.id.time_text);
if (getArguments() != null) {
name = getArguments().getString("MainActivity"); //getArguments读取数据
}
textView.setText(name);
return view;
}
}
a. Fragment定义并调用回调接口
public class FragmentA extends Fragment {
private FragmentListener mFragmentListener; //1. 定义回调接口
private String name;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState){
View view =inflater.inflate(R.layout.avtivity_time,container,false);
TextView textView = (TextView)view.findViewById(R.id.time_text);
if (getArguments() != null) {
name = getArguments().getString("MainActivity");
}
textView.setText(name);
mFragmentListener.sendToActivity("数据来自Fragment"); //2. 调用回调接口,给Activity传值"数据来自Fragment"
return view;
}
public interface FragmentListener{ //1. 定义回调接口
void sendToActivity(Object object);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof FragmentListener)
{
mFragmentListener = (FragmentListener)context; //2. 调用回调接口
}
else{
throw new IllegalArgumentException("Activity must implements FragmentListener");
}
}
}
b. Activity实现回调接口并解析读取的数据
public class MainActivity extends AppCompatActivity implements FragmentA.FragmentListener {
private Button button;
private FragmentA fragmentA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fragmentA = new FragmentA();
Bundle args = new Bundle();
args.putString("MainActivity","Hello");
//setArguments方法必须在fragment创建以后
fragmentA.setArguments(args);
addFragment(fragmentA);
}
});
}
private void addFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.fragment_contain,fragment);
transaction.commit();
}
@Override
public void sendToActivity(Object object){ //1. 实现回调接口,Fragment调用sendToActivity时会传入数据"数据来自Fragment"
button.setText(object.toString()); //2. 将接收到的数据显示出来
}
}
a. FragmentA传值给Activity
public class FragmentA extends Fragment {
private FragmentListener mFragmentListener; //1. 定义回调接口
private String name;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState){
View view =inflater.inflate(R.layout.avtivity_time,container,false);
TextView textView = (TextView)view.findViewById(R.id.time_text);
if (getArguments() != null) {
name = getArguments().getString("MainActivity");
}
textView.setText(name);
mFragmentListener.sendToActivity("数据来自Fragment"); //2. 调用回调接口,给Activity传值"数据来自Fragment"
return view;
}
public interface FragmentListener{ //1. 定义回调接口
void sendToActivity(Object object);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof FragmentListener)
{
mFragmentListener = (FragmentListener)context; //2. 调用回调接口
}
else{
throw new IllegalArgumentException("Activity must implements FragmentListener");
}
}
}
b. Activity接收FragmentA的数据并将接收到的数据传递给FragmentB
public class MainActivity extends AppCompatActivity implements FragmentA.FragmentListener {
private Button button;
private FragmentA fragmentA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fragmentA = new FragmentA();
Bundle args = new Bundle();
args.putString("MainActivity","Hello");
//setArguments方法必须在fragment创建以后
fragmentA.setArguments(args);
addFragment(fragmentA);
}
});
}
private void addFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.fragment_contain,fragment);
transaction.commit();
}
@Override
public void sendToActivity(Object object){ //1. 实现回调接口,Fragment调用sendToActivity时会传入数据"数据来自Fragment"
FragmentB fragmentB = (FragmentB)getFragmentManager().findFragmentById(R.id.container);
fragmentB.sendToFragmentB(object.toString()); //2. Activity将FragmentA传过来的数据传递给FragmentB
}
}
c. FragmentB读取接收到的数据
public class FragmentB extends Fragment {
public void sendToFragmentB(Object object) { //1. FragmentB接收Activity传过来的数据
button.setText(object.toString()); //2. 将接收到的数据显示出来
}
}
好文章:https://www.jianshu.com/p/a4c51309bc19