Fragment详解(2)

在上一篇中我们详细介绍了Fragment的作用,使用方法,更是主要介绍了一个叫做FragmentTransaction类的主要方法。这一片中我们将把Fragment剩下的内容全部说完:

  • Fragment退回栈介绍。
  • Fragment与Activity通信。

目测篇幅不会很长。

Fragment回退栈

还记得我们再说Activity时候提到的一个任务栈Task Stack吗,它的效果和Tack Stack效果差不多,在使用时候我们只需要调用一个FragmentTransaction的方法就好:

    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.add(R.id.frame, twoFragment)
            .hide(oneFragment)
            .addToBackStack(null)
            .commit();

其他代码相信都很熟悉了,我们要说的是addToBackStack这个方法,点进去看一下这个方法:

    /**
     * Add this transaction to the back stack.  This means that the transaction
     * will be remembered after it is committed, and will reverse its operation
     * when later popped off the stack.
     *
     * @param name An optional name for this back stack state, or null.
     */
    public abstract FragmentTransaction addToBackStack(@Nullable String name);

翻译备注:将此事务添加到回退栈中,这意味这个事务递交之后会被记录,而当弹出栈时会扭转其操作。

其实后半句话我也不太懂是什么意思-。+,但是大体的意思我们知道了,他是将我们的FragmentTransaction存储起来了。然后点击Back键时,会将事务弹出栈。那么我们显示的Fragment肯定就消失了。

可能在我们以前的理解中以为是将Fragment存储到回退栈中。的确,效果跟存储Fragment一样,但是通过这些注释我们知道,他的本质是存储FragmentTransaction。

接下来我们做几个个简单的小实验:

1.在MainActivity中先显示OneFragment,然后再显示TwoFragment,都把他们放入退回栈中:

    findViewById(R.id.fra1).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction.add(R.id.frame, oneFragment)
                    .addToBackStack(null)
                    .commit();
        }
    });
    findViewById(R.id.fra2).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction.add(R.id.frame, twoFragment)
                    .addToBackStack(null)
                    .commit();
        }
    });

还是上一次的代码:这是MainActivity中的分别显示两个碎片。两个碎片的布局就不展示了。

我们看一下效果:

效果很明显:依次OneFragment和TwoFragment,点击Back会依次退出Fragment,看一下打印的日志也是对应的:


可能大家注意到了,我在OneFragment中最下面放置了一个输入框。其实这样还有一个问题:

大家注意下我上面实现显示的代码,是使用add添加的。现在有这样一个案例:我们在MainActivity中显示OneFragment,然后在OneFragment中显示TwoFragment(其实道理跟上面的一样,只不过是在OneFragment中显示TwoFragment了)。

我们有没有想过:由于整个Fragment是在帧布局作为容器的,那么根据帧布局的特性,他会将当前视图覆盖,但是还是保留当前视图可获取焦点的状态,如果这么说,那么在点开TwoFragment之后,还是可以给OneFragment的输入框输入数据:


我在OneFragment界面输入了123,当显示TwoFragment之后,又点击了那个位置,输入了456。点击Back之后果然能看到456。我想在做设计的时候如果这样,那真是太糟糕了。

有以下两种解决办法:

1.将Fragment添加方式设置为replace(或者先remove后add)。

这样我们就不会在TwoFragment显示的时候点击到之前OneFragment的输入框了,因为此时OneFragment的视图层次已经被销毁了(但是Fragment实例仍然保存,详细请看上一篇中关于FragmentTransaction的介绍:Fragment详解(1)

    findViewById(R.id.fra2).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction
//                  .add(R.id.frame, twoFragment)
                    .replace(R.id.frame,twoFragment)
                    .addToBackStack(null)
                    .commit();
        }
    });

看一下现在的效果:


但是这样也有一个弊端:如果说我们在OneFragment中输入框输入到一半的时候就跳转,但是我们还想保留OneFragment中的内容,这该怎么办呢?

2.hide隐藏OneFragment,在通过add显示TwoFragment。

我们知道hide是让Fragment隐藏,这样一来我们显示TwoFragment的时候,就不会点开OneFragment中的EditText了,而且OneFragment也在回退栈中,所以在我们点击Back关闭TwoFragment时,OneFragment又会显示出来。

代码如下:

    findViewById(R.id.fra1).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction
                    .add(R.id.frame, oneFragment)
                    .addToBackStack(null)
                    .commit();
        }
    });
    findViewById(R.id.fra2).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            transaction
                    .hide(oneFragment)
                    .add(R.id.frame, twoFragment)
                    .addToBackStack(null)
                    .commit();
        }
    });

效果  :  

以上便是关于回退栈的常用知识。接下来我们了解一下Fragment和Activity之间的通信:

Fragment与Activity通信:

我们知道Fragment是绑定在某个Activity上的,在有些时候我们需要Fragment给Activity传递数据,或在Fragment中对Activity进行操作,而在Activity中也需要调用对应Fragment的方法,以及数据的传递。

其通信方式主要分为以下两种:

  • Activity调用Fragment相关属性。
  • Fragment中调用Activity相关属性。
1.Activity调用Fragment相关属性:

该情况下有以下几种方式:

  1. 在Activity中有Fragment的引用,通过引用调用Fragment的public方法。
  2. Activity中通过FragmentManager.findFragmentById或findFragmentByTag方法获取Fragment引用,调用public方法。
2.Fragment中调用Activity相关属性:

我们可以可以通过getActivity方法获取Activity实例,但是我们一般情况都不会直接这么用,而是通过接口回调的方式实现:

public class OneFragment extends Fragment {
    private static final String TAG = "OneFragment";
    Button back;

    private CallbackListener listener;

    public interface CallbackListener {
        void fromOneFragment(String str);
    }
    ......

}

我们在OneFragment中创建了一个内部接口,并在OneFragment中声明了一个接口引用。

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        listener = (CallbackListener) getActivity();
        Log.e(TAG, "onActivityCreated: ");
    }

在onActivityCreated方法中给接口属性赋值。我们发现获取的Activity对象强转成了CallBackListener,所以我们需要在MainActivity中实现该接口:

public class MainActivity extends AppCompatActivity implements OneFragment.CallbackListener{
...
@Override
    public void fromOneFragment(String str) {
        Log.e(TAG, "fromOneFragment:   " + str );

    }
}

我们没有干什么,只是将传入Activity的数据打印一条日志。

在OneFragment中我们添加了一个按钮,用于从OneFragment向Activity传入数据:

    back = (Button) view.findViewById(R.id.back_to_main);
        back.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (listener != null)
                listener.fromOneFragment("我是从OneFragment来的!!");
        }
    });
现在我们看一下效果:


这条日志确实是从MainActivity中打印出来的,而且就是我们从OneFragment传入的数据。

关于Fragment和Activity通信的最佳实践

关于这一点,本人是看了鸿阳大神的博客,在里面有提到这个说法,我觉得很有道理,所以这里融合自己的观点介绍一下:

我们可能通过Activity启动一个Fragment,也可能是从一个Fragment中启动另一个Fragment,但是我们都知道,这其中的Fragment都是与这个Activity相关联的,所以如果有Fragment启动Fragment的情况,我们不应该让一个Fragment直接去启动另一个Fragment,而是通过回调给MainActivity,让他去启动另一个Fragment。

    <Button
        android:id="@+id/add_twofragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="启动TwoFragment"
        android:textAllCaps="false"/>

首先这是OneFragment之前回调的按钮,我们进行界面上简单的修改。

接着我们看OneFragment中回调的方法:

@Override
    public void fromOneFragment(String str) {
        Log.e(TAG, "fromOneFragment:   " + str);
        getSupportFragmentManager().beginTransaction()
                .hide(oneFragment)
                .add(R.id.frame, twoFragment)
                .addToBackStack(null).commit();
}

这样就实现了在OneFragment中启动TwoFragment的规范设计。效果如下:



以上便是关于Fragment常用到的内容了,如果有扩充的话,本人会在后面继续补充。喜欢的朋友可以关注一波,还是希望各位大大可以指出不明确的地方,谢谢大家支持!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值