Android开发笔记(十七)——Fragment详解

Fragment基本介绍和使用

  • Fragment有自己的生命周期。
  • Fragment依赖于Activity。
  • Fragment通过 getActivity() 可以获取所在的Activity;Activity通过FragmentManager的 findFragmentById() 或者 findFragmentByTag() 获取Fragment。
  • Fragment和Activity是多对多的关系。

新建一个package名为 fragment ,在这个包内创建一个Activity ContainerActivity 作为Fragment演示界面,对应会创建好布局 activity_container.xml ,之后在该包内创建两个Fragment分别为 AFragmentBFragment ,创建相对应的布局 fragment_afragment_b

在布局 fragment_a 的布局如下: fragment_b 类似。

AFragment.java 中的代码如下: BFragment.java 类似。

public class AFragment extends Fragment {

    private TextView mTvTitle;

    //设置布局文件
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //相当于Activity中setContentView的作用
        View view = inflater.inflate(R.layout.fragment_a, container, false);
        return view;
    }

    //设置findViewById
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mTvTitle=view.findViewById(R.id.tv_title);
    }
}

布局 activity_container.xml 如下:其中 FrameLayout帧布局,这个布局直接在屏幕上开辟出一块空白的区域,当我们往里面添加控件的时候,会默认把他们放到这块区域的左上角。

MainActivity 进入 ContainerActivity ,想要把AFragment放到 ContainerActivity 中, ContainerActivity.java 中的代码如下:

当前效果展示:

想要点击按钮,FrameLayout 中显示的内容从AFragment变成BFragment, 在 ContainerActivity.java 中的代码如下:替换的实现使用replace()函数

效果如下:

Fragment中getActivity()为null的问题

Fragment中获取当前所在的Activity可以使用 getActivity() 方法,切换到后台或者横竖屏去转换的时候,可能会出现 getActivity() 为null的问题。

向Fragment传递参数

之前添加Fragment的时候实例化是直接new一个Fragment,如果在实例化Fragment的时候希望传参,可以这样做:在 AFragment.java 中添加 newInstance 方法:在这个方法中实例化一个AFragment,并且给这个fragment setArguments,通过getArguments得到具体的值:

之后在 ContainerActivity.java 中改变一下实例化的方法:调用AFragment的一个静态的方法 newInstance

Fragment回退栈

更改之前的布局,activity_container.xml 的整个界面是一个FrameLayout

布局 fragment_a 更改如下:

ContainerActivity 中代码修改如下:在MainActivity上点击Button进入整个屏幕都是AFragment。

AFragment中的Button "更换为BFragment" 实现的是点击按钮,当前界面显示为BFragment的内容,button “更改TextView的文字内容” 实现的是点击按钮,AFragment中TextView的内容更改。

此时效果如下:

当点击按钮 "更换为BFragment" ,当前界面显示为BFragment的内容,返回时会直接返回到MainActivity中,如果想要返回上一届界面也就是AFragment时,可以如下操作:在commit之前把Fragment添加到回退栈当中去

此时效果如下:

日志如下:

尽管AFragment没有被重新实例化,但是 onCreateView 方法还是会重新运行,也就是说实例还是同一个实例,但是视图会被重新创建,从显示效果可以看到,在AFragment中,先更换TextView的文字内容,再更换为BFragment,这时返回的时候可以发现,显示的文字依旧是之前的文字,这就验证了Activity还是之前的实例,但是 onCreateView 方法还是会重新调用,所以他的视图还又重新刷新了一遍,TextView回到了最开始设置的文字。

那么如果希望从BFragment返回的时候,AFragment中的TextView是之前更换过的状态,可以如下操作:先将AFragment隐藏掉,再add一个新的Fragment,之后再添加一个BFragment,接着添加到回退栈当中,最后commit,而不是直接replace(会导致前一个Fragment的视图没有被保存下来)

此时效果如下:

可以看到此时视图并没有被重新创建,而是保持了之前的视图状态,

Fragment和Activity的通信

希望在AFragment中点击Button,把ContainerActivity中的TextView设置文字(从Fragment中发出消息,通知Activity去执行的环节)

在ContainerActivity中写一个公共的方法:实现当调用这个方法的时候可以给TextView设置文字

    public void setData(String text){
        mTvTitle.setText(text);
    }

在AFragment中添加点击事件如下:把getActivity()转换成ContainerActivity,然后调用ContainerActivity中的setData方法

        mBtnMessage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((ContainerActivity)getActivity()).setData("你好!");
            }
        });

该方法可行但是并不推荐。更推荐下面的方法:

通过回调接口来实现Fragment和Activity的通信

在Activity中去实现一个在Fragment中声明好的接口,通过回调接口来实现数据的传递

首先在Fragment中写一个接口:

    public interface IOnMessageClick{
        void onClick(String text);
    }

让Activity去实现这个接口:

    @Override
    public void onClick(String text) {
        mTvTitle.setText(text);
    }

在AFragment中声明接口:

private IOnMessageClick listener;

当Fragment依附到Activity中时候,Fragment会调用一个方法 onAttach() :把context强制转换成接口,如果转换失败,则Activity没有实现这个接口IOnMessageClick,就抛出异常。

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (IOnMessageClick) context;
        }catch (ClassCastException e){
            throw new ClassCastException("Activity 必须实现IOnMessageClick接口");
        }
    }

添加点击事件如下:

        mBtnMessage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                ((ContainerActivity)getActivity()).setData("你好!");
                listener.onClick("你好呀!");
            }
        });

这时候可以正常通信。

posted @ 2020-07-21 19:58  Ylxxxxx  阅读(368)  评论(0编辑  收藏  举报