From 54ecee40c0303797b1c6bb99bfe3e3718cae0ad3 Mon Sep 17 00:00:00 2001
From: pan <1029559041@qq.com>
Date: Sun, 16 May 2021 00:17:10 +0800
Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89Gson=E5=BA=8F?=
=?UTF-8?q?=E5=88=97=E5=8C=96=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96=E9=A2=98?=
=?UTF-8?q?=E7=9B=AE=E5=AE=9E=E4=BD=93=E7=B1=BB=E9=80=BB=E8=BE=91=20SnackB?=
=?UTF-8?q?ar=E5=A2=9E=E5=8A=A0=E6=93=8D=E4=BD=9C=E5=B1=9E=E6=80=A7?=
=?UTF-8?q?=E5=92=8C=E5=9B=9E=E8=B0=83=E9=85=8D=E7=BD=AE=20=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E7=A4=BE=E5=9B=A2=E9=A2=98=E5=BA=93=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/build.gradle.kts | 6 +-
.../com/gyf/csams/ExampleInstrumentedTest.kt | 2 +
app/src/main/AndroidManifest.xml | 5 +
.../csams/association/model/ExamViewModel.kt | 227 +++++++++++++
.../association/ui/AssociationActivity.kt | 6 +-
.../gyf/csams/association/ui/ExamActivity.kt | 306 ++++++++++++++++++
.../csams/association/ui/ReNameActivity.kt | 2 +-
.../association/ui/RegAssociationActivity.kt | 2 +-
.../com/gyf/csams/main/ui/MainActivity.kt | 8 +-
.../main/java/com/gyf/csams/uikit/BaseView.kt | 229 ++++++++-----
.../java/com/gyf/csams/uikit/ViewModel.kt | 23 +-
.../main/java/com/gyf/csams/util/GsonUtil.kt | 111 +++++++
.../main/java/com/gyf/csams/util/HttpUtil.kt | 12 +-
app/src/main/res/drawable/ic_add_select.xml | 9 +
app/src/main/res/drawable/ic_sami_select.xml | 9 +
.../java/com/gyf/csams/ExampleUnitTest.kt | 11 +-
build.gradle.kts | 3 +-
17 files changed, 868 insertions(+), 103 deletions(-)
create mode 100644 app/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
create mode 100644 app/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
create mode 100644 app/src/main/java/com/gyf/csams/util/GsonUtil.kt
create mode 100644 app/src/main/res/drawable/ic_add_select.xml
create mode 100644 app/src/main/res/drawable/ic_sami_select.xml
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 6894e73..da5402c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -26,7 +26,7 @@ android {
val serverAddress=rootProject.extra["SERVER_ADDRESS"]
debug {
manifestPlaceholders.apply {
- this["APP_NAME"] = appName;
+ this["APP_NAME"] = appName
}
buildConfigField(type="String",name="APP_NAME",value = "\"$appName\"")
buildConfigField(type="String",name="SERVER_ADDRESS",value = "\"$serverAddress\"")
@@ -38,7 +38,7 @@ android {
"proguard-rules.pro"
)
manifestPlaceholders.apply {
- this["APP_NAME"] = appName;
+ this["APP_NAME"] = appName
}
buildConfigField(type="String",name="APP_NAME",value = "\"$appName\"")
buildConfigField(type="String",name="SERVER_ADDRESS",value = "\"$serverAddress\"")
@@ -122,6 +122,8 @@ dependencies {
* https://kotlinlang.org/docs/serialization.html
*/
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
+
+ implementation("org.jetbrains.kotlin:kotlin-reflect:${rootProject.extra["kotlin_version"]}")
/**
* https://developer.android.com/jetpack/androidx/releases/navigation
*/
diff --git a/app/src/androidTest/java/com/gyf/csams/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/gyf/csams/ExampleInstrumentedTest.kt
index 6833343..a2231e6 100644
--- a/app/src/androidTest/java/com/gyf/csams/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/gyf/csams/ExampleInstrumentedTest.kt
@@ -18,5 +18,7 @@ class ExampleInstrumentedTest {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.gyf.csams", appContext.packageName)
+
+
}
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6c4db7e..b7cb9b5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -57,6 +57,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt b/app/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
new file mode 100644
index 0000000..65c43b4
--- /dev/null
+++ b/app/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
@@ -0,0 +1,227 @@
+package com.gyf.csams.association.model
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.gyf.csams.uikit.ScrollList
+import com.gyf.csams.uikit.StringForm
+import kotlin.random.Random
+
+/**
+ * 题型
+ *
+ */
+enum class ExamType(val type:String){
+ //选择题
+ cq("选择题"),
+ //开放题
+ oq("开放题")
+}
+
+sealed class Exam{
+ abstract val examType:ExamType
+ abstract val question:StringForm
+}
+
+
+/**
+ * 开放题
+ *
+ * @property examType 题型描述
+ * @property question 问题
+ */
+data class OpenQuestionsVo(
+ override val examType: ExamType = ExamType.oq, override val question: StringForm
+
+) : Exam()
+
+/**
+ * 选择题
+ *
+ * @property examType 题型描述
+ * @property answers 答案
+ * @property rightAnswer 正确答案
+ * @property question 问题
+ */
+data class ChoiceQuestionVo(override val examType: ExamType = ExamType.cq,
+ val answers:List,
+ val rightAnswer:Int,
+ override val question: StringForm
+):Exam()
+
+
+
+
+/**
+ * 题库状态管理
+ *
+ */
+
+/**
+ * 问题长度
+ */
+const val QUESTION_TEXT_LENGTH=30
+
+/**
+ * 选择题选项数
+ */
+const val ANSWER_SIZE=4
+
+/**
+ * 答案长度
+ *
+ */
+const val ANSWER_TEXT_LENGTH=15
+
+
+class ExamViewModel:ScrollList() {
+ val questionIsNull: String = "问题不能为空"
+ val deleteLeastOne: String="至少保留一道题目"
+ val updateExam="更新题库"
+ val back="返回"
+ val menuName="入团题库"
+ val deleteTip="确定删除此题目?"
+ val addTip="确定添加此题目?"
+ val actionLabel="确定"
+
+
+ override val initSize = 10
+
+
+ private val _newExam:MutableLiveData = MutableLiveData(createExam(ExamType.cq))
+ val newExam:LiveData = _newExam
+
+ init {
+ load()
+ }
+
+ /**
+ * 切换题型
+ *
+ */
+ fun switchType(exam: Exam){
+ if(exam is ChoiceQuestionVo) _newExam.value=createExam(ExamType.oq) else _newExam.value=createExam(ExamType.cq)
+ }
+
+
+ /**
+ * 创建题目
+ *
+ * @param type
+ * @return
+ */
+ private fun createExam(type: ExamType): Exam {
+ val question=StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH)
+ return when(type){
+ ExamType.cq-> ChoiceQuestionVo(
+ answers = ('A'..'D').map { StringForm(formDesc = "选项",textLength = ANSWER_TEXT_LENGTH,value = "选项$it") },
+ rightAnswer = 0,
+ question = question
+ )
+ ExamType.oq-> OpenQuestionsVo(question = question)
+ }
+ }
+
+
+ /**
+ * 更新题目
+ *
+ */
+ fun update(oldExam: Exam,newExam:Exam){
+ if(oldExam==_newExam.value){
+ _newExam.value=newExam
+ }else{
+ _data.value?.apply {
+ this[indexOf(oldExam)]=newExam
+ val list = mutableListOf()
+ list.addAll(this)
+ _data.value?.clear()
+ _data.value=list
+ }
+ }
+ }
+
+ /**
+ * TODO 更新题库
+ *
+ * @param callback
+ */
+ fun updateExam(callback: (message: String) -> Unit){
+ callback("功能尚未实现,敬请期待")
+
+ }
+
+ /**
+ * 加载题目
+ *
+ */
+ override fun load() {
+ _data.value?.apply {
+ repeat(initSize) {
+ if (Random.nextBoolean()){
+ add(OpenQuestionsVo(question = StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH,value = "这是一道开放题:$size")))
+ } else{
+ add(
+ ChoiceQuestionVo(
+ question = StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH,value = "这是一道选择题:$size"),
+ answers = ('A'..'D').map { StringForm(formDesc = "选项",textLength = ANSWER_TEXT_LENGTH,value = "选项$it") },
+ rightAnswer = Random.nextInt(ANSWER_SIZE)
+ )
+ )
+ }
+
+ }
+ }
+ }
+
+ /**
+ *TODO 加载更多题目
+ *
+ * @param callback
+ */
+ override fun loadMore(callback: (message: String) -> Unit) {
+// _data.value?.apply {
+// val list= mutableListOf()
+// list.addAll(this)
+// list.apply {
+// repeat(10) {
+// if (Random.nextBoolean()) add(OpenQuestionsVo(question = "这是一道开放题:$size")) else add(
+// ChoiceQuestionVo(
+// question = "这是一道选择题:$size,请从选项中选出正确答案",
+// answers = listOf("选项A", "选项B", "选项C", "选项D"),
+// rightAnswer = 3
+// )
+// )
+// }
+// }
+// _data.postValue(list)
+// callback("成功加载更多题目")
+// }
+
+// callback("功能尚未实现,敬请期待")
+ }
+
+ fun addQuestion() {
+ _data.value?.apply {
+ _newExam.value?.let{
+ val list= mutableListOf()
+ list.addAll(this)
+ list.add(it)
+ _data.postValue(list)
+ }
+ _newExam.value=createExam(ExamType.cq)
+ }
+ }
+
+ /**
+ * TODO 删除题目
+ *
+ */
+ fun deleteQuestion(exam: Exam) {
+ _data.value?.apply {
+ val list = mutableListOf()
+ remove(exam)
+ list.addAll(this)
+ _data.postValue(list)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt b/app/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
index 5c3b9d3..6f825ad 100644
--- a/app/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
+++ b/app/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
@@ -108,7 +108,7 @@ private fun Search(modifier:Modifier=Modifier, model: MemberViewModel= viewModel
Spacer(modifier = Modifier.weight((spaceWeight)))
BaseTextField(modifier = Modifier.weight(textFieldWeight),form = model.name,singeLine = true)
Spacer(modifier = Modifier.weight(spaceWeight))
- OutlinedButton(onClick = { model.search { scaffoldModel.update(it) } },modifier = Modifier.weight(buttonWeight)) {
+ OutlinedButton(onClick = { model.search { scaffoldModel.update(message=it) } },modifier = Modifier.weight(buttonWeight)) {
Text(text = model.search)
}
Spacer(modifier = Modifier.weight(spaceWeight))
@@ -148,7 +148,7 @@ private fun MemberList(modifier: Modifier=Modifier, model: MemberViewModel=viewM
item {
Row(horizontalArrangement = Arrangement.Center,modifier = Modifier.fillMaxWidth()) {
IconButton(onClick = { model.loadMore{
- scaffoldModel.update(it)
+ scaffoldModel.update(message=it)
} }) {
Icon(painter = painterResource(id = R.drawable.ic_arrow_down), contentDescription = null)
}
@@ -302,7 +302,7 @@ private fun HistoryActivityList(modifier: Modifier,model:HistoryActViewModel= vi
}
}
if(listState.layoutInfo.totalItemsCount-listState.firstVisibleItemIndex==model.initSize/2-1){
- model.loadMore { scaffoldModel.update(it) }
+ model.loadMore { scaffoldModel.update(message=it) }
}
}
diff --git a/app/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt b/app/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
new file mode 100644
index 0000000..a16279d
--- /dev/null
+++ b/app/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
@@ -0,0 +1,306 @@
+package com.gyf.csams.association.ui
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.gyf.csams.R
+import com.gyf.csams.association.model.*
+import com.gyf.csams.uikit.*
+
+/**
+ * 题库管理
+ *
+ */
+class ExamActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Body { scaffoldState ->
+ MainFrame(background = { Background(image = BackgroundImage.exam) }) {
+ Spacer(modifier = Modifier.weight(0.1F))
+ Title(modifier = Modifier.weight(0.1F))
+ Exam(modifier = Modifier.weight(0.8F))
+ ShowSnackbar(scaffoldState = scaffoldState)
+ }
+ }
+ }
+ }
+}
+
+/**
+ * 底部按钮
+ *
+ */
+@Composable
+private fun BottomButton(
+ modifier: Modifier = Modifier,
+ model: ExamViewModel = viewModel(),
+ scaffoldModel: ScaffoldModel = viewModel()
+) {
+ val context = LocalContext.current as ExamActivity
+ Row(modifier = modifier, horizontalArrangement = Arrangement.Center) {
+ OutlinedButton(onClick = {
+ model.updateExam { scaffoldModel.update(message=it) }
+ }, modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
+ Text(text = model.updateExam)
+ }
+ Spacer(modifier = Modifier.width(10.dp))
+ OutlinedButton(onClick = {
+ context.onBackPressed()
+ }, modifier = Modifier.background(color = MaterialTheme.colors.secondary)) {
+ Text(text = model.back)
+ }
+ }
+}
+
+/**
+ * 标题
+ *
+ * @param modifier
+ * @param model
+ */
+@Composable
+private fun Title(modifier: Modifier = Modifier, model: ExamViewModel = viewModel()) {
+ Row(horizontalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth()) {
+ Text(text = model.menuName, style = MaterialTheme.typography.h4)
+ }
+}
+
+@Composable
+private fun ExamChild(it:Exam,examHeight: Dp,isAdd: Boolean=false){
+ val questionWeight=0.3F
+ when (it) {
+ is OpenQuestionsVo -> ExamOQ(
+ openQuestionsVo = it,
+ modifier = Modifier.height(examHeight*questionWeight),
+ isAdd = isAdd
+ )
+ is ChoiceQuestionVo -> ExamCQ(
+ choiceQuestionVo = it,
+ modifier = Modifier.height(examHeight),
+ questionWeight = questionWeight,
+ isAdd = isAdd
+ )
+ }
+}
+
+/**
+ * 题目列表
+ *
+ * @param modifier
+ * @param model
+ */
+@Composable
+private fun Exam(
+ modifier: Modifier = Modifier,
+ model: ExamViewModel = viewModel(),
+ scaffoldModel: ScaffoldModel = viewModel(),
+ examHeight: Dp = 350.dp
+) {
+ val listState = rememberLazyListState()
+ val data by model.data.observeAsState()
+ val newExam by model.newExam.observeAsState()
+ LazyColumn(state = listState, modifier = modifier) {
+ data?.forEach {
+ item {
+ ExamChild(it = it, examHeight = examHeight)
+ Spacer(modifier = Modifier.height(20.dp))
+ }
+ }
+ newExam?.let {
+ item {
+ Column {
+ OutlinedButton(onClick = { model.switchType(it) }) {
+ Text(text = "切换到${if (newExam is ChoiceQuestionVo) "开放题" else "选择题"}")
+ }
+ ExamChild(it = it, examHeight = examHeight,isAdd = true)
+ }
+ }
+ }
+
+ item {
+ Column {
+ Divider(color = MaterialTheme.colors.background)
+ Spacer(modifier = Modifier.height(30.dp))
+ BottomButton(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+
+ }
+ if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.initSize / 2 - 1) {
+ model.loadMore { scaffoldModel.update(message=it) }
+ }
+
+
+}
+
+/**
+ * 问题
+ *
+ * @param modifier
+ * @param exam
+ */
+@Composable
+private fun Question(modifier: Modifier = Modifier, exam: Exam) {
+ BaseTextField(
+ form = exam.question,
+ modifier = modifier
+ .fillMaxSize()
+ .background(color = MaterialTheme.colors.background)
+ )
+
+}
+
+/**
+ * 操作按钮
+ *
+ */
+@Composable
+private fun ActionButton(modifier: Modifier = Modifier, isAdd: Boolean,
+ model: ExamViewModel= viewModel(),
+ scaffoldModel: ScaffoldModel= viewModel(),
+ exam: Exam) {
+ val list by model.data.observeAsState()
+ val newExam by model.newExam.observeAsState()
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = modifier
+ ) {
+ IconButton(onClick = {
+ if(isAdd){
+ if((newExam?.question?.formValue?.value ?: "").isNotEmpty()){
+ scaffoldModel.update(message=model.addTip,actionLabel = model.actionLabel){
+ model.addQuestion()
+ }
+ }else{
+ scaffoldModel.update(message = model.questionIsNull)
+ }
+
+ }else{
+ if(list?.size==1){
+ scaffoldModel.update(model.deleteLeastOne)
+ }else{
+ scaffoldModel.update(message=model.deleteTip,actionLabel = model.actionLabel){
+ model.deleteQuestion(exam = exam)
+ }
+ }
+ }
+ }) {
+ Icon(
+ painter = painterResource(id = if (isAdd) R.drawable.ic_add_select else R.drawable.ic_sami_select),
+ contentDescription = null
+ )
+ }
+ }
+}
+
+
+/**
+ * 开放题
+ *
+ * @param openQuestionsVo
+ */
+@Composable
+private fun ExamOQ(
+ modifier: Modifier = Modifier,
+ openQuestionsVo: OpenQuestionsVo,
+ isAdd: Boolean = false
+) {
+ Row(modifier = modifier) {
+ Question(
+ exam = openQuestionsVo, modifier = Modifier
+ .weight(0.8F)
+ .fillMaxHeight()
+ )
+ ActionButton(
+ modifier = Modifier
+ .weight(0.2F)
+ .fillMaxHeight(),
+ isAdd = isAdd,
+ exam = openQuestionsVo
+ )
+ }
+}
+
+/**
+ * 选择题
+ *
+ * @param choiceQuestionVo
+ */
+@Composable
+private fun ExamCQ(
+ modifier: Modifier = Modifier,
+ choiceQuestionVo: ChoiceQuestionVo,
+ isAdd: Boolean = false,
+ model: ExamViewModel = viewModel(),
+ questionWeight:Float
+) {
+ Row(modifier = modifier) {
+ Column(
+ modifier = Modifier
+ .weight(0.8F)
+ .fillMaxHeight()
+ ) {
+ Question(
+ exam = choiceQuestionVo,
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(questionWeight)
+ )
+ Card(
+ backgroundColor = MaterialTheme.colors.secondaryVariant,
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1 - questionWeight)
+ ) {
+
+ choiceQuestionVo.answers.apply {
+ Column {
+ forEach {
+ Row(modifier = Modifier
+ .fillMaxWidth()
+ .weight(1F / ANSWER_SIZE)) {
+ val answerIndex: Int = indexOf(it)
+ val click = {
+ model.update(
+ oldExam = choiceQuestionVo,
+ newExam = choiceQuestionVo.copy(rightAnswer = answerIndex)
+ )
+ }
+ val isRightAnswer =
+ choiceQuestionVo.rightAnswer == answerIndex
+ RadioButton(selected = isRightAnswer, onClick = click)
+ BaseTextField(form = it)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ActionButton(
+ modifier = Modifier
+ .weight(0.2F)
+ .fillMaxHeight(),
+ isAdd = isAdd,
+ exam = choiceQuestionVo
+ )
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/association/ui/ReNameActivity.kt b/app/src/main/java/com/gyf/csams/association/ui/ReNameActivity.kt
index 5b858b4..b204ddd 100644
--- a/app/src/main/java/com/gyf/csams/association/ui/ReNameActivity.kt
+++ b/app/src/main/java/com/gyf/csams/association/ui/ReNameActivity.kt
@@ -101,7 +101,7 @@ private fun BottomButton(modifier: Modifier=Modifier,model:RenameViewModel= view
val context= LocalContext.current as ReNameActivity
Spacer(modifier = Modifier.weight(weight))
Row(modifier=Modifier.weight(0.5F)) {
- OutlinedButton(onClick = { model.post{scaffoldModel.update(it)} }) {
+ OutlinedButton(onClick = { model.post{scaffoldModel.update(message=it)} }) {
Text(text = model.postDesc)
}
Spacer(modifier = Modifier.width(10.dp))
diff --git a/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt b/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
index 9656279..cfd59f7 100644
--- a/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
+++ b/app/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
@@ -179,7 +179,7 @@ private fun BottomButton(modifier: Modifier=Modifier,scaffoldModel: ScaffoldMode
val context= LocalContext.current as RegAssociationActivity
Row(modifier = modifier,horizontalArrangement = Arrangement.Center) {
OutlinedButton(onClick = {
- model.register { scaffoldModel.update(it) }
+ model.register { scaffoldModel.update(message=it) }
},modifier = Modifier.background(color = MaterialTheme.colors.primary)) {
Text(text = model.register)
}
diff --git a/app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt b/app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
index 132e19a..5581691 100644
--- a/app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
+++ b/app/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
@@ -206,7 +206,7 @@ private fun AssociationListBody(model: ListViewModel = viewModel(),scaffoldModel
}
if(listState.layoutInfo.totalItemsCount-listState.firstVisibleItemIndex==model.associationListSize/2-1){
- model.loadMore { scaffoldModel.update(it) }
+ model.loadMore { scaffoldModel.update(message=it) }
}
}
@@ -243,7 +243,7 @@ private fun AssociationSearch(model: ListViewModel = viewModel(),scaffoldModel:
modifier = Modifier.height(10.dp)
)
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) {
- OutlinedButton(onClick = { model.search { scaffoldModel.update(it) } }, modifier = Modifier.width(100.dp)) {
+ OutlinedButton(onClick = { model.search { scaffoldModel.update(message=it) } }, modifier = Modifier.width(100.dp)) {
Text(text = model.searchDesc)
}
}
@@ -269,7 +269,7 @@ private fun Notification(mainViewModel: MainViewModel= viewModel(),scaffoldModel
.padding(10.dp)
) {
IconButton(onClick = {
- mainViewModel.openNotification { scaffoldModel.update(it) }
+ mainViewModel.openNotification { scaffoldModel.update(message=it) }
}) {
Icon(
painter = painterResource(id = R.drawable.ic_notification),
@@ -312,7 +312,7 @@ private fun MessageBoard(model: MarqueeViewModel = viewModel(),mainViewModel:Mai
) {
- IconButton(onClick = { mainViewModel.sendMessage { scaffoldModel.update(it) } }) {
+ IconButton(onClick = { mainViewModel.sendMessage { scaffoldModel.update(message=it) } }) {
Icon(
painter = painterResource(id = R.drawable.ic_comments),
contentDescription = null,
diff --git a/app/src/main/java/com/gyf/csams/uikit/BaseView.kt b/app/src/main/java/com/gyf/csams/uikit/BaseView.kt
index ce579d8..2482c64 100644
--- a/app/src/main/java/com/gyf/csams/uikit/BaseView.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/BaseView.kt
@@ -45,7 +45,7 @@ import kotlinx.coroutines.launch
* @param text
*/
@Composable
-fun AnimationText(text:String){
+fun AnimationText(text: String) {
val infiniteTransition = rememberInfiniteTransition()
val color by infiniteTransition.animateColor(
initialValue = MaterialTheme.colors.primary,
@@ -67,14 +67,18 @@ fun AnimationText(text:String){
* 主菜单
*
*/
-enum class MainMenu(@DrawableRes val selectedIcon:Int,
- @DrawableRes val unSelectedIcon:Int){
+enum class MainMenu(
+ @DrawableRes val selectedIcon: Int,
+ @DrawableRes val unSelectedIcon: Int
+) {
//主页
- Main(R.drawable.ic_home_fill,R.drawable.ic_home),
+ Main(R.drawable.ic_home_fill, R.drawable.ic_home),
+
//社团列表
- List(R.drawable.ic_all_fill,R.drawable.ic_all),
+ List(R.drawable.ic_all_fill, R.drawable.ic_all),
+
//个人中心
- Center(R.drawable.ic_account_fill,R.drawable.ic_account)
+ Center(R.drawable.ic_account_fill, R.drawable.ic_account)
}
/**
@@ -86,9 +90,10 @@ enum class MainMenu(@DrawableRes val selectedIcon:Int,
* @param onClick
*/
@Composable
-fun MenuIconButton(_menu: MainMenu,menu: MainMenu,modifier: Modifier,onClick: () -> Unit){
- Row(modifier = modifier
- ,horizontalArrangement = Arrangement.Center) {
+fun MenuIconButton(_menu: MainMenu, menu: MainMenu, modifier: Modifier, onClick: () -> Unit) {
+ Row(
+ modifier = modifier, horizontalArrangement = Arrangement.Center
+ ) {
IconButton(onClick = onClick) {
Icon(
painter = painterResource(id = if (_menu == menu) menu.selectedIcon else menu.unSelectedIcon),
@@ -106,19 +111,19 @@ fun MenuIconButton(_menu: MainMenu,menu: MainMenu,modifier: Modifier,onClick: ()
* @param modifier
*/
@Composable
-fun MainBottomAppBar(menu:MainMenu, nav: NavHostController, modifier: Modifier=Modifier){
- BottomAppBar(backgroundColor = MaterialTheme.colors.background,modifier=modifier) {
+fun MainBottomAppBar(menu: MainMenu, nav: NavHostController, modifier: Modifier = Modifier) {
+ BottomAppBar(backgroundColor = MaterialTheme.colors.background, modifier = modifier) {
//图标宽度平等分
- val weight=1/(MainMenu.values().size*1.0f)
+ val weight = 1 / (MainMenu.values().size * 1.0f)
Row(Modifier.fillMaxWidth()) {
- MenuIconButton(_menu = menu,menu = MainMenu.Main,Modifier.weight(weight),
- onClick = { nav.navigate(MainMenu.Main.name)})
- MenuIconButton(_menu = menu,menu = MainMenu.List,Modifier.weight(weight),
- onClick = { nav.navigate(MainMenu.List.name)})
- MenuIconButton(_menu = menu,menu = MainMenu.Center,Modifier.weight(weight),
- onClick = { nav.navigate(MainMenu.Center.name)})
+ MenuIconButton(_menu = menu, menu = MainMenu.Main, Modifier.weight(weight),
+ onClick = { nav.navigate(MainMenu.Main.name) })
+ MenuIconButton(_menu = menu, menu = MainMenu.List, Modifier.weight(weight),
+ onClick = { nav.navigate(MainMenu.List.name) })
+ MenuIconButton(_menu = menu, menu = MainMenu.Center, Modifier.weight(weight),
+ onClick = { nav.navigate(MainMenu.Center.name) })
}
}
@@ -128,7 +133,7 @@ fun MainBottomAppBar(menu:MainMenu, nav: NavHostController, modifier: Modifier=M
* 社团菜单
*
*/
-enum class AssociationMenu(val menuName:String){
+enum class AssociationMenu(val menuName: String) {
member("社团成员"),
main("社团主页"),
list("活动列表")
@@ -139,34 +144,52 @@ enum class AssociationMenu(val menuName:String){
*
*/
@Composable
-fun AssociationAppBar(menu: AssociationMenu,nav: NavHostController,back:()->Unit,dropMenu:()->Unit){
- TopAppBar(backgroundColor = MaterialTheme.colors.secondary) {
- Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {
- IconButton(onClick = back,modifier = Modifier.weight(0.1F)) {
- Icon(painter = painterResource(id = R.drawable.ic_arrow_left), contentDescription = null)
- }
- Row(modifier = Modifier
- .weight(0.8F)
- .fillMaxHeight(),
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically
- ) {
- val menus=AssociationMenu.values()
- menus.forEach {
- Row(modifier = Modifier
- .weight(1F / menus.size)
- .clickable(onClick = { nav.navigate(it.name) }),
- horizontalArrangement = Arrangement.Center) {
- Text(text = it.menuName,color = if(menu==it) MaterialTheme.colors.primary else MaterialTheme.colors.onBackground)
- }
- }
- }
- IconButton(onClick = dropMenu,modifier = Modifier.weight(0.1F)) {
- Icon(painter = painterResource(id = R.drawable.ic_configuration), contentDescription = null)
- }
- }
- }
+fun AssociationAppBar(
+ menu: AssociationMenu,
+ nav: NavHostController,
+ back: () -> Unit,
+ dropMenu: () -> Unit
+) {
+ TopAppBar(backgroundColor = MaterialTheme.colors.secondary) {
+ Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+ IconButton(onClick = back, modifier = Modifier.weight(0.1F)) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_arrow_left),
+ contentDescription = null
+ )
+ }
+ Row(
+ modifier = Modifier
+ .weight(0.8F)
+ .fillMaxHeight(),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ val menus = AssociationMenu.values()
+ menus.forEach {
+ Row(
+ modifier = Modifier
+ .weight(1F / menus.size)
+ .clickable(onClick = { nav.navigate(it.name) }),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = it.menuName,
+ color = if (menu == it) MaterialTheme.colors.primary else MaterialTheme.colors.onBackground
+ )
+ }
+ }
+ }
+ IconButton(onClick = dropMenu, modifier = Modifier.weight(0.1F)) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_configuration),
+ contentDescription = null
+ )
+ }
+ }
+ }
}
+
/**
* 跑马灯
*
@@ -174,11 +197,14 @@ fun AssociationAppBar(menu: AssociationMenu,nav: NavHostController,back:()->Unit
* @param content
*/
@Composable
-fun Marquee(model: MarqueeViewModel = viewModel(), content: @Composable BoxScope.(model: MarqueeViewModel,
- value: State
-) -> Unit) {
+fun Marquee(
+ model: MarqueeViewModel = viewModel(), content: @Composable BoxScope.(
+ model: MarqueeViewModel,
+ value: State
+ ) -> Unit
+) {
- val delayMillis=2000
+ val delayMillis = 2000
Column {
BoxWithConstraints {
val transition = rememberInfiniteTransition()
@@ -232,7 +258,12 @@ fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State) {
* @param body 内容
*/
@Composable
-fun MainFrame( background:@Composable ()->Unit,mainMenu: MainMenu,nav: NavHostController,body:@Composable ColumnScope.()->Unit){
+fun MainFrame(
+ background: @Composable () -> Unit,
+ mainMenu: MainMenu,
+ nav: NavHostController,
+ body: @Composable ColumnScope.() -> Unit
+) {
Box(modifier = Modifier.fillMaxSize()) {
background()
Column {
@@ -252,7 +283,7 @@ fun MainFrame( background:@Composable ()->Unit,mainMenu: MainMenu,nav: NavHostCo
* @param body
*/
@Composable
-fun MainFrame(background:@Composable ()->Unit,body:@Composable ColumnScope.()->Unit){
+fun MainFrame(background: @Composable () -> Unit, body: @Composable ColumnScope.() -> Unit) {
Box(modifier = Modifier.fillMaxSize()) {
background()
Column(content = body)
@@ -284,19 +315,23 @@ fun Carousel(
* @param singeLine
*/
@Composable
-fun BaseTextField(modifier:Modifier=Modifier,form:T, singeLine:Boolean=false){
+fun BaseTextField(
+ modifier: Modifier = Modifier,
+ form: T,
+ singeLine: Boolean = false
+) {
val name: String by form.formValue.observeAsState("")
val focusManager = LocalFocusManager.current
OutlinedTextField(
modifier = modifier,
value = name,
- onValueChange = {form.onChange(it)},
- label={ Text(text = form.formDesc)},
- placeholder = { Text(text = form.formPlaceholder)},
+ onValueChange = { form.onChange(it) },
+ label = { Text(text = form.formDesc) },
+ placeholder = { Text(text = form.formPlaceholder) },
singleLine = singeLine,
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
- trailingIcon={ Text(text = "${name.length}/${form.textLength}") })
+ trailingIcon = { Text(text = "${name.length}/${form.textLength}") })
}
/**
@@ -306,17 +341,34 @@ fun BaseTextField(modifier:Modifier=Modifier,form:T, singeLine:Bo
* @param scaffoldState
*/
@Composable
-fun ShowSnackbar(model:ScaffoldModel= viewModel(),scaffoldState: ScaffoldState){
- val message:String? by model.message.observeAsState()
- message?.let {
- Logger.i("message=$it")
- LaunchedEffect(scaffoldState){
- launch {
- scaffoldState.snackbarHostState.showSnackbar(
- message = it
- )
- model.update()
+fun ShowSnackbar(model: ScaffoldModel = viewModel(), scaffoldState: ScaffoldState) {
+ val snackBar: SnackBar? by model.data.observeAsState()
+ snackBar?.apply {
+ if (message != null) {
+ LaunchedEffect(scaffoldState) {
+ launch {
+ if (actionLabel != null) {
+ val result=scaffoldState.snackbarHostState.showSnackbar(
+ message = message, actionLabel = actionLabel,
+ duration = duration
+
+ )
+ when(result){
+ SnackbarResult.ActionPerformed->{
+ Logger.i("点击操作按钮")
+ callback()
+ }
+ SnackbarResult.Dismissed->{
+ Logger.d("窗口消失")
+ }
+ }
+ } else {
+ scaffoldState.snackbarHostState.showSnackbar(message = message)
+ }
+ model.update()
+ }
}
+
}
}
}
@@ -327,19 +379,28 @@ fun ShowSnackbar(model:ScaffoldModel= viewModel(),scaffoldState: ScaffoldState){
*
* @property id 资源id
*/
-enum class BackgroundImage(@DrawableRes val id:Int){
+enum class BackgroundImage(@DrawableRes val id: Int) {
//主页
main(R.drawable.mb_bg_fb_08),
+
//社团列表
list(R.drawable.mb_bg_fb_07),
+
//个人中心
center(R.drawable.mb_bg_fb_28),
+
//注册社团
reg_association(R.drawable.mb_bg_fb_06),
+
//社团主界面
association_main(R.drawable.mb_bg_fb_25_180),
+
//社团重命名
- rename(R.drawable.mb_bg_fb_27)
+ rename(R.drawable.mb_bg_fb_27),
+
+ //社团题库管理
+ exam(R.drawable.mb_bg_fb_02)
+
}
/**
@@ -349,12 +410,16 @@ enum class BackgroundImage(@DrawableRes val id:Int){
* @param alpha
*/
@Composable
-fun Background(image: BackgroundImage, alpha:Float= DefaultAlpha){
- val app= LocalContext.current.applicationContext as APP
+fun Background(image: BackgroundImage, alpha: Float = DefaultAlpha) {
+ val app = LocalContext.current.applicationContext as APP
BoxWithConstraints {
Image(
- bitmap = app.getImage(image = image,reqHeight = maxHeight.value.toInt()/2,reqWidth = maxWidth.value.toInt()/2),
+ bitmap = app.getImage(
+ image = image,
+ reqHeight = maxHeight.value.toInt() / 2,
+ reqWidth = maxWidth.value.toInt() / 2
+ ),
contentDescription = null,
contentScale = ContentScale.FillHeight,
alpha = alpha,
@@ -369,12 +434,12 @@ fun Background(image: BackgroundImage, alpha:Float= DefaultAlpha){
* @param content
*/
@Composable
-fun Body(content:@Composable (scaffoldState:ScaffoldState)->Unit){
+fun Body(content: @Composable (scaffoldState: ScaffoldState) -> Unit) {
Surface(color = MaterialTheme.colors.background) {
val scaffoldState = rememberScaffoldState()
Scaffold(scaffoldState = scaffoldState) {
- content(scaffoldState=scaffoldState)
+ content(scaffoldState = scaffoldState)
}
}
}
@@ -385,13 +450,13 @@ fun Body(content:@Composable (scaffoldState:ScaffoldState)->Unit){
* @param content
*/
@Composable
-fun Body( content:@Composable (nav:NavHostController, scaffoldState:ScaffoldState)->Unit){
+fun Body(content: @Composable (nav: NavHostController, scaffoldState: ScaffoldState) -> Unit) {
Surface(color = MaterialTheme.colors.background) {
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState()
Scaffold(scaffoldState = scaffoldState) {
- content(nav=navController,scaffoldState=scaffoldState)
+ content(nav = navController, scaffoldState = scaffoldState)
}
}
}
@@ -401,7 +466,7 @@ fun Body( content:@Composable (nav:NavHostController, scaffoldState:ScaffoldStat
*
*/
@Composable
-fun Poster(modifier: Modifier=Modifier,@DrawableRes id: Int){
+fun Poster(modifier: Modifier = Modifier, @DrawableRes id: Int) {
Card(
modifier = modifier,
backgroundColor = Color.Transparent
@@ -410,12 +475,12 @@ fun Poster(modifier: Modifier=Modifier,@DrawableRes id: Int){
Image(
painter = painterResource(id = R.drawable.hot_activity_background),
contentDescription = null,
- modifier=Modifier.fillMaxSize()
+ modifier = Modifier.fillMaxSize()
)
Image(
painter = painterResource(id = id),
contentDescription = null,
- modifier=Modifier.fillMaxSize()
+ modifier = Modifier.fillMaxSize()
)
}
}
@@ -426,7 +491,7 @@ fun Poster(modifier: Modifier=Modifier,@DrawableRes id: Int){
*
*/
@Composable
-fun DescCard(modifier: Modifier){
+fun DescCard(modifier: Modifier) {
Card(
modifier = modifier,
backgroundColor = Color.Transparent
@@ -459,13 +524,13 @@ fun DescCard(modifier: Modifier){
@Preview
@Composable
-fun AnimationTextPreview(){
+fun AnimationTextPreview() {
AnimationText(text = "6666")
}
//@Preview
@Composable
-fun MyBottomAppBarPreview(){
+fun MyBottomAppBarPreview() {
CSAMSTheme {
}
diff --git a/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt b/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
index 10c9c31..cff2820 100644
--- a/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
+++ b/app/src/main/java/com/gyf/csams/uikit/ViewModel.kt
@@ -1,5 +1,6 @@
package com.gyf.csams.uikit
+import androidx.compose.material.SnackbarDuration
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -29,6 +30,11 @@ abstract class FormName(val formDesc:String){
*/
open class StringForm(formDesc: String, val textLength: Int) : FormName(formDesc = formDesc),
FormLength {
+
+ constructor(formDesc: String,textLength: Int,value:String) : this(formDesc = formDesc,textLength = textLength) {
+ _formValue.value=value
+ }
+
override val nameLengthError="${formDesc}不能超过最大长度$textLength"
override fun onChange(value: String) {
@@ -41,16 +47,23 @@ open class StringForm(formDesc: String, val textLength: Int) : FormName(
}
}
+
+data class SnackBar(val message:String?, val actionLabel:String?=null,val duration: SnackbarDuration=SnackbarDuration.Short, val callback: () -> Unit?)
+
/**
* snackbar
*
*/
class ScaffoldModel:ViewModel(){
- private val _message=MutableLiveData()
- val message:LiveData = _message
-
- fun update(message:String?=null){
- _message.value=message
+ private val _data=MutableLiveData()
+ val data:LiveData = _data
+
+ fun update(message:String?=null,actionLabel: String? = null,callback: () -> Unit? = {}){
+ if(message==null){
+ _data.value=null
+ }else {
+ _data.value = SnackBar(message = message, actionLabel = actionLabel,callback = callback)
+ }
}
}
diff --git a/app/src/main/java/com/gyf/csams/util/GsonUtil.kt b/app/src/main/java/com/gyf/csams/util/GsonUtil.kt
new file mode 100644
index 0000000..45f0d0d
--- /dev/null
+++ b/app/src/main/java/com/gyf/csams/util/GsonUtil.kt
@@ -0,0 +1,111 @@
+package com.gyf.csams.util
+
+import com.google.gson.*
+import com.google.gson.reflect.TypeToken
+import com.gyf.csams.association.model.*
+import com.gyf.csams.uikit.StringForm
+import java.lang.reflect.Type
+
+class OpenQuestionsVoSerializer: JsonSerializer {
+ override fun serialize(
+ vo: OpenQuestionsVo?,
+ p1: Type?,
+ p2: JsonSerializationContext?
+ ): JsonElement {
+ val gson= Gson()
+ val c = JsonObject()
+ c.add("examType", gson.toJsonTree(vo?.examType?.name))
+ c.add("question",gson.toJsonTree(vo?.question?.formValue?.value))
+ return c
+ }
+}
+
+class OpenQuestionsVoDeserializer: JsonDeserializer {
+ override fun deserialize(
+ json: JsonElement?,
+ p1: Type?,
+ p2: JsonDeserializationContext?
+ ): OpenQuestionsVo {
+ val root=json?.asJsonObject
+ val value=root?.get("question")?.asString
+ val question= StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH)
+ if (value != null) {
+ question.onChange(value)
+ return OpenQuestionsVo(question = question)
+ }else{
+ throw NullPointerException("问题无法解析!!!!")
+ }
+ }
+}
+
+class ChoiceQuestionVoSerializer: JsonSerializer {
+ override fun serialize(
+ vo: ChoiceQuestionVo?,
+ p1: Type?,
+ p2: JsonSerializationContext?
+ ): JsonElement {
+ val gson= Gson()
+ val c = JsonObject()
+ c.add("examType", gson.toJsonTree(vo?.examType?.name))
+ c.add("question",gson.toJsonTree(vo?.question?.formValue?.value))
+ c.add("answers",gson.toJsonTree(vo?.answers))
+ c.add("rightAnswer",gson.toJsonTree(vo?.rightAnswer))
+ return c
+ }
+}
+
+class ChoiceQuestionVoDeserializer: JsonDeserializer {
+ override fun deserialize(
+ json: JsonElement?,
+ p1: Type?,
+ p2: JsonDeserializationContext?
+ ): ChoiceQuestionVo {
+ val root=json?.asJsonObject
+ val value=root?.get("question")?.asString
+ val question=StringForm(formDesc = "问题",textLength = QUESTION_TEXT_LENGTH)
+ if (value != null) {
+ question.onChange(value)
+ val gson=Gson()
+ val answers:List = gson.fromJson(root.get("answers"),object : TypeToken>() {}.type)
+ if(answers.size!= ANSWER_SIZE){
+ throw IllegalArgumentException("选项数量!=$QUESTION_TEXT_LENGTH")
+ }
+ val rightAnswer=root.get("rightAnswer").asInt
+ return ChoiceQuestionVo(
+ question = question,
+ answers = answers.map { StringForm(formDesc = "选项",textLength = ANSWER_TEXT_LENGTH,value=it) },
+ rightAnswer = rightAnswer
+ )
+ }else{
+ throw NullPointerException("问题无法解析!!!!")
+ }
+ }
+}
+
+/**
+ * 题目反序列化
+ *
+ */
+class ExamDeserializer:JsonDeserializer{
+ companion object{
+ val gson: Gson =GsonBuilder()
+ .registerTypeAdapter(OpenQuestionsVo::class.java,OpenQuestionsVoDeserializer())
+ .registerTypeAdapter(ChoiceQuestionVo::class.java,ChoiceQuestionVoDeserializer())
+ .create()
+ }
+
+ override fun deserialize(json: JsonElement?, p1: Type?, p2: JsonDeserializationContext?): Exam {
+ val root=json?.asJsonObject
+
+ val type=root?.get("examType")?.asString
+
+ return when (gson.fromJson(type, ExamType::class.java)) {
+ ExamType.cq -> gson.fromJson(json,
+ object : TypeToken() {}.type)
+ ExamType.oq -> gson.fromJson(json,
+ object : TypeToken() {}.type)
+ null->throw NullPointerException("无法识别题目类型!")
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gyf/csams/util/HttpUtil.kt b/app/src/main/java/com/gyf/csams/util/HttpUtil.kt
index aa8b9a4..dacfd92 100644
--- a/app/src/main/java/com/gyf/csams/util/HttpUtil.kt
+++ b/app/src/main/java/com/gyf/csams/util/HttpUtil.kt
@@ -1,6 +1,8 @@
package com.gyf.csams.util
import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.gyf.csams.association.model.Exam
import com.orhanobut.logger.Logger
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
@@ -93,10 +95,18 @@ object HttpClient{
data class ApiResponse(val code:Int,val message:String,val body:T?=null)
+
class SimpleCallback(private val action:String,
private val onSuccess:(res:ApiResponse) -> Unit,
private val onFail:(error:String) -> Unit,
private val type: Type):Callback{
+
+ companion object{
+ val gson: Gson =GsonBuilder()
+ .registerTypeAdapter(Exam::class.java,ExamDeserializer())
+ .create()
+ }
+
override fun onFailure(call: Call, e: IOException) {
onFail("${action}失败,请联系管理员")
Logger.e(e,"${action}请求失败,发生IO异常")
@@ -107,7 +117,7 @@ class SimpleCallback(private val action:String,
val body=response.body
if (body!=null&&body.contentType()?.subtype == "json") {
val jsonRes=body.string()
- val res:ApiResponse = Gson().fromJson(jsonRes, type)
+ val res:ApiResponse = gson.fromJson(jsonRes, type)
Logger.i("${action}请求响应成功:")
Logger.json(jsonRes)
onSuccess(res)
diff --git a/app/src/main/res/drawable/ic_add_select.xml b/app/src/main/res/drawable/ic_add_select.xml
new file mode 100644
index 0000000..94dd401
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_select.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_sami_select.xml b/app/src/main/res/drawable/ic_sami_select.xml
new file mode 100644
index 0000000..4e24c08
--- /dev/null
+++ b/app/src/main/res/drawable/ic_sami_select.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt b/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt
index 070132b..df62de0 100644
--- a/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt
+++ b/app/src/test/java/com/gyf/csams/ExampleUnitTest.kt
@@ -5,8 +5,6 @@ import com.google.gson.reflect.TypeToken
import com.gyf.csams.util.ApiResponse
import org.junit.Assert.assertEquals
import org.junit.Test
-import java.time.LocalDateTime
-import java.time.temporal.ChronoField
/**
@@ -36,7 +34,14 @@ class ExampleUnitTest {
@Test
fun testYear(){
- println( LocalDateTime.now().get(ChronoField.YEAR))
+ repeat(10,{
+ println(it)
+ })
+ }
+
+ @Test
+ fun testCharRange(){
+ println(('A'..'D').map { "选项$it" })
}
}
diff --git a/build.gradle.kts b/build.gradle.kts
index ea6d7c1..b5bab5d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,13 +8,14 @@ buildscript {
val APP_NAME by extra("大学生社团管理系统")
val SERVER_ADDRESS by extra("http://192.168.50.107:8080")
val room_version by extra("2.2.6")
+ val kotlin_version by extra("1.4.32")
repositories {
maven("https://maven.aliyun.com/repository/google")
maven("https://maven.aliyun.com/repository/public")
}
dependencies {
classpath("com.android.tools.build:gradle:7.0.0-alpha15")
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle.kts files