Android系统之Fragment用法

1. Fragment概述

Fragment是一种可以嵌入在Activity中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间。Fragment不能够单独使用,需要嵌套在Activity中使用,一个Activity可以有多个Fragment
一个Fragment可以被多个Activity重用,可以在Activity运行时动态地添加或删除Fragment

1.1 Fragment生命周期

Android系统之Fragment用法_第1张图片

生命周期方法 说明
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解除关联时调用

1.2 Fragment核心类与扩展子类

a. 核心类
Fragment:Fragment的基类,任何创建的Fragment都需要继承该类
FragmentManager:管理和维护Fragment。它是抽象类,具体的实现类是FragmentManagerImpl
FragmentTransaction:对Fragment的添加、删除等操作都需要通过事务方式进行。他是抽象类,具体的实现类是BackStackRecord
b. 扩展子类
DialogFragment:对话框
ListFragment:列表
PreferenceFragment:选项设置
WebViewFragment:WebView界面

2. Fragment使用方法

Fragment按照加载方式分为静态加载和动态加载

2.1 静态加载

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);
    }
}

2.2 动态加载

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();
    }
}

动态加载的步骤:

  • 获得FragmentManager对象,通过getSupportFragmentManager()
  • 获得FragmentTransaction对象,通过fm.beginTransaction()
  • 调用add()方法或者repalce()方法加载Fragment;
  • 最后调用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维护

2.3 Fragment使用注意事项

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隐藏

3. Fragment与Activity通信

Android系统之Fragment用法_第2张图片

Fragment可以通过getActivity得到当前绑定的Activity的实例
Activity可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例

3.1 Activity传递数据给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;
    }
}

3.2 Fragment传递数据给Activity

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. 将接收到的数据显示出来
    }
}

3.3 FragmentA传递数据给FragmentB

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

你可能感兴趣的:(Android,android,Fragment)