自从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复用这个特性而不涉及其余复杂的用法。