前提
做程序就是要基类尽量不做重复的事情.
基于下列组件,别人可能不能用.
- EasyRecyclerView gradle依赖
- 自己定义的Activity基类
- kotlin代码编写
- 自定义的数据为空时,断网时,的提示view
- activity基类的Loading组件
- 自定义的 HeightWrapListView
- 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在其中的使用