尋找Fragment的替代品的嘗試

    自從Android在3.0推出Fragment以後,如今不少應用的設計都是創建在Fragment的基礎上,像是多個tab切換這種需求,就可使用Fragment,而且Fragment提供了一系列生命週期的回調,能夠幫助咱們實現不少特殊的需求,像是數據保存和恢復等。設計模式

    Fragment自己的出現是爲了解決平板多屏界面展現問題,由於平板能夠展現比手機更多的內容,因此使用Fragment能夠實現根據不一樣尺寸展現不一樣內容的需求,而這不一樣內容更可能是指在更大的尺寸顯示更多的內容。異步

    隨着人們的實際編碼工做,發現使用Fragment能夠更好的管理界面,由於一個Activity能夠管理多個Fragment,若是將Fragment當作一個界面,咱們能夠實現多個界面的切換,而且這種工做比起之前在佈局文件中控制可見來說,更好管理,而且佈局能夠複用,致使Activity的做用就只是Fragment的管理容器而已,加上Fragment擁有和Activity同步的生命週期,因此不少業務工做均可以放在Fragment中。ide

    如今不少界面的開發工做都是使用Activity加多個Fragment的設計模式,這是很好的方式,但要想徹底掌握Fragment這個利器,須要瞭解的工做很是多,而且有關Fragment能夠開展至關多的話題,像是Fragment之間的參數傳遞,Fragment之間的切換和狀態的保存,等等,這些都是至關大的範圍,並且谷歌也看到Fragment的使用前景,封裝了DialogFrament,ListFragment等方便開發者使用,Fragment和Activity之間生命週期的關係,還能夠作監聽Activity生命週期實現某些功能,像是結束的時候中止當前異步任務等需求。佈局

    仔細看Fragment的代碼,咱們發現這無非就是在Activity的佈局中指定的地方添加相應的佈局,而後綁定一堆監聽用以實現各類生命週期的回調。this

    咱們甚至能夠模擬Android源碼,本身搞一個Fragment的替代品。編碼

    咱們此次的嘗試是實現Fragment界面複用的功能,這是最經常使用的場景。spa

    對應FragmentManager,咱們用ViewHolderManager來管理View,對應Fragment,用PartViewHolder。設計

    爲了方便咱們替換View的時候可以更快的找到對應的View,須要一個HashMap,相似FragmentManager在查找Fragment同樣,key值爲咱們指定的id,value則是對應的PartViewHolder。code

    PartViewHolder只是一個抽象,它表示的是View的佔位,更確切的說,是View的控制類,所以它具備一些共同的行爲,實現上是一個抽象類。blog

    在Java中,定義一組抽象有兩種:抽象類和接口,這兩種都是多態的表現。抽象類表示的是"is a"的關係,而接口表示的是"has a"的關係。咱們見過一些代碼,相似Duck這類的名字,居然只是一個接口,接口表明的應該是一組行爲協議,相似flyable這樣的接口名,若是抽象的名稱是一個名詞,能夠考慮這是不是一個抽象類,若是是一個形容詞或者動詞,能夠考慮這是不是一個接口。

    PartViewHolder的實現以下:

 1 public abstract class PartViewHolder {
 2 
 3    protected View rootView;
 4 
 5    protected LayoutInflater inflater;
 6 
 7    public PartViewHolder(LayoutInflater inflater) {
 8 
 9       this.inflater = inflater;
10 
11    }
12 
13    protected abstract View getRootView();
14 
15    protected abstract void bindViews();
16 
17    public abstract void resetView();
18 
19 }

     咱們這裏並無傳入Activity的Context,不少人在實現有關View的自定義時候,爲了方便,都會傳入Context,由於LayoutInflater須要經過Context來獲取,但若是不是爲了在佈局中渲染,能夠不用傳Context的,這也是爲了防止內存泄露的方法。

     傳入的rootView是爲了提供View要添加到的root上,而bindViews是爲了初始化控件,resetView提供了View的清理。

     咱們如今來實現一個PartViewHolder的子類。

 1 public class LoginViewHolder extends PartViewHolder {
 2     private LinearLayout llLogin;
 3     private LinearLayout llRegister;
 4     public LoginViewHolder(LayoutInflater inflater) {
 5         super(inflater);
 6         rootView = inflater.inflate(R.layout.view_login_register, null);
 7         rootView.setTag(false);
 8     }
 9     @Override
10     protected View getRootView() {
11         return rootView;
12     }
13     @Override
14     protected void bindViews() {
15         llLogin = (LinearLayout) rootView.findViewById(R.id.ll_login);
16         llRegister = (LinearLayout) rootView.findViewById(R.id.ll_register);
17         llLogin.setOnClickListener(new View.OnClickListener() {
18             @Override
19             public void onClick(View v) {
20                ...
21             }
22         });
23         llRegister.setOnClickListener(new View.OnClickListener() {
24             @Override
25             public void onClick(View v) {
26                ...
27             }
28         });
29     }
30     @Override
31     public void resetView() {
32     }
33 }

     上面實現的就是一個登陸的頁面。

     咱們在構造器中指定rootView,而後經過getRootView返回rootView,以便上層業務可以對rootView進行操做,bindViews進行組件的初始化和事件的監聽。

     PartViewHolder就是用來負責View的初始化和對應的操做。

     咱們來看一個最重要的類:ViewHolderManager。

     ViewHolderManager的實現以下:

 1 public class ViewHolderManager {
 2     private static Map<String, PartViewHolder> viewMap;
 3     private static Map<String, PartInnerViewHolder> innerViewMap;
 4     public ViewHolderManager() {
 5         if(viewMap == null) {
 6             viewMap = new HashMap<>();
 7         }
 8         if(innerViewMap == null) {
 9             innerViewMap = new HashMap<>();
10         }
11     }
12     public void add(String key, PartViewHolder viewHolder) {
13         viewMap.put(key, viewHolder);
14     }
15     public void addInner(String innerKey, PartInnerViewHolder viewHolder) {
16         innerViewMap.put(innerKey, viewHolder);
17     }
18     public void showView(ViewGroup viewGroup, String key) throws Exception {
19         if (viewMap.size() == 0) {
20             throw new Exception("You have not add any views");
21         }
22         if (viewGroup == null) {
23             throw new Exception("not rootView");
24         }
25         if (viewGroup.getChildCount() > 0) {
26             viewGroup.removeAllViews();
27         }
28         PartViewHolder viewHolder = viewMap.get(key);
29         viewGroup.addView(viewHolder.getRootView());
30         if (!(boolean) viewHolder.getRootView().getTag()) {
31             viewHolder.bindViews();
32         }
33     }
34     public void showInnerView(String innerKey) throws Exception {
35         if (viewMap.size() == 0) {
36             throw new Exception("You have not add any views");
37         }
38         PartInnerViewHolder viewHolder = innerViewMap.get(innerKey);
39         ViewGroup viewGroup = (ViewGroup) viewHolder.getRootView();
40         if (viewGroup.getChildCount() > 0) {
41             viewGroup.removeAllViews();
42         }
43         viewGroup.addView(viewHolder.getInnerRootView());
44         if (!(boolean) viewHolder.getInnerRootView().getTag()) {
45             viewHolder.bindInnerViews();
46         }
47     }
48     public void resetView(String key) {
49         if (viewMap.containsKey(key)) {
50             PartViewHolder viewHolder = viewMap.get(key);
51             viewHolder.resetView();
52         }
53         if (innerViewMap.containsKey(key)) {
54             PartInnerViewHolder viewHolder = innerViewMap.get(key);
55             viewHolder.resetView();
56         }
57     }
58 }

   ViewHolderManager負責維護一個HashMap,這個HashMap存放的是各類PartViewHolder。和FragmentManager相似,經過add添加對應的PartViewHolder,而後在調用showView的時候,經過id來指定要展現的PartViewHolder。

   咱們的作法很簡單,就是在指定的ViewGroup中添加rootView,來達到將須要的View展現在佈局上的目的,在添加以前,要判斷是否已經添加過了,若是有,就要remove掉。

   這裏咱們還增長了一個PartInnerViewHolder,這是對應Fragment自己也能夠添加Fragment的狀況。

 1 public abstract class PartInnerViewHolder{
 2     protected View rootView;
 3     protected View innerRootView;
 4     protected LayoutInflater inflater;
 5     public PartInnerViewHolder(LayoutInflater inflater, View rootView){
 6         this.inflater = inflater;
 7         this.rootView = rootView;
 8     }
 9     protected abstract View getInnerRootView();
10     protected abstract void bindInnerViews();
11     protected abstract View getRootView();
12     protected abstract void resetView();
13 }

   相比PartViewHolder,PartInnerViewHolder只不過是增長了一個innerRootView,用於指定該Fragment自己的佈局。

   到了如今,一個Fragment的簡單模仿品已經完成了,固然,咱們爲啥要本身造輪子呢?在使用嵌套Fragment的時候,遇到了不少問題,比較直觀的就是APP放後臺後,在內存不足的狀況下,會致使Fragment重疊,還有其餘各類狀況,因此咱們嘗試瞭如何去模仿一個最簡化的Fragment,只是利用它View複用這個特性而不涉及其餘複雜的用法。

相關文章
相關標籤/搜索