自从Android在3.0推出Fragment之后,现在很多应用的设计都是建立在Fragment的基础上,像是多个tab切换这种需求,就可以使用Fragment,并且Fragment提供了一系列生命周期的回调,可以帮助我们实现很多特殊的需求,像是数据保存和恢复等。

Fragment本身的出现是为了解决平板多屏界面展示问题,因为平板可以展示比手机更多的内容,所以使用Fragment可以实现根据不同尺寸展示不同内容的需求,而这不同内容更多是指在更大的尺寸显示更多的内容。

随着人们的实际编码工作,发现使用Fragment可以更好的管理界面,因为一个Activity可以管理多个Fragment,如果将Fragment当做一个界面,我们可以实现多个界面的切换,并且这种工作比起以前在布局文件中控制可见来讲,更好管理,并且布局可以复用,导致Activity的作用就只是Fragment的管理容器而已,加上Fragment拥有和Activity同步的生命周期,所以很多业务工作都可以放在Fragment中。

现在很多界面的开发工作都是使用Activity加多个Fragment的设计模式,这是很好的方式,但要想完全掌握Fragment这个利器,需要了解的工作非常多,并且有关Fragment可以开展相当多的话题,像是Fragment之间的参数传递,Fragment之间的切换和状态的保存,等等,这些都是相当大的范围,而且谷歌也看到Fragment的使用前景,封装了DialogFrament,ListFragment等方便开发者使用,Fragment和Activity之间生命周期的关系,还可以做监听Activity生命周期实现某些功能,像是结束的时候停止当前异步任务等需求。

仔细看Fragment的代码,我们发现这无非就是在Activity的布局中指定的地方添加相应的布局,然后绑定一堆监听用以实现各种生命周期的回调。

我们甚至可以模拟Android源码,自己搞一个Fragment的替代品。

我们这次的尝试是实现Fragment界面复用的功能,这是最常用的场景。

对应FragmentManager,我们用ViewHolderManager来管理View,对应Fragment,用PartViewHolder。

为了方便我们替换View的时候能够更快的找到对应的View,需要一个HashMap,类似FragmentManager在查找Fragment一样,key值为我们指定的id,value则是对应的PartViewHolder。

PartViewHolder只是一个抽象,它表示的是View的占位,更确切的说,是View的控制类,因此它具有一些共同的行为,实现上是一个抽象类。

在Java中,定义一组抽象有两种:抽象类和接口,这两种都是多态的表现。抽象类表示的是"is a"的关系,而接口表示的是"has a"的关系。我们见过一些代码,类似Duck这类的名字,竟然只是一个接口,接口代表的应该是一组行为协议,类似flyable这样的接口名,如果抽象的名称是一个名词,可以考虑这是否是一个抽象类,如果是一个形容词或者动词,可以考虑这是否是一个接口。

PartViewHolder的实现如下:

 public abstract class PartViewHolder {

    protected View rootView;

    protected LayoutInflater inflater;

    public PartViewHolder(LayoutInflater inflater) {

       this.inflater = inflater;

    }

    protected abstract View getRootView();

    protected abstract void bindViews();

    public abstract void resetView();

 }

我们这里并没有传入Activity的Context,很多人在实现有关View的自定义时候,为了方便,都会传入Context,因为LayoutInflater需要通过Context来获取,但如果不是为了在布局中渲染,可以不用传Context的,这也是为了防止内存泄露的方法。

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

我们现在来实现一个PartViewHolder的子类。

 public class LoginViewHolder extends PartViewHolder {
private LinearLayout llLogin;
private LinearLayout llRegister;
public LoginViewHolder(LayoutInflater inflater) {
super(inflater);
rootView = inflater.inflate(R.layout.view_login_register, null);
rootView.setTag(false);
}
@Override
protected View getRootView() {
return rootView;
}
@Override
protected void bindViews() {
llLogin = (LinearLayout) rootView.findViewById(R.id.ll_login);
llRegister = (LinearLayout) rootView.findViewById(R.id.ll_register);
llLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
...
}
});
llRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
...
}
});
}
@Override
public void resetView() {
}
}

上面实现的就是一个登录的页面。

我们在构造器中指定rootView,然后通过getRootView返回rootView,以便上层业务能够对rootView进行操作,bindViews进行组件的初始化和事件的监听。

PartViewHolder就是用来负责View的初始化和对应的操作。

我们来看一个最重要的类:ViewHolderManager。

ViewHolderManager的实现如下:

 public class ViewHolderManager {
private static Map<String, PartViewHolder> viewMap;
private static Map<String, PartInnerViewHolder> innerViewMap;
public ViewHolderManager() {
if(viewMap == null) {
viewMap = new HashMap<>();
}
if(innerViewMap == null) {
innerViewMap = new HashMap<>();
}
}
public void add(String key, PartViewHolder viewHolder) {
viewMap.put(key, viewHolder);
}
public void addInner(String innerKey, PartInnerViewHolder viewHolder) {
innerViewMap.put(innerKey, viewHolder);
}
public void showView(ViewGroup viewGroup, String key) throws Exception {
if (viewMap.size() == 0) {
throw new Exception("You have not add any views");
}
if (viewGroup == null) {
throw new Exception("not rootView");
}
if (viewGroup.getChildCount() > 0) {
viewGroup.removeAllViews();
}
PartViewHolder viewHolder = viewMap.get(key);
viewGroup.addView(viewHolder.getRootView());
if (!(boolean) viewHolder.getRootView().getTag()) {
viewHolder.bindViews();
}
}
public void showInnerView(String innerKey) throws Exception {
if (viewMap.size() == 0) {
throw new Exception("You have not add any views");
}
PartInnerViewHolder viewHolder = innerViewMap.get(innerKey);
ViewGroup viewGroup = (ViewGroup) viewHolder.getRootView();
if (viewGroup.getChildCount() > 0) {
viewGroup.removeAllViews();
}
viewGroup.addView(viewHolder.getInnerRootView());
if (!(boolean) viewHolder.getInnerRootView().getTag()) {
viewHolder.bindInnerViews();
}
}
public void resetView(String key) {
if (viewMap.containsKey(key)) {
PartViewHolder viewHolder = viewMap.get(key);
viewHolder.resetView();
}
if (innerViewMap.containsKey(key)) {
PartInnerViewHolder viewHolder = innerViewMap.get(key);
viewHolder.resetView();
}
}
}

ViewHolderManager负责维护一个HashMap,这个HashMap存放的是各种PartViewHolder。和FragmentManager类似,通过add添加对应的PartViewHolder,然后在调用showView的时候,通过id来指定要展示的PartViewHolder。

我们的做法很简单,就是在指定的ViewGroup中添加rootView,来达到将需要的View展示在布局上的目的,在添加之前,要判断是否已经添加过了,如果有,就要remove掉。

这里我们还增加了一个PartInnerViewHolder,这是对应Fragment本身也可以添加Fragment的情况。

 public abstract class PartInnerViewHolder{
protected View rootView;
protected View innerRootView;
protected LayoutInflater inflater;
public PartInnerViewHolder(LayoutInflater inflater, View rootView){
this.inflater = inflater;
this.rootView = rootView;
}
protected abstract View getInnerRootView();
protected abstract void bindInnerViews();
protected abstract View getRootView();
protected abstract void resetView();
}

相比PartViewHolder,PartInnerViewHolder只不过是增加了一个innerRootView,用于指定该Fragment本身的布局。

到了现在,一个Fragment的简单模仿品已经完成了,当然,我们为啥要自己造轮子呢?在使用嵌套Fragment的时候,遇到了很多问题,比较直观的就是APP放后台后,在内存不足的情况下,会导致Fragment重叠,还有其他各种情况,所以我们尝试了如何去模仿一个最简化的Fragment,只是利用它View复用这个特性而不涉及其他复杂的用法。

寻找Fragment的替代品的尝试的更多相关文章

  1. 【转】基于Android Fragment功能的例子

    原文网址:http://blog.csdn.net/eyu8874521/article/details/8252216 通过最近空闲时候对Fragment的学习,尝试着写了一个小Demo,将在开发的 ...

  2. 关于android fragment 某些使用记录

    1.首先是当android2.3.3之前还是用着android-support-v4.jar来加载Fragment时. a.在xml布局应该如何定义呢? 答案:用FrameLayout标签来定义(在a ...

  3. Android开发笔记(10)——使用Fragment传递

    转载请注明:http://www.cnblogs.com/igoslly/p/6911165.html 由于最近废寝忘食地在开发App,没来得及及时做总结,没有用很高级的部件,勉强也使用一些功能完成了 ...

  4. 基于Android Fragment功能的样例

    通过近期空暇时候对Fragment的学习,尝试着写了一个小Demo,将在开发的时候能经常使用到的Fragment知识放在一起,写过了这个Demo对Android Fragment的了解更加深入了,以后 ...

  5. 8款替代Dreamweaver的开源网页开发工具

    Adobe Dreamweaver虽然非常好用,但它并不是唯一一个能够设计.开发.发布精彩网站的Web开发集成环境.我们的开源世界里有很多非常棒的可以完全替代Dreamweaver的各种功能的优秀We ...

  6. [译]我们为何基于FreeBSD打造解决方案?

    [译注]翻译这篇文章,主要是觉得老外在思考问题时,勇于打破固有的技术栈积累,尝试不同的选择,从而找到最合适自己的技术方案.得到真正的实惠. Synergy SKY提供多种软件解决方案,本文想讨论的是关 ...

  7. (译)Minimal Shader(最小的着色器)

    (原文:https://en.wikibooks.org/wiki/Cg_Programming/Unity/Minimal_Shader) This tutorial covers the basi ...

  8. 2D动画如何做出3D体积感

    https://cowlevel.net/article/1959026 <AngerForce>幕后故事 这篇文章是个老坑,最近有时间开始写,也是对之前项目的一个总结和记录吧. 本篇文章 ...

  9. 通过Mono 在 Heroku 上运行 .NET 应用

    英文原文:Running .NET on Heroku 中文原文:在 Heroku 上运行 .NET 应用 自从加入了Heroku之后,我就想在这个平台上运行.NET程序.现在我很高兴向大家宣布,我们 ...

随机推荐

  1. 我和Java有个约定

    2012年8月31日,我在父亲的陪伴下,第一次登上的火车,千里迢迢从老家来到了现代化大都市--西安,目睹着这里的繁华,与从小生活的环境截然的天壤之别,蓦然间问自己,你确定这是想来的地方吗? 依稀记得曾 ...

  2. linux上创建ftp服务器下载文件///使用AWS服务器作为代理,下载sbt相关的包

    最近觉得自己下载有些jar的速度太慢了,就在aws上下好了,然后转到我电脑上来,在aws上开了ftp服务器.结果就倒腾了一上午,作个记录,以便后面查看. 1.安装vsftpd yum -y insta ...

  3. 调试SQLSERVER (二)使用Windbg调试SQLSERVER的环境设置

    调试SQLSERVER (二)使用Windbg调试SQLSERVER的环境设置 调试SQLSERVER (一)生成dump文件的方法调试SQLSERVER (三)使用Windbg调试SQLSERVER ...

  4. 使用git将代码push到osc上

    1.下载git客户端 2.在osc上创建项目 ①使用:git bash here ②在目录下执行:git init ③ssh-keygen -t rsa -C "xqs@gmail.com& ...

  5. 【Bugly干货】关于 Android N 那些你不知道的事儿

    今年3月,Google 破天荒提前半年发布了 Android N 开发者预览版.当然,作为一个不合格的谷粉并没有第一时间体验安装,因为至今仍然能够回忆起来去年今日此门中(雾)兴冲冲刷了 Android ...

  6. Jasmine测试ng Promises - Provide and Spy

    jasmine提供了很多些很实用的处理Promises的方法,首先我们来考虑下面的这个例子: angular.module("myApp.store").controller(&q ...

  7. css计数器详解

    什么是css计数器 体验更佳排版请戳原文链接:http://blog.liuxianan.com/css-counters.html 就是采用css给一些html元素自动生成编号,比如类似1.3.2这 ...

  8. bianma 水平 技巧

    能够写出这样的代码, 其实体现了水平 switch (state) { case 0: break; case 3: return; // already connected case 4: stat ...

  9. MyBatis入门学习(一)

    一.MyBatis入门简要介绍(百科) MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyB ...

  10. swift 项目 oc 和 swift 混用,文件相互引用

    创建swift工程后,如果后面想新建 oc 文件,这时会生成一个  AppName-Bridging-Header.h文件 一,在swift 文件中 1> 引用swift 文件 什么都不需要操作 ...