写列表界面必须的基础代码(增加开发效率)

308 阅读4分钟

前提

做程序就是要基类尽量不做重复的事情.

基于下列组件,别人可能不能用.

  1. EasyRecyclerView gradle依赖
  2. 自己定义的Activity基类
  3. kotlin代码编写
  4. 自定义的数据为空时,断网时,的提示view
  5. activity基类的Loading组件
  6. 自定义的 HeightWrapListView
  7. HeightWrapListViewUtils 工具类

目录结构

实际代码,(copy即可)

MTestAdapter.kt

import android.app.Activity
import android.view.ViewGroup
import cn.zhiup.mobile.course.taocan.mtest.bean.MTestBean
import com.jude.easyrecyclerview.adapter.BaseViewHolder
import com.jude.easyrecyclerview.adapter.RecyclerArrayAdapter

class MTestAdapter(activity:Activity) : RecyclerArrayAdapter<MTestBean>(activity) {
    override fun OnCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseViewHolder<*> {
        return MTestViewHolder(parent)
    }
}

MTestViewHolder.kt

import android.view.ViewGroup
import android.widget.TextView
import cn.zhiup.mobile.R
import cn.zhiup.mobile.course.record.discuss.HeightWrapListView
import cn.zhiup.mobile.course.record.discuss.HeightWrapListViewUtils
import cn.zhiup.mobile.course.taocan.mtest.bean.MTestBean
import com.blankj.utilcode.util.TimeUtils
import com.jude.easyrecyclerview.adapter.BaseViewHolder
import java.text.SimpleDateFormat
import java.util.*

class MTestViewHolder(view:ViewGroup?) :BaseViewHolder<MTestBean>(view, R.layout.item_mtest){
    var time: TextView
    var mTestList:HeightWrapListView
    init {
        time = `$`(R.id.mTestTime)
        mTestList=`$`(R.id.mTestList)
    }

    override fun setData(data: MTestBean?) {
        var timeStr = "时间暂无"
        if(data != null){
            timeStr = TimeUtils.millis2String(data.finished_ts,SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()))+"  交卷";
        }
        time.setText(timeStr)

        val adapter:MTestResaultAdapter
        adapter = MTestResaultAdapter(context, data!!.practice)
        mTestList.adapter = adapter
        mTestList.divider=null
        HeightWrapListViewUtils.setListViewHeightBasedOnChildren(mTestList)
    }
}

MTestBean.kt

package cn.zhiup.mobile.course.taocan.mtest.bean
data class Practice(var name:String,var score:String)
data class MTestBean(var finished_ts:Long,var practice:List<Practice>)

MTestPresenter.kt

import cn.zhiup.mobile.course.taocan.mtest.bean.MTestBean
import cn.zhiup.mobile.course.taocan.mtest.view.IMTestView
import cn.zhiup.mobile.retrofitOkhttpRx.retrofit.RetrofitClient
import com.zhiup.base.base.BasePresenter
import com.zhiup.base.base.BaseResult
import com.zhiup.base.net.CallBackKT
import com.zhiup.base.net.ServerErrorUtil
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

class MTestPresenter(view:IMTestView):BasePresenter<IMTestView>(view){
    fun getMachineTestList(){
        view.showLoading()
        RetrofitClient.getInstance().retrofitService.getMachine("")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object: CallBackKT<BaseResult<List<MTestBean>>>() {
                    override fun onSuccess(data: BaseResult<List<MTestBean>>) {
                        view.showLoading()
                        if (ServerErrorUtil.isSuccess(data,true)&&ServerErrorUtil.checkIsNull(data)){
                            if(data.data.size==0){
                                view.showDataNull()
                            }else{
                                view.setMTestList(data.data)
                            }
                        }
                    }

                    override fun onFail(e: Throwable) {
                        view.hideLoading()
                    }
                })

    }
}

IMTestView.kt

import cn.zhiup.mobile.course.taocan.mtest.bean.MTestBean
import com.zhiup.base.base.BaseActivityListView

interface IMTestView:BaseActivityListView{
    fun setMTestList(testList:List<MTestBean>)
}

MTestActivity.kt

import android.view.View
import cn.zhiup.mobile.R
import cn.zhiup.mobile.base.BaseTitleActivity
import cn.zhiup.mobile.course.taocan.mtest.adapter.MTestAdapter
import cn.zhiup.mobile.course.taocan.mtest.bean.MTestBean
import cn.zhiup.mobile.course.taocan.mtest.presenter.MTestPresenter
import cn.zhiup.mobile.course.taocan.mtest.view.IMTestView
import kotlinx.android.synthetic.main.activity_mtest.*
class MTestActivity: BaseTitleActivity(), IMTestView {
    var mPresenter:MTestPresenter?=null
    var mAdapter:MTestAdapter?=null
    override fun getLayoutId(): Int {
        return R.layout.activity_mtest
    }
    override fun initView() {

    }

    override fun setMTestList(mTestList:List<MTestBean>) {
        mAdapter?.addAll(mTestList)
    }

    override fun initData() {
        promptView.init(R.string.mtest_datanull_tips,R.drawable.icon_default_data_null,null)
        setTitle(R.string.mtest_title)
        setBackVisiable(View.VISIBLE)
        resaultList.setLayoutManager(LinearLayoutManager(this))
        val itemDecoration = DividerDecoration(resources.getColor(R.color.color_F8F8F8), SizeUtils.dp2px(15f), 0, 0)//颜色 & 高度 & 左边距 & 右边距
        itemDecoration.setDrawLastItem(false)//有时候你不想让最后一个item有分割线,默认true.
        itemDecoration.setDrawHeaderFooter(false)//是否对Header于Footer有效,默认false.
        resaultList.addItemDecoration(itemDecoration)
        mAdapter = MTestAdapter(this)
        resaultList.adapter=mAdapter

        mPresenter = MTestPresenter(this)
        mPresenter?.getMachineTestList()
    }

    override fun initEvent() {

    }

    override fun onMoreClick() {
    }

    override fun showLoading() {
        showLoading(this)
    }

    override fun showDataNull() {
    }

    override fun showNetError() {
    }

    override fun hiddenPrompt() {
    }

    override fun hideLoading() {
        hideLoading(this)
    }

}

activity_mtest.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="@color/color_F8F8F8"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include layout="@layout/title_layout"/>
    <com.zhiup.base.view.prompt.PromptView
        android:id="@+id/promptView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <com.jude.easyrecyclerview.EasyRecyclerView
        android:id="@+id/resaultList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

item_mtest.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:background="@color/white"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/x48"
        android:gravity="center_vertical"
        android:orientation="horizontal">
        <TextView
            android:text="考试记录"
            android:textSize="@dimen/text_size_9"
            android:textColor="@color/color_F51E55"
            android:background="@drawable/course_paly_selector"
            android:paddingLeft="@dimen/x5"
            android:paddingRight="@dimen/x5"
            android:paddingTop="@dimen/x2"
            android:paddingBottom="@dimen/x2"
            android:layout_marginLeft="@dimen/x40"
            android:layout_marginRight="@dimen/x10"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/mTestTime"
            tools:text="2018-08-08 18:02 交卷"
            android:textStyle="bold"
            android:layout_marginRight="@dimen/x21"
            android:textSize="@dimen/text_size_15"
            android:textColor="@color/color_2D2D2D"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#ffd8d8d8"
    />
    <cn.zhiup.mobile.course.record.discuss.HeightWrapListView
        android:id="@+id/mTestList"
        android:layout_width="match_parent"
        android:layout_height="80dp"/>
</LinearLayout>

列表中的列表的情况需要增加

列表中的列表的Adapter MTestResaultAdapter.kt

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.TextView
import cn.zhiup.mobile.R
import cn.zhiup.mobile.course.taocan.mtest.bean.Practice

class MTestResaultAdapter(context: Context,arr:List<Practice>) :BaseAdapter() {
    var resaultList:List<Practice>
    var context: Context
    var inflater:LayoutInflater
    init {
        this.resaultList = arr
        this.context = context
        inflater = LayoutInflater.from(context)
    }
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        var itemView = convertView
        var viewHolder:ResualtHolder
        if(itemView==null){
            itemView=inflater.inflate(R.layout.item_mtest_resualt,null,false)
            val name = itemView.findViewById<TextView>(R.id.name)
            val score = itemView.findViewById<TextView>(R.id.score)
            viewHolder = ResualtHolder(name,score)
            itemView.setTag(viewHolder)
        }else{
            viewHolder=itemView.tag as ResualtHolder
        }
        viewHolder.name.setText(resaultList[position].name)
        viewHolder.scroe.setText(resaultList[position].score)
        return itemView!!
    }

    override fun getItem(position: Int): Any {
        return resaultList.get(position)
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getCount(): Int {
        return resaultList.size
    }
    internal inner class ResualtHolder(var name:TextView,var scroe:TextView)//
}

上面Adapter的xml item_mtest_resualt

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:layout_width="match_parent"
    android:layout_height="@dimen/x40">
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/score"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

附:

HeightWrapListView.kt

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.ListView

/**
 * Created by zhou on 2017/12/21.
 */

class HeightWrapListView : ListView {

    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val expandSpec = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE shr 2,
                View.MeasureSpec.AT_MOST)
        super.onMeasure(widthMeasureSpec, expandSpec)
    }
}

HeightWrapListViewUtils.kt

import android.view.View
import android.view.ViewGroup
import android.widget.ListAdapter
import android.widget.ListView

/**
 * Created by zhou on 2017/12/21.
 */

object HeightWrapListViewUtils {
    fun setListViewHeightBasedOnChildren(listView: ListView) {
        // 获取ListView对应的Adapter
        val listAdapter = listView.adapter ?: return
        var totalHeight = 0

        for (i in 0 until listAdapter.count) { // listAdapter.getCount()返回数据项的数目
            val listItem = listAdapter.getView(i, null, listView)
            listItem.measure(0, 0) // 计算子项View 的宽高
            totalHeight += listItem.measuredHeight // 统计所有子项的总高度
        }
        val params = listView.layoutParams
        params.height = totalHeight + listView.dividerHeight * (listAdapter.count - 1)
        // listView.getDividerHeight()获取子项间分隔符占用的高度
        // params.height最后得到整个ListView完整显示需要的高度
        listView.layoutParams = params
    }
    
    /**
     * paddingNum = paddingTop+paddingBottom
     * space = verticalSpacing
     */
    fun setListViewHeightBasedOnChildren(listView: GridView,colNum:Int,paddingNum:Int,space:Int) {
        // 获取listview的adapter
        val listAdapter = listView.adapter ?: return
        // 固定列宽,有多少列
        var col=colNum;
        var rolNum:Int=0;
        var totalHeight = 0
        // i每次加4,相当于listAdapter.getCount()小于等于4时 循环一次,计算一次item的高度,
        // listAdapter.getCount()小于等于8时计算两次高度相加
        var i = 0
        while (i < listAdapter.count) {
            var maxHeight=0
            for (j in i until i+col){//算出最高的一个item的高度。
                // 获取listview的每一个item
                if (j<listAdapter.count){
                    val listItem = listAdapter.getView(j, null, listView)
                    listItem.measure(0, 0)
                    if(maxHeight<listItem.measuredHeight){
                        maxHeight=listItem.measuredHeight
                    }
                }
            }
            // 获取item的高度和
            totalHeight += maxHeight
            i += col
            rolNum++
        }

        // 获取listview的布局参数
        val params = listView.layoutParams
        // 设置高度
        params.height = totalHeight+((rolNum-1)*space)+paddingNum
        // 设置参数
        listView.layoutParams = params
    }
}

令GridView在其中的使用