安卓中MVC模式的深度思索和实践(二)

本文是安卓MVC模式系列的第二篇,探讨如何优化Controller,通过一个完整示例分析页面模块化的实现。文章提出了三种方案,并推荐使用RecyclerView结合ViewHolder实现布局的灵活管理和数据分发,降低Controller的复杂性。通过组合模式,数据结构更加清晰,易于扩展。
摘要由CSDN通过智能技术生成

这是一个有关安卓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层的优化思想来场实战,以使得笔者的观点更容易理解。

篇幅较长,码字不易。

欢迎交流,共同进步!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值