安卓中MVC模式的深度思索和实践(二)
来源:互联网 发布:什么蛋糕品牌最好 知乎 编辑:程序博客网 时间:2024/06/05 07:22
这是一个有关安卓MVC框架模式的短系列,目的是思索和分析安卓中MVC模式更为真实的一面。
系列:
-安卓中MVC模式的深度思索和实践(一)
-安卓中MVC模式的深度思索和实践(三)
转载请标明出处: http://blog.csdn.net/cysion1989/article/details/71486875
在上一篇中,主要从一个比较传统但又精致的角度重新审视了一下安卓中的MVC模式。首先回顾一下上篇中最后有关MVC的观点,核心是分层,重点是职责如何单一和清晰化:
- 视图V,具备展示职责,职责的划分是通过与控制者的改变无关这条原则来进行的;比如在安卓中,以LinearLayout为例,不管使用者怎么改变,它要么纵向布局,要么横向布局,其本身就是这样,而与使用的场景无关。整体来说,V层,就是要好好的担起展示的职责,不要把属于展示的职责,交给其它层去做。
- 数据M,具备数据存取和操作职责,职责的划分是通过是否要与V发生联系来进行的。比如对于C来说,M应该就是一个最终且最有目的性的需求品,C不该关心这个目标数据怎么来的。这使得一般的校验和缓存等操作,应该存在于M层。
- 控制器C,或者叫做调度器,主要用来委托操作和调度,它不应该着眼于业务逻辑或者视图逻辑;而要将业务逻辑和视图逻辑,都还给对应的V层和M层。而视图和业务耦合的视图业务逻辑,可视情况委托给业务工具类(Helper)。对于调度职责来说,要构建一个调度体系,上级调度管理下层调度,下层调度管理下下层调度,整体就是采用分而治之的思想,实现页面级别的模块化。
本文主要来分析C的优化方式,理论难免枯燥,所以本篇就以一个完整的简单demo来聊聊页面的模块化,本文主要说明怎么分模块的问题,很多细节问题没有指出,这些细节问题很多并不是demo中方案带来的,而是项目实践中都会遇到的。
demo的仓库地址
下面是demo项目的大体介绍。
首先是demo的页面原型图,见附图所示。
需求分析:这个页面是个整体可滚动的视图,单从原型图来看在实际项目中应该至少有一个网络请求接口,甚至4个;顶部UI需要填充多个数据;WebView应该有其对应的配置甚至js交互;纵向list可能还能上拉加载等。滑动冲突的问题不作考虑,因为无论什么布局都要考虑。
方案一,ScrollView嵌套LinearLayout,内部纵向嵌套4种布局,分别对应原型图。这种方案应该是80%以上的童鞋都会采用的方案;但是本文并不推荐采用这种方式。这里先列三条原因:1. 除了List里面的UI,所有的UI都将会出现在ScrollView所在的Controller中,少说也要超过10个ui要find和设置数据吧,容易造成C的臃肿;2. 这种页面肯定有网络请求,假设以比较多的一种方式,4个接口来说,所有的数据请求以及给View设置都出现在C中,也会臃肿;3. 增加和删除布局,会比较麻烦,因为C中代码臃肿耦合较多。
方案二,ScrollView嵌套LinearLayout,内部纵向嵌套4种布局,在C中分别替换成Fragment。这种方案相对方案一来说,有了一些进步,至少一些View的find和数据请求被分散了。但本文仍不建议这样,原因有三:1. Fragment的生命周期真的有点难以把控,尤其是当上述布局出现在二级嵌套的Fragment中时;2. Fragment的替换本身有一定的复杂度;3. 扩展性有不足,若横向List和纵向List交替实现多次,这种替换方式显得被动。
方案三,当然就是本文推荐的方式:RecyclerView作为大C中的唯一的UI存在,那四种布局,分别对应四种item,即对应四种holder。这么做的优势有:1. 大C几乎只处理一个View的find和设置,网络请求也只处理部分接口,C更多的作用是调度;2.RecyclerView的adapter负责分发布局,职责比较清晰;3. 对应的几个Holder都相当于小C,分别处理View的find和set,也能处理自身的网络请求;4. 布局增删,复用都很容易–by CysionLiu。
代码实现,还是上代码比较容易理解,本demo省略了网络部分。
大C的主要代码
public class MainActivity extends AppCompatActivity { private RecyclerView mGlobalList; private GlobalAdapter mGlobalAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mGlobalList = (RecyclerView) findViewById(R.id.list_main_acty); mGlobalList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); mGlobalAdapter = new GlobalAdapter(this, Provider.create()); mGlobalList.setAdapter(mGlobalAdapter); }}
- adapter的主要代码
public class GlobalAdapter extends RecyclerView.Adapter { private Activity mActivity; private Context mContext; private List<BaseBean> mDataList; public static final int TOP = 900; public static final int WEB = 901; public static final int GALLERY = 902; public static final int SHOW = 903; public GlobalAdapter(Activity aActivity, List<BaseBean> aDataList) { mActivity = aActivity; mContext = aActivity.getApplicationContext(); mDataList = aDataList; if (mDataList == null) { mDataList = new ArrayList<>(); } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case TOP: return new TopViewHolder(LayoutInflater.from(mContext).inflate(R.layout.holder_top, parent, false)); case WEB: return new WebHolder(LayoutInflater.from(mContext).inflate(R.layout.holder_web, parent, false)); case GALLERY: return new GalleryHolder(LayoutInflater.from(mContext).inflate(R.layout.holder_gallery, parent, false)); case SHOW: return new ShowHolder(LayoutInflater.from(mContext).inflate(R.layout.holder_show, parent, false)); default: break; } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((BaseViewHolder) holder).bindData(mActivity, mDataList, position); } @Override public int getItemCount() { return mDataList.size(); } @Override public int getItemViewType(int position) { return mDataList.get(position).getItemType(); }}
重点来了,数据和布局的分发是关键,毕竟这四种布局差别很大,数据也没什么直接关系。但是本系列第一篇最后也提到,利用组合模式的思想,可实现数据的组合。还是上代码好理解。
先定义一个基础数据
public class BaseBean { private int itemType; public int getItemType() { return itemType; } public void setItemType(int aItemType) { itemType = aItemType; }}
- 顶部ui的数据比较好解释,权且贴出;
public class TopBean extends BaseBean { private String name; private String imgHead; private int resImgHead; private int countNum; private int commentNum; private String description;.......省略}
- 重点是子列表型数据的处理,以横向列表数据为例,这里看ContainerBean和GalleryBean;这里的两个bean类就使用了组合思想,让ContainerBean可当做一个元素,也可被用作列表元素,如此带来的额外的好处是adapter也可被内部list复用–by CysionLiu。
public class ContainerBean extends BaseBean { private List<BaseBean> dataList; public List<BaseBean> getDataList() { return dataList; } public void setDataList(List<BaseBean> aDataList) { dataList = aDataList; }}
public class GalleryBean extends BaseBean { private String imgUrl; private int resId; ....省略}
- Holder的处理也是关键,为了复用和减少很多代码,一个父Holder是很重要的。里面有三个抽象方法,都有其作用。其中,数据接收或者网络请求便可发生在bindData方法中。
public abstract class BaseViewHolder extends RecyclerView.ViewHolder implements IBindData, View.OnAttachStateChangeListener { protected Activity mActivity; public BaseViewHolder(View itemView) { super(itemView); itemView.addOnAttachStateChangeListener(this); } @Override public abstract void bindData(Activity aActivity, List<BaseBean> dataList, int position); @Override public void onViewAttachedToWindow(View v) { onAttached(); } @Override public void onViewDetachedFromWindow(View v) { onDetached(); } public abstract void onAttached(); public abstract void onDetached();}
- 最后,模拟一下数据,搭建一下这个列表。
public class Provider { public static List<BaseBean> create() { List<BaseBean> baseBeanList = new ArrayList<>(); //top BaseBean top = new TopBean(); top.setItemType(GlobalAdapter.TOP); baseBeanList.add(top); //web WebBean web = new WebBean(); web.setItemType(GlobalAdapter.WEB); web.setUrl("https://m.baidu.com/"); baseBeanList.add(web); //ga List<BaseBean> innerGallery = new ArrayList<>(); innerGallery.add(new GalleryBean()); innerGallery.add(new GalleryBean()); ....... ContainerBean containerBean1 = new ContainerBean(); containerBean1.setDataList(innerGallery); containerBean1.setItemType(GlobalAdapter.GALLERY); baseBeanList.add(containerBean1); //show List<BaseBean> shows = new ArrayList<>(); shows.add(new ShowBean()); shows.add(new ShowBean()); ..... ContainerBean containerBean2 = new ContainerBean(); containerBean2.setDataList(shows); containerBean2.setItemType(GlobalAdapter.SHOW); baseBeanList.add(containerBean2); return baseBeanList; }}
以上就是将页面模块的代码实现,整体还是比较简单的,通过这些设置,方案一中唯一的C将会变成5个C,顶级C是MainActivity,它和adapter共同来实现布局的调度和分发;子布局分别对应一个小C-holder,每个holder可实现其自己的逻辑。增加布局也很容易,只需增加一个新type数据,增加一个holder即可;另外,布局复用也很方便,比如横向List和纵向List交替复用,相当于数据中出现了多个ContainnerBean,仍在List和RecyclerView的适配中。基本不用动什么代码。
写在本篇最后,本文主要目的是,通过一步步分析demo示例,揭示MVC中的C如何变得更小和清晰。demo粗糙,但想法不粗,希望能给读者带来启发。在接下来的一篇中,将主要针对M层的优化思想来场实战,以使得笔者的观点更容易理解。
篇幅较长,码字不易。
欢迎交流,共同进步!
- 安卓中MVC模式的深度思索和实践(二)
- 安卓中MVC模式的深度思索和实践(一)
- 安卓中MVC模式的深度思索和实践(三)
- Sencha的MVC模式实践
- Open API分析、实践和思索
- Open API分析、实践和思索(后半篇)
- [技术]Open API分析、实践和思索
- Open API分析、实践和思索
- Open API分析、实践和思索
- 对云计算时代软件技术发展的若干思考和实践和软件工程技术思索 读后感
- mvc原理和mvc模式的优缺点
- MVC原理和MVC模式的优缺点
- mvc原理和mvc模式的优缺点
- mvc原理和mvc模式的优缺点
- mvc原理和mvc模式的优缺点
- MVC原理和MVC模式的优缺点
- thinkPHP中MVC模式的分析(二)
- 设计模式一点思索
- JavaScript-提问(prompt 消息对话框)
- 小蚂蚁~STM32命名规则
- Android业务组件化开发实践
- mac 终端命令
- "浪潮杯”山东省第八届ACM大学生程序设计竞赛 CF
- 安卓中MVC模式的深度思索和实践(二)
- tpshop5,php干净的代码是改出来的(二)
- 图片测试
- 自定义View Measure过程
- Comparable和Comparator区别
- 如何开启Dubbo框架内部的日志?
- QT容器类(一) 之 QVector 、QLinkedList 和 QList
- sqlserver 存储过程 使用guid的相关记录
- 重写View的onMeasure方法