1. Fragment概述
Fragment是一种可以嵌入在Activity中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间。Fragment不能够单独使用,需要嵌套在Activity中使用,一个Activity可以有多个Fragment 一个Fragment可以被多个Activity重用,可以在Activity运行时动态地添加或删除Fragment
1.1 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解除关联时调用 |
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);
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);
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"
android:name="com.vivo.a11085273.secondfragmenttest.LeftFragment"
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通信
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");
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();
}
}
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");
}
textView.setText(name);
return view;
}
}
3.2 Fragment传递数据给Activity
a. Fragment定义并调用回调接口
public class FragmentA extends Fragment {
private FragmentListener mFragmentListener;
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");
return view;
}
public interface FragmentListener{
void sendToActivity(Object object);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof FragmentListener)
{
mFragmentListener = (FragmentListener)context;
}
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");
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){
button.setText(object.toString());
}
}
3.3 FragmentA传递数据给FragmentB
a. FragmentA传值给Activity
public class FragmentA extends Fragment {
private FragmentListener mFragmentListener;
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");
return view;
}
public interface FragmentListener{
void sendToActivity(Object object);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof FragmentListener)
{
mFragmentListener = (FragmentListener)context;
}
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");
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){
FragmentB fragmentB = (FragmentB)getFragmentManager().findFragmentById(R.id.container);
fragmentB.sendToFragmentB(object.toString());
}
}
c. FragmentB读取接收到的数据
public class FragmentB extends Fragment {
public void sendToFragmentB(Object object) {
button.setText(object.toString());
}
}
好文章:https://www.jianshu.com/p/a4c51309bc19
|