Android之Fragment的前世今生

Posted 痕迹天涯119

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android之Fragment的前世今生相关的知识,希望对你有一定的参考价值。

本篇文章接上一篇Android Fragment的前世今生(一),不同的是本文将深入的分析Fragment的一些使用技巧和方法

Fragment使用进阶

常用类的解释

Fragment :主要用于定义Fragment
FragmentManager: 主要用于在Activity中操作Fragment
FragmentTransaction: 保证Fragment操作的原子性
FragmentTransaction :通过benginTransatcion()获取进而开启一个事务
transaction.add() :往Activity中添加一个Fragment
transaction.remove() :从Activity中移除一个Fragment
transaction.replace():替换Fragment,等价于remove+add
transaction.hide():隐藏当前的Fragment,
transaction.show():显示之前隐藏的Fragment
transaction.detach():解除视图状态与fragment实例的关联
transaction.attach():重建view视图,附加到UI上并显示。
transatcion.commit():提交一个事务

remove和detach的区别

在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。

关于support.v4包中的fragment

在上一篇文章中,我主要使用android.app.Fragment包中的FragmentFragmentManger等,当使用android.support.v4.app.Fragment包时,必须通过getSupportFragmentManager()`来获取FragmentManger,此外v4包中提供了FragmentPagerAdapter用于支持和ViewPager的配合使用,但前者并没有提供该适配器。


在Fragment中模拟回退栈

关于这个回退栈的问题我真是够了,studio的模拟器无限卡死,主要是因为硬盘的原因,来回测试了数十次,通过back键无法实现页面回退的效果,此外,还意外发现了两个Crash,这里记录一下。

思路上很简单,我让FragmentFirst和FragmentSecond之间进行来回调用,有两种模式,一种是隐藏前一个,添加下一个(当然你可以show,为了模拟o(∩_∩)o 哈哈),第二种是直接销毁第一个并添加第二个,同时为了展示效果,我改变imageView的图片。。可惜,测试失败,于是去测试了鸿洋和郭神的Demo,依旧失败(back键直接返回home主页,我觉得应该是直接销毁了Activity),那好么我用Log好吧。请了解问题所在的朋友评论下指出问题,不胜感激,

代码:
FirstFragment

public class FirstFragment extends Fragment implements View.OnClickListener 
    ImageView imageView;

    private Button change;
    private Button hide;
    private Button destroy;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        View view = inflater.inflate(R.layout.first_layout, container, false);

        //将控件绑定到View上
        imageView = (ImageView) view.findViewById(R.id.imageFirst);
        change = (Button) view.findViewById(R.id.imageChange);
        hide = (Button) view.findViewById(R.id.hide);
        destroy = (Button) view.findViewById(R.id.destroy);
        change.setOnClickListener(this);
        destroy.setOnClickListener(this);
        hide.setOnClickListener(this);

        //Log下显示BackStack元素个数
        int TAG = getFragmentManager().getBackStackEntryCount();
        Log.d("TAG1",String.valueOf(TAG));
        if(TAG>0)
            Log.d("TAG1",getFragmentManager().getBackStackEntryAt(TAG-1).toString());
        return view;
    


    @Override
    public void onClick(View v) 
        SecondFragment secondFragment = new SecondFragment();
        FragmentTransaction transaction = getFragmentManager().beginTransaction();

        switch (v.getId()) 
            case R.id.destroy:
                //保存fragment实例,移除视图与fragment实例的关联
                transaction.addToBackStack(null);
                transaction.replace(R.id.mainLayout, secondFragment);

                break;
            case R.id.imageChange:
                imageView.setImageResource(R.drawable.dog);
                break;
            case R.id.hide:
                transaction.hide(this);
                transaction.addToBackStack(null);
                transaction.add(R.id.mainLayout, secondFragment);
                break;

        
        transaction.commit();
    

SecondFragment

public class SecondFragment extends Fragment 

    FragmentTransaction transaction;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

        return inflater.inflate(R.layout.second_layout, container, false);
    

    //在onCreateView之后执行
    @Override
    public void onActivityCreated(Bundle savedInstanceState) 

        super.onActivityCreated(savedInstanceState);
        FirstFragment firstFragment = new FirstFragment();
        transaction = getFragmentManager().beginTransaction();
        transaction.hide(this);
        transaction.add(R.id.mainLayout,firstFragment);
        transaction.addToBackStack(null);
        transaction.commit();


        int TAG = getFragmentManager().getBackStackEntryCount();
        Log.d("TAG2",String.valueOf(TAG));
        if(TAG>0)
            Log.d("TAG2",getFragmentManager().getBackStackEntryAt(TAG-1).toString());

    

MainActivity

public class MainActivity extends AppCompatActivity 
    Fragment fragmentFirst;
    FragmentTransaction transaction;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fragmentFirst = new FirstFragment();
        transaction = getFragmentManager().beginTransaction();
        transaction.add(R.id.mainLayout,fragmentFirst);
        transaction.commit();
    

    @Override
    public void onBackPressed() 
        super.onBackPressed();
    

Log结果:

Log里倒是没什么问题,前两次并没有创建fragment实例,故为0,后面每次都会创建fragment实例,则依次+1.

演示图:


Fragment和Activity之间的通信

  1. 如果在Activity中包含了对应的Fragment实例,那么直接访问Fragment的属性和方法即可。

  2. 如果没有上述实例,可以通过getFragmentManager.findFragmentById()获取Fragment实例。
    Fragment fragment = getFragmentManager().findFragmentById();

  3. 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

利用第三种方式实现Fragment之间的通信

测试Demo:

主要更改Frgment的代码即可,原理上就是获取FirstFragment的imageFirst引用的资源并显示到SecondFragment的imageSeocond视图上。

public class SecondFragment extends Fragment 

    FragmentTransaction transaction;
    private ImageView imageViewSecond;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        //绑定imageSecond到view
        View view = inflater.inflate(R.layout.second_layout,container,false);
        imageViewSecond = (ImageView)view.findViewById(R.id.imageSecond);
        return view;
    

    //在onCreateView之后执行
    @Override
    public void onActivityCreated(Bundle savedInstanceState) 

        super.onActivityCreated(savedInstanceState);


        //获取ImageFirst的图片资源
        ImageView img = (ImageView) getActivity().findViewById(R.id.imageFirst);
        imageViewSecond.setImageDrawable(img.getDrawable());

        int TAG = getFragmentManager().getBackStackEntryCount();
        Log.d("TAG2",String.valueOf(TAG));
        if(TAG>0)
            Log.d("TAG2",getFragmentManager().getBackStackEntryAt(TAG-1).toString());

        //延迟新建FirstFragment
        new Handler().postDelayed(new Runnable() 
            @Override
            public void run() 
                FirstFragment firstFragment = new FirstFragment();
                transaction = getFragmentManager().beginTransaction();
                transaction.hide(SecondFragment.this);
                transaction.add(R.id.mainLayout,firstFragment);
                transaction.addToBackStack(null);
                transaction.commit();
            
        ,5000);

    

特别注意:imageSecond所显示的图片取决于回退栈栈底的那个Fragment视图的引用。所以在执行一次后不会再改变。


引入EventBus

对与Fragment的通信,通过接口回调进行封装可以使Activity和Fragment的耦合完全分离,提高开发的效率,但相对来说代码上会复杂一些,现在有更好的解决办法,那就是EventBus

关于利用EventBus进行通信我准备通过第三篇进行介绍,欢迎关注。


结合ViewPager实战

Tab实战Demo效果图:

ViewPager+pagerAdapter

特点:代码耦合度高

Fragment+FragmentManger

特点:代码简介,每个fragment单独管理一个页面,但不支持滑动

Fragment+ViewPager

全面解耦并支持滑动和点击事件

其实还可以进行很多的优化使用,后续会继续总结出来,实践代码上主要参考了郭霖和鸿洋的博客文章,有什么疑问欢迎指出。

本文源码(包括TabDemo):源码地址

以上是关于Android之Fragment的前世今生的主要内容,如果未能解决你的问题,请参考以下文章

从Preference组件的更迭看Jetpack的前世今生

Android OpenGL ES 入门系列 --- 了解OpenGL ES的前世今生

Selenium之前世今生

Selenium之前世今生

机器学习Logistic Regression 的前世今生(理论篇)

IEnumerable,IQueryable之前世今生