zoukankan      html  css  js  c++  java
  • 老潘 ListView分析 学以致用篇(一)

    ListView分析
    学以致用篇(1)

    在我们查看别人的博客的时候,一个人是一个风格的.先说下我的风格,我喜欢思想类比,然后介绍知识,不太喜欢填鸭式的灌输.
    如果只是想单纯的从我的博客中直接看到代码,我个人建议直接到网上搜索其他的案例,我喜欢一步一步的分析,
    然后分析完一个过程,会有一个对应的例子这个样子

    (1) 什么是ListView

    ListView 首先就是一个View,View顾名思义,就是用来展示数据的,但是android中的view和普通的view有些区别,View不仅用来展示数据,还有对这块区域的处理,以后有时间再说这个

    ListView是用来展示列表数据的,可以认作是一列多行的表格

    表格的组成其实与人的身体组成类似

    人是由头部,身体,脚部组成

    表格呢,也是有对应的header,body和footer组成

    listView同理,也是由header,body和footer组成

    个人认为这就是为什么说计算机的思想是想通的

    (2) 怎么用呢?

    既然有ListView,并且他是用来展示数据的,那么该怎么用呢?
    你想想,android中的View是有两大派系的

    一大派系是直接继承View的,就是不能添加子元素的
    令一大派系是继承ViewGroup的,就是可以添加子元素的,至于原因呢?自己查资料

    既然ListView是可以展示列表的数据,那么肯定是继承自ViewGroup

    继承自ViewGroup,我们是不是也是直接addView的方式添加呢?

    能考虑到这里,我们已经入门ListView了

    (3)ListView用之前的考虑

    稍微深入一下:

    ListView的设计呢?其实是MVC在android中很好的体现

    MVC为何物

    在此借鉴一下他人的博客(http://www.tuicool.com/articles/myeYNjJ)

    1.什么是MVC

          MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑----摘自百度百科。

    M:model:指的是用来封装信息的对象。 
    V:view:用来显示model中封装的信息的组件。 
    C:controller:用来控制model中的信息怎么输出到view中的。

    在android中最典型的MVC就是listview的显示 
    M:model指你要显示的数据,如封装数据的cursor,array等等 
    V:view:就是listView用来显示封装好的数据 
    C:controller:就是adaptor,用来控制数据如何向listview中显示,如arrayadaptor,cursoradaptor等等

          MVC可以使程序耦合性降低,视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可.同时可以让代码复用性提高,由于已经将数据和业务规则从表示层分开,所以可以最大化的重用代码了。

    (4)MVC在ListView相关的体现

    既然都提到了MVC,并且ListView是典型代表,那么ListView相关的,何为视图,何为模型,何为控制器?带着这些疑问,我来分享一下吧

    刚才提到了,ListVIew是视图,因为他是View,控制器呢?android中命名为adapter(适配器,其实就是Controller,命名为Controller就更加直观了),模型呢?就是数据,自己随便创建个数组或者队列就是数据模型了(图示 : 参照最后的图片)


    (5)demo

    listView的入门demo已经烂大街了,就像北京的程序员一样,我就不献丑了


    学以致用篇(2)

    为什么说我这个叫学以致用,是因为我目前分析的都是我工作中遇到问题,然后自己总结一些,慢慢汇总出来的

    第二篇呢?想抛出几个问题
    (1)不是说在第一篇中说道ListView是存在header和footer的,那么该如何操作呢?
    (2)listView的position与adapter的position是否相同呢?
    (3)onItemClick的parent是否与listview为同一个view
    (4)在获取类型时,到底应该使用那个类型 
    (5)如果我有多个类型要展示,总不能总是用convertView和自己写类型来判断吧,adapter中是否有这样的机制呢?

    还是一个一个来吧,其实还有很多问题,后续有时间我再总结其他的吧

    ListView的header和footer呢?写程序,很简单,addHeadView()和addFooterView()

    但是呢?有哪些问题需要注意的呢?
    1. addHeadView和addFootView需要在setAdapter之前设置,为什么不能之后设置?先看源码(source version code - 2.3),最后分析
    在ListView的setAdapter的源码中可以发现,其实设置adapter的时候,会检查是否存在headviewInfos和footerViewInfos,google的命名非常值得借鉴
    就是headView的数量和footerView的数量
      /**
         * Sets the data behind this ListView.
         *
         * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
         * depending on the ListView features currently in use. For instance, adding
         * headers and/or footers will cause the adapter to be wrapped.
         *
         * @param adapter The ListAdapter which is responsible for maintaining the
         *        data backing this list and for producing a view to represent an
         *        item in that data set.
         *
         * @see #getAdapter() 
         */
        @Override
        public void setAdapter(ListAdapter adapter) {
    .....
            if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
            } else {
                mAdapter = adapter;
            }
           .......
        }


    至于设置的相关源码,查看ListView的addHeadView和addFooterView即可,看看如下的结构,在addHeadView的时候,首先检查是否mAdapter为null,如果不是的话,就会抛出异常,程序会挂掉的,但是,footerView的处理有区别

    /**
         * Add a fixed view to appear at the top of the list. If addHeaderView is
         * called more than once, the views will appear in the order they were
         * added. Views added using this call can take focus if they want.
         * <p>
         * NOTE: Call this before calling setAdapter. This is so ListView can wrap
         * the supplied cursor with one that will also account for header and footer
         * views.
         *
         * @param v The view to add.
         * @param data Data to associate with this view
         * @param isSelectable whether the item is selectable
         */
        public void addHeaderView(View v, Object data, boolean isSelectable) {

            if (mAdapter != null) {
                throw new IllegalStateException(
                        "Cannot add header view to list -- setAdapter has already been called.");
            }

            FixedViewInfo info = new FixedViewInfo();
            info.view = v;
            info.data = data;
            info.isSelectable = isSelectable;
            mHeaderViewInfos.add(info);
        }


    在addFooterView的时候,并没有检查adapter是否为null这一项


    /**
         * Add a fixed view to appear at the bottom of the list. If addFooterView is
         * called more than once, the views will appear in the order they were
         * added. Views added using this call can take focus if they want.
         * <p>
         * NOTE: Call this before calling setAdapter. This is so ListView can wrap
         * the supplied cursor with one that will also account for header and footer
         * views.
         *
         * @param v The view to add.
         * @param data Data to associate with this view
         * @param isSelectable true if the footer view can be selected
         */
        public void addFooterView(View v, Object data, boolean isSelectable) {
            FixedViewInfo info = new FixedViewInfo();
            info.view = v;
            info.data = data;
            info.isSelectable = isSelectable;
            mFooterViewInfos.add(info);

            // in the case of re-adding a footer view, or adding one later on,
            // we need to notify the observer
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }


    但是在删除的时候就没有这个限制(移除个人不常用,只是看源码分析)
        /**
         * Removes a previously-added header view.
         *
         * @param v The view to remove
         * @return true if the view was removed, false if the view was not a header
         *         view
         */
        public boolean removeHeaderView(View v) {
            if (mHeaderViewInfos.size() > 0) {
                boolean result = false;
                if (((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
                    mDataSetObserver.onChanged();
                    result = true;
                }
                removeFixedViewInfo(v, mHeaderViewInfos);
                return result;
            }
            return false;
        }


        /**
         * Removes a previously-added footer view.
         *
         * @param v The view to remove
         * @return
         * true if the view was removed, false if the view was not a footer view
         */
        public boolean removeFooterView(View v) {
            if (mFooterViewInfos.size() > 0) {
                boolean result = false;
                if (((HeaderViewListAdapter) mAdapter).removeFooter(v)) {
                    mDataSetObserver.onChanged();
                    result = true;
                }
                removeFixedViewInfo(v, mFooterViewInfos);
                return result;
            }
            return false;
        }


    说这么多理论的,来点实际的吧,一个比较实际的例子,在即时通讯软件中,例如微信,在有网络的时候,没有显示有网络,但是在没有网络的时候,会提示


    这个实现方式呢?比较多,先说说思路
    思路1: 可以采用一个把这条信息作为adapter的一个条目即可,
    但是这样总感觉不太好,这是个与数据没有很明显关系的,而且每次在刷新数据的时候,都需要去通知adapter数据改变
    思路2: 采用我们刚刚提到的将网络的条目放置到header中,如果有动手操作过的,就应该遇到过这个问题,如果只是单单的将这个信息放置到头部中,然后在有网络的时候,gone掉header,发现条目是gone了,但是位置还存在,这个问题怎么办呢?
    这个应该是listview设计的问题吧?在stackoverflow中发现了解决的思路
    思路如下: 
    我们将需要改变的条目,外面再包裹一层,最外层采用包裹内容的方式,并且不设置背景等等其他属性
    我们只需要改变自己想要改变内容的大小即可
    (其实实现下拉刷新也可以直接采用这个方式)
    连接地址:http://stackoverflow.com/questions/19656782/listview-not-contracting-when-header-view-set-to-view-gone

    (2)listView的position和我们的adapter的position是否相同呢?
    从第一部分分析的listView的setAdapter也便可知,在存在headView或者footerView的时候,我们设置listView中的adapter并不是我们设置的adapter,而是被包装过的HeaderViewListAdapter,,我感觉如果明明为AdapterWrapper就更加形象了,但是google的命名呢?体现了这个adapter是和header有关的.

    从面向对象的角度讲,这其实是不同的东西,ListView中的position,我们就应该认为是ListView自身孩子的position,比如说有headView,headView也是我的孩子,而对于我们的adapter,只是和自己的adapter有关,因为在存在headview和footerView的时候,
    我们设置的listView的setOnItemClick其实是对listView的position进行的操作
    因而需要考虑headView和footerView的点击,并且要防止错位


    有时间继续总结吧


    安卓源码分析群: Android源码分析QQ1群号:164812238

  • 相关阅读:
    okhttp连接池:put,get方法&connection回收
    okhttp拦截器之ConnectInterceptor解析
    okhttp拦截器之CacheInterceptor解析
    okhttp缓存策略源码分析:put&get方法
    java线程基础巩固---ThreadGroup API学习
    通过JDBC驱动加载深刻理解线程上下文类加载器机制
    线程上下文类加载器实战分析与难点剖析
    ServiceLoader在SPI中的重要作用分析
    小试牛刀【龙哥翻译】
    小试牛刀【自己翻译】
  • 原文地址:https://www.cnblogs.com/pandapan/p/4381913.html
Copyright © 2011-2022 走看看