用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:項目還剩不到兩周時間就要交差了,自己一定要好好努力,爭取把自己第一個項目做好,做到不讓自己留下遺憾!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM