耳朵(四)常见的上下拉列表

作者: a_mean | 来源:发表于2016-09-16 00:22 被阅读240次

    耳朵(四)常见的上下拉列表

    tags: 耳朵_android


    本节来实现一个常见的带下拉刷新, 上拉加载更多的列表界面.
    最后完成的效果图:


    首先分析一下我们的web端,安利一下ear.life, 有很多分类目录, 而每个目录下的样式都是一样的,


    所以可以想像我们的APP也只需要一个界面, 根据不同的分类id来加载不同的内容(这里可以脑补一下今日头条).

    首先在ui包下添加article包,并创建一个ArticleListFragment,



    继承自BaseListFragment, 这里的两个泛型T和H先暂时不管吧.

    接着需要分析接口交互了, 上一节已经介绍并测试成功了的, 这里公布下API地址吧:

    文章列表API: http://ear.life/?json=get_posts

    创建对应的Model类, 暂时只需要有几个演示的字段:

        class HttpPostListModel(var posts: ArrayList<PostModel>) : BaseModel() {
            class PostModel(var id: Int, var url: String, var title: String, var excerpt: String)
        }
    

    可以看到只取了posts字段, 而在posts集合中的对象也只取了id、h5地址、标题和简短的内容摘要,已经完全够用了。
    现在回到ArticleListFragment中声明一个ViewHolder继承自BaseViewHolder:

        inner class ArticleHolder(itemView: View) : BaseViewHolder<PostModel>(itemView) {
            override fun setContent(position: Int) {
                //一会来设置
            }
        }
    

    inner的作用相当于java中的内部类, 如果不用inner的话, 跟Context交互起来相对来说稍微复杂一些, 这里自行考虑.
    现在可以指定之前BaseListFragment的T和H两个泛型了, 并实现BaseListFragment中的抽象方法:

    class ArticleListFragment : BaseListFragment<PostModel, ArticleHolder>(){
        override fun getView(parent: ViewGroup?, position: Int): ArticleHolder {
        }
        //...
    }
    

    好了, 到现在为止, BaseListFragment这边差不多配置完了, 我们去建个简单的item的布局:

        <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                        android:layout_width="match_parent"
                        android:layout_height="130dp">
        
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">
        
                <TextView
                    android:id="@+id/tv_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="#565656"
                    android:textSize="16sp"/>
        
                <TextView
                    android:id="@+id/tv_excerpt"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/tv_title"
                    android:layout_marginTop="5dp"
                    android:ellipsize="marquee"
                    android:maxLines="3"
                    android:textColor="#717171"
                    android:textSize="12sp"/>
        
                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.5dp"
                    android:layout_alignParentBottom="true"
                    android:background="@color/subscribe_item_drag_stroke"/>
        
            </RelativeLayout>
        </RelativeLayout>
    

    样子可能有点丑, 那不重要. 回到Fragment, 为其设置item的资源ID, 同时给ViewHolder中的控件也用起来:

        class ArticleListFragment(override var itemResID: Int = R.layout.item_article) : BaseListFragment<PostModel, ArticleHolder>()
        ...
        inner class ArticleHolder(itemView: View) : BaseViewHolder<PostModel>(itemView) {
            override fun setContent(position: Int) {
                itemView.tv_title.text = data.title
                itemView.tv_excerpt.text = data.excerpt
    
                itemView.onClick { showToast(data.url) }
            }
        }
    

    ok, 现在是最后一步, 请求数据. BaseListFragment对此进行了很好的封装, 我们只需要实现其中的loadData方法即可, 下拉刷新及下拉加载更多,还有空数据友好提示等等的功能,完全不需要再操心了:

    override fun loadData() {
        val params = HashMap<String, Any>()
        params.put("json", "get_posts")
    
        HMRequest.go<HttpPostListModel>(params = params, needCallBack = true) {
            //仅需要调用这一个方法完成上下拉功能
            loadCompleted(it?.posts)
        }
    }
    

    运行前不要忘记去MainTabActivity把第一个BlankFragment换成刚刚的ArticleListFragment


    ok,效果已经出来了, 不过细心的同学一定会发现, 怎么每一页的内容都是一样的呢? 是的, 我们还没有添加类似count和page这样的参数呢.
    将loadData方法中val params = HashMap<String, Any>()改为BaseListFragment内置的val params = listParams, 这样如果你的服务器需要的参数正好是pagepageSize的话, 那不需要多余的代码, 现在完全可以正常显示了, 不过我们正在演示的这个项目, 需要的是pagecount这两个参数, 我们需要将默认的pageSize修改为当前项目的count:
    override fun setUIParams() {
        default_params_pageSize = "count"
    }
    

    要是不想重写setUIParams方法的话, 也可以直接在itemResID后面跟上也是可以的。 除了默认的参数名之外, 还有默认的count、index、拖拽排序、侧滑删除、滑动菜单等等,要是你不需要刷新或者加载更多功能也是一个属性的事儿,非常简单。另外,BaseListFragment用的列表控件是RecyclerView, 所以即使你想要把列表换成两行三行的, 也是可以直接row = 2 就能实现。这正是HMLibrary的目的,将繁琐的一成不变的东西装起来,换成简单的直观的暴露出来,让UI层的代码更清晰明了,更便于维护升级。
    最后奉上完整的代码:

    class ArticleListFragment(override var itemResID: Int = R.layout.item_article) : BaseListFragment<PostModel, ArticleHolder>() {
    
        override fun setUIParams() {
            default_params_pageSize = "count"
        }
    
        override fun loadData() {
            val params = listParams
            params.put("json", "get_posts")
    
            HMRequest.go<HttpPostListModel>(params = params, needCallBack = true) {
                //仅需要调用这一个方法完成上下拉功能
                loadCompleted(it?.posts)
            }
        }
    
        override fun getView(parent: ViewGroup?, position: Int): ArticleHolder = ArticleHolder(getItemView(parent))
    
        inner class ArticleHolder(itemView: View) : BaseViewHolder<PostModel>(itemView) {
            override fun setContent(position: Int) {
                itemView.tv_title.text = data.title
                itemView.tv_excerpt.text = data.excerpt
    
                itemView.onClick { showToast(data.url) }
            }
        }
    
        class HttpPostListModel(var posts: ArrayList<PostModel>) : BaseModel() {
            class PostModel(var id: Int, var url: String, var title: String, var excerpt: String)
        }
    
    }
    

    item界面还是非常丑的, 不知道有没有美工同志... 优化的事儿就当家庭作业吧.

    github: https://github.com/bxcx/ear
    本节分支: https://github.com/bxcx/ear/tree/list

    相关文章

      网友评论

        本文标题:耳朵(四)常见的上下拉列表

        本文链接:https://www.haomeiwen.com/subject/jfobettx.html