@@ -63,6 +63,7 @@ | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_total_test.xml" value="0.33" /> | |||
<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_memo.xml" value="0.20104166666666667" /> | |||
<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_control_button.xml" value="0.46467391304347827" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_spell_content.xml" value="0.45153985507246375" /> |
@@ -1,5 +1,6 @@ | |||
package com.xkl.cdl | |||
import android.annotation.SuppressLint | |||
import android.util.TypedValue | |||
import android.view.View | |||
import android.widget.LinearLayout | |||
@@ -20,6 +21,7 @@ import com.xkl.cdl.databinding.IncWordDetailBinding | |||
* @param example String? 例句 | |||
* @param refrence String? 参考 | |||
*/ | |||
@SuppressLint("SetTextI18n") | |||
fun IncWordDetailBinding.initValue(phrase : String?, example : String?, refrence : String?){ | |||
phrase?.let { | |||
tvPhraseFlag.visibility = View.VISIBLE | |||
@@ -45,7 +47,7 @@ fun IncWordDetailBinding.initValue(phrase : String?, example : String?, refrence | |||
example?.let { | |||
tvExampleFlag.visibility = View.VISIBLE | |||
tvExample.visibility = View.VISIBLE | |||
tvExample.text = example | |||
tvExample.text = "▪ ${it.replace("\n", "\n▪ ")}" | |||
}?:let { | |||
tvExampleFlag.visibility = View.GONE | |||
tvExample.visibility = View.GONE |
@@ -78,6 +78,15 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C | |||
} | |||
} | |||
} | |||
private val normalColorNotLearnOver by lazy { | |||
ContextCompat.getColor(context, R.color.gray_2) //学习未完成未选中时的颜色 | |||
} | |||
private val normalColorLearnOver by lazy { ContextCompat.getColor(context, R.color.gray_1) } //学习已完成未选中时的颜色 | |||
private val mainTextColor by lazy {ContextCompat.getColor(context, R.color.main_text_color) } //选中颜色 | |||
private val translationColor by lazy {ContextCompat.getColor(context, R.color.translation) } //透明颜色 | |||
private val themeColor by lazy { ContextCompat.getColor(context,R.color.theme_color) } //主题颜色 | |||
/** | |||
* 处理颜色 选中色 未选中色(学习完成/学习未完成) | |||
@@ -86,10 +95,6 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C | |||
* @param lesson Lesson | |||
*/ | |||
private fun ItemCourseLessonBinding.initColor(position: Int, lesson: Lesson) { | |||
val normalColorNotLearnOver = ContextCompat.getColor(context, R.color.gray_2) //学习未完成未选中时的颜色 | |||
val normalColorLearnOver = ContextCompat.getColor(context, R.color.gray_1) //学习已完成未选中时的颜色 | |||
val mainTextColor = ContextCompat.getColor(context, R.color.main_text_color) //选中颜色 | |||
val translationColor = ContextCompat.getColor(context, R.color.translation) //透明颜色 | |||
//item的背景和状态设置 | |||
if (selectPos == position) { //选中,背景设置 | |||
layoutContent.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_3)) //选中背景 | |||
@@ -106,7 +111,7 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C | |||
} | |||
} else { //未选中:根据学习情况判断 | |||
layoutContent.setBackgroundColor(translationColor) //未选中背景透明 | |||
if (lesson.learnIsOver) { | |||
if (lesson.learnIsOver) { //学习完成 | |||
tvLessonName.run { | |||
setTextColor(normalColorLearnOver) //课时名称颜色 | |||
paint.isFakeBoldText = false //加粗 | |||
@@ -114,10 +119,10 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C | |||
tvLessonNumber.setTextColor(normalColorLearnOver) //数量颜色 | |||
ivArrowRight.run { | |||
setBackgroundColor(translationColor) | |||
imageTintList = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.theme_color)) | |||
strokeColor = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.theme_color)) | |||
imageTintList = ColorStateList.valueOf(normalColorLearnOver) | |||
strokeColor = ColorStateList.valueOf(normalColorLearnOver) | |||
} | |||
} else { | |||
} else { //未完成 | |||
tvLessonName.run { | |||
setTextColor(normalColorNotLearnOver) //课时名称颜色 | |||
paint.isFakeBoldText = false //加粗 | |||
@@ -125,8 +130,8 @@ class AdapterLesson(vm: CourseMainFragmentViewModel) : BaseRVAdapterVM<Lesson, C | |||
tvLessonNumber.setTextColor(normalColorNotLearnOver) //数量颜色 | |||
ivArrowRight.run { | |||
setBackgroundColor(translationColor) | |||
imageTintList = ColorStateList.valueOf(normalColorNotLearnOver) | |||
strokeColor = ColorStateList.valueOf(normalColorNotLearnOver) | |||
imageTintList = ColorStateList.valueOf(themeColor) | |||
strokeColor = ColorStateList.valueOf(themeColor) | |||
} | |||
} | |||
} |
@@ -154,10 +154,19 @@ object AppConstants { | |||
const val DIALOG_TYPE_EXAM_OVER = 2 | |||
/** 对话框类型: 学习结束弹窗类型 */ | |||
const val DIALOG_TYPE_LEARN_OVER = 3 | |||
/**对话框类型,item学习完成*/ | |||
const val DIALOG_TYPE_LESSON_ITEM_OVER = 4 | |||
/**--- 总线动作 --------------------------------- */ | |||
/**action key 改变界面 到目录页 */ | |||
const val EVENT_COURSE = "action_change_page" | |||
const val EVENT_CHANGE_PAGE = "action_change_page" | |||
/** lesson学习 数据传递 */ | |||
const val EVENT_LESSON_DATA = "lesson_learn_data" | |||
/** lesson学习中动作事件 : 课时学习中的动作*/ | |||
const val EVENT_LESSON_ACTION = "lesson_learn_action" | |||
/**总测数据与动作*/ | |||
const val EVENT_TOTAL_TEST = "total_test" | |||
/** 事件动作:学前总测结束弹窗之 开始学习 */ | |||
const val ACTION_COURSE_TEST_START_LEARN = 1 | |||
@@ -177,6 +186,12 @@ object AppConstants { | |||
const val ACTION_LESSON_AFTER_TEST_NEXT = 8 | |||
/**数据动作: 课时学习完成*/ | |||
const val DATA_LESSON_LEARN_OVER = 9 | |||
/**学后总测试完成: 再测一次*/ | |||
const val ACTION_COURSE_TEST_AFTER_TOTAL_AGAIN = 10 | |||
/** 学后总测试完成,切换到目录页显示 */ | |||
const val ACTION_COURSE_TEST_AFTER_TOTAL_OVER = 11 | |||
/**学后总测结束,传递数据*/ | |||
const val DATA_COURSE_AFTER_TEST_OVER = 12 | |||
/**--- 弹窗动作 --------------------------------- */ | |||
/** 学前总测结束弹窗: 开始学习 ,课时学前测试开始弹窗*/ | |||
@@ -189,6 +204,8 @@ object AppConstants { | |||
const val DIALOG_LESSON_AFTER_TEST_AGAIN = 4 | |||
/**课时学后测试弹窗动作: 下一步*/ | |||
const val DIALOG_LESSON_AFTER_TEST_NEXT = 5 | |||
/**弹窗动作提示: 完成*/ | |||
const val DIALOG_OVER = 6 | |||
/**课时学后总测的再测一次*/ | |||
const val DIALOG_AFTER_TOTAL_TEST_AGAIN = 7 | |||
} |
@@ -29,7 +29,7 @@ import android.os.Parcelable | |||
* 词汇量测试 | |||
* | |||
*/ | |||
class LearnDialogBean(val dialogType:Int) : Parcelable { | |||
class LearnDialogBean(val dialogType:Int) :Parcelable { | |||
/*---------------测试使用------------------------------------*/ | |||
/** 测试类型 */ | |||
@@ -46,19 +46,13 @@ class LearnDialogBean(val dialogType:Int) : Parcelable { | |||
/** 题数与时间 */ | |||
var showTimeCount = "" | |||
constructor(parcel : Parcel) : this(parcel.readInt()) { | |||
examType = parcel.readInt() | |||
score = parcel.readInt() | |||
correctNumber = parcel.readInt() | |||
errorNumber = parcel.readInt() | |||
chapter_lesson_name = parcel.readString().toString() | |||
showTimeCount = parcel.readString().toString() | |||
} | |||
override fun writeToParcel(parcel : Parcel, flags : Int) { | |||
@@ -67,6 +61,8 @@ class LearnDialogBean(val dialogType:Int) : Parcelable { | |||
parcel.writeInt(score) | |||
parcel.writeInt(correctNumber) | |||
parcel.writeInt(errorNumber) | |||
parcel.writeString(chapter_lesson_name) | |||
parcel.writeString(showTimeCount) | |||
} | |||
override fun describeContents() : Int { | |||
@@ -82,6 +78,4 @@ class LearnDialogBean(val dialogType:Int) : Parcelable { | |||
return arrayOfNulls(size) | |||
} | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
package com.xkl.cdl.data.bean.course | |||
import androidx.databinding.BaseObservable | |||
/** | |||
* author suliang | |||
* create 2022/3/22 10:08 |
@@ -1,15 +1,18 @@ | |||
package com.xkl.cdl.data.bean.course | |||
import com.xkl.cdl.data.AppConstants | |||
import java.io.Serializable | |||
/** | |||
* author suliang | |||
* create 2022/3/29 9:52 | |||
* Describe: 课程学习的详情 | |||
*/ | |||
class CourseDetail { | |||
class CourseDetail:Serializable{ | |||
var courseLearnProgress: Double = 0.0 //课程学习进度 | |||
var st_before: Double = -1.0 //学前总成绩 : -1代表没有进行学前总测试 | |||
var st_after: Double = -1.0 //学后总成绩 : -1代表没有进行学后总测试 | |||
var st_before: Double = AppConstants.NOT_DOING //学前总成绩 : -1代表没有进行学前总测试 | |||
var st_after: Double = AppConstants.NOT_DOING //学后总成绩 : -1代表没有进行学后总测试 | |||
var before: HashMap<String, Double> = hashMapOf() //章节学前测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩 | |||
var after: HashMap<String, Double> = hashMapOf() //章节学后测试成绩,key=>{chapter_id}_{lesson_id} value=>成绩 | |||
var right = hashMapOf<String, Int>() //正确条目数,key=>{chapter_id}_{lesson_id} value=>条目数量 |
@@ -30,9 +30,9 @@ data class Lesson( | |||
/** 学习进度位置,为已学下标的位置,在作文课堂练习时,学习取当前条,其他取下一条*/ | |||
var learnedIndex: Int = -1 | |||
/**课时学前测试成绩 */ | |||
var beforeTestScore = -1.0 | |||
var beforeTestScore = AppConstants.NOT_DOING | |||
/** 课时学后测试 */ | |||
var afterTestScore = -1.0 | |||
var afterTestScore = AppConstants.NOT_DOING | |||
/**是否学习完成: 为学习内容是否完成,不包含学前,学后测试*/ | |||
var learnIsOver = false | |||
/**是否是最后一个课时 */ |
@@ -225,7 +225,7 @@ object CourseManager { | |||
} | |||
/** | |||
* 计算项目总进度 | |||
* 计算项目总进度 ,并将课程进度值设置进入集合中 | |||
* @param subjectId Int 项目Id | |||
* @param packId Long 修改进度的课程包id | |||
* @param courseId Long 修改进度的课程id | |||
@@ -289,4 +289,35 @@ object CourseManager { | |||
if (totalCourseSize == 0) return 0.0 | |||
return totalProgress / totalCourseSize | |||
} | |||
/** | |||
* 重学课程的课时时,计算项目的进度,不会进行赋值 | |||
* @param subjectId Int 项目 | |||
* @param courseId Long 重学的课程 | |||
* @param courseProgress Double 被重学课时的课程进度 | |||
* @return Double 项目的总进度 | |||
*/ | |||
fun calculateSubjectProgressWithCourseLessonRelearn(subjectId : Int,coursePackId:Long, courseId : Long,courseProgress:Double) : Double { | |||
val totalProgress = mSortInfoList.get(subjectId)?.let { | |||
var tempProgress = 0.0 | |||
it.forEach { | |||
if (it.packId == coursePackId && it.courseId == courseId){ | |||
tempProgress += courseProgress | |||
return@forEach | |||
}else { | |||
tempProgress += it.s | |||
} | |||
} | |||
tempProgress | |||
}?: 0.0 | |||
if (totalProgress == 0.0 ) return totalProgress | |||
var totalCourseSize = 0 | |||
subjectWithCoursePackMap.get(subjectId)?.forEach { | |||
totalCourseSize += it.childrenCourses.size | |||
} | |||
if (totalCourseSize == 0) return 0.0 | |||
return totalProgress / totalCourseSize | |||
} | |||
} |
@@ -124,8 +124,8 @@ object DBCourseManager { | |||
totalNumber = this.wordIds.size //总数 | |||
correctNumber = detail.right.getOrElse(key, { 0 }) //正确数 | |||
errorNumber = detail.wrong.getOrElse(key, { 0 }) //错误数 | |||
beforeTestScore = detail.before.getOrElse(key, { -1.0 }) //课时学前测试成绩 | |||
afterTestScore = detail.after.getOrElse(key, { -1.0 }) //课时学后测试成绩 | |||
beforeTestScore = detail.before.getOrElse(key, { AppConstants.NOT_DOING }) //课时学前测试成绩 | |||
afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩 | |||
this.learnedIndex = learnIndex //学习位置,当前位置为已学 | |||
this.learnIsOver = learnIsOver | |||
this.lessonType = lessonType |
@@ -3,7 +3,6 @@ package com.xkl.cdl.data.repository | |||
import android.util.LruCache | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.AppConfig | |||
import com.suliang.common.eventbus.NonStickyMutableLiveData | |||
import com.suliang.common.extension.diskIo2Main | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.AppConstants | |||
@@ -30,12 +29,12 @@ object AudioCache { | |||
} | |||
/** 通知查到的录音 */ | |||
var audioLiveData = NonStickyMutableLiveData<String?>() | |||
private lateinit var audioLiveData : MutableLiveData<String?> | |||
// fun initAudioLiveData() : MutableLiveData<String?>{ | |||
// audioLiveData = NonStickyMutableLiveData<String?>() | |||
// return audioLiveData | |||
// } | |||
fun initAudioLiveData() : MutableLiveData<String?> { | |||
audioLiveData = MutableLiveData<String?>() | |||
return audioLiveData | |||
} | |||
/** | |||
* 获取对应音频 | |||
@@ -78,6 +77,7 @@ object AudioCache { | |||
return@fromCallable lruCache.get(defaultKey) ?: "" | |||
}.compose(diskIo2Main()).subscribe { | |||
if (this::audioLiveData.isInitialized) | |||
audioLiveData.value = it | |||
} | |||
} |
@@ -1,12 +1,14 @@ | |||
package com.xkl.cdl.data.repository | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.bean.course.CourseDetail | |||
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 io.reactivex.rxjava3.core.Observable | |||
import mqComsumerV1.Struct | |||
import java.io.File | |||
/** | |||
* author suliang | |||
@@ -33,9 +35,16 @@ object DataRepository { | |||
} | |||
/** 获取课程的详情 */ | |||
fun getCourseStatistics(): Observable<CourseDetail>{ | |||
fun getCourseStatistics(subjectId:Int,coursePackId:Long,courseId:Long): Observable<CourseDetail>{ | |||
return Observable.create{ | |||
val courseDetail = CourseDetail() | |||
// TODO: 2022/5/6 这里自己使用的缓存 | |||
val file = File(FileUtil.getSaveDirPath("appcache"), "${subjectId}_${coursePackId}_${courseId}") | |||
val courseDetail = if (file.exists()){ | |||
FileUtil.bytesToObject(FileUtil.readFile(file)) as CourseDetail | |||
}else{ | |||
CourseDetail() | |||
} | |||
it.onNext(courseDetail) | |||
it.onComplete() | |||
} |
@@ -2,15 +2,10 @@ package com.xkl.cdl.data.repository | |||
import android.util.LruCache | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.AppConfig | |||
import com.suliang.common.eventbus.NonStickyMutableLiveData | |||
import com.suliang.common.extension.diskIo2Main | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.manager.db.DBCourseManager | |||
import com.xkl.cdl.data.manager.db.DbControlBase | |||
import io.reactivex.rxjava3.core.Observable | |||
import java.io.File | |||
import java.util.* | |||
/** | |||
@@ -29,9 +24,13 @@ object PhotoCache { | |||
} | |||
} | |||
/** 通知查到的录音 */ | |||
var photoLiveData = NonStickyMutableLiveData<ByteArray?>() | |||
/** 通知查到的图片字节码 */ | |||
private lateinit var photoLiveData : MutableLiveData<ByteArray?> | |||
fun initPhotoLiveData() : MutableLiveData<ByteArray?> { | |||
photoLiveData = MutableLiveData<ByteArray?>() | |||
return photoLiveData | |||
} | |||
/** | |||
* 获取对应图片 | |||
* 1缓存 2文件 3数据库 | |||
@@ -50,6 +49,7 @@ object PhotoCache { | |||
return@fromCallable lruCache.get(defaultKey) | |||
}.compose(diskIo2Main()).subscribe { | |||
if (this::photoLiveData.isInitialized) | |||
photoLiveData.value = it | |||
} | |||
} |
@@ -1,17 +0,0 @@ | |||
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,6 +52,8 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
AppConstants.DIALOG_TYPE_EXAM_START -> when(learnDialogBean.examType){ | |||
//课时学前测试开始弹窗 | |||
AppConstants.TEST_TYPE_BEFORE -> initLessonBeforeTestStart() | |||
//课时最后一个课时测试结束的下一步弹出此弹窗:学后总测试开始的弹窗 | |||
AppConstants.TEST_TYPE_AFTER_TOTAL -> initCourseAfterTestStart() | |||
} | |||
//测试结束弹窗 | |||
AppConstants.DIALOG_TYPE_EXAM_OVER -> when (learnDialogBean.examType) { | |||
@@ -59,9 +61,15 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
AppConstants.TEST_TYPE_BEFORE_TOTAL -> initCourseBeforeTotalTestOver() | |||
//课时学前测试结束弹窗 | |||
AppConstants.TEST_TYPE_BEFORE-> initLessonBeforeTestOver() | |||
//课时后测试结束 | |||
AppConstants.TEST_TYPE_AFTER -> initLessonAfterTestOver() | |||
//学后总测试结束弹窗 | |||
AppConstants.TEST_TYPE_AFTER_TOTAL -> initCourseAfterTestOver() | |||
} | |||
//学习结束弹窗 | |||
AppConstants.DIALOG_TYPE_LEARN_OVER -> initLessonLearningOver() | |||
/**课时列表点击课时完全完成*/ | |||
AppConstants.DIALOG_TYPE_LESSON_ITEM_OVER -> initLessonItemClickLessonOver() | |||
} | |||
} | |||
@@ -131,6 +139,7 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
*/ | |||
private fun initLessonBeforeTestStart() { | |||
binding.run { | |||
ivClose.visibility = View.VISIBLE | |||
tvTitle.visibility = View.VISIBLE | |||
tvLessonName.visibility = View.VISIBLE | |||
tvCountTime.visibility = View.VISIBLE | |||
@@ -138,19 +147,12 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
tvLessonName.text = learnDialogBean.chapter_lesson_name | |||
tvCountTime.text = learnDialogBean.showTimeCount | |||
tvRight.text = "开始测试" | |||
imgIv.setImageResource(R.mipmap.girl_1) | |||
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_1 else R.mipmap.girl_1) | |||
//开始测试,进入课时学前测试界面 | |||
binding.tvRight.click { | |||
onDialogListener(AppConstants.DIALOG_START_TEST, this@LearnDialog) | |||
} | |||
// tvLeft.visibility = View.VISIBLE | |||
// vSplit.visibility = View.VISIBLE | |||
// tvLeft.text = "跳过测试" | |||
//跳过测试,直接进入学习界面 | |||
// binding.tvLeft.click { | |||
// onDialogListener(AppConstants.DIALOG_START_LEARN, this@LearnDialog) | |||
// } | |||
binding.ivClose.click { dismissAllowingStateLoss() } | |||
} | |||
} | |||
@@ -159,6 +161,7 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
*/ | |||
private fun initLessonBeforeTestOver() { | |||
binding.run { | |||
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2) | |||
tvScore.visibility = View.VISIBLE | |||
tvTip.visibility = View.VISIBLE | |||
tvTitle.visibility = View.VISIBLE | |||
@@ -185,7 +188,7 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
binding.run { | |||
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2) | |||
tvTitle.visibility = View.VISIBLE | |||
tvTitle.text = "恭喜你,本课时学习完成" | |||
tvTitle.text = "恭喜你,本课时学习完成!" | |||
incStatisticsNumber.root.visibility = View.VISIBLE | |||
tvLearnOverTip.visibility = View.VISIBLE | |||
tvLearnOverForAfterCountTime.visibility = View.VISIBLE | |||
@@ -217,7 +220,7 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
tvLeft.visibility = View.VISIBLE | |||
vSplit.visibility = View.VISIBLE | |||
tvTitle.text = "恭喜你,完成了课时学后测试" | |||
tvTop1.text = "重新学习" | |||
tvTop.text = "重新学习" | |||
tvLeft.text = "再测一次" | |||
tvRight.text = "下一课时" | |||
@@ -236,5 +239,98 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
onDialogListener(AppConstants.DIALOG_LESSON_AFTER_TEST_NEXT,this) | |||
} | |||
} | |||
/** | |||
* 课程学后总测试开始前提示弹窗 | |||
*/ | |||
private fun initCourseAfterTestStart() { | |||
binding.run { | |||
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2) | |||
tvLessonName.visibility = View.INVISIBLE //占布局,避免布局变形 | |||
tvTitle.visibility = View.VISIBLE | |||
tvTitle.text = "恭喜你,本课程全部学习完成!" | |||
tvTip1.visibility = View.VISIBLE | |||
tvTip1.text = "我的学习效果是?快去学后总测试吧!" | |||
tvCountTime.visibility = View.VISIBLE | |||
tvCountTime.text = learnDialogBean.showTimeCount | |||
tvRight.text = "开始测试" | |||
//开始测试,进入课时学前测试界面 | |||
binding.tvRight.click { | |||
onDialogListener(AppConstants.DIALOG_START_TEST, this@LearnDialog) | |||
} | |||
tvLeft.visibility = View.VISIBLE | |||
vSplit.visibility = View.VISIBLE | |||
tvLeft.text = "完成" | |||
binding.tvLeft.click { | |||
onDialogListener(AppConstants.DIALOG_OVER, this@LearnDialog) | |||
} | |||
} | |||
} | |||
/** | |||
* 课程学后总测试结束弹窗 | |||
*/ | |||
private fun initCourseAfterTestOver(){ | |||
initNumber() | |||
initScore() | |||
binding.run { | |||
tvScore.visibility = View.VISIBLE | |||
tvTip.visibility = View.VISIBLE | |||
tvTitle.visibility = View.VISIBLE | |||
tvTip1.visibility = View.VISIBLE | |||
incStatisticsNumber.root.visibility = View.VISIBLE | |||
tvTop.visibility = View.VISIBLE | |||
tvLeft.visibility = View.VISIBLE | |||
vSplit.visibility = View.VISIBLE | |||
tvTitle.text = "恭喜你,完成了学后总测试" | |||
tvTop.text = "继续学习" | |||
tvLeft.text = "再测一次" | |||
tvRight.text = "完成" | |||
} | |||
binding.tvTop.click { | |||
onDialogListener(AppConstants.DIALOG_OVER, this) | |||
} | |||
binding.tvLeft.click { | |||
onDialogListener(AppConstants.DIALOG_AFTER_TOTAL_TEST_AGAIN,this) | |||
} | |||
binding.tvRight.click { | |||
onDialogListener(AppConstants.DIALOG_OVER, this) | |||
} | |||
} | |||
/** | |||
* 课时列表item点击时课程已完全学习完成(学前测、学后测、学习都已完成)的提示弹窗 | |||
* tv_title,inc_statistics_number,tv_learn_over_tip,tv_learn_over_for_after_count_time,tv_left,vSplit | |||
*/ | |||
private fun initLessonItemClickLessonOver(){ | |||
initNumber() | |||
binding.run { | |||
imgIv.setImageResource(if (Random.nextBoolean()) R.mipmap.boy_2 else R.mipmap.girl_2) | |||
ivClose.visibility = View.VISIBLE | |||
tvTitle.visibility = View.VISIBLE | |||
tvTitle.text = "恭喜你,本课时学习完成!" | |||
incStatisticsNumber.root.visibility = View.VISIBLE | |||
tvLearnOverTip.visibility = View.VISIBLE | |||
tvLearnOverTip.text = "你可重新学习本课时,或重新进行课时学后测试" | |||
tvLearnOverForAfterCountTime.visibility = View.VISIBLE | |||
tvLearnOverForAfterCountTime.text = "(上次课时学后测试成绩:${learnDialogBean.score})" | |||
tvLeft.visibility = View.VISIBLE | |||
tvLeft.text = "重新学习" | |||
vSplit.visibility = View.VISIBLE | |||
tvRight.text = "课时学后测试" | |||
} | |||
binding.ivClose.click { dismissAllowingStateLoss() } | |||
binding.tvLeft.click { onDialogListener(AppConstants.DIALOG_LESSON_RELEARN, this) } | |||
binding.tvRight.click { onDialogListener(AppConstants.DIALOG_START_TEST, this) } | |||
} | |||
} |
@@ -12,8 +12,8 @@ import androidx.lifecycle.ViewModelProvider | |||
import androidx.recyclerview.widget.GridLayoutManager | |||
import androidx.recyclerview.widget.SimpleItemAnimator | |||
import com.airbnb.lottie.LottieAnimationView | |||
import com.jeremyliao.liveeventbus.LiveEventBus | |||
import com.suliang.common.base.activity.BaseActivityVM | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.suliang.common.extension.click | |||
import com.suliang.common.util.DateUtil | |||
import com.suliang.common.util.DrawableUti | |||
@@ -96,7 +96,7 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
} | |||
/** 查询到播放数据过来 */ | |||
AudioCache.audioLiveData.observe(this) { | |||
AudioCache.initAudioLiveData().observe(this) { | |||
if (it == null) { | |||
//发音文件为空 | |||
showToast("未找到发音文件") | |||
@@ -600,15 +600,14 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
LearnDialog.newInstance(learnDialogBean).apply { | |||
onDialogListener = { action, dialog -> | |||
when (vm.intentData.examType) { | |||
//学前测试结束,只有一个按钮,开始学习 -> 发送消息切换到课时列表页 | |||
//学前总测试结束,只有一个按钮,开始学习 -> 发送消息切换到课时列表页 | |||
AppConstants.TEST_TYPE_BEFORE_TOTAL -> when (action) { | |||
AppConstants.DIALOG_START_LEARN -> { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 继续学习 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_COURSE_TEST_START_LEARN) | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).post(LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_COURSE_TEST_START_LEARN)) | |||
finish() | |||
} | |||
} | |||
@@ -616,13 +615,13 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
AppConstants.TEST_TYPE_BEFORE -> when (action) { | |||
AppConstants.DIALOG_START_LEARN -> { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 继续学习 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
//发送动作 : 开始学习 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN).apply { | |||
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! | |||
} | |||
}) | |||
finish() | |||
} | |||
} | |||
@@ -640,35 +639,61 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
if (isRightClick){ | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 重新学习 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply { | |||
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! | |||
} | |||
}) | |||
finish() | |||
} | |||
} | |||
}.show(childFragmentManager,"lesson_relearn_tip") | |||
} | |||
//再测一次 | |||
AppConstants.DIALOG_LESSON_AFTER_TEST_AGAIN -> { | |||
//发送动作 : 继续学习 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
//发送动作 : 再测一次 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply { | |||
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! | |||
} | |||
}) | |||
finish() | |||
} | |||
//下一步 | |||
AppConstants.DIALOG_LESSON_AFTER_TEST_NEXT -> { | |||
//发送动作 : 继续学习 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
//发送动作 : 下一步 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_NEXT).apply { | |||
leesonPositionIndex = vm.intentData.lesson?.lessonPositionInList!! | |||
} | |||
}) | |||
finish() | |||
} | |||
} | |||
//学后总测试结束 | |||
AppConstants.TEST_TYPE_AFTER_TOTAL -> when(action){ | |||
//测试完成,切换到目录页 | |||
AppConstants.DIALOG_OVER -> { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 继续学习 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).post( | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_COURSE_TEST_AFTER_TOTAL_OVER)) | |||
finish() | |||
} | |||
//再测一次 | |||
AppConstants.DIALOG_AFTER_TOTAL_TEST_AGAIN -> { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 再测一次 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).post( | |||
LearnEventData(vm.intentData.subjectId, | |||
vm.intentData.courseId, | |||
AppConstants.ACTION_COURSE_TEST_AFTER_TOTAL_AGAIN)) | |||
finish() | |||
} | |||
} | |||
} |
@@ -4,8 +4,7 @@ import android.graphics.drawable.Drawable | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.viewModelScope | |||
import com.googlecode.protobuf.format.JsonFormat | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.jeremyliao.liveeventbus.LiveEventBus | |||
import com.suliang.common.extension.createRandomNewChar | |||
import com.suliang.common.extension.diskIo2Main | |||
import com.suliang.common.util.DateUtil | |||
@@ -135,6 +134,10 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
//倒计时结束 | |||
val currentCountingTimeOver = MutableLiveData<Boolean>() | |||
init { | |||
isRunValidTime = false | |||
} | |||
/** 修改获取下一题的标记为true,则在onResume时,自动获取第一题 */ | |||
fun loadFirst(){ | |||
isGetNextIng = true | |||
@@ -231,9 +234,9 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
//计算分数 | |||
calculateScore() | |||
when(intentData.examType){ | |||
AppConstants.TEST_TYPE_BEFORE -> { //学前总测为lesson添加错误数 与 分数 | |||
AppConstants.TEST_TYPE_BEFORE -> { //课时学前测试为lesson添加错误数 与 分数 | |||
intentData.lesson?.let { | |||
it.errorNumber = it.errorNumber + newErrorMap.size | |||
it.errorNumber += newErrorMap.size | |||
it.beforeTestScore = scoreValue.toDouble() | |||
} | |||
} | |||
@@ -461,10 +464,6 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
} else { //相当于新错 | |||
if (!newErrorMap.containsKey(key)) { | |||
newErrorMap[key] = true | |||
//课时前错误 ,则更新课时错误数量 | |||
if (intentData.examType == AppConstants.TEST_TYPE_BEFORE) { | |||
intentData.lesson!!.errorNumber = intentData.lesson!!.errorNumber + 1 | |||
} | |||
} | |||
} | |||
mLearnEntities.add(builder.build()) | |||
@@ -495,13 +494,12 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
//其他测试 | |||
else -> { | |||
val calculateCorrectNumber : Double = correctLiveData.value!! + unAnswerNumber * 0.25 | |||
val scoreValue = ((calculateCorrectNumber - testData.size * 0.25) / (testData.size * 0.25) * 100).toInt() | |||
val scoreValue = ((calculateCorrectNumber - testData.size * 0.25) / (testData.size * 0.75) * 100).toInt() | |||
this.scoreValue = when { | |||
scoreValue < 0 -> 0 | |||
scoreValue > 100 -> 100 | |||
else -> scoreValue | |||
} | |||
} | |||
} | |||
} | |||
@@ -651,16 +649,16 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
private fun sendEventBus(){ | |||
when(intentData.examType){ | |||
AppConstants.TEST_TYPE_BEFORE_TOTAL -> { //学前总测发送数据、学前总测发送数据 | |||
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData( | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_TOTAL_TEST).post( LearnEventData( | |||
intentData.subjectId, | |||
intentData.courseId, | |||
AppConstants.DATA_COURSE_BEFORE_TEST_OVER).apply { | |||
this.scoreValue = this@LearnExamViewModel.scoreValue | |||
this.newErrorMap = this@LearnExamViewModel.newErrorMap | |||
} | |||
}) | |||
} | |||
AppConstants.TEST_TYPE_BEFORE-> { // 课时学前测试结束发送数据 | |||
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData( | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post( LearnEventData( | |||
intentData.subjectId, | |||
intentData.courseId, | |||
AppConstants.DATA_LESSON_BEFORE_TEST_OVER).apply { | |||
@@ -668,18 +666,25 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
intentData.lesson?.let { | |||
leesonPositionIndex = it.lessonPositionInList | |||
} | |||
} | |||
}) | |||
} | |||
AppConstants.TEST_TYPE_AFTER -> { //课时学后测试结束发送数据 | |||
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData( | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData( | |||
intentData.subjectId, | |||
intentData.courseId, | |||
AppConstants.DATA_LESSON_AFTER_TEST_OVER).apply { | |||
this.newErrorMap = this@LearnExamViewModel.newErrorMap | |||
intentData.lesson?.let { | |||
leesonPositionIndex = it.lessonPositionInList | |||
} | |||
} | |||
}) | |||
} | |||
AppConstants.TEST_TYPE_AFTER_TOTAL -> { //学后总测发送数据 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_TOTAL_TEST).post(LearnEventData( | |||
intentData.subjectId, | |||
intentData.courseId, | |||
AppConstants.DATA_COURSE_AFTER_TEST_OVER).apply { | |||
this.scoreValue = this@LearnExamViewModel.scoreValue | |||
}) | |||
} | |||
} | |||
} |
@@ -13,8 +13,8 @@ import androidx.recyclerview.widget.LinearLayoutManager | |||
import androidx.recyclerview.widget.RecyclerView | |||
import androidx.recyclerview.widget.RecyclerView.OnScrollListener | |||
import androidx.recyclerview.widget.SimpleItemAnimator | |||
import com.jeremyliao.liveeventbus.LiveEventBus | |||
import com.suliang.common.base.activity.BaseActivityVM | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.suliang.common.extension.click | |||
import com.suliang.common.util.DateUtil | |||
import com.suliang.common.util.LogUtil | |||
@@ -244,7 +244,7 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView | |||
vm.loadNext() | |||
//发音数据 | |||
AudioCache.audioLiveData.observe(this) { | |||
AudioCache.initAudioLiveData().observe(this) { | |||
it?.run { | |||
if (vm.learnData.lesson.courseType == AppConstants.COURSE_TYPE_ENGLISH_VOICE && bindingWord.ivVoice.visibility == View.VISIBLE) MPManager.play( | |||
it, listener = impListener) | |||
@@ -254,7 +254,7 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView | |||
//如果需要显示图片,则添加监听 | |||
if (vm.isNeedLoadPhoto) { | |||
//图片数据 | |||
PhotoCache.photoLiveData.observe(this) { | |||
PhotoCache.initPhotoLiveData().observe(this) { | |||
it?.run { ImageLoader.loadImage(bindingWord.imgWord, it) } ?: let { bindingWord.imgWord.visibility = View.GONE } | |||
} | |||
} | |||
@@ -619,8 +619,8 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView | |||
private fun showLearnOverDialog() { | |||
vm.loadAfterTest().observe(this) { showTimeCount -> | |||
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LEARN_OVER).apply { | |||
correctNumber = vm.learnData.lesson.correctNumber + vm.learnRuleUtil.currentCorrectMap.size | |||
errorNumber = vm.learnData.lesson.errorNumber + vm.learnRuleUtil.currentErrorMap.size | |||
correctNumber = vm.learnData.lesson.correctNumber | |||
errorNumber = vm.learnData.lesson.errorNumber | |||
this.showTimeCount = showTimeCount | |||
}).apply { | |||
onDialogListener = { action, dialog -> | |||
@@ -636,12 +636,13 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView | |||
if (isRightClick){ | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 重新学习 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
LearnEventData(vm.learnData.lesson.subjectId, | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(vm.learnData.lesson.subjectId, | |||
vm.learnData.lesson.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply { | |||
leesonPositionIndex = vm.learnData.lesson.lessonPositionInList | |||
} | |||
}) | |||
finish() | |||
} | |||
} | |||
}.show(childFragmentManager,"lesson_relearn_tip") | |||
@@ -649,12 +650,13 @@ class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordView | |||
AppConstants.DIALOG_START_TEST -> { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 学后测试 | |||
LiveDataBus.with<LearnEventData>(AppConstants.EVENT_COURSE).value = | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(vm.learnData.lesson.subjectId, | |||
vm.learnData.lesson.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply { | |||
leesonPositionIndex = vm.learnData.lesson.lessonPositionInList | |||
} | |||
}) | |||
finish() | |||
} | |||
} | |||
} |
@@ -3,12 +3,10 @@ package com.xkl.cdl.module.learn | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.viewModelScope | |||
import com.googlecode.protobuf.format.JsonFormat | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.jeremyliao.liveeventbus.LiveEventBus | |||
import com.suliang.common.extension.createRandomNewChar | |||
import com.suliang.common.extension.diskIo2Main | |||
import com.suliang.common.util.DateUtil | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.DataTransferHolder | |||
import com.xkl.cdl.data.bean.LearnWord | |||
@@ -75,6 +73,7 @@ class LearnWordViewModel : LearnBaseViewModel() { | |||
//数据上传完成监听: true 学习完成的上传 false可能是半途的返回事件,直接退出 | |||
val saveDataLiveData = MutableLiveData<Boolean>() | |||
/** 获取数据 */ | |||
fun loadNext() { | |||
//修改标记 | |||
@@ -89,9 +88,10 @@ class LearnWordViewModel : LearnBaseViewModel() { | |||
currentLearnWord.value = it | |||
} ?: let { //没有数据 即学习完成 | |||
isAllOver = true | |||
// 停止有效计时 | |||
isRunValidTime = false | |||
//停止总计时 | |||
stopTotalCountTing() | |||
// TODO: 2022/4/26 停止有效计时 | |||
//封装保存数据 | |||
saveData() | |||
} | |||
@@ -246,7 +246,7 @@ class LearnWordViewModel : LearnBaseViewModel() { | |||
learnedIndex = currentLessonLearnedPosition | |||
correctNumber += learnRuleUtil.currentCorrectMap.size | |||
errorNumber += learnRuleUtil.currentErrorMap.size | |||
learnIsOver = (correctNumber + errorNumber == totalNumber) | |||
learnIsOver = learnedIndex == wordIds.size - 1 | |||
} | |||
//添加到错误本集合中,主要用于小游戏练习(学前总 课时都没有添加,从这里添加到集合后发送出去,添加到集合) | |||
learnData.examErrorMap?.putAll(learnRuleUtil.currentErrorMap) | |||
@@ -282,12 +282,12 @@ class LearnWordViewModel : LearnBaseViewModel() { | |||
/** 发送数据事件 */ | |||
private fun sendEventBus() { | |||
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).value = LearnEventData(learnData.lesson.subjectId, | |||
learnData.lesson.courseId, | |||
AppConstants.DATA_LESSON_LEARN_OVER).apply { | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).post(LearnEventData(learnData.lesson.subjectId, | |||
learnData.lesson.courseId, | |||
AppConstants.DATA_LESSON_LEARN_OVER).apply { | |||
this.leesonPositionIndex = learnData.lesson.lessonPositionInList | |||
this.newErrorMap = learnData.examErrorMap | |||
} | |||
}) | |||
} | |||
@@ -22,11 +22,14 @@ import com.xkl.cdl.R | |||
import com.xkl.cdl.adapter.AdapterAutoPlaySelectRepeat | |||
import com.xkl.cdl.adapter.ViewPagerAdapter | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.bean.LearnDialogBean | |||
import com.xkl.cdl.data.binding.BindingAdapter | |||
import com.xkl.cdl.data.manager.CourseManager | |||
import com.xkl.cdl.databinding.ActivityCourseMainBinding | |||
import com.xkl.cdl.databinding.DialogBottomAutoPlaySelectBinding | |||
import com.xkl.cdl.databinding.DialogBottomCourseMoreBinding | |||
import com.xkl.cdl.dialog.CommonDialog | |||
import com.xkl.cdl.dialog.CommonDialogBean | |||
import com.xkl.cdl.module.m_center_learn.coursechildren.CourseMainFragment | |||
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly | |||
@@ -170,7 +173,7 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP | |||
} | |||
} | |||
} | |||
//改变page后设置进度 | |||
//改变page后设置进度,获取该课程的进度进行设置 | |||
vm.currentCourseProgress.value = vm.coursePack.childrenCourses[position].courseLearnProgress | |||
} | |||
}) | |||
@@ -188,20 +191,23 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP | |||
progressbar.progress = it.toInt() | |||
tvProgress.text = if (binding.includeCourseTypeTab.root.visibility == View.VISIBLE) { | |||
val format = when (binding.viewPager2.currentItem) { | |||
0 -> "认读进度:%s" | |||
1 -> "拼写进度:%s" | |||
2 -> "辨音进度:%s" | |||
else -> "进度:%s" | |||
0 -> "认读进度: %s%%" | |||
1 -> "拼写进度: %s%%" | |||
2 -> "辨音进度: %s%%" | |||
else -> "进度: %s%%" | |||
} | |||
String.format(format, CourseManager.useToShowProgress(it)) | |||
} else { | |||
String.format("进度:%s", CourseManager.useToShowProgress(it)) | |||
String.format("进度: %s%%", CourseManager.useToShowProgress(it)) | |||
} | |||
} | |||
} | |||
//更多按钮点击 | |||
binding.includeCourseProgress.ivMore.click { | |||
if (vm.showMoreEnable) | |||
showMoreDialog() | |||
else | |||
showToast("请先进行学习") | |||
} | |||
} | |||
@@ -232,13 +238,30 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP | |||
} | |||
moreBinding.tvClearLearnRecord.click { | |||
dismiss() | |||
// TODO: 2022/3/29 弹窗显示清除学习记录 | |||
showCourseRelearnDialog() | |||
} | |||
} | |||
} | |||
moreDialog?.show() | |||
} | |||
/** 课程重学弹窗提示 */ | |||
fun showCourseRelearnDialog(){ | |||
//弹窗显示清除学习记录 | |||
val bean = CommonDialogBean(titleText = R.string.course_relearn_title, | |||
contentText = R.string.course_relearn_content, | |||
leftText = R.string.course_relearn_sure, rightText = R.string.cancel) | |||
CommonDialog.newInstance(bean).apply { | |||
onCommonDialogButtonClickListener = { dialog, isRightClick -> | |||
dialog.dismissAllowingStateLoss() | |||
if (!isRightClick){ | |||
((this@CoursePackMainActivity.binding.viewPager2.adapter as ViewPagerAdapter).getItem(this@CoursePackMainActivity.binding.viewPager2.currentItem) as CourseMainFragment).run { | |||
this.courseRelearn() | |||
} | |||
} | |||
} | |||
}.show(supportFragmentManager,"course_relearn_dialog") | |||
} | |||
/** 自动播放次数选择: 仅单词会有该选项 */ | |||
private fun showAutoPlaySelectDialog() { | |||
if (autoPlaySeletDialog == null) { | |||
@@ -269,6 +292,14 @@ class CoursePackMainActivity : BaseActivityVM<ActivityCourseMainBinding, CourseP | |||
private fun startAutoPlay(time: Int) { | |||
} | |||
override fun onStop() { | |||
super.onStop() | |||
//如果是中文项目,更新课程包的进度 | |||
if (vm.coursePack.subjectId == AppConstants.SUBJECT_CHINESE){ | |||
vm.coursePack.learnProgress = vm.coursePack.childrenCourses[0].courseLearnProgress | |||
} | |||
} | |||
/** ViewModel Factory工厂 */ |
@@ -10,12 +10,14 @@ import com.xkl.cdl.data.manager.CourseManager | |||
* Describe: 课程主页 | |||
*/ | |||
class CoursePackMainActivityViewModel(subjectId: Int , coursePackInPosition : Int) : BaseViewModel() { | |||
//操作的课程包 | |||
val coursePack = CourseManager.subjectWithCoursePackMap[subjectId]!![coursePackInPosition] | |||
//设置显示当前课程的进度值和显示内容 | |||
val currentCourseProgress = MutableLiveData<Double>() | |||
//更多点击是否有效 | |||
var showMoreEnable = false | |||
} |
@@ -5,9 +5,8 @@ import androidx.constraintlayout.widget.ConstraintLayout | |||
import androidx.lifecycle.ViewModelProvider | |||
import androidx.recyclerview.widget.LinearLayoutManager | |||
import androidx.recyclerview.widget.RecyclerView | |||
import com.jeremyliao.liveeventbus.LiveEventBus | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.suliang.common.util.DateUtil | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.adapter.AdapterLesson | |||
import com.xkl.cdl.data.AppConstants | |||
@@ -18,12 +17,11 @@ import com.xkl.cdl.data.bean.course.Lesson | |||
import com.xkl.cdl.data.bean.intentdata.ExamData | |||
import com.xkl.cdl.data.event.LearnEventData | |||
import com.xkl.cdl.data.manager.CourseManager | |||
import com.xkl.cdl.data.repository.DataRepository | |||
import com.xkl.cdl.databinding.FragmentCourseLessonBinding | |||
import com.xkl.cdl.dialog.CommonDialog | |||
import com.xkl.cdl.dialog.CommonDialogBean | |||
import com.xkl.cdl.dialog.LearnDialog | |||
import com.xkl.cdl.module.learn.LearnWordActivity | |||
import mqComsumerV1.Struct | |||
import java.text.DateFormat | |||
/** | |||
* 课程章节目录 | |||
@@ -49,6 +47,8 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) | |||
adapterLesson = AdapterLesson(vm).apply { | |||
onItemClick = onLessonClick | |||
//默认选中位置,默认为0 ,如果学习完成则设置为-1 | |||
selectPos = vm.initSelectPosition() | |||
} | |||
adapter = adapterLesson | |||
} | |||
@@ -65,9 +65,10 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
private fun listenerLiveBus() { | |||
//监听数据 | |||
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).observe(this) { learnEventData -> | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_DATA).observe(this) { learnEventData -> | |||
if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe | |||
when (learnEventData.actionFlag) { | |||
//课时学前测试结束,传递数据回来更新数据 | |||
AppConstants.DATA_LESSON_BEFORE_TEST_OVER -> { | |||
// 更新Lesson 更新detail课时学前成绩,错误条目数,测试前错误列表 | |||
@@ -82,17 +83,17 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
} | |||
} | |||
} | |||
//课时学前测试结束,开始学习 -> 进入学习界面 | |||
AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN -> { | |||
startLearn(vm.allLesson[learnEventData.leesonPositionIndex]) | |||
} | |||
//课时学习结束 | |||
//课时学习完成 | |||
AppConstants.DATA_LESSON_LEARN_OVER -> { | |||
// 更新Lesson 更新detail错误条目数 | |||
if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe | |||
//更新Lesson 更新detail错误条目数 | |||
adapterLesson.notifyItemChanged(learnEventData.leesonPositionIndex) | |||
vm.allLesson[learnEventData.leesonPositionIndex].let { | |||
val key = "${it.chapterId}_${it.lessonId}" | |||
//更新错误数 | |||
vm.courseDetail.wrong.put(key, it.errorNumber) | |||
//更新正确数 | |||
vm.courseDetail.right.put(key, it.correctNumber) | |||
//添加到错误本中,现在主要用于小游戏取值 | |||
learnEventData.newErrorMap?.forEach { | |||
vm.courseDetail.temporary_words.put(it.key, "") | |||
@@ -112,6 +113,7 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
} | |||
vm.courseDetail.courseLearnProgress = courseProgress | |||
vm.course.courseLearnProgress = courseProgress | |||
vm.coursePackMainActivityVM.currentCourseProgress.value = courseProgress | |||
//项目总进度 | |||
val subjectProgress = CourseManager.calculateSubjectProgress(vm.course.subjectId, vm.course.coursePackId, | |||
vm.course.courseId, courseProgress) | |||
@@ -128,27 +130,50 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
vm.courseDetail.after.put(key, it.beforeTestScore) | |||
} | |||
} | |||
//学后测试结束,弹窗动作 | |||
} | |||
} | |||
//监听课时学习的动作 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).observe(this) { learnEventData -> | |||
if (learnEventData.subjectId != vm.course.subjectId || learnEventData.courseId != vm.course.courseId) return@observe | |||
when (learnEventData.actionFlag) { | |||
//课时学前测试结束,开始学习 -> 进入学习界面 | |||
AppConstants.ACTION_LESSON_BEFORE_TEST_OVER_START_LEARN -> { | |||
startLearn(vm.allLesson[learnEventData.leesonPositionIndex]) | |||
} | |||
//学后测试结束,弹窗动作 课时重新学习 | |||
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN -> { | |||
//课时学后测试,点击重学 | |||
// TODO: 2022/4/22 清除当前课时数据,并更新当前课时,后进入学习界面 | |||
learnEventData.leesonPositionIndex | |||
vm.allLesson[learnEventData.leesonPositionIndex].learnIsOver = false | |||
vm.relearnLesson(learnEventData.leesonPositionIndex).observe(this) { | |||
//item更新 | |||
adapterLesson.notifyItemChanged(learnEventData.leesonPositionIndex) | |||
//进行点击,此时当前课时item应该处于显示中,可直接点击 | |||
(binding.recyclerView.layoutManager as LinearLayoutManager).findViewByPosition( | |||
learnEventData.leesonPositionIndex) | |||
?.findViewById<ConstraintLayout>(R.id.layout_content) | |||
?.performClick() | |||
} | |||
} | |||
/**课时学后测试弹窗动作: 再测一次 ,学习结束弹窗: 开始学后测试,共同点:直接进入测试,没有弹窗提示*/ | |||
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN -> { | |||
//学后测试,再测一次 | |||
loadLessonAfterTest(vm.allLesson[learnEventData.leesonPositionIndex]) | |||
} | |||
//下一步 | |||
AppConstants.ACTION_LESSON_AFTER_TEST_NEXT -> { | |||
//下一课时 or 下一步(提示课程学习完成) | |||
// TODO: 2022/4/22 判断课程是否学习完成,学习完成,显示课程学习完成弹窗,否则进入下一个没有学习完成的课时,进行学习 | |||
when(vm.course.courseLearnProgress){ | |||
100.0 -> showCourseOverDialog() | |||
when (vm.course.courseLearnProgress) { | |||
100.0 -> { | |||
adapterLesson.selectPos = -1 | |||
adapterLesson.notifyDataSetChanged() | |||
showCourseOverDialog() | |||
} | |||
else -> { | |||
var nextLessonPosition = -1 | |||
//从下一课时开始到最后一个课时 | |||
for ( i in learnEventData.leesonPositionIndex + 1 until vm.allLesson.size){ | |||
for (i in learnEventData.leesonPositionIndex + 1 until vm.allLesson.size) { | |||
if (!vm.allLesson[i].learnIsOver) { | |||
nextLessonPosition = i | |||
break | |||
@@ -167,9 +192,11 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
val linearLayoutManager = binding.recyclerView.layoutManager as LinearLayoutManager | |||
val findFirstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition() //第一个可见位置 | |||
val findLastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition() //最后一个可见位置 | |||
if (nextLessonPosition in findFirstVisibleItemPosition .. findLastVisibleItemPosition){ | |||
linearLayoutManager.findViewByPosition(nextLessonPosition)?.findViewById<ConstraintLayout>(R.id.layout_content)?.performClick() | |||
}else{ | |||
if (nextLessonPosition in findFirstVisibleItemPosition .. findLastVisibleItemPosition) { | |||
linearLayoutManager.findViewByPosition(nextLessonPosition) | |||
?.findViewById<ConstraintLayout>(R.id.layout_content) | |||
?.performClick() | |||
} else { | |||
recycleViewScrollListener.lessonPosition = nextLessonPosition | |||
binding.recyclerView.addOnScrollListener(recycleViewScrollListener) | |||
binding.recyclerView.smoothScrollToPosition(nextLessonPosition) | |||
@@ -187,10 +214,12 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
var lessonPosition = 0 | |||
override fun onScrollStateChanged(recyclerView : RecyclerView, newState : Int) { | |||
super.onScrollStateChanged(recyclerView, newState) | |||
if (newState == RecyclerView.SCROLL_STATE_IDLE){ | |||
if (newState == RecyclerView.SCROLL_STATE_IDLE) { | |||
binding.recyclerView.removeOnScrollListener(this) | |||
val linearLayoutManager = binding.recyclerView.layoutManager as LinearLayoutManager | |||
linearLayoutManager.findViewByPosition(lessonPosition)?.findViewById<ConstraintLayout>(R.id.layout_content)?.performClick() | |||
linearLayoutManager.findViewByPosition(lessonPosition) | |||
?.findViewById<ConstraintLayout>(R.id.layout_content) | |||
?.performClick() | |||
} | |||
} | |||
} | |||
@@ -200,17 +229,16 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
private val onLessonClick : (v : View, position : Int, lesson : Lesson) -> Unit = { view, position, entity -> | |||
when (entity.lessonType) { | |||
AppConstants.LESSON_TYPE_WORD -> { | |||
startLearn(entity) | |||
/* if (entity.beforeTestScore == AppConstants.NOT_DOING){ //课时学前测试,没有做 | |||
//弹窗显示学前测试提示 | |||
showLessonBeforeTestStartDialog(entity) | |||
}else if (!entity.learnIsOver){ //当前课时未学完,直接开始学习 | |||
startLearn(entity) | |||
}else if (entity.afterTestScore != AppConstants.NOT_DOING){ | |||
loadLessonAfterTest(entity) | |||
}else{ //当前课时学习完成的弹窗 | |||
showLessonAllOverDialog(entity) | |||
}*/ | |||
if (entity.beforeTestScore == AppConstants.NOT_DOING) { //课时学前测试,没有做 | |||
//弹窗显示学前测试提示 | |||
showLessonBeforeTestStartDialog(entity) | |||
} else if (!entity.learnIsOver) { //当前课时未学完,直接开始学习 | |||
startLearn(entity) | |||
} else if (entity.afterTestScore == AppConstants.NOT_DOING) { | |||
loadLessonAfterTest(entity) | |||
} else { //当前课时学习完成的弹窗 | |||
showLessonAllOverDialog(entity) | |||
} | |||
} | |||
AppConstants.LESSON_TYPE_SENTENCE -> { | |||
@@ -271,9 +299,42 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
* @param lesson Lesson 课时 | |||
*/ | |||
private fun startLearn(lesson : Lesson) { | |||
vm.loadLessonLearnData(lesson).observe(this) { | |||
DataTransferHolder.instance.putData(value = it) | |||
startActivity(LearnWordActivity::class.java) | |||
when (lesson.lessonType) { | |||
AppConstants.LESSON_TYPE_WORD -> { | |||
startLessonLearnForWord(lesson) | |||
/* if (entity.beforeTestScore == AppConstants.NOT_DOING){ //课时学前测试,没有做 | |||
//弹窗显示学前测试提示 | |||
showLessonBeforeTestStartDialog(entity) | |||
}else if (!entity.learnIsOver){ //当前课时未学完,直接开始学习 | |||
startLearn(entity) | |||
}else if (entity.afterTestScore != AppConstants.NOT_DOING){ | |||
loadLessonAfterTest(entity) | |||
}else{ //当前课时学习完成的弹窗 | |||
showLessonAllOverDialog(entity) | |||
}*/ | |||
} | |||
AppConstants.LESSON_TYPE_SENTENCE -> { | |||
} | |||
AppConstants.LESSON_TYPE_DIALOGUE -> { | |||
} | |||
AppConstants.LESSON_TYPE_COMPOSITION_VIDEO -> { | |||
} | |||
AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE -> { | |||
} | |||
AppConstants.LESSON_TYPE_COMPOSITION_EXAM -> { | |||
} | |||
AppConstants.LESSON_TYPE_COMPOSITION_READING -> { | |||
} | |||
AppConstants.LESSON_TYPE_COMPOSITION_TASK -> { | |||
} | |||
} | |||
} | |||
@@ -305,22 +366,87 @@ class CourseLessonFragment : BaseFragmentVM<FragmentCourseLessonBinding, CourseM | |||
/** lesson的学习 lessonType 为 word类型 */ | |||
private fun startLessonLearnForWord() { | |||
private fun startLessonLearnForWord(lesson : Lesson) { | |||
vm.loadLessonLearnData(lesson).observe(this) { | |||
DataTransferHolder.instance.putData(value = it) | |||
startActivity(LearnWordActivity::class.java) | |||
} | |||
} | |||
/** | |||
* 当前课时学前、学习、学后都完成了的弹窗 | |||
*/ | |||
private fun showLessonAllOverDialog(lesson : Lesson) { | |||
LearnDialog.newInstance(LearnDialogBean(AppConstants.DIALOG_TYPE_LESSON_ITEM_OVER).apply { | |||
correctNumber = lesson.correctNumber | |||
errorNumber = lesson.errorNumber | |||
score = lesson.afterTestScore.toInt() | |||
}).apply { | |||
onDialogListener = { action, dialog -> | |||
when (action) { | |||
//重学动作 | |||
AppConstants.DIALOG_LESSON_RELEARN -> CommonDialog.newInstance( | |||
CommonDialogBean(titleText = R.string.lesson_relearn_title, | |||
contentText = R.string.lesson_relearn_content, leftText = R.string.cancel, | |||
rightText = R.string.sure)).apply { | |||
onCommonDialogButtonClickListener = { relearnDialog, isRightClick -> | |||
relearnDialog.dismissAllowingStateLoss() | |||
if (isRightClick) { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 重新学习 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( | |||
LearnEventData(lesson.subjectId, lesson.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_RELEARN).apply { | |||
leesonPositionIndex = lesson.lessonPositionInList | |||
}) | |||
} | |||
} | |||
}.show(childFragmentManager, "lesson_relearn_tip") | |||
// 开始学后测试 | |||
AppConstants.DIALOG_START_TEST -> { | |||
dialog.dismissAllowingStateLoss() | |||
//发送动作 : 学后测试 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_LESSON_ACTION).post( LearnEventData(lesson.subjectId, lesson.courseId, | |||
AppConstants.ACTION_LESSON_AFTER_TEST_AGAIN).apply { | |||
leesonPositionIndex = lesson.lessonPositionInList | |||
}) | |||
} | |||
} | |||
} | |||
}.show(childFragmentManager, "learn_over_dialog") | |||
} | |||
/** | |||
* 显示课程学习完成的弹窗 | |||
* 显示课程学习完成的弹窗,在最后一个课时测试完成的下一步,相当于在课时目录页显示去学后总测试的弹窗 | |||
*/ | |||
private fun showCourseOverDialog() { | |||
// TODO: 2022/4/29 课程学习完成弹窗 | |||
vm.loadTest(AppConstants.TEST_TYPE_AFTER_TOTAL).observe(this) { | |||
val learnDialogBean = LearnDialogBean(AppConstants.DIALOG_TYPE_EXAM_START).apply { | |||
examType = AppConstants.TEST_TYPE_AFTER_TOTAL | |||
showTimeCount = CourseManager.expectedTestTime(vm.course.courseType, examType, it) | |||
} | |||
LearnDialog.newInstance(learnDialogBean).apply { | |||
onDialogListener = { action, dialog -> | |||
dialog.dismissAllowingStateLoss() | |||
when (action) { | |||
//开始是学后总测试 | |||
AppConstants.DIALOG_START_TEST -> {//生成数据 | |||
val examData = ExamData(vm.course.subjectId, learnDialogBean.examType, vm.course.courseTitle, | |||
vm.course.courseTitle).apply { | |||
coursePackId = vm.course.coursePackId | |||
coursePackType = vm.course.coursePackType | |||
courseId = vm.course.courseId | |||
courseType = vm.course.courseType | |||
this.testData = it | |||
} | |||
(parentFragment as CourseMainFragment).startExam(examData) | |||
} | |||
} | |||
} | |||
}.show(childFragmentManager, "course_learn_after_test_before") | |||
} | |||
} | |||
@@ -1,13 +1,13 @@ | |||
package com.xkl.cdl.module.m_center_learn.coursechildren | |||
import android.os.Bundle | |||
import androidx.fragment.app.Fragment | |||
import androidx.lifecycle.ViewModel | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.jeremyliao.liveeventbus.LiveEventBus | |||
import com.suliang.common.AppConfig | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.suliang.common.eventbus.LiveDataBus | |||
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.DataTransferHolder | |||
@@ -34,28 +34,36 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
} | |||
} | |||
/**标记当前显示的fragment*/ | |||
private lateinit var currentFragment : Fragment | |||
override fun initViewModel() : CourseMainFragmentViewModel { | |||
return ViewModelProvider(this, | |||
ViewModelFactory(requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply { | |||
val vmmodel = ViewModelProvider(this, ViewModelFactory( | |||
requireArguments().getInt(AppConfig.INTENT_1)))[CourseMainFragmentViewModel::class.java].apply { | |||
coursePackMainActivityVM = ViewModelProvider(requireActivity())[CoursePackMainActivityViewModel::class.java] | |||
// LogUtil.e("CourseMainFragment coursePackMainActivityVM hashCode -> ${coursePackMainActivityVM.hashCode()}") | |||
} | |||
lifecycle.addObserver(vmmodel) | |||
return vmmodel | |||
} | |||
override fun initFragment() { | |||
//设置课程 和 需要操作的类型 | |||
vm.course = vm.coursePackMainActivityVM.coursePack.childrenCourses[vm.courseIndex].apply { | |||
vm.dbControlBase = DbControlBase(subjectId, coursePackId,coursePackType, courseId, courseType) | |||
vm.dbControlBase = DbControlBase(subjectId, coursePackId, coursePackType, courseId, courseType) | |||
} | |||
//监听动作 总测完成,切换到目录页 | |||
LiveDataBus.withNonSticky<LearnEventData>(AppConstants.EVENT_COURSE).observe(this) { | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_CHANGE_PAGE).observe(this) { | |||
if (it.subjectId != vm.course.subjectId || it.courseId != vm.course.courseId) return@observe | |||
when (it.actionFlag) { | |||
// 学前总测、学后总测 之继续学习 -> 切换到目录页 | |||
AppConstants.ACTION_COURSE_TEST_START_LEARN -> changeFragment(1) | |||
AppConstants.ACTION_COURSE_TEST_START_LEARN -> if (currentFragment !is CourseLessonFragment) changeFragment(1) | |||
} | |||
} | |||
//监听动作 总测完成 | |||
LiveEventBus.get<LearnEventData>(AppConstants.EVENT_TOTAL_TEST).observe(this) { | |||
if (it.subjectId != vm.course.subjectId || it.courseId != vm.course.courseId) return@observe | |||
when (it.actionFlag) { | |||
//学前总测结束,传递数据回来更新数据 | |||
AppConstants.DATA_COURSE_BEFORE_TEST_OVER -> { | |||
vm.courseDetail.st_before = it.scoreValue.toDouble() //学前总分 | |||
@@ -70,13 +78,28 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
} | |||
} | |||
} | |||
//学后总测结束传递数据回来。更新数据 | |||
AppConstants.DATA_COURSE_AFTER_TEST_OVER -> vm.courseDetail.st_after = it.scoreValue.toDouble() //学后总分 | |||
//学后总测试结束动作: 再测一次 | |||
AppConstants.ACTION_COURSE_TEST_AFTER_TOTAL_AGAIN -> { | |||
vm.loadTest(AppConstants.TEST_TYPE_AFTER_TOTAL).observe(this) { | |||
val examData = ExamData(vm.course.subjectId, AppConstants.TEST_TYPE_AFTER_TOTAL, vm.course.courseTitle, | |||
vm.course.courseTitle).apply { | |||
coursePackId = vm.course.coursePackId | |||
coursePackType = vm.course.coursePackType | |||
courseId = vm.course.courseId | |||
courseType = vm.course.courseType | |||
this.testData = it | |||
} | |||
startExam(examData) | |||
} | |||
} | |||
} | |||
} | |||
} | |||
override fun loadData() { | |||
LogUtil.e("CourseMainFragment 加载数据 --》 ${vm.courseIndex}") | |||
vm.loadMain().observe(this) { | |||
changeChildrenFragment() | |||
} | |||
@@ -87,22 +110,17 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
* 改变加载的子Fragment | |||
*/ | |||
private fun changeChildrenFragment() { | |||
// //加载复习 | |||
// replaceFragment(R.id.layout_root, CourseReviewFragment.newInstance()) | |||
// //学前总测 | |||
// replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL)) | |||
//学后总测 | |||
// replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL)) | |||
//学习目录 | |||
// TODO: 2022/5/5 复习页面加载 | |||
vm.courseDetail.run { | |||
if (st_before == AppConstants.NOT_DOING) { //学前总测未做 | |||
replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL)) | |||
currentFragment = CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_BEFORE_TOTAL) | |||
replaceFragment(R.id.layout_root, currentFragment) | |||
} else if (courseLearnProgress != 100.0) { //学习未完成 | |||
replaceFragment(R.id.layout_root, CourseLessonFragment.newInstance()) | |||
currentFragment = CourseLessonFragment.newInstance() | |||
replaceFragment(R.id.layout_root, currentFragment) | |||
} else { //学习完成,学后总测试界面 | |||
replaceFragment(R.id.layout_root, CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL)) | |||
currentFragment = CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL) | |||
replaceFragment(R.id.layout_root,currentFragment) | |||
} | |||
} | |||
} | |||
@@ -113,8 +131,14 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
*/ | |||
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)) | |||
1 -> { | |||
currentFragment = CourseLessonFragment.newInstance() | |||
replaceFragment(R.id.layout_root,currentFragment ) | |||
} | |||
2 -> { | |||
currentFragment = CourseTotalTestFragment.newInstance(AppConstants.TEST_TYPE_AFTER_TOTAL) | |||
replaceFragment(R.id.layout_root, currentFragment) | |||
} | |||
} | |||
} | |||
@@ -125,10 +149,12 @@ class CourseMainFragment : BaseFragmentVM<FragmentCourseMainBinding, CourseMainF | |||
LearnExamActivity.newInstance(requireContext()) | |||
} | |||
override fun onDestroy() { | |||
super.onDestroy() | |||
//退出本课程后移除该课程的事件监听 | |||
// LiveDataBus.remove(AppConstants.EVENT_COURSE) | |||
/** 课程重学方法 */ | |||
fun courseRelearn() { | |||
//调用重学,重学重新加载数据,重新加载界面 | |||
vm.relearnCourse().observe(this){ | |||
loadData() | |||
} | |||
} | |||
@@ -1,16 +1,20 @@ | |||
package com.xkl.cdl.module.m_center_learn.coursechildren | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.suliang.common.extension.diskIo2DiskIo | |||
import com.suliang.common.extension.diskIo2Main | |||
import com.suliang.common.util.DateUtil | |||
import com.suliang.common.util.file.FileUtil | |||
import com.xkl.cdl.data.AppConstants | |||
import com.xkl.cdl.data.bean.LearnWord | |||
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.bean.intentdata.LearnData | |||
import com.xkl.cdl.data.manager.CourseManager | |||
import com.xkl.cdl.data.manager.db.DBCourseManager | |||
import com.xkl.cdl.data.manager.db.DbControlBase | |||
import com.xkl.cdl.data.repository.DataRepository | |||
@@ -18,6 +22,7 @@ import com.xkl.cdl.module.m_center_learn.CoursePackMainActivityViewModel | |||
import io.reactivex.rxjava3.core.Observable | |||
import io.reactivex.rxjava3.functions.BiFunction | |||
import mqComsumerV1.Struct | |||
import java.io.File | |||
import java.util.* | |||
import kotlin.collections.HashMap | |||
@@ -55,10 +60,10 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
//获取课程的复习数据 | |||
//获取课程的章节数据 | |||
fun loadMain() : MutableLiveData<Boolean> { | |||
// showHideLoading(true) | |||
val mutableLiveData = MutableLiveData<Boolean>() | |||
Observable.zip(DataRepository.getCourseStatistics().flatMap { | |||
Observable.zip(DataRepository.getCourseStatistics(course.subjectId,course.coursePackId,course.courseId).flatMap { | |||
courseDetail = it | |||
course.courseLearnProgress = it.courseLearnProgress | |||
return@flatMap DataRepository.getCourseAllLesson(dbControlBase, it) | |||
}.flatMap { | |||
allLesson = it | |||
@@ -66,14 +71,20 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
}, | |||
DataRepository.getReviewData(), | |||
BiFunction<HashMap<String, Long>, Array<String>, Boolean> { t1 : HashMap<String, Long>?, t2 : Array<String>? -> | |||
// TODO: 2022/5/5 初始化需要复习的数据 | |||
showMoreIsEnable() | |||
true | |||
}).compose(diskIo2Main()).subscribe { | |||
// showHideLoading(false) | |||
mutableLiveData.value = it | |||
} | |||
return mutableLiveData | |||
} | |||
/** 课程包主页上的更多按钮点击是否有效 */ | |||
private fun showMoreIsEnable(){ | |||
coursePackMainActivityVM.showMoreEnable = courseDetail.st_before != AppConstants.NOT_DOING | |||
} | |||
/** | |||
* 获取课程的测试数据 | |||
* @param testType Int 测试类型 | |||
@@ -129,6 +140,7 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
.subscribe { | |||
result.value = LearnData(lesson).apply { | |||
learnWordList = it | |||
examErrorMap = courseDetail.exam_w_r_list | |||
} | |||
} | |||
return result | |||
@@ -153,5 +165,126 @@ class CourseMainFragmentViewModel(val courseIndex : Int) : BaseViewModel() { | |||
}.compose(diskIo2DiskIo()).subscribe() | |||
} | |||
/** 初始化默认选中位置 */ | |||
fun initSelectPosition() : Int { | |||
//没有学习点,默认课程学习课时位置为第一个 | |||
var temposition = 0 | |||
when{ | |||
//学后总测试完成,默认无选中 | |||
courseDetail.st_after != AppConstants.NOT_DOING -> temposition = -1 | |||
//有学习点 | |||
courseDetail.course_learn_point.isNotEmpty() -> { | |||
run m@{ | |||
allLesson.forEachIndexed { index, lesson -> | |||
if ("${lesson.chapterId}_${lesson.lessonId}" == courseDetail.course_learn_point) { | |||
temposition = index | |||
return@m | |||
} | |||
} | |||
} | |||
} | |||
} | |||
return temposition | |||
} | |||
/** | |||
* 课时重学 | |||
* @param lessonPositionIndex Int 重学课时所在位置 | |||
*/ | |||
fun relearnLesson(lessonPositionIndex : Int):MutableLiveData<Boolean> { | |||
val result = MutableLiveData<Boolean>() | |||
//重学课时后课程的进度 | |||
val courseProgress = CourseManager.calculateEnglishCourseProgress(allLesson,true,lessonPositionIndex) | |||
//重学课时后项目的总进度 | |||
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseLessonRelearn(course.subjectId,course.coursePackId,course.courseId,courseProgress) | |||
Observable.fromCallable { | |||
// TODO: 2022/5/5 进行数据清除和保存 | |||
}.compose(diskIo2DiskIo()).subscribe{ | |||
//更新lesson | |||
val lesson = allLesson[lessonPositionIndex].apply { | |||
errorNumber = 0 | |||
correctNumber = 0 | |||
learnedIndex = -1 | |||
learnIsOver = false | |||
afterTestScore = AppConstants.NOT_DOING | |||
} | |||
//更新courseDetail | |||
courseDetail.run { | |||
val lessonKey = "${lesson.chapterId}_${lesson.lessonId}" | |||
//正确错误数归0,不会清除备忘本数据 | |||
right[lessonKey] = 0 | |||
wrong[lessonKey] = 0 | |||
//移除学后测试成绩 | |||
after.remove(lessonKey) | |||
//移除章节学习点 | |||
lesson_learn_point.remove(lessonKey) | |||
val wordStartKey = "${lessonKey}_" | |||
//移除总测、课时学前测试的学习错误数据 | |||
exam_w_r_list.filterKeys { | |||
it.startsWith(wordStartKey) | |||
}.forEach{ | |||
exam_w_r_list.remove(it.key) | |||
} | |||
//移除错误本中的记录 | |||
temporary_words.filterKeys { it.startsWith(wordStartKey) }.forEach{ | |||
temporary_words.remove(it.key) | |||
} | |||
//更新课程进度 | |||
courseLearnProgress = courseProgress | |||
course.courseLearnProgress = courseProgress | |||
coursePackMainActivityVM.currentCourseProgress.postValue(courseProgress) | |||
//更新统计总进度 | |||
CourseManager.calculateSubjectProgress(course.subjectId, course.coursePackId, | |||
course.courseId, courseProgress) | |||
//完成 | |||
result.postValue(true) | |||
} | |||
} | |||
return result | |||
} | |||
/** | |||
* 课程重学方法 | |||
*/ | |||
fun relearnCourse(): MutableLiveData<Boolean> { | |||
val result = MutableLiveData<Boolean>() | |||
//重学需要重新计算进度 | |||
val subjectProgress = CourseManager.calculateSubjectProgressWithCourseRelearn(course.subjectId,course.coursePackId,course.courseId) | |||
Observable.fromCallable { | |||
// TODO: 2022/5/5 调用课程重学 重新请求详情 | |||
val file = File(FileUtil.getSaveDirPath("appcache"), "${course.subjectId}_${course.coursePackId}_${course.courseId}") | |||
if (file.exists()){ file.delete() } | |||
}.compose(diskIo2DiskIo()) | |||
.subscribe{ | |||
courseDetail.run { | |||
courseLearnProgress = 0.0 | |||
st_before = AppConstants.NOT_DOING | |||
st_after = AppConstants.NOT_DOING | |||
before.clear() | |||
after.clear() | |||
right.clear() | |||
wrong.clear() | |||
lesson_learn_point.clear() | |||
exam_w_r_list.clear() | |||
course_learn_point = "" | |||
rl += 1 | |||
temporary_words.clear() | |||
vp.clear() | |||
exercise_schedule.clear() | |||
} | |||
result.postValue(true) | |||
} | |||
return result | |||
} | |||
// TODO: 2022/5/6 这里在退出时进行了统计数据缓存保存,在loadMain的时候进行了read缓存 ,后期需要清除 | |||
override fun onDestroy(owner : LifecycleOwner) { | |||
val objectToBytes = FileUtil.objectToBytes(courseDetail) | |||
FileUtil.writeBytesToFile(FileUtil.getSaveDirPath("appcache"),"${course.subjectId}_${course.coursePackId}_${course.courseId}",objectToBytes) | |||
super.onDestroy(owner) | |||
} | |||
} |
@@ -13,23 +13,24 @@ import com.xkl.cdl.data.bean.course.ExamBean | |||
import com.xkl.cdl.data.bean.intentdata.ExamData | |||
import com.xkl.cdl.data.manager.CourseManager | |||
import com.xkl.cdl.databinding.FragmentCourseTotalTestBinding | |||
import com.xkl.cdl.module.m_center_learn.CoursePackMainActivity | |||
/** | |||
* 课程总测试: 学前总,学后总 | |||
* @property totalTestType 测试类型 | |||
*/ | |||
class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, CourseMainFragmentViewModel>() { | |||
companion object { | |||
@JvmStatic | |||
fun newInstance(totalTestType: Int) = CourseTotalTestFragment().apply { | |||
fun newInstance(totalTestType : Int) = CourseTotalTestFragment().apply { | |||
arguments = Bundle().apply { | |||
putInt(AppConfig.INTENT_1, totalTestType) | |||
} | |||
} | |||
} | |||
override fun initViewModel(): CourseMainFragmentViewModel { | |||
override fun initViewModel() : CourseMainFragmentViewModel { | |||
return ViewModelProvider(requireParentFragment())[CourseMainFragmentViewModel::class.java] | |||
} | |||
@@ -38,25 +39,26 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C | |||
//由于使用的是与父类相同的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){ | |||
vm.loadTest(totalTestType).observe(this) { | |||
testData = it | |||
initView() | |||
} | |||
} | |||
private fun initView(){ | |||
binding.tvCountTip.text = CourseManager.expectedTestTime(vm.course.courseType,totalTestType,testData!!) | |||
private fun initView() { | |||
binding.tvCountTip.text = CourseManager.expectedTestTime(vm.course.courseType, totalTestType, testData!!) | |||
when (totalTestType) { | |||
AppConstants.TEST_TYPE_BEFORE_TOTAL -> { | |||
// TODO: 2022/4/21 需要把开始学习给取消了 | |||
@@ -86,10 +88,9 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C | |||
tvTitle.setText(R.string.tips_current_course_learn_over) | |||
tvMainTip.visibility = View.VISIBLE | |||
//根据测试成绩处理 | |||
vm.courseDetail.st_before = 81.0 | |||
vm.courseDetail.st_before.let { | |||
when { | |||
it == -1.0 -> { //未测 | |||
when (it) { | |||
AppConstants.NOT_DOING -> { //未测 | |||
tvMainTip.setText(R.string.test_total_after_tip_1) | |||
button2.run { | |||
setText(R.string.test_type_after_total) | |||
@@ -98,26 +99,12 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C | |||
} | |||
} | |||
} | |||
it < AppConstants.TEST_SCORE_LEVEL_1 -> { //测试未通过 | |||
tvMainTip.setHtml("你还未通过学后总测试,上次得分: <font color=\'#F26255\'>$it</font>") | |||
button1.run { | |||
visibility = View.VISIBLE | |||
setText(R.string.continue_learn) | |||
click { view -> | |||
continueLearn(view) | |||
} | |||
} | |||
button2.run { | |||
setText(R.string.start_test) | |||
click { view -> | |||
startTest(view) | |||
} | |||
else -> { //有成绩,直接完成 | |||
if (it >= AppConstants.TEST_SCORE_LEVEL_2) { | |||
tvMainTip.setHtml("你已完成学后总测试,上次得分: <font color=\'#5082E6\'>$it</font>") | |||
}else { | |||
tvMainTip.setHtml("你已完成学后总测试,上次得分: <font color=\'#F26255\'>$it</font>") | |||
} | |||
} | |||
it >= AppConstants.TEST_SCORE_LEVEL_1 -> { //测试通过 | |||
tvMainTip.setHtml("你已通过学后总测试,上次得分: <font color=\'#5082E6\'>$it</font>") | |||
tvTips.visibility = View.VISIBLE | |||
tvCountTip.visibility = View.GONE | |||
button1.run { | |||
@@ -134,6 +121,9 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C | |||
} | |||
} | |||
tvClearLearnRecord.visibility = View.VISIBLE | |||
tvClearLearnRecord.click { | |||
(activity as CoursePackMainActivity).showCourseRelearnDialog() | |||
} | |||
} | |||
} | |||
} | |||
@@ -142,17 +132,15 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C | |||
} | |||
} | |||
/**继续学习*/ | |||
private fun continueLearn(view: View) { | |||
private fun continueLearn(view : View) { | |||
(requireParentFragment() as CourseMainFragment).changeFragment(1) | |||
} | |||
/** 开始测试 : 生成测试题,跳转到测试 | |||
* | |||
*/ | |||
private fun startTest(view: View) { | |||
private fun startTest(view : View) { | |||
//生成数据 | |||
val examData = ExamData(vm.course.subjectId, totalTestType, vm.course.courseTitle, vm.course.courseTitle).apply { | |||
coursePackId = vm.course.coursePackId | |||
@@ -164,5 +152,5 @@ class CourseTotalTestFragment : BaseFragmentVM<FragmentCourseTotalTestBinding, C | |||
} | |||
(parentFragment as CourseMainFragment).startExam(examData) | |||
} | |||
} |
@@ -3,10 +3,8 @@ package com.xkl.cdl.module.main | |||
import android.os.Bundle | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.base.activity.BaseActivityVM | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.suliang.common.extension.loadFragment | |||
import com.suliang.common.extension.showHideFragment | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.ActivityMainBinding | |||
import com.xkl.cdl.module.m_center_learn.LearnCenterFragment | |||
@@ -14,7 +12,6 @@ import com.xkl.cdl.module.m_memo.MemoFragment | |||
import com.xkl.cdl.module.m_my.MyFragment | |||
import com.xkl.cdl.module.m_service_center.ServiceCenterFragment | |||
import com.xkl.cdl.module.m_statics.StaticsFragment | |||
import com.xkl.cdl.module.splash.SplashActivity | |||
/** | |||
* 主界面、控制切换子Fragment |
@@ -1,5 +1,6 @@ | |||
package com.xkl.cdl.util | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.data.bean.BaseWord | |||
import java.io.* | |||
import java.util.* | |||
@@ -307,6 +308,7 @@ class LearnRuleUtil<T : BaseWord> constructor(private val originLearnList : List | |||
currentCorrectMap[key] = true | |||
} | |||
if (isLearnFirst) currentIsError = false | |||
LogUtil.e("currentCorrectMap--->${currentCorrectMap.size} --> $currentCorrectMap") | |||
} | |||
/** | |||
@@ -324,6 +326,7 @@ class LearnRuleUtil<T : BaseWord> constructor(private val originLearnList : List | |||
} else if (!isInExamErrorMap(key) && !isInCurrentErrorMap(key)) { //测试中数据的错误 | |||
currentErrorMap[key] = true | |||
} | |||
LogUtil.e("currentErrorMap --->${currentErrorMap.size} $currentErrorMap") | |||
//开始进行数据扩容替换 | |||
replaceErrorToNull(item) //替换已有错误为空 | |||
insertErrorWord(item, false) //开始插入 | |||
@@ -334,7 +337,7 @@ class LearnRuleUtil<T : BaseWord> constructor(private val originLearnList : List | |||
* @return true在 false不存在 | |||
*/ | |||
fun isInExamErrorMap(key : String) : Boolean { | |||
return examErrorsMap?.getOrDefault(key, null) ?: false | |||
return examErrorsMap?.getOrDefault(key, false) ?: false | |||
} | |||
/** 是否是在学习的错误列表中 */ |
@@ -242,17 +242,23 @@ | |||
android:visibility="gone" | |||
/> | |||
<!--所有布局id--> | |||
<!-- <androidx.constraintlayout.widget.Group--> | |||
<!-- android:layout_width="wrap_content"--> | |||
<!-- android:layout_height="wrap_content"--> | |||
<!-- app:constraint_referenced_ids="iv_close,tv_score,tv_tip,tv_title,--> | |||
<!-- tv_lesson_name,tv_count_time,,tv_tip_1,--> | |||
<!-- inc_statistics_number,--> | |||
<!-- tv_learn_over_tip,--> | |||
<!-- tv_learn_over_for_after_count_time,--> | |||
<!-- tv_top,tv_top_1,tv_left,vSplit"--> | |||
<!-- tools:visibility="gone"--> | |||
<!-- />--> | |||
<!-- <androidx.constraintlayout.widget.Group | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
app:constraint_referenced_ids="iv_close,tv_score,tv_tip,tv_title, | |||
tv_lesson_name,tv_count_time,,tv_tip_1, | |||
inc_statistics_number, | |||
tv_learn_over_tip, | |||
tv_learn_over_for_after_count_time, | |||
tv_top,tv_top_1,tv_left,vSplit" | |||
tools:visibility="gone" | |||
/> | |||
<androidx.constraintlayout.widget.Group | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
app:constraint_referenced_ids="tv_title,tv_tip_1,tv_lesson_name,tv_count_time,tv_left,vSplit" | |||
/>--> | |||
<!-- <!–学前总测试控制显示的布局id–>--> | |||
<!-- <androidx.constraintlayout.widget.Group--> | |||
<!-- android:id="@+id/group_total_test"--> | |||
@@ -278,7 +284,7 @@ | |||
/> --> | |||
<!--课时正常学习结束的布局组 --> | |||
<!-- 课时完全结束的点击显示弹窗的布局组 --> | |||
<!-- <androidx.constraintlayout.widget.Group--> | |||
<!-- android:layout_width="wrap_content"--> | |||
<!-- android:layout_height="wrap_content"--> |
@@ -39,7 +39,7 @@ | |||
android:layout_marginStart="28dp" | |||
android:layout_marginTop="8dp" | |||
android:text="@string/error" | |||
android:textColor="@color/gray_2" | |||
android:textColor="@color/main_text_color" | |||
android:textSize="@dimen/smallerSize" | |||
app:layout_constraintStart_toEndOf="@+id/guideline_1" | |||
app:layout_constraintTop_toTopOf="parent" /> |
@@ -75,5 +75,8 @@ | |||
<string name="auto_playing">自动播放中···</string> | |||
<string name="quit_learn_title">你确定要退出本课程的学习吗?</string> | |||
<string name="quit_learn_content">退出后系统将保存你的学习进度</string> | |||
<string name="course_relearn_title">你确定要清空本课程的学习记录吗?</string> | |||
<string name="course_relearn_content">清空后你的学习记录将不可恢复,请谨慎操作</string> | |||
<string name="course_relearn_sure">确认清空</string> | |||
</resources> |
@@ -99,6 +99,8 @@ ext { | |||
grpc_android: "io.grpc:grpc-android:1.27.0", | |||
grpc_protobuf: "io.grpc:grpc-protobuf:1.27.0", | |||
grpc_stub: "io.grpc:grpc-stub:1.27.0", | |||
//liveEventBus https://github.com/JeremyLiao/LiveEventBus | |||
liveEventBus : "io.github.jeremyliao:live-event-bus-x:1.8.0" | |||
] | |||
@@ -67,6 +67,8 @@ dependencies { | |||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' | |||
//MMKV | |||
api customDependencies.MMKV | |||
// liveEventBus | |||
api customDependencies.liveEventBus | |||
@@ -10,48 +10,48 @@ import com.suliang.common.util.LogUtil | |||
* Describe: 打印生命周期的Activity | |||
*/ | |||
open class LifecycleLogActivity : AppCompatActivity() { | |||
override fun onCreate(savedInstanceState: Bundle?) { | |||
super.onCreate(savedInstanceState) | |||
LogUtil.e("onCreate()") | |||
} | |||
override fun onRestart() { | |||
super.onRestart() | |||
LogUtil.e("onRestart()") | |||
} | |||
override fun onStart() { | |||
super.onStart() | |||
LogUtil.e("onStart()") | |||
} | |||
override fun onResume() { | |||
super.onResume() | |||
LogUtil.e("onResume()") | |||
} | |||
override fun onPause() { | |||
super.onPause() | |||
LogUtil.e("onPause()") | |||
} | |||
override fun onStop() { | |||
super.onStop() | |||
LogUtil.e("onStop()") | |||
} | |||
override fun onDestroy() { | |||
super.onDestroy() | |||
LogUtil.e("onDestroy()") | |||
} | |||
override fun onSaveInstanceState(outState: Bundle) { | |||
super.onSaveInstanceState(outState) | |||
LogUtil.e("onSaveInstanceState()") | |||
} | |||
override fun onRestoreInstanceState(savedInstanceState: Bundle) { | |||
super.onRestoreInstanceState(savedInstanceState) | |||
LogUtil.e("onRestoreInstanceState()") | |||
} | |||
// override fun onCreate(savedInstanceState: Bundle?) { | |||
// super.onCreate(savedInstanceState) | |||
// LogUtil.e("onCreate()") | |||
// } | |||
// | |||
// override fun onRestart() { | |||
// super.onRestart() | |||
// LogUtil.e("onRestart()") | |||
// } | |||
// | |||
// override fun onStart() { | |||
// super.onStart() | |||
// LogUtil.e("onStart()") | |||
// } | |||
// | |||
// override fun onResume() { | |||
// super.onResume() | |||
// LogUtil.e("onResume()") | |||
// } | |||
// | |||
// override fun onPause() { | |||
// super.onPause() | |||
// LogUtil.e("onPause()") | |||
// } | |||
// | |||
// override fun onStop() { | |||
// super.onStop() | |||
// LogUtil.e("onStop()") | |||
// } | |||
// | |||
// override fun onDestroy() { | |||
// super.onDestroy() | |||
// LogUtil.e("onDestroy()") | |||
// } | |||
// | |||
// override fun onSaveInstanceState(outState: Bundle) { | |||
// super.onSaveInstanceState(outState) | |||
// LogUtil.e("onSaveInstanceState()") | |||
// } | |||
// | |||
// override fun onRestoreInstanceState(savedInstanceState: Bundle) { | |||
// super.onRestoreInstanceState(savedInstanceState) | |||
// LogUtil.e("onRestoreInstanceState()") | |||
// } | |||
} |
@@ -17,6 +17,7 @@ abstract class BaseFragmentVM<VB : ViewBinding, VM : BaseViewModel> : BaseFragme | |||
override fun initFirst() { | |||
vm = initViewModel() | |||
lifecycle.addObserver(vm) | |||
vm.pageEvent.observe(this) { startActivity(it) } | |||
vm.toastEvent.observe(this){ showToast(it) } | |||
vm.loadingEvent.observe(this) { |
@@ -16,37 +16,37 @@ import com.suliang.common.util.LogUtil | |||
open class BaseViewModel : ViewModel(), ViewModelLifecycle, ViewBehavior { | |||
override fun onCreate(owner: LifecycleOwner) { | |||
super.onCreate(owner) | |||
LogUtil.i("${javaClass.name} onCreate() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onCreate() ") | |||
} | |||
override fun onStart(owner: LifecycleOwner) { | |||
super.onStart(owner) | |||
LogUtil.i("${javaClass.name} onStart() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onStart() ") | |||
} | |||
override fun onResume(owner: LifecycleOwner) { | |||
super.onResume(owner) | |||
LogUtil.i("${javaClass.name} onResume() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onResume() ") | |||
} | |||
override fun onPause(owner: LifecycleOwner) { | |||
super.onPause(owner) | |||
LogUtil.i("${javaClass.name} onPause() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onPause() ") | |||
} | |||
override fun onStop(owner: LifecycleOwner) { | |||
super.onStop(owner) | |||
LogUtil.i("${javaClass.name} onStop() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onStop() ") | |||
} | |||
override fun onDestroy(owner: LifecycleOwner) { | |||
super.onDestroy(owner) | |||
LogUtil.i("${javaClass.name} onDestroy() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onDestroy() ") | |||
} | |||
override fun onCleared() { | |||
super.onCleared() | |||
LogUtil.i("${javaClass.name} onCleared() ") | |||
LogUtil.i("${javaClass.name}_${hashCode()} onCleared() ") | |||
} | |||
@@ -1,15 +1,85 @@ | |||
package com.suliang.common.eventbus | |||
import android.os.Looper | |||
import androidx.lifecycle.MutableLiveData | |||
/** | |||
* author suliang | |||
* create 2022/3/15 10:01 | |||
* Describe: 事件总线,这条总线把任何类中的数据直接传递到activity 或是 fragment 上 | |||
* https://blog.csdn.net/lp131452064/article/details/82736296 | |||
* https://juejin.cn/post/6844903768459329544 | |||
*/ | |||
@Deprecated("this class is deprecated" ,replaceWith = ReplaceWith("LiveEventBus"),level = DeprecationLevel.ERROR) | |||
object LiveDataBus { | |||
private val bus = mutableMapOf<String, MutableLiveData<Any>>() | |||
/** | |||
* 发送事件 | |||
* @param key String | |||
* @param value T | |||
* @param isSticky 是否是粘性事件,默认为false | |||
*/ | |||
fun <T : Any> postEvent(key:String, value:T, isSticky : Boolean = false){ | |||
if (Looper.getMainLooper().thread == Thread.currentThread()){ | |||
subscribeEvent<T>(key,isSticky).value = value | |||
}else{ | |||
subscribeEvent<T>(key,isSticky).postValue(value) | |||
} | |||
} | |||
/** | |||
* 订阅事件 | |||
* @param key bus中的key | |||
* @param isSticky 是否是粘性事件,默认为false | |||
*/ | |||
fun <T:Any> subscribeEvent(key:String,isSticky:Boolean = false): MutableLiveData<T> { | |||
if (!bus.containsKey(key)){ | |||
if (isSticky){ | |||
bus[key] = MutableLiveData() | |||
}else{ | |||
bus[key] = NonStickyMutableLivedata() | |||
} | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
fun remove(key : String) { | |||
bus.remove(key) | |||
} | |||
fun clearAll() { | |||
bus.clear() | |||
} | |||
// @SuppressWarnings("UNCHECKED_CAST") | |||
// @Synchronized | |||
// fun <T> with(key : String) : MutableLiveData<T> { | |||
// if (!bus.containsKey(key)) { | |||
// bus[key] = MutableLiveData() | |||
// } | |||
// return bus[key] as MutableLiveData<T> | |||
// } | |||
// | |||
// @SuppressWarnings("UNCHECKED_CAST") | |||
// @Synchronized | |||
// fun <T> withNonSticky(key : String) : MutableLiveData<T> { | |||
// if (!bus.containsKey(key)) { | |||
// bus[key] = NonStickyMutableLivedata() | |||
// } | |||
// return bus[key] as MutableLiveData<T> | |||
// } | |||
private val bus = mutableMapOf<String,MutableLiveData<Any>>() | |||
// /** | |||
// * 注册订阅者 | |||
@@ -28,52 +98,22 @@ object LiveDataBus { | |||
//java使用传入泛型class, kotlin使用可直接传入泛型,不需要class类型 | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> with(key:String) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = MutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> withNonSticky(key:String) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = NonStickyMutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> with(key:String,type: Class<T>) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = MutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> withNonSticky(key:String,type: Class<T>) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = NonStickyMutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
fun remove(key:String){ | |||
bus.remove(key) | |||
} | |||
fun clearAll(){ | |||
bus.clear() | |||
} | |||
// @SuppressWarnings("UNCHECKED_CAST") | |||
// @Synchronized | |||
// fun <T> with(key:String,type: Class<T>) : MutableLiveData<T>{ | |||
// if (!bus.containsKey(key)){ | |||
// bus[key] = MutableLiveData() | |||
// } | |||
// return bus[key] as MutableLiveData<T> | |||
// } | |||
// @SuppressWarnings("UNCHECKED_CAST") | |||
// @Synchronized | |||
// fun <T> withNonSticky(key:String,type: Class<T>) : MutableLiveData<T>{ | |||
// if (!bus.containsKey(key)){ | |||
// bus[key] = NonStickyMutableLiveData() | |||
// } | |||
// return bus[key] as MutableLiveData<T> | |||
// } | |||
} |
@@ -12,21 +12,37 @@ import java.lang.reflect.Method | |||
* author suliang | |||
* create 2022/3/15 10:51 | |||
* Describe: 非粘性LiveData ,需要先设置监听再发送数据,即后注册的监听收不到以前的监听 | |||
* 事件总线的LiveData 非粘性事件传false(刚注册时如果有事件则不发送) 粘性事件传true | |||
* @param isSticky false非粘性事件 true粘性事件 | |||
*/ | |||
class NonStickyMutableLiveData<T> : MutableLiveData<T>() { | |||
// private var stickFlag : Boolean = false | |||
internal class NonStickyMutableLivedata<T> : MutableLiveData<T>() { | |||
// override fun observe(owner : LifecycleOwner, observer : Observer<in T>) { | |||
// super.observe(owner, ObserverWrapper<T>(observer)) | |||
// } | |||
// | |||
// class ObserverWrapper<T>(var observer:Observer<in T>) : Observer<T> { | |||
// var isSticky = false | |||
// override fun onChanged(t : T) { | |||
// if (isSticky){ | |||
// observer.onChanged(t) | |||
// }else{ | |||
// isSticky = true | |||
// } | |||
// } | |||
// } | |||
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { | |||
super.observe(owner, observer) | |||
hook(observer) | |||
// if (!stickFlag){ | |||
// hook(observer) | |||
// stickFlag = true | |||
// } | |||
} | |||
private fun hook(observer: Observer<in T>) = try { | |||
override fun removeObserver(observer : Observer<in T>) { | |||
super.removeObserver(observer) | |||
} | |||
private fun hook(observer : Observer<in T>) = try { | |||
//得到 mLastVersion | |||
//获取到LiveData的类中的mObserver对象 | |||
// SafeIterableMap<Observer<? super T> , ObserverWrapper> mObservers | |||
@@ -38,7 +54,7 @@ class NonStickyMutableLiveData<T> : MutableLiveData<T>() { | |||
//得到map对应的class对象 | |||
val mObserversClass = mObserVersObject.javaClass | |||
//获取到mObservers对象的get方法 | |||
val get:Method = mObserversClass.getDeclaredMethod("get", Any::class.java) | |||
val get : Method = mObserversClass.getDeclaredMethod("get", Any::class.java) | |||
get.isAccessible = true | |||
//执行get方法,获取到对象 | |||
val invokeEntry = get.invoke(mObserVersObject, observer) | |||
@@ -46,7 +62,7 @@ class NonStickyMutableLiveData<T> : MutableLiveData<T>() { | |||
val observerWrapper = if (invokeEntry is Map.Entry<*, *>) { | |||
invokeEntry.value | |||
} else throw NullPointerException("observerWrapper is null") | |||
//得到observerWrapper的对象,编译擦除问题会引起多态冲突,所以用getSupperClass | |||
//getClass 返回对应的当前正在运行是的类所对应的 | |||
val mLastVersion = observerWrapper!!::class.java.superclass.getDeclaredField("mLastVersion") | |||
@@ -56,11 +72,10 @@ class NonStickyMutableLiveData<T> : MutableLiveData<T>() { | |||
mVersion.isAccessible = true | |||
//把mVersion数据填入到mLastVersion中 | |||
val mVersionValue = mVersion.get(this) | |||
mLastVersion.set(observerWrapper,mVersionValue) | |||
}catch (e: Exception){ | |||
mLastVersion.set(observerWrapper, mVersionValue) | |||
} catch (e : Exception) { | |||
e.printStackTrace() | |||
} | |||
} |
@@ -34,7 +34,7 @@ fun View.click(clickListener : ((view : View) -> Unit)?){ | |||
} | |||
this.setOnTouchListener{ view , motionEvent -> | |||
when(motionEvent.action){ | |||
MotionEvent.ACTION_DOWN -> view.alpha = 0.7f | |||
MotionEvent.ACTION_DOWN -> view.alpha = 0.8f | |||
MotionEvent.ACTION_UP -> view.alpha = 1f | |||
else -> view.alpha = 1f | |||
} |
@@ -400,9 +400,15 @@ object FileUtil { | |||
closeStream(out) | |||
return result | |||
} | |||
/*** | |||
* 从对象获取字节数组 | |||
* @param data Any 对象,需要实现序列化 | |||
* @return ByteArray 字节数组 | |||
* @throws Exception | |||
*/ | |||
@Throws(Exception::class) | |||
fun objectToBytes(data: Any): ByteArray { | |||
fun objectToBytes(data: Serializable): ByteArray { | |||
var oos: ObjectOutputStream? = null | |||
return try { | |||
val bos = ByteArrayOutputStream() | |||
@@ -413,6 +419,15 @@ object FileUtil { | |||
oos?.close() | |||
} | |||
} | |||
/** | |||
* 从字节数组获取对象 | |||
* @param data ByteArray | |||
* @return Any | |||
*/ | |||
fun bytesToObject(data:ByteArray): Any{ | |||
return ObjectInputStream(ByteArrayInputStream(data)).readObject() | |||
} | |||
/** | |||
* 获取文件大小 |
@@ -1,4 +1,3 @@ | |||
<resources> | |||
<!-- TODO: Remove or change this placeholder text --> | |||
<string name="hello_blank_fragment">Hello blank fragment</string> | |||
</resources> |