用Fragment实现Tab页面切换效果初步总结

前言:

最近在Android项目中需要在活动中实现多页面切换的功能,第一次是实现的过程中,是让Activity同时去加载三个界面的,通过点击tab按钮对页面设置隐藏和显示,实现页面切换效果,但是后面发现这种实现方式其实存在很多问题:

  1. 首先,同时去加载三个页面,切换Activity的速度会变慢,尤其是布局中如果有很多ImageView,ImageButton等使用到图片资源的控件时,切换效果非常不好;
  2. 其次,由于使用了很多图片资源,在退出Activity的时候,像Drawable,Bitmap等一些资源并不会随着Activity的销毁而立即回收,导致手机占用很多内存空间,导致ANR(应用程序无响应)或者OOM(内存溢出)的Bug。然而,很多经验不足的Android开发人员,包括我自己,每次写Activity的时候,并没有去考虑这些问题,导致项目后面出现一系列的问题,还好没到那种无可挽回的地步,知道这点以后,一定要养成好的代码习惯。
  3. 然后,一个Activity里面会有大量的初始化控件的代码,还有其他的一些方法等等,是的Activity变的非常臃肿,难以维护,有时候出现一个错误,都不太好找。

然后网上找到一些资料,使用Fragment去实现Tab切换,是一个很不错的方式,基本可以解决以上问题。

我对Fragment的理解:中文翻译就叫碎片,它可以最大限度的利用手机和平板的显示空间,让开发者可以活用布局,实现很多不错的布局效果,当然要学会使用它,需要了解它的一些基本特性,方法,以及生命周期等。

关于Fragment的详细介绍,在这里推荐鸿洋的博客http://blog.csdn.net/lmj623565791/article/details/37970961,里面分了三篇将Fragment的使用以及一些需要注意的地方讲的非常详细,篇幅比较长,所以需要拿出点耐心来看。

第一部分:

在开展项目的这段时间中,将遇到的一些Bug贴出来,网上并不是那么容易就能找到答案的Bug,同时也是困扰了很久才解决的

Bug1:

描述:(java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.zaina/wayto.com.cn.task.TaskInfoActivity}: android.view.InflateException: Binary XML file line #17: Error inflating class <unknown>)

思路分析:起初认为是布局文件的错误,然而并没有找到答案,之后再网上搜寻答案,大部分都是说由于使用了自定义控件导致的,其他的原因也有,无奈的是,我并没有使用自定义控件,只是简单的不了个局,为控件使用了统一的Style样式而已。但是这个Bug有点奇怪的是,程序刚运行的时候,进入多页面的Activity,退出再进,没问题,随着次数的增多,程序就报这个错误。Bug的出现并没有固定的一个时机,于是,我就想到,可能是手机内存不足导致,后面用了eclipse里面一个强大的插件内存分析工具MAT(Memory Analyzer Tool) ,果然不出所料,找到了内存泄露的地方,正是布局里面使用了过多的图片资源而没有手动回收导致异常。

解决方案:1,在Activity中使用了图片资源,一定要记得在onStop()方法里面去手动释放资源,这里贴一段代码,也是网上找到的,仅供参考。

 1 try {
 2             for (ImageView image : imageList) {
 3                 if (image!=null) {
 4 //                    Drawable d=image.getDrawable();
 5 //                    if (d!=null) d.setCallback(null);
 6 //                    image.setImageDrawable(null);
 7                     BitmapDrawable bd=(BitmapDrawable) image.getBackground();
 8                     if (bd!=null) {
 9                         System.gc();
10                         //释放的大小
11                         long size=Runtime.getRuntime().totalMemory();
12                         bd.setCallback(null);
13                         bd.getBitmap().recycle();
14                         bd=null;
15                         System.gc();
16                         Log.i("TaskInfo", "释放"+size);
17                     }
18                 }
19                 image=null;
20             }
21             imageList=null;
22         } catch (Exception e) {
23             e.printStackTrace();
24             Log.i("TaskInfo", "错误"+e.getMessage());
25         }

2,如果程序中有多次重复使用图片资源的情况,可以使用SoftReference或者LruCache一些缓存机制去处理,而不是每次去重新加载图片,相关内容可以参考http://blog.csdn.net/xiaanming/article/details/9825113,也是写的非常好的一篇博客,里面主要是讲到使用LruCache+线程池的方式异步下载图片,设计到的技术点也很多了。


相关参考:http://www.2cto.com/kf/201408/326452.html  

     http://stackoverflow.com/questions/7536988/android-app-out-of-memory-issues-tried-everything-and-still-at-a-loss/7576275

Bug2:

描述:Android FragmentManager BackStackRecord.run throwing NullPointerException

思路分析:这是我后面采用Fragment实现多页面效果的过程中遇到的一个错误,也是困扰了我一段时间,其实后面想想,根本原因还是自己对Fragment还不够了解,使用过程中遇到的一些错误就不知道该从哪里入手分析,后面还是借助百度找到了答案。其实是使用Fragment切换页面过程中,之前用每次都是用FragmentTransaction的replace()方法去实现,没有问题,但是每次都是销毁上一个Fragment,然后重新加载新的Fragment,这样也可以,然是效率不高,因为每次切换都要重新new 一个Fragment,重新createView(),切换页面过程中会有卡顿。看了弘扬的博客里面写到,如果想要保留前一个Fragment的参数,最好使用FragmentTransaction的hide()和show()方法。

第一次的写法:

//Fragment 声明三个Fragment为全局变量
    private TaskDetailFragment detailFragment;
    private TaskTimeLineFragment timeLineFragment;
    private TaskAttachFragment attachFragment;
private void ChangeFragment(int tabId){
        FragmentManager fm=getFragmentManager();
        //开发Fragment事务
        FragmentTransaction transaction = fm.beginTransaction();
        String tag="";
        switch (tabId) {
        case R.id.btn_detail:
            //详情
            if (detailFragment==null) {
                detailFragment=TaskDetailFragment.newInstance(ticket);
                transaction.add(R.id.taskinfo_content,detailFragment,"DETAIL");
                break;
            }
            transaction.show(detailFragment);//隐藏
            transaction.hide(timeLineFragment);//显示
            transaction.hide(attachFragment);//显示
            break;
            /后面代码类似...
            }    

依次按顺序,从第一个Fragment切换第二的Fragment,然后切换到第三个,都没问题,但是,从第一个切换到第二个Fragment,然后再切换回第一个Fragment时,第三个Fragment并没有加载,也就是为null,这也就是为什么会出现这个空指针的Bug。


解决:在执行FragmentTransaction的hide(),show(),replace(),add(),detach()等方法时,首先要判断Frangment是否为null,这里就直接贴代码了,判断为空的方式在代码中体现出来了

首先在transaction的add()方法中为Fragment制定Tag,然后用FragmentManager的findFragmentByTag("DETAIL")方法,判断返回值是否为空。

    //开始Fragment事务
        FragmentTransaction transaction = fm.beginTransaction();
//        String tag="";
        switch (tabId) {
        case R.id.btn_detail:
            //详情
            if (detailFragment==null) {
                detailFragment=TaskDetailFragment.newInstance(ticket);
                transaction.add(R.id.taskinfo_content,detailFragment,"DETAIL");
                break;
            }
            if(fm.findFragmentByTag("DETAIL")!=null)transaction.show(detailFragment);
            if(fm.findFragmentByTag("TIME")!=null)transaction.hide(timeLineFragment);
            if(fm.findFragmentByTag("ATTACH")!=null)transaction.hide(attachFragment);
            break;
View Code

相关参考:http://stackoverflow.com/questions/13393693/android-fragmentmanager-backstackrecord-run-throwing-nullpointerexception

第三部分:

最后,总结以上几点,发现自己对于Android的学习还是存在很多需要认真钻研的地方,对于自己不熟悉的技术和知识,一定要知其根源,多加练习,才能熟练运用,被自己所掌握,然后,分析问题的时候,思维不能形成固式,多发散,多参考资料,最终要的还是要多分析自己的代码,不要盲目修改,这样只会把Bug越改越多。

ps:项目还剩不到两周时间就要交差了,自己一定要好好努力,争取把自己第一个项目做好,做到不让自己留下遗憾!

原文地址:https://www.cnblogs.com/David-Young/p/4655854.html