自從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複用這個特性而不涉及其餘複雜的用法。