Fragment是Activity的碎片,起初Fragment的出现是为了适配大屏平板出现的,但是在小屏幕手机上也经常使用到,经常使用的控件组合是ViewPager+Fragment,ViewPager不是本章的重点,本章主要针对Fragrant讲解。
(1)生命周期
图片.pngFragment的生命周期要比Activity多几个方法,下面简单说明一下:
- onAttach(Activity)
Activity和Fragment发生关联时调用,也就是说Activity和Fragment之间必须存在依赖关系,它的作用就是将Activity和Fragment绑定在一起。只要执行了这个方法,Activity对象就会传递到Fragment,所以在Fragment中可以直接调用getActivity()
来获取当前Fragment对应的Activity对象,如果没有执行onAttach(Activity)
方法,将获取不到Activity对象。
- onCreateView(LayoutInflater, ViewGroup,Bundle)
创建Fragment的视图。
- onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用。
- onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用。
- onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用。
(2)选择正确的导包
图片.png如图所示,怎么选择Fragment的导包已经显而易见了,android.app
中的Fragment已经过时,所以目前只能使用support.v4
中的Fragment。
(3)如何去创建Fragment视图
首先,我们需要新建一个类,继承于Fragment,然后重写onCreateView
方法,默认情况下Fragment是没有视图的,所以我们需要自己新建一个视图,这里必须重写onCreateView
新建视图。代码如下:
public class Demo1Fragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_main, null);
return view;
}
}
有关LayoutInflater的使用,可以看一下这篇博客:LayoutInflater使用
(4)在Activity中添加Fragment
public class Demo1Fragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, null);
return view;
}
public static Demo1Fragment newInstance() {
Demo1Fragment fragment = new Demo1Fragment();
return fragment;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//加载默认Fragment
getSupportFragmentManager().beginTransaction().add(R.id.container, Demo1Fragment.newInstance()).commit();
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
fragment1.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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_centerInParent="true"
android:textSize="14sp"
android:visibility="visible"
android:background="@drawable/ripple_ani"
android:layout_centerHorizontal="true"
android:text="点击"/>
</RelativeLayout>
(6)常用方法介绍
-
getSupportFragmentManager():
这是support.v4
特有的方法,以前android.app
的getFragmentManager()
方法已经过时,大家不要再使用了。 -
add:
添加一个Fragment
常用add方法如下:
FragmentTransaction add(@IdRes int var1, @NonNull Fragment var2)
FragmentTransaction add(@IdRes int var1, @NonNull Fragment var2, @Nullable String var3)
第一个参数是:装载Fragment容器的ID;
第二个参数是:Fragment对象;
第三个参数是:Fragment的Tag;
如果您在添加Fragment时给它设置了Tag,比如:
getSupportFragmentManager().beginTransaction().add(R.id.container, Demo1Fragment.newInstance(), "demo1").commit();
这样Demo1Fragment
的Tag为demo1
,我们可以根据Tag获取fragment对象,如下:
Demo1Fragment frag = (Demo1Fragment) getActivity().getSupportFragmentManager().findFragmentByTag("demo1");
-
hide:
隐藏一个Fragment,当您想隐藏一个Fragment时,可以使用这个方法。getSupportFragmentManager().beginTransaction() .add(R.id.container, fragment1) .add(R.id.container, fragment2) .add(R.id.container, fragment3) .hide(fragment2) .commit();
-
remove:
移除一个Fragment,当您想移除一个Fragment时,可以使用这个方法。getSupportFragmentManager().beginTransaction() .add(R.id.container, fragment1) .add(R.id.container, fragment2) .add(R.id.container, fragment3) .remove(fragment2) .commit();
-
replace:
替换一个FragmentgetSupportFragmentManager().beginTransaction() .replace(R.id.container, fragment2) .commit();
-
show:
显示Fragment -
commit:
提交一个事务。
我们都知道,当Fragment状态发生改变时(比如add、remove、hide、replace操作)都会执行commit
方法提交事务,但是commit
方法只能在onSaveInstanceState之前执行,如果在onSaveInstanceState之后执行就会报以下错误:
为了解决这个问题,可以从三方面考虑:
[一]: 是都可以将commit
放到onSaveInstanceState之前执行;
[二]:尽量避免在异步线程中执行Fragment事务操作(commit
);
[三]: 将commit
改成commitAllowingStateLoss
;
-
addToBackStack:
回退栈
我们知道Activity有任务栈,用户通过startActivity将Activity加入栈,点击返回按钮将Activity出栈。Fragment也有类似的栈,称为回退栈(Back Stack),回退栈是由FragmentManager管理的。默认情况下,Fragment事务是不会加入回退栈的,如果想将Fragment事务加入回退栈,则可以加入addToBackStack("")
。如果没有加入回退栈,则用户点击返回按钮会直接将Activity出栈;如果加入了回退栈,则用户点击返回按钮会回滚Fragment事务。代码如下:
getSupportFragmentManager().beginTransaction()
.add(R.id.container, Demo1Fragment.newInstance(), "demo1")
.addToBackStack("a")
.commit();
(7)Fragment通信
Fragment通信分为两个方向:Activity-->Fragment
、Fragment-->Activity
。
Activity访问Fragment
比较简单,大致有三种方式:
[方式一]:
我们可以在创建Fragment对象的时候使用setArguments
传递参数,代码如下:
public static Demo1Fragment newInstance(String str) {
Demo1Fragment fragment = new Demo1Fragment();
Bundle bundle = new Bundle();
bundle.putString("key", str);
fragment.setArguments(bundle);
return fragment;
}
[方式二]:
如果Activity中存在Fragment对象,那么可以使用Fragment对象调用Fragment中public方法。
[方式三]:
使用findFragmentByTag方法
可以通过以下方式获取某Fragment对象,然后再调用Fragment中的方法,或就想数据从Activity传递到Fragment。
getActivity().getSupportFragmentManager().findFragmentByTag("demo1");
但是,如果在Fragment中想调用Activity中的方法,或者将数据从Fragment传递到Activity该怎么做呢?
[方式一]:
通过广播
在Activity中注册广播,在Fragment中发送广播可以轻松实现Fragment访问Activity,但是不推荐这种方式,因为广播是非常消耗性能的。
[方式二]:
自定义监听方式
第一步,在Fragment中定义接口:
public interface FragmentClickListener {
void doString();
}
第二步,在Activity中实现FragmentClickListener 接口
第三步,调用接口中的方法
if(getActivity() instanceof FragmentClickListener){
((FragmentClickListener) getActivity()).doString();
}
只要完成以上三步就可以实现Fragment访问Activity。
[方式三]:
EventBus实现
这个其实本质上就是一个监听,这里不做具体说明了。
[方式四]:
RxBus
RxbBus
是RxJava模仿EventBus实现的,具体代码如下:
RxBus类
public class RxBus {
private FlowableProcessor<Object> bus;
private RxBus() {
//把非线程安全的PublishSubject包装成线程安全的SerializedSubject
bus = PublishProcessor.create().toSerialized();
}
public static RxBus getDefault() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static volatile RxBus INSTANCE = new RxBus();
}
/**
* 发送事件
*
* @param event 事件对象
*/
public void post(Object event) {
if (bus.hasSubscribers()) {
bus.onNext(event);
}
}
/**
* 监听事件
*
* @return 特定类型的Observable
*/
public Flowable<Object> observe() {
return bus;
}
/**
* 监听事件
*
* @param event 事件对象
* @param <T> 事件类型
* @return 特定类型的Observable
*/
public <T> Flowable<T> observe(Class<T> event) {
return bus.ofType(event);
}
}
注册监听
RxBus.getDefault().observe(String.class).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d("aaa", "我收到的数据是:"+s);
}
});
发送数据
RxBus.getDefault().post("我是数据,可以是字符串,也可以是一个任意对象");
[方式五]:
使用github框架FABridge
FABridge的使用也比较简单,具体使用方法可以查看githubFABridge。
(8)DialogFragment的使用
DialogFragment是Android 3.0提出的,代替了Dialog,用于实现对话框。他的优点是:即使旋转屏幕,也能保留对话框状态(即使屏幕旋转了,对话框也不会消失)。
代码如下:
public class ProgressDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); //消除Title区域
setCancelable(false); //点击外部不可取消
View root = inflater.inflate(R.layout.fragment_dialog, container);
return root;
}
public static ProgressDialogFragment newInstance() {
return new ProgressDialogFragment();
}
}
fragment_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是一个对话框"/>
</RelativeLayout>
显示对话框
ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getActivity().getSupportFragmentManager(), "tag");
另外,当屏幕旋转的时候,Dialog就一定会消失吗?
答:屏幕旋转时,其生命周期是:onPause --> onSaveInstanceState --> onStop --> onDestroy --> onStart --> onRestoreInstanceState --> onResume,我们发现屏幕旋转时是先销毁当前的Activity,然后重启当前Activity,所以如果旋转的时候屏幕上的Dialog肯定会消失的。
但是有一种情况可以做到即使屏幕旋转了,Dialog也不会消失,只需要在AndroidManifest.xml配置文件中添加
android:configChanges="orientation|screenSize"
即可,这样屏幕旋转的时候就不会走生命周期了,不走生命周期的话当前Activity也不会销毁。
[本章完...]
网友评论