Browse Source

test_data test_ui

master
suliang 2 years ago
parent
commit
3836f58409
41 changed files with 1183 additions and 416 deletions
  1. 14
    12
      .idea/codeStyles/Project.xml
  2. 5
    0
      .idea/misc.xml
  3. 11
    0
      README.md
  4. 11
    8
      app/build.gradle
  5. 1
    1
      app/src/main/AndroidManifest.xml
  6. 29
    1
      app/src/main/java/com/xkl/cdl/data/AppConstants.kt
  7. 17
    4
      app/src/main/java/com/xkl/cdl/data/DataTransferHolder.kt
  8. 26
    0
      app/src/main/java/com/xkl/cdl/data/bean/course/ExamBean.kt
  9. 57
    16
      app/src/main/java/com/xkl/cdl/data/manager/CourseManager.kt
  10. 170
    58
      app/src/main/java/com/xkl/cdl/data/manager/db/DBCourseManager.kt
  11. 17
    0
      app/src/main/java/com/xkl/cdl/dialog/DialogEventAction.kt
  12. 52
    16
      app/src/main/java/com/xkl/cdl/dialog/LearnDialog.kt
  13. 61
    0
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamActivity.kt
  14. 0
    12
      app/src/main/java/com/xkl/cdl/module/learn/LearnExamWordActivity.kt
  15. 22
    1
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragment.kt
  16. 60
    38
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragmentViewModel.kt
  17. 34
    12
      app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseTotalTestFragment.kt
  18. 19
    19
      app/src/main/java/com/xkl/cdl/module/splash/SplashActivity.kt
  19. 100
    0
      app/src/main/java/com/xkl/cdl/widget/VoiceSwitch.kt
  20. 10
    0
      app/src/main/res/drawable/shape_switch_thumb.xml
  21. 8
    0
      app/src/main/res/drawable/shape_switch_track.xml
  22. 7
    1
      app/src/main/res/layout/activity_learn_exam_word.xml
  23. 1
    2
      app/src/main/res/layout/fragment_course_total_test.xml
  24. 112
    0
      app/src/main/res/layout/inc_learn_test_statistic.xml
  25. 70
    0
      app/src/main/res/layout/inc_learn_title.xml
  26. BIN
      app/src/main/res/mipmap-xxhdpi/test_score_level_1.png
  27. BIN
      app/src/main/res/mipmap-xxhdpi/test_score_level_2.png
  28. BIN
      app/src/main/res/mipmap-xxhdpi/test_score_level_3.png
  29. 0
    1
      app/src/main/res/values/attrs.xml
  30. 1
    0
      app/src/main/res/values/colors.xml
  31. 4
    3
      lib/common/build.gradle
  32. 26
    4
      lib/common/src/main/java/com/suliang/common/base/BaseDialogFragment.kt
  33. 4
    2
      lib/common/src/main/java/com/suliang/common/base/LoadingDialog.kt
  34. 1
    1
      lib/common/src/main/java/com/suliang/common/base/ViewBehavior.kt
  35. 8
    181
      lib/common/src/main/java/com/suliang/common/base/activity/BaseActivity.kt
  36. 4
    1
      lib/common/src/main/java/com/suliang/common/base/activity/BaseActivityVM.kt
  37. 196
    0
      lib/common/src/main/java/com/suliang/common/base/activity/UIBaseActivity.kt
  38. 7
    10
      lib/common/src/main/java/com/suliang/common/base/fragment/BaseFragment.kt
  39. 6
    1
      lib/common/src/main/java/com/suliang/common/base/fragment/BaseFragmentVM.kt
  40. 5
    11
      lib/common/src/main/java/com/suliang/common/base/viewmodel/BaseViewModel.kt
  41. 7
    0
      lib/common/src/main/java/com/suliang/common/extension/RxScheduler.kt.kt

+ 14
- 12
.idea/codeStyles/Project.xml View File

@@ -1,5 +1,8 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<ComposeCustomCodeStyleSettings>
<option name="USE_CUSTOM_FORMATTING_FOR_MODIFIERS" value="false" />
</ComposeCustomCodeStyleSettings>
<DBN-PSQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
@@ -25,12 +28,9 @@
</formatting-settings>
</DBN-SQL>
<JetCodeStyleSettings>
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="true" />
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" />
<option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="true" />
<option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="0" />
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
<option name="IF_RPAREN_ON_NEW_LINE" value="false" />
<option name="SPACE_AROUND_RANGE" value="true" />
<option name="SPACE_BEFORE_TYPE_COLON" value="true" />
<option name="WRAP_ELVIS_EXPRESSIONS" value="2" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<DBN-PSQL>
@@ -172,15 +172,17 @@
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="RIGHT_MARGIN" value="120" />
<option name="RIGHT_MARGIN" value="130" />
<option name="KEEP_LINE_BREAKS" value="false" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
<option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
<option name="EXTENDS_LIST_WRAP" value="0" />
<option name="VARIABLE_ANNOTATION_WRAP" value="2" />
<option name="ENUM_CONSTANTS_WRAP" value="2" />
<option name="WRAP_ON_TYPING" value="0" />
<indentOptions>
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

+ 5
- 0
.idea/misc.xml View File

@@ -25,11 +25,15 @@
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/progress_center.xml" value="0.287962962962963" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_12_white.xml" value="0.5140625" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_toplr_24_white.xml" value="0.5061538461538462" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_switch_thumb.xml" value="0.14074074074074075" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/spoken_autoplay_btn_text_color_.xml" value="0.5140625" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/switch_thumb_selector.xml" value="0.3768518518518518" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/switch_track_selector.xml" value="0.3768518518518518" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/theme_splash_bg.xml" value="0.30520833333333336" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout-v23/include_main_learn_center_course_type_title.xml" value="0.4963768115942029" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_course_main.xml" value="0.33" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_base.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam_word.xml" value="0.33" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word2.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_main.xml" value="0.5" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_splash.xml" value="0.4921875" />
@@ -46,6 +50,7 @@
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_first.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_learn_center.xml" value="0.25" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_my.xml" value="0.28229166666666666" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_test_statistic.xml" value="0.35260416666666666" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_over_number.xml" value="0.4979166666666667" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_main_learn_center_course_progress.xml" value="0.503125" />
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_main_learn_center_course_type_title.xml" value="0.67" />

+ 11
- 0
README.md View File

@@ -37,6 +37,17 @@ image包: 实现了图片加载的封装

注意include为merge的坑,viewbinding中需动态绑定 https://juejin.cn/post/6844904065655111693

讨论点:
状态加载的判断: 加载复习状态页没问题,学前总测试状态页也没问题,
但如何判断课程学习完成,目前是课程学习完成后才判断进入课程学后总测试
我的考虑是在进入课程详情界面时,对所有课时的学习进度进行判断,不包含课时的学后测试,当所有课时都学习完了,则进入学后总测试的状态页,
而在学习中,同样做引导课时前测试,学习,课时后测试,
课时后测试将包含 小游戏练习、再测一次、重新学习、下一步(进入学后总测试)或者下一课时(为当前课时的下一课时),
需要考虑的是,下一步或者下一课时的判断为循环判断,如,共10课时,现在点击学习的第5个,如果后面几个课时都学习完后,而1课时没有学习完成,下一课时将直接进入1课时的学习,
而下一步是在所有课时都学习完成的情况下,直接进入学后总测试的界面。




Kotlin的单例模式: https://developer.aliyun.com/article/642618

+ 11
- 8
app/build.gradle View File

@@ -50,6 +50,7 @@ android {
// }

compileOptions {
coreLibraryDesugaringEnabled true //为了使用jdk8的脱糖属性
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@@ -69,15 +70,15 @@ android {

dependencies {
implementation fileTree(include: ['*.jar',"*.aar"], dir: 'libs')
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
// implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation project(path: ':lib:common')
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
// implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
// implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
// implementation 'androidx.appcompat:appcompat:1.2.0'
// implementation 'com.google.android.material:material:1.3.0'
// implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
// implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
// implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'

rootProject.ext.dependencies_required.each{ k, v -> implementation v}
testImplementation rootProject.ext.dependencies_testImplementation.junit
@@ -94,4 +95,6 @@ dependencies {
//Lottie
implementation customDependencies.Lottie

coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'

}

+ 1
- 1
app/src/main/AndroidManifest.xml View File

@@ -25,7 +25,7 @@
android:name=".module.learn.LearnExamSpellActivity"
android:exported="true" />
<activity
android:name=".module.learn.LearnExamWordActivity"
android:name=".module.learn.LearnExamActivity"
android:exported="true" />
<activity
android:name=".module.learn.LearnSpellActivity"

+ 29
- 1
app/src/main/java/com/xkl/cdl/data/AppConstants.kt View File

@@ -113,6 +113,34 @@ object AppConstants {

/** 测试通过:>80 < 90 继续加油 <80 悲伤 */
const val TEST_SCORE_LEVEL_1 = 80
/** 测试通过 >= 太棒了 */
/** 测试通过 >= 太棒了 90 */
const val TEST_SCORE_LEVEL_2 = 90

const val ACTION_START_BEFORE_TEST = 1
const val ACTION_START_AFTER_TEST = 2
const val ACTION_START_TOTAL_TEST = 3
const val ACTION_START_TOTAL_AFTER_TEST = 4

//测试的题目类型
/** 选择题类型 */
const val TEST_QUEST_TYPE_CHOICE = 1
/** 口语对话测试 */
const val TEST_QUEST_TYPE_SPOKEN_DIALOGUE = 4
const val TEST_QUEST_TYPE_GAP_FILLING = 2 //填空题
const val TEST_QUEST_TYPE_JUDGE = 3 //判断题
/**总测试 */
const val TEST_COUNT_TOTAL = 25
/**章节测试*/
const val TEST_COUNT_LESSON = 20
/**词汇量测试 */
const val TEST_COUNT_VOCABULARY = 100
/** 美 */
const val SOUND_TYPE_US = 1 //美
/** 发音默认type 英 */
const val SOUND_TYPE_UK = 2 //英
/**中文*/
const val SOUND_TYPE_CN = 3 //中文
}

+ 17
- 4
app/src/main/java/com/xkl/cdl/data/DataTransferHolder.kt View File

@@ -17,15 +17,28 @@ class DataTransferHolder private constructor(){

private val dataMap = hashMapOf<String,Any>()

fun <T : Any> putData(t : T){
dataMap.put(AppConfig.INTENT_1,t)
/**
* 存放数据
* @param key String
* @param value T
*/
fun <T : Any> putData(key: String, value : T){
dataMap[key] = value
}

/**
* 获取数据
* @param key String
* @return T
*/
@Suppress("UNCHECKED_CAST")
fun <T> getData() : T{
return dataMap.get(AppConfig.INTENT_1) as T
fun <T> getData(key : String) : T{
return dataMap[key] as T
}

/**
* 清空集合
*/
fun clear(){
dataMap.clear()
}

+ 26
- 0
app/src/main/java/com/xkl/cdl/data/bean/course/ExamBean.kt View File

@@ -0,0 +1,26 @@
package com.xkl.cdl.data.bean.course

/**
* author suliang
* create 2022/4/6 11:03
* Describe: 测试数据
*/
class ExamBean {
var id : Long = 0 //当前记录所在的id 英语: 测试题的id 作文:exam_id
var word_id : Long = 0 //英语:词汇id 作文: knowledge_id知识点id
var word : String = "" //测试题干
var correct : String = "" //正确选项 在拼写时记录为该单词的释义
var error1: String = "" //错误选项1
var error2: String = "" //错误选项2
var error3 : String = "" //错误选项3
var type = 0 //测试题类型

var chapterId : Long = 0 //章节id
var lessonId : Long = 0 //课时id

var questionIsAudio = false //问题是否是音频
var answersIsAudio = false //答案是否是音频



}

+ 57
- 16
app/src/main/java/com/xkl/cdl/data/manager/CourseManager.kt View File

@@ -1,7 +1,10 @@
package com.xkl.cdl.data.manager

import android.util.Size
import com.suliang.common.util.file.FileUtil
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.course.CoursePack
import com.xkl.cdl.data.bean.course.ExamBean
import java.io.File
import java.math.BigDecimal
import java.math.RoundingMode
@@ -13,25 +16,27 @@ import java.text.DecimalFormat
* Describe: 课程管理类
*/
object CourseManager {
/** 项目对应的课程包 */
val subjectWithCoursePackMap = hashMapOf<Int, List<CoursePack>>()
/**
* 获取对应项目的课程数量
* @param subjectId Int 项目
* @return Int 课程数量
*/
fun getSubjectForCourseSize(subjectId: Int): Int {
fun getSubjectForCourseSize(subjectId : Int) : Int {
return subjectWithCoursePackMap[subjectId]?.let {
var count = 0
it.forEach { coursePack ->
count += coursePack.childrenCourses?.size ?: 0
count += coursePack.childrenCourses?.size
?: 0
}
count
} ?: 0
}
?: 0
}
/**
* 搜索项目下的课程包
* @param subjectId Int 项目id
@@ -39,7 +44,7 @@ object CourseManager {
* @return MutableList<CoursePack>? 结果值
*/
@Synchronized
fun filterSearchCoursePack(subjectId: Int, searchValue: String?): MutableList<CoursePack>? {
fun filterSearchCoursePack(subjectId : Int, searchValue : String?) : MutableList<CoursePack>? {
return if (searchValue.isNullOrEmpty() || searchValue.trim().isEmpty()) {
subjectWithCoursePackMap[subjectId]?.toMutableList()
} else {
@@ -54,7 +59,7 @@ object CourseManager {
}
}
}
/**
* 根据subjectId,courseId 获取CoursePack
* @param subjectId Int 项目Id
@@ -62,7 +67,7 @@ object CourseManager {
* @return CoursePack? 课程包
*/
@Synchronized
fun getCoursePackWithCoursePackId(subjectId: Int, coursePackId: Long): CoursePack? {
fun getCoursePackWithCoursePackId(subjectId : Int, coursePackId : Long) : CoursePack? {
return subjectWithCoursePackMap[subjectId]?.let {
//返回很符合条件的第一个元素
it.firstOrNull {
@@ -70,7 +75,7 @@ object CourseManager {
}
}
}
/***
* 检查课程包下课程的db数据文件是否存在,不存在,则复制过去
*/
@@ -79,9 +84,8 @@ object CourseManager {
entry.value.forEach { coursePack ->
coursePack.childrenCourses.forEach {
val file = File(
FileUtil.getSaveDirPath("db"),
"${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db"
)
FileUtil.getSaveDirPath("db"), "${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db"
)
//不存在,复制,存在则不操作
if (!file.exists()) {
FileUtil.copyAsset(it.dbPathName, file)
@@ -90,14 +94,14 @@ object CourseManager {
}
}
}
/**
* 用于显示的进度
* @param progress Double 课程的进度
* @return String
*/
@JvmStatic
fun useToShowProgress(progress: Double): String {
fun useToShowProgress(progress : Double) : String {
return when {
progress == 0.0 -> "0"
progress < 0.1 -> "0.1"
@@ -108,5 +112,42 @@ object CourseManager {
else -> "100"
}
}

/**
* 计算预计测试时间
* 总测: 口语总测试与辨音课程测试不显示时长
* 课时测:辨音不显示时长, 作文测试没有显示时长
* @param courseType Int 课程类型
* @param testData List<ExamBean> 测试题
* @return String 格式化好的数据: 共%d题,预计%d分钟
*/
@JvmStatic
fun expectedTestTime(courseType : Int, testType : Int, testData : List<ExamBean>) : String {
return when (courseType) {
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> {
""
}
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> {
String.format("共${testData.size}题,预计%d分钟", (Math.ceil(testData.stream().mapToInt {
it.word.length
}.sum() * 1.6 / 60)).toInt())
}
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> {
when(testType) {
AppConstants.TEST_TYPE_BEFORE_TOTAL, AppConstants.TEST_TYPE_AFTER_TOTAL -> {
"共${testData.size}题"
}
else -> {
String.format("共${testData.size}题,预计%d分钟", Math.ceil(testData.size * 0.1).toInt())
}
}
}
AppConstants.COURSE_TYPE_ENGLISH_VOICE -> {
"共${testData.size}题"
}
else -> {
String.format("共${testData.size}题,预计%d分钟", Math.ceil(testData.size * 0.1).toInt())
}
}
}
}

+ 170
- 58
app/src/main/java/com/xkl/cdl/data/manager/db/DBCourseManager.kt View File

@@ -2,10 +2,12 @@ package com.xkl.cdl.data.manager.db

import android.annotation.SuppressLint
import android.widget.Toast
import androidx.core.database.getLongOrNull
import com.suliang.common.util.AppGlobals
import com.suliang.common.util.thread.AppExecutors
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.course.CourseDetail
import com.xkl.cdl.data.bean.course.ExamBean
import com.xkl.cdl.data.bean.course.Lesson
import com.xkl.cdl.data.manager.FilePathManager
import net.sqlcipher.database.SQLiteDatabase
@@ -21,9 +23,9 @@ object DBCourseManager {
private const val SPOKEN = "XKL_SPOKEN_COURSE_DATA_KEY"
private const val COMPOSITION = "XKL_LOCAL_COMPOSITION_DATA_KEY"
private const val LITERACY = "XKL_LOCAL_CHINESE_COURSE_KEY"
private var mDataBase: SQLiteDatabase? = null
private fun open(base: DbControlBase) {
private var mDataBase : SQLiteDatabase? = null
private fun open(base : DbControlBase) {
synchronized(DBCourseManager) {
if (mDataBase != null && currentBase != base) {
mDataBase?.close()
@@ -37,27 +39,27 @@ object DBCourseManager {
AppConstants.COURSE_TYPE_CHINESE_LITERACY -> LITERACY
else -> NORMAL
}
mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getCourseDbPath(currentBase).path,
p,
null,
SQLiteDatabase.OPEN_READONLY)
mDataBase = SQLiteDatabase.openDatabase(
FilePathManager.getCourseDbPath(currentBase).path, p, null, SQLiteDatabase.OPEN_READONLY
)
}
if (mDataBase == null) {
AppExecutors.mainThread.run {
Toast.makeText(AppGlobals.application, "课程数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG)
Toast.makeText(
AppGlobals.application, "课程数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG
)
}
}
}
}
/**获取该课程的所有课时
* @param base DbControlBase
* @param detail CourseDetail 课程信息
* @return List<Lesson>
*/

@SuppressLint("Range")
fun queryAllLesson(base: DbControlBase, detail: CourseDetail): List<Lesson> {
fun queryAllLesson(base : DbControlBase, detail : CourseDetail) : List<Lesson> {
val mutableList = mutableListOf<Lesson>()
open(base)
//聚合所有课时,先所有chapter_sort和word_sort排序,然后根据leesonId分组并聚合leeson_id到
@@ -75,38 +77,40 @@ object DBCourseManager {
var positionIndex = 0
mDataBase?.rawQuery(rawQurySql, null)?.let {
while (it.moveToNext()) {
val chapterId: Long = it.getLong(it.getColumnIndex("chapter_id"))
val chapterName: String = it.getString(it.getColumnIndex("chapter_title"))
val chapterId : Long = it.getLong(it.getColumnIndex("chapter_id"))
val chapterName : String = it.getString(it.getColumnIndex("chapter_title"))
val lessonId = it.getLong(it.getColumnIndex("lesson_id"))
val lessonName = when (base.courseType) {
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> it.getString(it.getColumnIndex("lesson_title"))
else -> it.getString(it.getColumnIndex("lesson"))
}
val wordIds = when (base.courseType) {
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> it.getString(it.getColumnIndex("relation_id"))
.split(",")
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> it.getString(it.getColumnIndex("relation_id")).split(",")
else -> it.getString(it.getColumnIndex("wordIds")).split(",")
}.map { value -> value.toLong() }.toMutableList()
val lessonType: Int = when (base.courseType) {
val lessonType : Int = when (base.courseType) {
AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> it.getInt(it.getColumnIndex("type"))
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> it.getInt(it.getColumnIndex("lesson_type"))
else -> AppConstants.LESSON_TYPE_WORD
}
val key = "${chapterId}_${lessonId}"

val learnIndex = wordIds.indexOf(detail.lesson_learn_point.getOrElse(key, { -1 })) //学习位置,当前位置为已学

val learnIndex = wordIds.indexOf(
detail.lesson_learn_point.getOrElse(key, { -1 })
) //学习位置,当前位置为已学
// 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。
val learnIsOver = wordIds.size - 1 == learnIndex

val lesson = Lesson(base.subjectId,
base.coursePackId,
base.courseId,
base.courseType,
chapterId,
chapterName,
lessonId,
lessonName).apply {
val lesson = Lesson(
base.subjectId,
base.coursePackId,
base.courseId,
base.courseType,
chapterId,
chapterName,
lessonId,
lessonName).apply {
lessonPositionInList = positionIndex
this.wordIds = wordIds //内容
totalNumber = this.wordIds.size //总数
@@ -126,41 +130,149 @@ object DBCourseManager {
mutableList.last().lastLesson = true
return mutableList.toList()
}

/** 获取总测试的数据 */
fun queryTotalTest(coursePackId: Long, courseId: Long) {

}

/** 获取课时测试数据 */
fun queryLessonTest(coursePackId: Long, courseId: Long, chapterId: Long, lessonId: Long, courseType: Long) {

/** 获取学习时的测试的数据 (学前、学后总测、课时学前、学后总测)
* @param base DbControlBase
* @param testType Int 测试类型
* @param lesson Lesson? 测试章节 仅当课时学前学后测试时才传入课时
* @return List<ExamBean> 返回数据
*/
fun queryLearnTest(base : DbControlBase, testType : Int, lesson : Lesson? = null) : List<ExamBean> {
open(base)
val result = mutableListOf<ExamBean>()
//查询数量
val count = when (testType) {
AppConstants.TEST_TYPE_BEFORE_TOTAL, AppConstants.TEST_TYPE_AFTER_TOTAL -> AppConstants.TEST_COUNT_TOTAL //学前、学后总测
AppConstants.TEST_TYPE_BEFORE, AppConstants.TEST_TYPE_AFTER -> AppConstants.TEST_COUNT_LESSON //课时学前、学后
else -> 0
}
val sql = when (base.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE,
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK, AppConstants.COURSE_TYPE_CHINESE_LITERACY,
AppConstants.COURSE_TYPE_CHINESE_PINYIN,
-> { //没有作文,作文没有学前总测
"SELECT exam.*, chapter_id,lesson_id,basic_explaination FROM chapter JOIN exam ON chapter.word_id = exam.word_id AND exam.type = ${AppConstants.TEST_QUEST_TYPE_CHOICE} " + when (testType) {
AppConstants.TEST_TYPE_BEFORE, AppConstants.TEST_TYPE_AFTER -> { //学前、学后测
" AND chapter_id = ${lesson!!.chapterId} AND lesson_id = ${lesson.lessonId} "
}
else -> ""
} + " ORDER by random() LIMIT $count"
}
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> { //拼写 不需要查询测试表,不需要选项
"SELECT id, word_id,word,chapter_id,lesson_id,basic_explaination FROM chapter " + when (testType) {
AppConstants.TEST_TYPE_BEFORE, AppConstants.TEST_TYPE_AFTER -> { //学前、学后测
" AND chapter_id = ${lesson!!.chapterId} AND lesson_id = ${lesson.lessonId} "
}
else -> " ORDER by random() LIMIT $count"
}
}
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> { //口语
when (testType) {
AppConstants.TEST_TYPE_BEFORE_TOTAL, AppConstants.TEST_TYPE_AFTER_TOTAL -> { //总测
"SELECT * FROM exam WHERE type = 5 ORDER by random() LIMIT $count"
}
AppConstants.TEST_TYPE_BEFORE, AppConstants.TEST_TYPE_AFTER -> {
"SELECT exam.* FROM chapter JOIN exam on chapter.word_id = exam.word_id AND exam.type = 1 AND chapter_id = ${lesson!!.chapterId} AND lesson_id = ${lesson.lessonId} ORDER by random() LIMIT $count"
}
else -> ""
}
}
else -> "" //没有任何课程,课程都特别写入类型,用于匹配
}
mDataBase?.rawQuery(
sql, null
)?.run {
when (base.courseType) {
AppConstants.COURSE_TYPE_ENGLISH_DISCERN, AppConstants.COURSE_TYPE_ENGLISH_VOICE,
AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK, AppConstants.COURSE_TYPE_CHINESE_LITERACY,
AppConstants.COURSE_TYPE_CHINESE_PINYIN,
-> {
while (moveToNext()) {
result.add(ExamBean().apply {
id = getLong(0)
word_id = getLong(1)
word = getString(2)
correct = getString(3)
error1 = getString(4)
error2 = getString(5)
error3 = getString(6)
type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1
chapterId = getLong(8)
lessonId = getLong(9)
})
}
}
AppConstants.COURSE_TYPE_ENGLISH_SPELL -> {
while (moveToNext()) {
result.add(ExamBean().apply {
id = getLong(0)
word_id = getLong(1)
word = getString(2)
chapterId = getLong(3)
lessonId = getLong(4)
correct = getString(5)
type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1
})
}
}
AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> {
while (moveToNext()) {
result.add(ExamBean().apply {
id = getLong(0)
word_id = getLong(1)
word = getString(2)
correct = getString(3)
error1 = getString(4)
error2 = getString(5)
error3 = getString(6)
type = AppConstants.TEST_QUEST_TYPE_CHOICE // 为1
lesson?.let { //课时测试时才会有课时存在
chapterId = it.chapterId
lessonId = it.lessonId
}
})
}
}
}
close()
}
return result
}

/** 获取指定课时(指定数据源wordIds)的随机个数测试数据 */
fun queryLessonTestRandomSize(coursePackId: Long,
courseId: Long,
chapterId: Long,
lessonId: Long,
courseType: Long,
limitSize: Int,
wordIds: ArrayList<Long>? = null) {

fun queryLessonTestRandomSize(
coursePackId : Long,
courseId : Long,
chapterId : Long,
lessonId : Long,
courseType : Long,
limitSize : Int,
wordIds : ArrayList<Long>? = null,
) {
}

/** 获取课时学习内容 */
fun queryLessonLearnData(coursePackId: Long,
courseId: Long,
chapterId: Long,
lessonId: Long,
wordIds: ArrayList<Long>) {

fun queryLessonLearnData(
coursePackId : Long,
courseId : Long,
chapterId : Long,
lessonId : Long,
wordIds : ArrayList<Long>,
) {
}

/** 获取课程今日需要复习的数据 */
fun queryCourseReviewDatas(coursePackId: Long, courseId: Long, wordIds: ArrayList<Long>) {

fun queryCourseReviewDatas(
coursePackId : Long,
courseId : Long,
wordIds : ArrayList<Long>,
) {
}


}

+ 17
- 0
app/src/main/java/com/xkl/cdl/dialog/DialogEventAction.kt View File

@@ -0,0 +1,17 @@
package com.xkl.cdl.dialog

/**
* author suliang
* create 2022/4/6 9:54
* Describe: LearnDialog 按钮点击动作
*/
enum class DialogEventAction {
/**下一步*/
NEXT,
/** 开始学习 */
START_LEARN,
/** 课时学前测试 */
START_LESSON_BEFORE_TEST

// TODO: 2022/4/6 可以添加相应的动作action,来处理对应的动作
}

+ 52
- 16
app/src/main/java/com/xkl/cdl/dialog/LearnDialog.kt View File

@@ -1,10 +1,13 @@
package com.xkl.cdl.dialog

import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager
import com.suliang.common.base.BaseDialogFragment
import com.suliang.common.extension.click
import com.suliang.common.util.LogUtil
import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.databinding.DialogLessonLearnBinding

/**
@@ -14,7 +17,7 @@ import com.xkl.cdl.databinding.DialogLessonLearnBinding
*/
class LearnDialog : BaseDialogFragment<DialogLessonLearnBinding>() {

private lateinit var dialogListener : DialogFragmentListener
private lateinit var dialogListener: DialogFragmentListener

override fun initFragment() {
initClick()
@@ -22,15 +25,15 @@ class LearnDialog : BaseDialogFragment<DialogLessonLearnBinding>() {
initLessonBeforeTestOver()
}

fun initClick(){
dialogListener = if (parentFragment != null){
fun initClick() {
dialogListener = if (parentFragment != null) {
parentFragment as DialogFragmentListener
}else{
} else {
activity as DialogFragmentListener
}
}

fun initLessonBeforeTest(){
fun initLessonBeforeTest() {
binding.tvTitle.text = "课时学前测试"
binding.tvLessonName.text = "章节课时名称"
binding.tvCountTime.text = "测试题目时间"
@@ -40,16 +43,16 @@ class LearnDialog : BaseDialogFragment<DialogLessonLearnBinding>() {

binding.tvRight.click {
LogUtil.e("Dialog -- > show()")
dialogListener.rightClick()
}

binding.tvLeft.click {
LogUtil.e("Dialog -- > hide()")
dialogListener.leftClick()
}
}

fun initLessonBeforeTestOver(){
fun initLessonBeforeTestOver() {
binding.tvScore.run {
visibility = View.VISIBLE
text = "100分"
@@ -68,20 +71,53 @@ class LearnDialog : BaseDialogFragment<DialogLessonLearnBinding>() {
}

}
fun initLessonAfterTestOver(){
//未通过

//已通过
fun initLessonAfterTestOver() {
//未通过、通过和通过已通过,均为一致,只是显示的状态不同
val score = if (1 + 1 == 2) 100 else 88

when {
score < AppConstants.TEST_SCORE_LEVEL_1 -> { //小于80
binding.imgIv.setImageResource(R.mipmap.test_score_level_1)
binding.tvScore.setTextColor(ContextCompat.getColor(requireContext(), R.color.gray_2))
}
score >= AppConstants.TEST_SCORE_LEVEL_2 -> { //大于90
binding.imgIv.setImageResource(R.mipmap.test_score_level_2)
binding.tvScore.setTextColor(ContextCompat.getColor(requireContext(), R.color.red_1))
}
else -> { // 大于等于80 且小于90
binding.imgIv.setImageResource(R.mipmap.test_score_level_3)
binding.tvScore.setTextColor(ContextCompat.getColor(requireContext(), R.color.red_2))
}
}

//已通过的再通过
binding.tvScore.run {
visibility = View.VISIBLE
text = "${score}分"
}

}
binding.tvTip.visibility = View.VISIBLE
binding.tvTitle.text = "恭喜你,完成了课时学前测试"
binding.tvLessonName.visibility = View.GONE
binding.tvCountTime.visibility = View.GONE
binding.incStatisticsNumber.root.visibility = View.VISIBLE


// binding.tvTip1.visibility = View.VISIBLE
// binding.tvTip1.setText(resources.getString(R.string.test_before_test_over_tip))


binding.tvRight.setText(resources.getString(R.string.start_learn))
binding.tvRight.click {
// TODO: 2022/4/2 开始学习
}

}


public interface DialogFragmentListener{
fun rightClick()
fun leftClick()
interface DialogFragmentListener {
fun dialogButtonClick(action: DialogEventAction)
}

}

+ 61
- 0
app/src/main/java/com/xkl/cdl/module/learn/LearnExamActivity.kt View File

@@ -0,0 +1,61 @@
package com.xkl.cdl.module.learn

import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.viewbinding.ViewBinding
import com.suliang.common.AppConfig
import com.suliang.common.base.activity.UIBaseActivity
import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.databinding.ActivityLearnExamWordBinding
import com.xkl.cdl.databinding.ActivityLearnSpellBinding
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivity

/**
* author suliang
* create 2022/4/6 19:18
* Describe: 测试界面 总测 课时测
*/
class LearnExamActivity : UIBaseActivity() {
companion object {
@JvmStatic
fun newInstance(context: Context) {
context.startActivity(Intent(context, LearnExamActivity::class.java))
}
}
/** 音频测试、单词测试布局 */
private var _normalBinding : ActivityLearnExamWordBinding? = null
/**布局 */
private var _spellBinding : ActivityLearnSpellBinding? = null
override fun setContentView() {
_normalBinding = ActivityLearnExamWordBinding.inflate(layoutInflater)
setContentView(_normalBinding!!.root)
}
override fun initActivity(savedInstanceState : Bundle?) {
}
override fun loadData() {
_normalBinding?.let {
it.incLearnTitle.voiceSwitch.soundWayChange.observe(this){
println("发音 --》 $it ")
}
it.incLearnTitle.voiceSwitch.setSoundWay(AppConstants.SOUND_TYPE_US)
}
}
}

+ 0
- 12
app/src/main/java/com/xkl/cdl/module/learn/LearnExamWordActivity.kt View File

@@ -1,12 +0,0 @@
package com.xkl.cdl.module.learn

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.xkl.cdl.R

class LearnExamWordActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_learn_exam_word)
}
}

+ 22
- 1
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragment.kt View File

@@ -8,6 +8,7 @@ import com.suliang.common.base.fragment.BaseFragmentVM
import com.suliang.common.extension.replaceFragment
import com.suliang.common.util.LogUtil
import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.manager.db.DbControlBase
import com.xkl.cdl.databinding.FragmentCourseMainBinding
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel
@@ -64,7 +65,27 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF
//学后总测
// replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL))
//学习目录
replaceFragment(R.id.layout_root, CourseLessonFragment.newInstance())

vm.courseDetail.run {
if (st_before == AppConstants.NOT_DOING) { //学前总测未做
replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL))
} else if (courseLearnProgress != 100.0) { //学习未完成
replaceFragment(R.id.layout_root, CourseLessonFragment.newInstance())
} else { //学习完成,学后总测试界面
replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL))
}
}
}
/**
* 供其他地方手动调用切换界面
* @param position Int 1目录页 2学后总测
*/
fun changeFragment(position: Int){
when(position){
1 -> replaceFragment(R.id.layout_root, CourseLessonFragment.newInstance())
2 -> replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL))
}
}



+ 60
- 38
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseMainFragmentViewModel.kt View File

@@ -1,51 +1,52 @@
package com.xkl.cdl.module.m_center_learn.coursechildren

import android.provider.ContactsContract
import androidx.lifecycle.MutableLiveData
import com.suliang.common.base.activity.ToastEvent
import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.extension.diskIo2DiskIo
import com.suliang.common.extension.diskIo2Main
import com.xkl.cdl.data.bean.course.Course
import com.xkl.cdl.data.bean.course.CourseDetail
import com.xkl.cdl.data.bean.course.ExamBean
import com.xkl.cdl.data.bean.course.Lesson
import com.xkl.cdl.data.manager.db.DBCourseManager
import com.xkl.cdl.data.manager.db.DbControlBase
import com.xkl.cdl.data.repository.DataRepository
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap

/**
* author suliang
* create 2022/3/28 15:26
* Describe:
* @property courseIndex 该课程在课程包的课程集合中的位置
*
*/
class CourseMainFragmentViewModel(val courseIndex: Int) : BaseViewModel() {
class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() {
//课程包主页的ViewModel
lateinit var coursePackMainActivityVM: CoursePackMainActivityViewModel
lateinit var dbControlBase: DbControlBase
lateinit var coursePackMainActivityVM : CoursePackMainActivityViewModel
lateinit var dbControlBase : DbControlBase
//当前操作课程
lateinit var course: Course
lateinit var course : Course
/** 课程的收藏本: 目前仅作用在口语课程会使用到该数据 */
lateinit var collectList: HashMap<String, Long>
lateinit var collectList : HashMap<String, Long>
/** 复习的数据 */
lateinit var reviewDataList: MutableList<String>
lateinit var reviewDataList : MutableList<String>
/** 课程所有课时 */
lateinit var allLesson: List<Lesson>
lateinit var allLesson : List<Lesson>
//课程的详细信息
lateinit var courseDetail: CourseDetail
lateinit var courseDetail : CourseDetail
//获取课程的统计信息
//课程收藏本: 主要用于口语对话学习的数据
//获取课程的复习数据
@@ -53,38 +54,59 @@ class CourseMainFragmentViewModel(val courseIndex: Int) : BaseViewModel() {
fun loadMain() : MutableLiveData<Boolean> {
showHideLoading(true)
val mutableLiveData = MutableLiveData<Boolean>()
Observable.concat(
DataRepository.getCourseStatistics().flatMap {
courseDetail = it
Observable.concat(DataRepository.getCourseStatistics().flatMap {
courseDetail = it
return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it)
}, DataRepository.getReviewData()
, DataRepository.getCourseCollect())
.compose(diskIo2DiskIo())
.subscribe(object :Observer<Any>{
override fun onSubscribe(d: Disposable?) {

}, DataRepository.getReviewData(), DataRepository.getCourseCollect()).compose(diskIo2DiskIo())
.subscribe(object : Observer<Any> {
override fun onSubscribe(d : Disposable?) {
}
override fun onNext(t: Any?) {
override fun onNext(t : Any?) {
t?.let { it ->
if (it is List<*>){
if (it is List<*>) {
allLesson = it as List<Lesson>
}
}
}
override fun onError(e: Throwable?) {
override fun onError(e : Throwable?) {
e?.printStackTrace()
showHideLoading(false)
showToast(ToastEvent(content = "数据加载异常"))
}

override fun onComplete() {
showHideLoading(false)
mutableLiveData.postValue(true)
}
})
return mutableLiveData
}


/**
* 获取课程的测试数据
* @param testType Int 测试类型
* @param lesson Lesson? 课时章节
*/
fun loadTest(testType : Int, lesson : Lesson? = null) : MutableLiveData<List<ExamBean>> {
val result = MutableLiveData<List<ExamBean>>()
// showHideLoading(true)
Observable.create<List<ExamBean>> {
it.onNext(DBCourseManager.queryLearnTest(dbControlBase, testType, lesson))
it.onComplete()
}.compose(diskIo2Main()).subscribe({
// showHideLoading(false)
result.value = it
}, {
it.printStackTrace()
// showHideLoading(false)
})
return result
}
}

+ 34
- 12
app/src/main/java/com/xkl/cdl/module/m_center_learn/coursechildren/CourseTotalTestFragment.kt View File

@@ -9,12 +9,14 @@ import com.suliang.common.extension.click
import com.suliang.common.extension.setHtml
import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants
import com.xkl.cdl.data.bean.course.ExamBean
import com.xkl.cdl.data.manager.CourseManager
import com.xkl.cdl.databinding.FragmentCourseTotalTestBinding
import com.xkl.cdl.module.learn.LearnExamActivity

/**
* 课程总测试: 学前总,学后总
* @property param1 String?
* @property param2 String?
* @property totalTestType 测试类型
*/
class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, CourseMainFragmentViewModel>() {

@@ -30,18 +32,42 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C
override fun initViewModel(): CourseMainFragmentViewModel {
return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java]
}
override fun initFirst() {
super.initFirst()
//由于使用的是与父类相同的loadingEvent,所以将此evnet移除
vm.loadingEvent.removeObservers(this)
}

//传递过来的总测类型: 学前,学后
private var totalTestType = 0

//测试的数据
private var testData : List<ExamBean> = emptyList()
override fun initFragment() {
totalTestType = requireArguments().getInt(AppConfig.INTENT_1)
}

override fun loadData() {
vm.loadTest(totalTestType).observe(this){
testData = it
initView()
}
}
private fun initView(){
binding.tvCountTip.text = CourseManager.expectedTestTime(vm.course.courseType,totalTestType,testData!!)
when (totalTestType) {
AppConstants.TEST_TYPE_BEFORE_TOTAL -> {
//学前总测
binding.run {
tvTitle.setText(R.string.test_total_before_title)
tvCountTip.text = "共25题,预计3分钟"
button1.run {
setText(R.string.start_learn)
click {
continueLearn(it)
}
}
//按钮初始
button2.run {
setText(R.string.start_test)
@@ -51,7 +77,6 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C
}
}
}

AppConstants.TEST_TYPE_AFTER_TOTAL -> {
//学后总测
binding.run {
@@ -64,7 +89,6 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C
when {
it == -1.0 -> { //未测
tvMainTip.setText(R.string.test_total_after_tip_1)
tvCountTip.text = "共25题,预计3分钟"
button2.run {
setText(R.string.test_type_after_total)
click { view ->
@@ -115,19 +139,17 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C
}
}
}

override fun loadData() {

}

/**继续学习*/
private fun continueLearn(view: View) {
showToast("继续学习")
(requireParentFragment() as CourseMainFragment).changeFragment(1)
}

/** 开始测试 */
private fun startTest(view: View) {
showToast("开始测试")
LearnExamActivity.newInstance(requireContext())
}

}

+ 19
- 19
app/src/main/java/com/xkl/cdl/module/splash/SplashActivity.kt View File

@@ -19,7 +19,7 @@ import java.io.File
import java.util.concurrent.TimeUnit

@SuppressLint("CustomSplashScreen")
class SplashActivity : BaseActivity<ActivitySplashBinding>(), LearnDialog.DialogFragmentListener {
class SplashActivity : BaseActivity<ActivitySplashBinding>(){

override fun onCreate(savedInstanceState: Bundle?) {
if (!isTaskRoot) {
@@ -45,11 +45,11 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(), LearnDialog.Dialog
// e.printStackTrace()
// }

learnDialog = LearnDialog().apply {
show(supportFragmentManager,javaClass.name)
}
LogUtil.e("Dialog -- > ${learnDialog.hashCode()}")
if (true) return
// learnDialog = LearnDialog().apply {
// show(supportFragmentManager,javaClass.name)
// }
// LogUtil.e("Dialog -- > ${learnDialog.hashCode()}")
// if (true) return


showHideLoading(true)
@@ -72,20 +72,20 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(), LearnDialog.Dialog
}


lateinit var learnDialog :LearnDialog
lateinit var learnDialog1: LearnDialog

override fun rightClick() {
if (!this::learnDialog1.isInitialized) {
learnDialog1 = LearnDialog()
}
LogUtil.e("Dialog 1 -- > ${learnDialog1.hashCode()}")
learnDialog1.show(supportFragmentManager,javaClass.name)
}
// lateinit var learnDialog :LearnDialog
// lateinit var learnDialog1: LearnDialog

override fun leftClick() {
learnDialog1.dismiss()
}
// override fun rightClick() {
// if (!this::learnDialog1.isInitialized) {
// learnDialog1 = LearnDialog()
// }
// LogUtil.e("Dialog 1 -- > ${learnDialog1.hashCode()}")
// learnDialog1.show(supportFragmentManager,javaClass.name)
// }
//
// override fun leftClick() {
// learnDialog1.dismiss()
// }


}

+ 100
- 0
app/src/main/java/com/xkl/cdl/widget/VoiceSwitch.kt View File

@@ -0,0 +1,100 @@
package com.xkl.cdl.widget

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.MutableLiveData
import com.suliang.common.util.os.ScreenUtil
import com.xkl.cdl.R
import com.xkl.cdl.data.AppConstants

/**
* author suliang
* create 2022/4/7 16:12
* Describe: 自定义英美发音switch
*/
class VoiceSwitch @JvmOverloads constructor(context : Context, attr : AttributeSet? = null, defStyle : Int = 0) :
View(context, attr, defStyle) {
//背景图片和滑块
private val strack : Bitmap by lazy {
val drawable = ResourcesCompat.getDrawable(resources, R.drawable.shape_switch_track, null)!!
val createBitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(createBitmap)
drawable.setBounds(0,0,drawable.intrinsicWidth,drawable.intrinsicHeight)
drawable.draw(canvas)
createBitmap
// BitmapFactory.decodeResource(resources, R.drawable.shape_switch_track)
}
private val thumb : Bitmap by lazy {
val drawable = ResourcesCompat.getDrawable(resources, R.drawable.shape_switch_thumb, null)!!
val createBitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(createBitmap)
drawable.setBounds(0,0,drawable.intrinsicWidth,drawable.intrinsicHeight)
drawable.draw(canvas)
createBitmap
}
//默认英音
private var soundWay = AppConstants.SOUND_TYPE_UK
private val uk = "英音"
private val us = "美音"
val soundWayChange = MutableLiveData<Int>()
private var w : Int = 0
private var h : Int = 0
private val paint : Paint = Paint().apply {
isAntiAlias = true
textSize = ScreenUtil.dp2px(12f).toFloat()
color = ResourcesCompat.getColor(resources,R.color.main_text_color,null)
textAlign = Paint.Align.CENTER
style = Paint.Style.FILL
}
override fun onMeasure(widthMeasureSpec : Int, heightMeasureSpec : Int) {
w = strack.width + paddingStart + paddingRight
h = strack.height
setMeasuredDimension(w,h)
}
override fun onDraw(canvas : Canvas) {
// super.onDraw(canvas)
//背景滑块
// val drawable = ResourcesCompat.getDrawable(resources, R.drawable.shape_switch_track, null)!!
// drawable.setBounds(0,0,drawable.intrinsicWidth,drawable.intrinsicHeight)
// drawable.draw(canvas)
canvas.drawBitmap(strack,paddingStart.toFloat(),0f,paint)
when(soundWay){
AppConstants.SOUND_TYPE_UK -> {
canvas.drawBitmap(thumb,0f,0f,paint) //左边
canvas.drawText(uk, thumb.width/2f + paddingStart , h/2 + ((paint.fontMetrics.bottom - paint.fontMetrics.top)/2) - paint.fontMetrics.bottom,paint)
}
AppConstants.SOUND_TYPE_US -> {
canvas.drawBitmap(thumb,strack.width - thumb.width.toFloat(), 0f,paint) //右边
canvas.drawText(us,(w - thumb.width - paddingRight) + thumb.width/2f,h/2 + ((paint.fontMetrics.bottom - paint.fontMetrics.top)/2) - paint.fontMetrics.bottom,paint)
}
}
}
override fun onTouchEvent(event : MotionEvent) : Boolean {
when(event.action){
MotionEvent.ACTION_UP -> {
soundWay = if (soundWay == AppConstants.SOUND_TYPE_UK) AppConstants.SOUND_TYPE_US else AppConstants.SOUND_TYPE_UK
soundWayChange.value = soundWay
invalidate()
}
}
return true
}
/** 设置默认发音 */
fun setSoundWay(way : Int){
soundWay = way
invalidate()
}
}

+ 10
- 0
app/src/main/res/drawable/shape_switch_thumb.xml View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--滑块图片-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="24dp" android:width="40dp" />
<corners android:radius="12dp" />
<solid android:color="@color/white" />
<stroke
android:width="1dp"
android:color="@color/gray_1" />
</shape>

+ 8
- 0
app/src/main/res/drawable/shape_switch_track.xml View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--背景图片:滑动条-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="24dp" android:width="50dp" />
<corners android:radius="12dp" />
<solid android:color="@color/gray_1" />
</shape>

+ 7
- 1
app/src/main/res/layout/activity_learn_exam_word.xml View File

@@ -4,6 +4,12 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".module.learn.LearnExamWordActivity">
android:background="@color/white"
tools:context=".module.learn.LearnExamActivity">

<include
android:id="@+id/inc_learn_title"
layout="@layout/inc_learn_title" />


</androidx.constraintlayout.widget.ConstraintLayout>

+ 1
- 2
app/src/main/res/layout/fragment_course_total_test.xml View File

@@ -100,8 +100,7 @@
app:layout_constraintTop_toTopOf="@+id/button_2"
app:strokeColor="@color/theme_color"
app:strokeWidth="@dimen/line_height"
android:visibility="gone"
tools:visibility="visible"/>
/>

<Button
android:id="@+id/button_2"

+ 112
- 0
app/src/main/res/layout/inc_learn_test_statistic.xml View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
<variable
name="examType"
type="Long" />
<variable
name="totalNum"
type="Long" />
<variable
name="progressNum"
type="Integer" />
<!--时间 : 毫秒-->
<variable
name="time"
type="Long" />
<variable
name="correctNum"
type="Long" />
<variable
name="errorNum"
type="Long" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.mainlearncenter.com_fragment.TestingStatisticFragment">


<!--测试数量进度-->
<ProgressBar
android:id="@+id/progress_test_number"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_2"
android:indeterminateOnly="false"
app:layout_constraintTop_toTopOf="parent"
android:progressDrawable="@drawable/progress_statistics"
android:max="@{(int)totalNum}"
android:progress="@{progressNum}"/>

<!--错误数量-->
<TextView
android:id="@+id/tv_error_number"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_34"
android:textSize="@dimen/dp_12"
android:textColor="@color/color_6"
android:drawablePadding="@dimen/dp_2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progress_test_number"
android:drawableLeft="@mipmap/test_error_icon"
android:text="@{@string/error_number_format(errorNum)}"
android:layout_marginRight="@dimen/dp_16"
android:gravity="center"/>

<!--正确数量-->
<TextView
android:id="@+id/tv_correct_number"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_34"
android:textSize="@dimen/dp_12"
android:textColor="@color/color_6"
android:drawableLeft="@mipmap/test_right_icon"
android:drawablePadding="@dimen/dp_2"
app:layout_constraintRight_toLeftOf="@+id/tv_error_number"
app:layout_constraintTop_toBottomOf="@+id/progress_test_number"
android:text="@{@string/correct_number_foramt(correctNum)}"
android:layout_marginRight="@dimen/dp_16"
android:gravity="center" />

<!--测试类型-->
<TextView
android:id="@+id/tv_test_type_name"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_34"
android:textSize="@dimen/dp_12"
android:textColor="@color/color_6"
android:drawablePadding="@dimen/dp_2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progress_test_number"
tools:text="学后总测试"
android:gravity="center"
android:layout_marginLeft="@dimen/dp_16"
android:layout_marginRight="@dimen/dp_16"
android:singleLine="true"
android:maxEms="6"
android:ellipsize="end"
app:learnTestExamType="@{examType}"
/>

<!--测试时间-->
<TextView
android:id="@+id/tv_test_time"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_34"
android:textSize="@dimen/dp_12"
android:textColor="@color/color_6"
tools:text="00:00:00"
app:statisticsTotalTime="@{time}"
android:gravity="center"
app:layout_constraintLeft_toRightOf="@+id/tv_test_type_name"
app:layout_constraintTop_toBottomOf="@+id/progress_test_number"
android:layout_marginLeft="@dimen/dp_16"/>


</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

+ 70
- 0
app/src/main/res/layout/inc_learn_title.xml View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="0dp"
android:layout_height="@dimen/title_bar_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:showIn="@layout/activity_learn_exam_word">

<ImageView
android:id="@+id/img_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:paddingStart="@dimen/global_spacing"
android:scaleType="centerInside"
android:src="@drawable/ic_back"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="66dp"
android:ellipsize="end"
android:maxLines="1"
android:text="小学英语单词课程小学英语单词课程小学英语单词课程"
android:textColor="@color/main_text_color"
android:textSize="@dimen/bigSize"
android:textStyle="bold"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tv_num_progress"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginEnd="66dp" />

<TextView
android:id="@+id/tv_num_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="66dp"
android:text="90/100"
android:textColor="@color/main_text_color"
android:textSize="@dimen/bigSize"
android:textStyle="bold"
android:visibility="visible"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_title"
app:layout_constraintTop_toTopOf="parent" />


<com.xkl.cdl.widget.VoiceSwitch
android:id="@+id/voice_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/global_spacing"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

BIN
app/src/main/res/mipmap-xxhdpi/test_score_level_1.png View File


BIN
app/src/main/res/mipmap-xxhdpi/test_score_level_2.png View File


BIN
app/src/main/res/mipmap-xxhdpi/test_score_level_3.png View File


+ 0
- 1
app/src/main/res/values/attrs.xml View File

@@ -8,5 +8,4 @@
<attr name="progressNumber" format="integer"/> <!--弧形进度-->
<attr name="backgroundColor" format="color"/> <!--背景颜色-->
</declare-styleable>

</resources>

+ 1
- 0
app/src/main/res/values/colors.xml View File

@@ -26,6 +26,7 @@
<color name="gray_3">#FAFAFA</color>
<color name="green_1">#40A540</color>
<color name="red_1">#FFF26255</color>
<color name="red_2">#F7874F</color>


<color name="num0">#E50213</color>

+ 4
- 3
lib/common/build.gradle View File

@@ -12,7 +12,7 @@ android {
targetSdk rootProject.ext.android.target_sdk_version
versionCode 1
versionName "1.0"
// multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
@@ -30,6 +30,7 @@ android {
}
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@@ -44,7 +45,7 @@ android {

dependencies {
// implementation fileTree(include: ['*.jar',"*.aar"], dir: 'libs')
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
// implementation 'androidx.legacy:legacy-support-v4:1.0.0'
rootProject.ext.dependencies_required.each{ k, v -> implementation v}
testImplementation rootProject.ext.dependencies_testImplementation.junit
rootProject.ext.dependencies_androidTestImplementation.each{ k,v -> androidTestImplementation v}
@@ -63,7 +64,7 @@ dependencies {
//RxJava RxAndroid
api customDependencies.RxJava
api customDependencies.RxAndroid
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'




+ 26
- 4
lib/common/src/main/java/com/suliang/common/base/BaseDialogFragment.kt View File

@@ -1,5 +1,6 @@
package com.suliang.common.base

import android.R.attr
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
@@ -11,8 +12,11 @@ import com.suliang.common.extension.initBinding
import android.graphics.drawable.ColorDrawable

import android.util.DisplayMetrics


import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import android.R.attr.tag
import androidx.fragment.app.Fragment
import java.lang.IllegalStateException


/**
@@ -49,12 +53,30 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment() {
dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
}
}


abstract fun initFragment()

/** 是否布局背景透明 */
open fun isTransparent():Boolean {
return true
}
override fun show(manager : FragmentManager, tag : String?) {
//避免重复添加的异常 java.lang.IllegalStateException: Fragment already added
val fragment = manager.findFragmentByTag(tag)
if (fragment != null) {
val fragmentTransaction = manager.beginTransaction()
fragmentTransaction.remove(fragment)
fragmentTransaction.commitAllowingStateLoss()
}
//避免状态丢失的异常 java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
try {
super.show(manager, tag)
} catch (e : IllegalStateException) {
e.printStackTrace()
}
}

}

+ 4
- 2
lib/common/src/main/java/com/suliang/common/base/LoadingDialog.kt View File

@@ -13,9 +13,11 @@ class LoadingDialog : BaseDialogFragment<LoadingFragmentBinding>() {

companion object{
@JvmStatic
fun newInstance(msg : String): LoadingDialog {
fun newInstance(msg : String?): LoadingDialog {
return LoadingDialog().apply {
arguments?.putString(AppConfig.INTENT_1,msg)
if (!msg.isNullOrEmpty()) {
arguments?.putString(AppConfig.INTENT_1, msg)
}
}
}
}

+ 1
- 1
lib/common/src/main/java/com/suliang/common/base/ViewBehavior.kt View File

@@ -10,7 +10,7 @@ import com.suliang.common.base.activity.ToastEvent
interface ViewBehavior {
/** 是否显示视图 */
fun showHideLoading(isShow: Boolean)
/** 显示toast */
fun showToast(msg: ToastEvent)


+ 8
- 181
lib/common/src/main/java/com/suliang/common/base/activity/BaseActivity.kt View File

@@ -1,199 +1,26 @@
package com.suliang.common.base.activity

import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.viewbinding.ViewBinding
import com.suliang.common.base.LoadingDialog
import com.suliang.common.extension.initBinding
import com.suliang.common.util.ActivityStackManager
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly
import com.suliang.common.base.ViewBehavior
import com.suliang.common.util.LogUtil


/**
* 基类Activity
*/
abstract class BaseActivity<VB : ViewBinding> : LifecycleLogActivity(), ViewBehavior {
private var _binding: VB? = null
val binding: VB
abstract class BaseActivity<VB : ViewBinding> : UIBaseActivity() {
private var _binding : VB? = null
val binding : VB
get() = _binding!!

// val binding : VB by lazy {
// initViewBinding()
// }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//入栈
ActivityStackManager.addActivity(this)
initFirst()
initActivity(savedInstanceState)
loadData()
}

/***
* 实例化binding,
*/
protected open fun initFirst() {
setLayoutBefore()
override fun setContentView() {
_binding = initBinding(layoutInflater)
setContentView(binding.root)
initStatusBar()
setLayoutAfter()
}

/**
* 状态栏初始化方法
*/
open fun initStatusBar() {
statusBarOnly {
fitWindow = true
color = Color.WHITE
light = true
}
}

override fun onDestroy() {
super.onDestroy()
ActivityStackManager.removeActivity(this)
_binding = null
}

/** 布局前操作 : 空实现 */
open fun setLayoutBefore() {

}

/**
* 布局后操作:空实现
*/
open fun setLayoutAfter() {

}

/**
* onCreate中给与app中用户的具体实现
* binding 与 ViewModel都已实现好
* @param savedInstanceState Bundle?
*/
abstract fun initActivity(savedInstanceState: Bundle?)

/** onCreate()最后实现的数据加载调用 */
abstract fun loadData()

private val loadingDialog by lazy {
LoadingDialog()
}

/** toast提示 */
override fun showToast(event: ToastEvent) {
val showTime = if (event.showLong) Toast.LENGTH_LONG else Toast.LENGTH_LONG
event.content?.let {
Toast.makeText(this, it, showTime).show()
} ?: let {
Toast.makeText(this, event.contentResId!!, showTime).show()
}
}

protected fun showToast(text: String, showLong: Boolean = false) {
showToast(ToastEvent(content = text, showLong = showLong))
}

protected fun showToast(@StringRes resId: Int, showLong: Boolean = false) {
showToast(ToastEvent(contentResId = resId, showLong = showLong))
}

override fun showHideLoading(isShow: Boolean) {
if (isShow) {
loadingDialog.show(supportFragmentManager, javaClass.name)
} else if (loadingDialog.activity != null) {
loadingDialog.dismiss()
}
}

/** 单独封装一个方法,用于进行无参数的activity的跳转 */
override fun startActivity(clazz: Class<*>) {
startActivity(Intent(this, clazz))
}

//覆写该方法,避免多次跳转失败的问题
override fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) {
if (checkDoubleClick(intent)) {
super.startActivityForResult(intent, requestCode, options)
} else {
LogUtil.e("多次跳转无效操作--> $intent")
}
}

// private var mActivityJumpTag : String = "" // activity跳转tag
private var clickTime = 0L //点击时间

//避免多次点击跳转到同一个activity 和 避免短时间内多次点击跳转到不同activity
private fun checkDoubleClick(intent: Intent): Boolean {
val currentTime = System.currentTimeMillis()
var result = true
var tag: String = ""
if (intent.component != null) { //显示跳转
tag = intent.component!!.className
} else if (intent.action != null) { //隐式跳转
tag = intent.action!!
} else {
return true
}

// result = tag == mActivityJumpTag && currentTime - clickTime >= 500
result = currentTime - clickTime >= 500

// mActivityJumpTag = tag
clickTime = currentTime

return result
}

// 反射实现 ViewBinding
// @SuppressWarnings("unchecked")
// fun initBinding(inflater: LayoutInflater): VB {
// val vbClass =
// (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance<Class<VB>>()
// val inflate = vbClass[0].getDeclaredMethod("inflate", LayoutInflater::class.java)
// return inflate.invoke(null, inflater) as VB
// }

// https://juejin.cn/post/6926007534188773384#heading-1
// private fun initViewBinding(): VB{
// return reflexViewBinding(this::class.java,layoutInflater)
// }
// private fun <V : ViewBinding?> reflexViewBinding(aClass: Class<*>, from: LayoutInflater?): V {
// return try {
// val genericSuperclass: Type = aClass.genericSuperclass
// if (genericSuperclass is ParameterizedType) {
// val actualTypeArguments: Array<Type> =
// (Objects.requireNonNull(genericSuperclass) as ParameterizedType).getActualTypeArguments()
// for (actualTypeArgument in actualTypeArguments) {
// var tClass: Class<*>
// tClass = try {
// actualTypeArgument as Class<*>
// } catch (e: Exception) {
// continue
// }
// if (ViewBinding::class.java.isAssignableFrom(tClass)) {
// val inflate: Method = tClass.getMethod("inflate", LayoutInflater::class.java)
// return inflate.invoke(null, from) as V
// }
// }
// }
// reflexViewBinding(aClass.superclass, from)
// } catch (e: Exception) {
// e.printStackTrace()
// throw RuntimeException(e.message, e)
// }
// }


}

+ 4
- 1
lib/common/src/main/java/com/suliang/common/base/activity/BaseActivityVM.kt View File

@@ -2,6 +2,7 @@ package com.suliang.common.base.activity

import androidx.viewbinding.ViewBinding
import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.util.LogUtil

/**
* Activity DataBinding 与 ViewModel基类,封装DataBinding和ViewModel
@@ -26,7 +27,9 @@ abstract class BaseActivityVM<VB : ViewBinding, VM : BaseViewModel> : BaseActivi

vm.pageEvent.observe(this) { startActivity(it) }
vm.toastEvent.observe(this){ showToast(it) }
vm.loadingEvent.observe(this) { showHideLoading(it)}
vm.loadingEvent.observe(this) {
LogUtil.e("监听到 activity vm 的 loadingEvent isShow $it")
showHideLoading(it)}

}


+ 196
- 0
lib/common/src/main/java/com/suliang/common/base/activity/UIBaseActivity.kt View File

@@ -0,0 +1,196 @@
package com.suliang.common.base.activity

import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.widget.Toast
import androidx.annotation.StringRes
import com.suliang.common.base.LoadingDialog
import com.suliang.common.base.ViewBehavior
import com.suliang.common.util.ActivityStackManager
import com.suliang.common.util.LogUtil
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly

/**
* author suliang
* create 2022/4/7 11:45
* Describe:
*/
abstract class UIBaseActivity : LifecycleLogActivity(), ViewBehavior {
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
//入栈
ActivityStackManager.addActivity(this)
initFirst()
initActivity(savedInstanceState)
loadData()
}
/***
* 实例化binding,
*/
protected open fun initFirst() {
setLayoutBefore()
initContentView()
initStatusBar()
setLayoutAfter()
}
private fun initContentView() {
setContentView()
}
abstract fun setContentView()
/**
* 状态栏初始化方法
*/
open fun initStatusBar() {
statusBarOnly {
fitWindow = true
color = Color.WHITE
light = true
}
}
override fun onDestroy() {
super.onDestroy()
ActivityStackManager.removeActivity(this)
}
/** 布局前操作 : 空实现 */
open fun setLayoutBefore() {}
/**
* 布局后操作:空实现
*/
open fun setLayoutAfter() {}
/**
* onCreate中给与app中用户的具体实现
* binding 与 ViewModel都已实现好
* @param savedInstanceState Bundle?
*/
abstract fun initActivity(savedInstanceState : Bundle?)
/** onCreate()最后实现的数据加载调用 */
abstract fun loadData()
private var loadingDialog : LoadingDialog? = null
private var dialogList = arrayListOf<String>()
/** toast提示 */
override fun showToast(event : ToastEvent) {
val showTime = if (event.showLong) Toast.LENGTH_LONG else Toast.LENGTH_LONG
event.content?.let {
Toast.makeText(this, it, showTime).show()
}
?: let {
Toast.makeText(this, event.contentResId!!, showTime).show()
}
}
protected fun showToast(text : String, showLong : Boolean = false) {
showToast(ToastEvent(content = text, showLong = showLong))
}
protected fun showToast(@StringRes resId : Int, showLong : Boolean = false) {
showToast(ToastEvent(contentResId = resId, showLong = showLong))
}
override fun showHideLoading(isShow : Boolean) {
val findFragment = supportFragmentManager.findFragmentByTag("loading")
if (isShow) { //显示
findFragment?.let {
if (findFragment is LoadingDialog) {
findFragment.dialog?.show()
}
}
?: let {
val loadingDialog = LoadingDialog.newInstance("")
loadingDialog.show(supportFragmentManager, "loading")
}
} else { //关闭
findFragment?.let {
if (it is LoadingDialog) {
it.dismissAllowingStateLoss()
}
}
}
}
/** 单独封装一个方法,用于进行无参数的activity的跳转 */
override fun startActivity(clazz : Class<*>) {
startActivity(Intent(this, clazz))
}
//覆写该方法,避免多次跳转失败的问题
override fun startActivityForResult(intent : Intent, requestCode : Int, options : Bundle?) {
if (checkDoubleClick(intent)) {
super.startActivityForResult(intent, requestCode, options)
} else {
LogUtil.e("多次跳转无效操作--> $intent")
}
}
private var clickTime = 0L //点击时间
//避免多次点击跳转到同一个activity 和 避免短时间内多次点击跳转到不同activity
private fun checkDoubleClick(intent : Intent) : Boolean {
val currentTime = System.currentTimeMillis()
var result = true
var tag : String = ""
if (intent.component != null) { //显示跳转
tag = intent.component!!.className
} else if (intent.action != null) { //隐式跳转
tag = intent.action!!
} else {
return true
}
result = currentTime - clickTime >= 500
clickTime = currentTime
return result
}
}
// 反射实现 ViewBinding
// @SuppressWarnings("unchecked")
// fun initBinding(inflater: LayoutInflater): VB {
// val vbClass =
// (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance<Class<VB>>()
// val inflate = vbClass[0].getDeclaredMethod("inflate", LayoutInflater::class.java)
// return inflate.invoke(null, inflater) as VB
// }

// https://juejin.cn/post/6926007534188773384#heading-1
// private fun initViewBinding(): VB{
// return reflexViewBinding(this::class.java,layoutInflater)
// }
// private fun <V : ViewBinding?> reflexViewBinding(aClass: Class<*>, from: LayoutInflater?): V {
// return try {
// val genericSuperclass: Type = aClass.genericSuperclass
// if (genericSuperclass is ParameterizedType) {
// val actualTypeArguments: Array<Type> =
// (Objects.requireNonNull(genericSuperclass) as ParameterizedType).getActualTypeArguments()
// for (actualTypeArgument in actualTypeArguments) {
// var tClass: Class<*>
// tClass = try {
// actualTypeArgument as Class<*>
// } catch (e: Exception) {
// continue
// }
// if (ViewBinding::class.java.isAssignableFrom(tClass)) {
// val inflate: Method = tClass.getMethod("inflate", LayoutInflater::class.java)
// return inflate.invoke(null, from) as V
// }
// }
// }
// reflexViewBinding(aClass.superclass, from)
// } catch (e: Exception) {
// e.printStackTrace()
// throw RuntimeException(e.message, e)
// }
// }


+ 7
- 10
lib/common/src/main/java/com/suliang/common/base/fragment/BaseFragment.kt View File

@@ -12,8 +12,10 @@ import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import com.suliang.common.base.LoadingDialog
import com.suliang.common.base.ViewBehavior
import com.suliang.common.base.activity.BaseActivity
import com.suliang.common.base.activity.ToastEvent
import com.suliang.common.extension.initBinding
import com.suliang.common.util.LogUtil

/**
* Fragment ViewBinding基类,封装ViewDataBinding 自带懒加载
@@ -82,12 +84,7 @@ abstract class BaseFragment<VB : ViewBinding> : Fragment(),ViewBehavior {
* 界面显示出来后再加载数据
*/
abstract fun loadData()



private val loadingDialog by lazy {
LoadingDialog()
}

/** toast提示 */
override fun showToast(event: ToastEvent) {
@@ -107,11 +104,11 @@ abstract class BaseFragment<VB : ViewBinding> : Fragment(),ViewBehavior {
showToast(ToastEvent(contentResId = resId, showLong = showLong))
}

/** 调用父类的 加载框 */
override fun showHideLoading(isShow: Boolean) {
if (isShow) {
loadingDialog.show(childFragmentManager, javaClass.name)
} else if (loadingDialog.activity != null){
loadingDialog.dismiss()
synchronized(this){
// LogUtil.e(" BaseFragment showHideLoding -> $isShow")
(activity as BaseActivity<*>).showHideLoading(isShow)
}
}


+ 6
- 1
lib/common/src/main/java/com/suliang/common/base/fragment/BaseFragmentVM.kt View File

@@ -4,9 +4,11 @@ import androidx.databinding.ViewDataBinding
import androidx.lifecycle.ViewModel
import androidx.viewbinding.ViewBinding
import com.suliang.common.base.viewmodel.BaseViewModel
import com.suliang.common.util.LogUtil

/**
* Fragment DataBinding 与 ViewModel基类,封装DataBinding和ViewModel 自带懒加载
* 如果vm 初始的为已存在的(即父fragmnet或父Activity)vm,则重写initFirst,避免出现loading显示异常的问题
* @property VB : ViewDataBinding
* @property VM: ViewModel
*/
@@ -17,7 +19,10 @@ abstract class BaseFragmentVM<VB : ViewBinding, VM : BaseViewModel> : BaseFragme
vm = initViewModel()
vm.pageEvent.observe(this) { startActivity(it) }
vm.toastEvent.observe(this){ showToast(it) }
vm.loadingEvent.observe(this) { showHideLoading(it)}
vm.loadingEvent.observe(this) {
LogUtil.e("监听到 fragment vm 的 loadingEvent isShow $it")
showHideLoading(it)
}
}

abstract fun initViewModel(): VM

+ 5
- 11
lib/common/src/main/java/com/suliang/common/base/viewmodel/BaseViewModel.kt View File

@@ -48,24 +48,18 @@ open class BaseViewModel : ViewModel(), ViewModelLifecycle, ViewBehavior {
super.onCleared()
LogUtil.i("${javaClass.name} onCleared() ")
}

var loadingEvent = MutableLiveData<Boolean>()
private set

var toastEvent = MutableLiveData<ToastEvent>()
private set

var pageEvent = MutableLiveData<Class<*>>()
private set
val loadingEvent = MutableLiveData<Boolean>()
val toastEvent = MutableLiveData<ToastEvent>()
val pageEvent = MutableLiveData<Class<*>>()

override fun showHideLoading(isShow: Boolean) {
loadingEvent.postValue(isShow)
}

override fun showToast(msg: ToastEvent) {
toastEvent.postValue(msg)
}

override fun startActivity(clazz: Class<*>) {
pageEvent.postValue(clazz)
}

+ 7
- 0
lib/common/src/main/java/com/suliang/common/extension/RxScheduler.kt.kt View File

@@ -15,4 +15,11 @@ fun <T> diskIo2DiskIo(): ObservableTransformer<T, T> {
upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO))
.observeOn(Schedulers.from(AppExecutors.diskIO))
}
}

fun <T> diskIo2Main(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream ->
upstream!!.subscribeOn(Schedulers.from(AppExecutors.diskIO))
.observeOn(Schedulers.from(AppExecutors.mainThread))
}
}

Loading…
Cancel
Save