diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt new file mode 100644 index 0000000..93177c2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt @@ -0,0 +1,31 @@ +package com.casic.br.ktd.utils + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object LoadingDialogHub { + + private lateinit var loadingDialog: QMUITipDialog + + fun show(activity: Activity, message: String) { + loadingDialog = QMUITipDialog + .Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismiss() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt new file mode 100644 index 0000000..93177c2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt @@ -0,0 +1,31 @@ +package com.casic.br.ktd.utils + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object LoadingDialogHub { + + private lateinit var loadingDialog: QMUITipDialog + + fun show(activity: Activity, message: String) { + loadingDialog = QMUITipDialog + .Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismiss() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 860de82..ce02c7b 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -23,4 +23,12 @@ * ============================================================================================= * */ const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val SERVER_BASE_URL = "http://111.198.10.15:21609" } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt new file mode 100644 index 0000000..93177c2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt @@ -0,0 +1,31 @@ +package com.casic.br.ktd.utils + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object LoadingDialogHub { + + private lateinit var loadingDialog: QMUITipDialog + + fun show(activity: Activity, message: String) { + loadingDialog = QMUITipDialog + .Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismiss() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 860de82..ce02c7b 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -23,4 +23,12 @@ * ============================================================================================= * */ const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val SERVER_BASE_URL = "http://111.198.10.15:21609" } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt new file mode 100644 index 0000000..92286ad --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt @@ -0,0 +1,15 @@ +package com.casic.br.ktd.utils + +import java.io.File + +interface OnImageCompressListener { + /** + * Fired when a compression returns successfully, override to handle in your own code + */ + fun onSuccess(file: File) + + /** + * Fired when a compression fails to complete, override to handle in your own code + */ + fun onError(e: Throwable) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt new file mode 100644 index 0000000..93177c2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt @@ -0,0 +1,31 @@ +package com.casic.br.ktd.utils + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object LoadingDialogHub { + + private lateinit var loadingDialog: QMUITipDialog + + fun show(activity: Activity, message: String) { + loadingDialog = QMUITipDialog + .Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismiss() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 860de82..ce02c7b 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -23,4 +23,12 @@ * ============================================================================================= * */ const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val SERVER_BASE_URL = "http://111.198.10.15:21609" } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt new file mode 100644 index 0000000..92286ad --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt @@ -0,0 +1,15 @@ +package com.casic.br.ktd.utils + +import java.io.File + +interface OnImageCompressListener { + /** + * Fired when a compression returns successfully, override to handle in your own code + */ + fun onSuccess(file: File) + + /** + * Fired when a compression fails to complete, override to handle in your own code + */ + fun onError(e: Throwable) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt b/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt new file mode 100644 index 0000000..0576f31 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_MODE, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt new file mode 100644 index 0000000..93177c2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt @@ -0,0 +1,31 @@ +package com.casic.br.ktd.utils + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object LoadingDialogHub { + + private lateinit var loadingDialog: QMUITipDialog + + fun show(activity: Activity, message: String) { + loadingDialog = QMUITipDialog + .Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismiss() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 860de82..ce02c7b 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -23,4 +23,12 @@ * ============================================================================================= * */ const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val SERVER_BASE_URL = "http://111.198.10.15:21609" } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt new file mode 100644 index 0000000..92286ad --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt @@ -0,0 +1,15 @@ +package com.casic.br.ktd.utils + +import java.io.File + +interface OnImageCompressListener { + /** + * Fired when a compression returns successfully, override to handle in your own code + */ + fun onSuccess(file: File) + + /** + * Fired when a compression fails to complete, override to handle in your own code + */ + fun onError(e: Throwable) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt b/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt new file mode 100644 index 0000000..0576f31 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_MODE, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/vm/TaskViewModel.kt b/app/src/main/java/com/casic/br/ktd/vm/TaskViewModel.kt new file mode 100644 index 0000000..1842e89 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/vm/TaskViewModel.kt @@ -0,0 +1,41 @@ +package com.casic.br.ktd.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.extensions.separateResponseCode +import com.casic.br.ktd.extensions.toErrorMessage +import com.casic.br.ktd.model.TaskListModel +import com.casic.br.ktd.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class TaskViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val taskList = MutableLiveData() + + fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.getTaskList( + startTime, endTime, taskName, taskCode, offset + ) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + taskList.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + }) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f07600..0a3659a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,4 +99,7 @@ implementation 'com.amap.api:navi-3dmap:latest.integration' //日期范围选择 implementation project(path: ':cosmocalendar') + //腾讯Android UI框架 + implementation 'com.qmuiteam:qmui:2.0.0-alpha10' + implementation 'com.qmuiteam:arch:0.3.1' } \ No newline at end of file diff --git a/app/src/main/assets/TestData.json b/app/src/main/assets/TestData.json new file mode 100644 index 0000000..83b6c14 --- /dev/null +++ b/app/src/main/assets/TestData.json @@ -0,0 +1,59 @@ +{ + "code": 200, + "data": { + "rows": [ + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + }, + { + "taskCode": "202304120001", + "taskName": "永定路日常巡检任务", + "carNumber": "京E 72314", + "deviceCode": "czyt20230001", + "startTime": "2023-07-05 09:40:10", + "endTime": "2023-07-05 11:40:10", + "attr": "10", + "taskState": "已完成" + } + ], + "total": 5 + }, + "message": "请求成功" +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt new file mode 100644 index 0000000..a974190 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/TaskAdapter.kt @@ -0,0 +1,97 @@ +package com.casic.br.ktd.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.CheckBox +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.holder.SwipeViewHolder +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.convertDrawable +import com.pengxh.kt.lite.extensions.dp2px +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction + +abstract class TaskAdapter( + ctx: Context, @LayoutRes private val xmlResource: Int, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private val kTag = "TaskAdapter" + private var inflater: LayoutInflater = LayoutInflater.from(ctx) + private var multipleSelected = mutableSetOf() + private var selectedItems = ArrayList() + + private var deleteAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.delete_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("删除") + .backgroundColor(Color.RED) + .build() + + private var handleAction = QMUISwipeAction.ActionBuilder() + .icon(R.mipmap.handle_task.convertDrawable(ctx)) + .textSize(16f.dp2px(ctx)) + .textColor(Color.WHITE) + .paddingStartEnd(16f.dp2px(ctx)).text("处置") + .backgroundColor(R.color.themeColor.convertColor(ctx)) + .build() + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwipeViewHolder { + val view = inflater.inflate(xmlResource, parent, false) + val swipeViewHolder = SwipeViewHolder(view) + swipeViewHolder.addSwipeAction(deleteAction) + swipeViewHolder.addSwipeAction(handleAction) + return swipeViewHolder + } + + @SuppressLint("NotifyDataSetChanged") + fun setRefreshData(dataRows: MutableList) { + this.dataRows.clear() + this.dataRows.addAll(dataRows) + notifyDataSetChanged() + } + + fun setLoadMoreData(dataRows: MutableList) { + this.dataRows.addAll(dataRows) + notifyItemRangeInserted(this.dataRows.size, dataRows.size) + } + + override fun onBindViewHolder(holder: SwipeViewHolder, position: Int) { + val multiSelectCheckBox: CheckBox = holder.itemView.findViewById(R.id.multiSelectCheckBox) + + multiSelectCheckBox.isSelected = multipleSelected.contains(position) + multiSelectCheckBox.setOnClickListener { + if (multipleSelected.contains(position)) { + multipleSelected.remove(position) + selectedItems.remove(dataRows[position]) + holder.itemView.isSelected = false + } else { + multipleSelected.add(position) + selectedItems.add(dataRows[position]) + holder.itemView.isSelected = true + } + + itemCheckedListener?.onItemChecked(selectedItems) + } + + convertView(holder, position, dataRows[position]) + } + + abstract fun convertView(viewHolder: SwipeViewHolder, position: Int, item: T) + + private var itemCheckedListener: OnItemCheckedListener? = null + + interface OnItemCheckedListener { + fun onItemChecked(items: ArrayList) + } + + fun setOnItemCheckedListener(listener: OnItemCheckedListener?) { + itemCheckedListener = listener + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt new file mode 100644 index 0000000..21f7960 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.extensions + +import android.content.Context +import com.casic.br.ktd.model.ErrorMessageModel +import com.casic.br.ktd.utils.OnImageCompressListener +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.createCompressImageDir +import org.json.JSONObject +import top.zibin.luban.Luban +import top.zibin.luban.OnCompressListener +import java.io.File +import java.util.* + + +/** + * String扩展方法 + */ +fun String.separateResponseCode(): Int { + if (this.isBlank()) { + return 404 + } + val responseCode = JSONObject(this).getInt("code") + if (responseCode == 401) { + + } + return responseCode +} + +fun String.toErrorMessage(): String { + val errorModel = Gson().fromJson( + this, object : TypeToken() {}.type + ) + return errorModel.message.toString() +} + +fun String.compressImage(context: Context, listener: OnImageCompressListener) { + Luban.with(context) + .load(this) + .ignoreBy(100) + .setTargetDir(context.createCompressImageDir().toString()) + .filter { + !(it.isBlank() || it.lowercase(Locale.getDefault()).endsWith(".gif")) + } + .setCompressListener(object : OnCompressListener { + override fun onStart() { + + } + + override fun onSuccess(file: File) { + listener.onSuccess(file) + } + + override fun onError(e: Throwable) { + listener.onError(e) + } + }).launch() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt index f176059..02dec40 100644 --- a/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt +++ b/app/src/main/java/com/casic/br/ktd/fragment/TaskPageFragment.kt @@ -1,36 +1,193 @@ package com.casic.br.ktd.fragment import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import com.casic.br.ktd.R +import com.casic.br.ktd.adapter.TaskAdapter +import com.casic.br.ktd.holder.SwipeViewHolder +import com.casic.br.ktd.model.TaskListModel import com.casic.br.ktd.view.InspectionActivity +import com.casic.br.ktd.vm.TaskViewModel +import com.casic.br.ktd.widgets.AlertControlDialog import com.casic.br.ktd.widgets.AlertInputDialog import com.casic.br.ktd.widgets.DateRangeActionSheet import com.pengxh.kt.lite.base.KotlinBaseFragment import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.qmuiteam.qmui.recyclerView.QMUIRVItemSwipeAction +import com.qmuiteam.qmui.recyclerView.QMUISwipeAction import kotlinx.android.synthetic.main.fragment_task.view.* -class TaskPageFragment : KotlinBaseFragment() { - override fun initData(savedInstanceState: Bundle?) { +class TaskPageFragment : KotlinBaseFragment(), Handler.Callback { + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var taskViewModel: TaskViewModel + private lateinit var taskAdapter: TaskAdapter + private var dataBeans: MutableList = ArrayList() + private var selectedItems: MutableList = ArrayList() + private var pageIndex = 1 + private var isRefresh = false + private var isLoadMore = false + private var startTime = "" + private var endTime = "" + + override fun initData(savedInstanceState: Bundle?) { + weakReferenceHandler = WeakReferenceHandler(this) + taskViewModel = ViewModelProvider(this)[TaskViewModel::class.java] + taskViewModel.taskList.observe(this) { + if (it.code == 200) { + val dataRows = it.data?.rows!! + when { + isRefresh -> { + taskAdapter.setRefreshData(dataRows) + baseView.taskLayout.finishRefresh() + isRefresh = false + } + isLoadMore -> { + if (dataRows.size == 0) { + "到底了,别拉了".show(requireContext()) + } + taskAdapter.setLoadMoreData(dataRows) + baseView.taskLayout.finishLoadMore() + isLoadMore = false + } + else -> { + dataBeans = dataRows + weakReferenceHandler.sendEmptyMessage(2023070501) + } + } + } + } } + override fun onResume() { + super.onResume() + //fragment切换时候重置侧滑按钮 + swipeAction.clear() + getTaskList() + } + + override fun handleMessage(msg: Message): Boolean { + when (msg.what) { + 2023070501 -> { + if (dataBeans.size == 0) { + baseView.emptyLayout.visibility = View.VISIBLE + } else { + baseView.emptyLayout.visibility = View.GONE + taskAdapter = object : TaskAdapter( + requireContext(), R.layout.item_task_rv_l, dataBeans + ) { + override fun convertView( + viewHolder: SwipeViewHolder, position: Int, + item: TaskListModel.DataModel.RowsModel + ) { + viewHolder.setText(R.id.taskCodeView, item.taskCode) + .setText(R.id.taskNameView, item.taskName) + .setText(R.id.carNumberView, item.carNumber) + .setText(R.id.deviceCodeView, item.deviceCode) + .setText(R.id.startTimeView, item.startTime) + .setText(R.id.endTimeView, item.endTime) + .setText(R.id.attrView, item.attr) + .setText(R.id.taskStateView, item.taskState) + } + } + //绑定侧滑事件 + swipeAction.attachToRecyclerView(baseView.taskRecyclerView) + baseView.taskRecyclerView.adapter = taskAdapter + taskAdapter.setOnItemCheckedListener(object : + TaskAdapter.OnItemCheckedListener { + override fun onItemChecked(items: ArrayList) { + selectedItems = items + } + }) + } + } + } + return true + } + + private val swipeAction = + QMUIRVItemSwipeAction(false, object : QMUIRVItemSwipeAction.Callback() { + override fun getSwipeDirection( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + return QMUIRVItemSwipeAction.SWIPE_LEFT + } + + override fun onClickAction( + swipeAction: QMUIRVItemSwipeAction?, + selected: RecyclerView.ViewHolder?, + action: QMUISwipeAction? + ) { + super.onClickAction(swipeAction, selected, action) + if (action?.text == "删除") { + deleteItem(selected!!.bindingAdapterPosition) + } else { + "处理".show(requireContext()) + } + } + }) + + private fun deleteItem(adapterPosition: Int) { + AlertControlDialog.Builder() + .setContext(requireContext()) + .setTitle("提示") + .setMessage("删除后将无法恢复,是否继续?") + .setNegativeButton("容我想想") + .setPositiveButton("已经想好") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + + } + }).build().show() + } + + override fun initEvent() { baseView.calendarView.setOnClickListener { DateRangeActionSheet.Builder().setContext(requireContext()) .setOnActionSheetListener(object : DateRangeActionSheet.OnDateRangeSelectedListener { override fun onDateRangeSelected(startDate: String, endDate: String) { + startTime = startDate + endTime = endDate + + //显示 baseView.selectedDateView.text = "$startDate ~ $endDate" } }).build().show() } baseView.addTaskButton.setOnClickListener { - requireContext().navigatePageTo() + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新建巡检任务") + .setHintMessage("请输入巡检任务名称") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertInputDialog.OnDialogButtonClickListener { + override fun onConfirmClick(value: String) { + requireContext().navigatePageTo(value) + } + + override fun onCancelClick() {} + }).build().show() } baseView.deleteTaskButton.setOnClickListener { - + selectedItems.size.toString().show(requireContext()) } baseView.taskSettingsButton.setOnClickListener { @@ -49,6 +206,29 @@ override fun onCancelClick() {} }).build().show() } + + baseView.taskLayout.setOnRefreshListener { + isRefresh = true + //刷新之后页码重置 + pageIndex = 1 + getTaskList() + } + + baseView.taskLayout.setOnLoadMoreListener { + isLoadMore = true + pageIndex++ + getTaskList() + } + } + + private fun getTaskList() { + taskViewModel.getTaskList( + startTime, + endTime, + baseView.taskNameView.text.toString(), + baseView.taskCodeView.text.toString(), + pageIndex + ) } override fun initLayoutView(): Int = R.layout.fragment_task diff --git a/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt new file mode 100644 index 0000000..a6b3daa --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/holder/SwipeViewHolder.kt @@ -0,0 +1,50 @@ +package com.casic.br.ktd.holder + +import android.util.SparseArray +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.annotation.IdRes +import com.qmuiteam.qmui.recyclerView.QMUISwipeViewHolder + +class SwipeViewHolder(itemView: View) : QMUISwipeViewHolder(itemView) { + private val convertView: View + private val views: SparseArray = SparseArray() + + init { + convertView = itemView + } + + /** + * 根据资源获取View对象 + * + * @param res 控件ID + * @param 类型 + * @return 控件 + */ + private fun getView(@IdRes res: Int): T { + var view = views[res] + if (view == null) { + view = convertView.findViewById(res) + views.put(res, view) + } + return view as T + } + + /** + * 提供TextView和Button设置文本简化操作 + * + * @param idRes 控件ID + * @param charSequence 字符串 + * @return holder + */ + fun setText(@IdRes idRes: Int, charSequence: CharSequence?): SwipeViewHolder { + val view = getView(idRes) + if (view is TextView) { + view.text = charSequence + } else if (view is Button) { + view.text = charSequence + } + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java new file mode 100644 index 0000000..dbf629b --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ErrorMessageModel.java @@ -0,0 +1,49 @@ +package com.casic.br.ktd.model; + +public class ErrorMessageModel { + private int code; + private String data; + private String exceptionClazz; + private String message; + private boolean isSuccess; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getExceptionClazz() { + return exceptionClazz; + } + + public void setExceptionClazz(String exceptionClazz) { + this.exceptionClazz = exceptionClazz; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean success) { + isSuccess = success; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java new file mode 100644 index 0000000..3110a26 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/TaskListModel.java @@ -0,0 +1,129 @@ +package com.casic.br.ktd.model; + +import java.util.List; + +public class TaskListModel { + private int code; + private DataModel data; + private String message; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public DataModel getData() { + return data; + } + + public void setData(DataModel data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public static class DataModel { + private List rows; + private int total; + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public static class RowsModel { + private String taskCode; + private String taskName; + private String carNumber; + private String deviceCode; + private String startTime; + private String endTime; + private String attr; + private String taskState; + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getCarNumber() { + return carNumber; + } + + public void setCarNumber(String carNumber) { + this.carNumber = carNumber; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getAttr() { + return attr; + } + + public void setAttr(String attr) { + this.attr = attr; + } + + public String getTaskState() { + return taskState; + } + + public void setTaskState(String taskState) { + this.taskState = taskState; + } + } + } +} diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt new file mode 100644 index 0000000..7460ea7 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitService.kt @@ -0,0 +1,4 @@ +package com.casic.br.ktd.retrofit + +interface RetrofitService { +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..16a22b9 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,25 @@ +package com.casic.br.ktd.retrofit + +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.readAssetsFile +import com.pengxh.kt.lite.utils.RetrofitFactory +import com.pengxh.kt.lite.utils.SaveKeyValues + +object RetrofitServiceManager { + private val api by lazy { + val httpConfig = SaveKeyValues.getValue( + LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL + ) as String + RetrofitFactory.createRetrofit(httpConfig) + } + + /** + * 获取任务列表 + */ + suspend fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ): String { + return BaseApplication.get().readAssetsFile("TestData.json") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt new file mode 100644 index 0000000..d2a0a11 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/AuthenticationHelper.kt @@ -0,0 +1,24 @@ +package com.casic.br.ktd.utils + +import com.pengxh.kt.lite.utils.SaveKeyValues + +object AuthenticationHelper { + + fun savePublicKey(key: String) { + SaveKeyValues.putValue("keyString", key) + } + + val publicKey: String + get() = SaveKeyValues.getValue("keyString", "") as String + + fun saveToken(token: String) { + SaveKeyValues.putValue("token", token) + } + + val token: String + get() = SaveKeyValues.getValue("token", "") as String + + fun removeToken() { + SaveKeyValues.removeKey("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..9e017e2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.br.ktd.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.br.ktd.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val get: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.mipmap.load_image_error) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.mipmap.load_image_error)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt new file mode 100644 index 0000000..93177c2 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/LoadingDialogHub.kt @@ -0,0 +1,31 @@ +package com.casic.br.ktd.utils + +import android.app.Activity +import android.view.WindowManager +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog + +object LoadingDialogHub { + + private lateinit var loadingDialog: QMUITipDialog + + fun show(activity: Activity, message: String) { + loadingDialog = QMUITipDialog + .Builder(activity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + if (!activity.isDestroyed) { + try { + loadingDialog.show() + } catch (e: WindowManager.BadTokenException) { + e.printStackTrace() + } + } + } + + fun dismiss() { + if (loadingDialog.isShowing) { + loadingDialog.dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 860de82..ce02c7b 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -23,4 +23,12 @@ * ============================================================================================= * */ const val PERMISSIONS_CODE = 999 + + /** + * ============================================================================================= + * String + * ============================================================================================= + * */ + const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val SERVER_BASE_URL = "http://111.198.10.15:21609" } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt new file mode 100644 index 0000000..92286ad --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/OnImageCompressListener.kt @@ -0,0 +1,15 @@ +package com.casic.br.ktd.utils + +import java.io.File + +interface OnImageCompressListener { + /** + * Fired when a compression returns successfully, override to handle in your own code + */ + fun onSuccess(file: File) + + /** + * Fired when a compression fails to complete, override to handle in your own code + */ + fun onError(e: Throwable) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt b/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt new file mode 100644 index 0000000..0576f31 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/utils/RSAUtils.kt @@ -0,0 +1,58 @@ +package com.casic.br.ktd.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(Cipher.ENCRYPT_MODE, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/vm/TaskViewModel.kt b/app/src/main/java/com/casic/br/ktd/vm/TaskViewModel.kt new file mode 100644 index 0000000..1842e89 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/vm/TaskViewModel.kt @@ -0,0 +1,41 @@ +package com.casic.br.ktd.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.ktd.base.BaseApplication +import com.casic.br.ktd.extensions.separateResponseCode +import com.casic.br.ktd.extensions.toErrorMessage +import com.casic.br.ktd.model.TaskListModel +import com.casic.br.ktd.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class TaskViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val taskList = MutableLiveData() + + fun getTaskList( + startTime: String, endTime: String, taskName: String, taskCode: String, offset: Int + ) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.getTaskList( + startTime, endTime, taskName, taskCode, offset + ) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + taskList.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/AlertControlDialog.kt b/app/src/main/java/com/casic/br/ktd/widgets/AlertControlDialog.kt new file mode 100644 index 0000000..0a6773d --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/AlertControlDialog.kt @@ -0,0 +1,104 @@ +package com.casic.br.ktd.widgets + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.widget.Button +import android.widget.TextView +import com.casic.br.ktd.R +import com.pengxh.kt.lite.extensions.initDialogLayoutParams + +class AlertControlDialog private constructor(builder: Builder) : Dialog( + builder.context, R.style.UserDefinedDialogStyle +) { + private val title = builder.title + private val message = builder.message + private val positiveBtn = builder.positiveBtn + private val negativeBtn = builder.negativeBtn + private val listener = builder.listener + + class Builder { + lateinit var context: Context + lateinit var title: String + lateinit var message: String + lateinit var positiveBtn: String + lateinit var negativeBtn: String + lateinit var listener: OnDialogButtonClickListener + + fun setContext(context: Context): Builder { + this.context = context + return this + } + + fun setTitle(title: String): Builder { + this.title = title + return this + } + + fun setMessage(message: String): Builder { + this.message = message + return this + } + + fun setPositiveButton(name: String): Builder { + positiveBtn = name + return this + } + + fun setNegativeButton(name: String): Builder { + negativeBtn = name + return this + } + + fun setOnDialogButtonClickListener(listener: OnDialogButtonClickListener): Builder { + this.listener = listener + return this + } + + fun build(): AlertControlDialog { + return AlertControlDialog(this) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.initDialogLayoutParams(0.3f) + setContentView(R.layout.dialog_alert) + setCancelable(false) + setCanceledOnTouchOutside(false) + initView() + } + + private fun initView() { + val dialogTitleView: TextView = findViewById(R.id.dialogTitleView) + val dialogMessageView: TextView = findViewById(R.id.dialogMessageView) + val dialogCancelButton = findViewById