| @@ -65,6 +65,7 @@ | |||
| <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/_xpopup_ext_time_picker.xml" value="0.23632218844984804" /> | |||
| <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_course_statistics_detail.xml" value="0.33" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_exam_learn_spell.xml" value="0.47690217391304346" /> | |||
| <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_creading.xml" value="0.4304093567251462" /> | |||
| @@ -79,6 +80,7 @@ | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_memo_list_detail.xml" value="0.3621621621621622" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_memo_test.xml" value="0.25" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_splash.xml" value="0.4921875" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_subject_statistics.xml" value="0.23454913880445796" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_test_detail.xml" value="0.33" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/content_learn_base.xml" value="0.4979166666666667" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/custom_marker_view.xml" value="0.23353596757852077" /> | |||
| @@ -132,6 +134,9 @@ | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_memo_detail_list_pinyin.xml" value="0.67" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_spell_single_word.xml" value="0.23632218844984804" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_statics_for_time.xml" value="0.27717391304347827" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_statistics_bottom_course_change.xml" value="0.30842391304347827" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_statistics_course.xml" value="0.33" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_statistics_course_chapter_info.xml" value="0.5" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_task_image.xml" value="0.23353596757852077" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_test_detail.xml" value="0.348731884057971" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_test_detail_option.xml" value="0.348731884057971" /> | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| package="com.xkl.cdl"> | |||
| package="com.xkl.cdl" > | |||
| <application | |||
| android:name=".module.XKLApplication" | |||
| @@ -11,7 +11,13 @@ | |||
| android:roundIcon="@mipmap/ic_launcher_round" | |||
| android:supportsRtl="true" | |||
| android:theme="@style/Theme.XklLocal" | |||
| tools:ignore="LockedOrientationActivity"> | |||
| tools:ignore="LockedOrientationActivity" > | |||
| <activity | |||
| android:name=".module.m_statics.CourseStatisticsDetailActivity" | |||
| android:exported="true" /> | |||
| <activity | |||
| android:name=".module.m_statics.SubjectStatisticsDetailActivity" | |||
| android:exported="true" /> | |||
| <activity | |||
| android:name=".module.m_service_center.TestDetailActivity" | |||
| android:exported="true" /> | |||
| @@ -48,7 +54,7 @@ | |||
| android:configChanges="orientation|keyboardHidden|screenSize" | |||
| android:exported="true" | |||
| android:screenOrientation="portrait" | |||
| android:theme="@style/Theme.Splash"> | |||
| android:theme="@style/Theme.Splash" > | |||
| <intent-filter> | |||
| <action android:name="android.intent.action.MAIN" /> | |||
| @@ -0,0 +1,97 @@ | |||
| package com.xkl.cdl.adapter | |||
| import android.view.View | |||
| import android.view.ViewGroup | |||
| import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||
| import com.suliang.common.base.adapter.BaseRVAdapter | |||
| import com.suliang.common.databinding.ItemEmptyBinding | |||
| import com.suliang.common.extension.setHtml | |||
| import com.suliang.common.widget.NumberUtils | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.bean.course.Lesson | |||
| import com.xkl.cdl.databinding.ItemStatisticsCourseChapterInfoBinding | |||
| /** | |||
| * author suliang | |||
| * create 2022/7/1 10:27 | |||
| * Describe: 统计详情下的章节学习情况 | |||
| */ | |||
| class AdapterStatisticsCourseChapterInfo : BaseRVAdapter<Lesson>() { | |||
| override fun onBindEmptyViewHolder(holder : BaseAdapterViewHolder) { | |||
| (holder.binding as ItemEmptyBinding).apply { | |||
| imgEmpty.setImageResource(R.mipmap.empty_nothing) | |||
| tvContent.text = "没有课程进度" | |||
| } | |||
| } | |||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
| return BaseAdapterViewHolder(inflateBinding(parent, R.layout.item_statistics_course_chapter_info)) | |||
| } | |||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
| (holder.binding as ItemStatisticsCourseChapterInfoBinding).apply { | |||
| getItem(position).let { | |||
| when (it.courseType) { | |||
| AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> { | |||
| tv1.text = "章节" | |||
| tv1Value.text = it.chapterName | |||
| tv2.text = "学习进度" | |||
| tv2Value.text = "${NumberUtils.formatHalfUp(it.beforeTestScore, "0.#")}%" | |||
| tv3.setHtml( | |||
| "<font color=\"#323232\">条目-</font><font color=\"#40A540\">正确</font>|<font color=\"#F26255\">错误</font>") | |||
| tv3Value.setHtml( | |||
| "<font color=\"#323232\">${it.totalNumber}-</font><font color=\"#40A540\">${it.correctNumber}</font>|<font color=\"#F26255\">${it.errorNumber}</font>") | |||
| tv4.text = "知识点测试" | |||
| if (it.afterTestScore != AppConstants.NOT_DOING) { | |||
| it.afterTestScore.toInt().let { | |||
| p4.progress = it | |||
| tv5Value.text = it.toString() | |||
| } | |||
| } else { | |||
| p4.visibility = View.GONE | |||
| tv4Value.text = "——" | |||
| } | |||
| View.GONE.let { | |||
| v5.visibility = it | |||
| tv5.visibility = it | |||
| tv5Value.visibility = it | |||
| p5.visibility = it | |||
| } | |||
| } | |||
| else -> { | |||
| tv1Value.text = it.lessonName | |||
| tv2.text = "学习进度" | |||
| tv2Value.text = NumberUtils.formatHalfUp( | |||
| it.wordIds.subList(0, it.learnedIndex + 1).size / it.totalNumber.toDouble(), "0.#%") | |||
| tv3.setHtml( | |||
| "<font color=\"#323232\">条目-</font><font color=\"#40A540\">正确</font> | <font color=\"#F26255\">错误</font>") | |||
| tv3Value.setHtml( | |||
| "<font color=\"#323232\">${it.totalNumber}-</font><font color=\"#40A540\">${it.correctNumber}</font> | <font color=\"#F26255\">${it.errorNumber}</font>") | |||
| tv4.text = "课时学前测试" | |||
| tv5.text = "课时学前测试" | |||
| if (it.beforeTestScore != AppConstants.NOT_DOING) { | |||
| it.beforeTestScore.toInt().let { | |||
| p4.progress = it | |||
| tv4Value.text = it.toString() | |||
| } | |||
| } else { | |||
| p4.visibility = View.GONE | |||
| tv4Value.text = "——" | |||
| } | |||
| if (it.afterTestScore != AppConstants.NOT_DOING) { | |||
| it.afterTestScore.toInt().let { | |||
| p5.progress = it | |||
| tv5Value.text = it.toString() | |||
| } | |||
| } else { | |||
| p5.visibility = View.GONE | |||
| tv5Value.text = "——" | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| package com.xkl.cdl.adapter | |||
| import android.view.ViewGroup | |||
| import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||
| import com.suliang.common.base.adapter.BaseRVAdapter | |||
| import com.suliang.common.databinding.ItemEmptyBinding | |||
| import com.suliang.common.extension.click | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.bean.StatisticsCourse | |||
| import com.xkl.cdl.data.bean.course.Course | |||
| import com.xkl.cdl.databinding.ItemStatisticsCourseBinding | |||
| /** | |||
| * author suliang | |||
| * create 2022/6/27 14:24 | |||
| * Describe: | |||
| */ | |||
| class AdapterSubjectCourseStatistics : BaseRVAdapter<StatisticsCourse>() { | |||
| override fun onBindEmptyViewHolder(holder : BaseAdapterViewHolder) { | |||
| (holder.binding as ItemEmptyBinding).apply { | |||
| imgEmpty.setImageResource( R.mipmap.empty_nothing ) | |||
| tvContent.text = "没有可分析的课程" | |||
| } | |||
| } | |||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
| return BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_statistics_course)) | |||
| } | |||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
| (holder.binding as ItemStatisticsCourseBinding).course = getItem(position) | |||
| holder.binding.root.click { | |||
| if (onItemClickIsInitialized()){ | |||
| onItemClick.invoke(it,position,getItem(position)) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -34,7 +34,7 @@ class DataTransferHolder private constructor(){ | |||
| @Suppress("UNCHECKED_CAST") | |||
| fun <T> getData(key : String = AppConfig.INTENT_1) : T{ | |||
| return (dataMap[key] as T).also { | |||
| clear() | |||
| dataMap.remove(key) | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package com.xkl.cdl.data.bean | |||
| import com.xkl.cdl.data.bean.course.Course | |||
| /** | |||
| * author suliang | |||
| * create 2022/6/27 14:42 | |||
| * Describe: 统计课程item | |||
| */ | |||
| class StatisticsCourse(val course:Course,val cover: ByteArray) { | |||
| } | |||
| @@ -1,7 +1,5 @@ | |||
| package com.xkl.cdl.data.bean.course | |||
| import androidx.databinding.BaseObservable | |||
| /** | |||
| * author suliang | |||
| * create 2022/3/22 10:08 | |||
| @@ -2,6 +2,7 @@ package com.xkl.cdl.data.binding | |||
| import android.graphics.drawable.Drawable | |||
| import android.widget.ImageView | |||
| import android.widget.ProgressBar | |||
| import android.widget.TextView | |||
| import androidx.databinding.BindingAdapter | |||
| import com.suliang.common.util.DrawableUti | |||
| @@ -82,6 +83,42 @@ object BindingAdapter { | |||
| } | |||
| } | |||
| /** | |||
| * 统计课程文字显示的进度格式化 | |||
| * @param view TextView | |||
| * @param learnProgress Double | |||
| */ | |||
| @BindingAdapter("courseStatisticsLearnProgressTextViewFormat") | |||
| @JvmStatic | |||
| fun courseStatisticsLearnProgressTextViewFormat(view: TextView,learnProgress:Double){ | |||
| view.text = when{ | |||
| learnProgress == 0.0 -> "0%" | |||
| learnProgress < 0.1 -> "0.1%" | |||
| learnProgress < 100 -> { | |||
| DecimalFormat("#.#").apply { | |||
| roundingMode = RoundingMode.DOWN | |||
| }.format(learnProgress) + "%" | |||
| } | |||
| else -> "100%" | |||
| } | |||
| } | |||
| /** | |||
| * 课程统计进度条显示时的进度,最大进度值 1000 | |||
| * @param view ProgressBar | |||
| * @param learnProgress Double 默认为百分进度 | |||
| */ | |||
| @BindingAdapter("courseStatisticsLearnProgressFormat") | |||
| @JvmStatic | |||
| fun courseStatisticsLearnProgressFormat(view: ProgressBar,learnProgress:Double){ | |||
| val p = learnProgress * 10 //进度值改千分进度 | |||
| view.progress = when{ | |||
| p == 0.0 -> 0 | |||
| p < 0.1 -> 1 | |||
| p < 1000 -> p.toInt() | |||
| else -> 1000 | |||
| } | |||
| } | |||
| } | |||
| @@ -82,31 +82,41 @@ object CourseManager { | |||
| coursePack.childrenCourses.forEach { | |||
| //如果是作文,作文需要检查数据库,同时需要检查其下的图片、视频和关键帧 | |||
| if (it.subjectId == AppConstants.SUBJECT_CHINESE && it.coursePackType == AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION){ | |||
| if (it.subjectId == AppConstants.SUBJECT_CHINESE && it.coursePackType == AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION) { | |||
| val assetFileName = it.dbPathName.substringBeforeLast(".") + ".zip" | |||
| val inputStream = AppGlobals.application.resources.assets.open(assetFileName) | |||
| val zipInputStream = ZipInputStream(inputStream) | |||
| var count = 0 | |||
| var count = 0 | |||
| val buffer = ByteArray(5120) | |||
| var nextEntry = zipInputStream.nextEntry | |||
| while (nextEntry != null){ | |||
| when{ | |||
| nextEntry.name.endsWith(".mp4.png") -> File(FilePathManager.getMp4PngRootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/${nextEntry.name.substringAfterLast("/")}") | |||
| nextEntry.name.endsWith(".mp4") -> File(FilePathManager.getMp4RootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/${nextEntry.name.substringAfterLast("/")}") | |||
| nextEntry.name.endsWith(".png") -> File(FilePathManager.getPngRootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/${nextEntry.name.substringAfterLast("/")}") | |||
| nextEntry.name.endsWith(".db") -> File(FilePathManager.getDbRootPath(),"${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
| while (nextEntry != null) { | |||
| when { | |||
| nextEntry.name.endsWith(".mp4.png") -> File(FilePathManager.getMp4PngRootPath(), | |||
| "${entry.key}/${coursePack.coursePackId}/${it.courseId}/${ | |||
| nextEntry.name.substringAfterLast("/") | |||
| }") | |||
| nextEntry.name.endsWith(".mp4") -> File(FilePathManager.getMp4RootPath(), | |||
| "${entry.key}/${coursePack.coursePackId}/${it.courseId}/${ | |||
| nextEntry.name.substringAfterLast("/") | |||
| }") | |||
| nextEntry.name.endsWith(".png") -> File(FilePathManager.getPngRootPath(), | |||
| "${entry.key}/${coursePack.coursePackId}/${it.courseId}/${ | |||
| nextEntry.name.substringAfterLast("/") | |||
| }") | |||
| nextEntry.name.endsWith(".db") -> File(FilePathManager.getDbRootPath(), | |||
| "${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
| else -> null | |||
| }?.let { file -> | |||
| //文件不存在 ,复制当前文件进入该文件 | |||
| if (!file.exists()){ | |||
| //文件不存在 ,复制当前文件进入该文件 | |||
| if (!file.exists()) { | |||
| file.parentFile?.let { parentFile -> | |||
| if (!parentFile.exists()) parentFile.mkdirs() | |||
| } | |||
| val outputStream = BufferedOutputStream(FileOutputStream(file)) | |||
| while ((zipInputStream.read(buffer).also { count = it }) != -1 ){ | |||
| outputStream.write(buffer,0,count) | |||
| while ((zipInputStream.read(buffer).also { count = it }) != -1) { | |||
| outputStream.write(buffer, 0, count) | |||
| } | |||
| outputStream.close() | |||
| } | |||
| @@ -114,8 +124,9 @@ object CourseManager { | |||
| nextEntry = zipInputStream.nextEntry | |||
| } | |||
| }else{ //其他的项目只需要复制和检查数据库 | |||
| val file = File(FilePathManager.getDbRootPath(), "${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
| } else { //其他的项目只需要复制和检查数据库 | |||
| val file = File(FilePathManager.getDbRootPath(), | |||
| "${entry.key}/${coursePack.coursePackId}/${it.courseId}/course.db") | |||
| //不存在,复制,存在则不操作 | |||
| if (!file.exists()) { | |||
| FileUtil.copyAsset(it.dbPathName, file) | |||
| @@ -284,6 +295,29 @@ object CourseManager { | |||
| return totalProgress / chapterTotalSize * 100 | |||
| } | |||
| /** | |||
| * 作文课程单章节的进度计算 | |||
| * @param allLesson List<Lesson> 章节所有课时 | |||
| */ | |||
| fun calculateCompositionChapterProgress(allLesson : List<Lesson>) : Double { | |||
| var lessonSize = 0 | |||
| var chapterProgressSize = 0.0 | |||
| //循环 | |||
| allLesson.forEach { | |||
| //单章节内的进度累加 | |||
| if (it.lessonType != AppConstants.LESSON_TYPE_COMPOSITION_TASK) { | |||
| lessonSize += 1 | |||
| //章节添加课时进度 | |||
| chapterProgressSize += if (it.learnIsOver) 1.0 else if (it.lessonType == AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE) { | |||
| //知识点的进度设置,设置该课时总进度为 1 | |||
| if (it.learnedIndex == -1) 0.0 else (it.learnedIndex + 1.0) / it.wordIds.size | |||
| } else 0.0 | |||
| } | |||
| } | |||
| //计算进度 | |||
| return chapterProgressSize / lessonSize * 100 | |||
| } | |||
| /** | |||
| * 口语课程的进度计算 | |||
| * @param allLesson List<Lesson> | |||
| @@ -410,9 +444,10 @@ object CourseManager { | |||
| // TODO: 2022/6/2 时间需改为day 开发期间用于调式,故意设此值 | |||
| //间隔时间单位 | |||
| private val reviewIntervalUnit = "hour" | |||
| //间隔单位时间 | |||
| private val reviewTime = IntArray(7){ | |||
| when(it){ | |||
| private val reviewTime = IntArray(7) { | |||
| when (it) { | |||
| 0 -> 1 | |||
| 1 -> 2 | |||
| 2 -> 3 | |||
| @@ -430,20 +465,23 @@ object CourseManager { | |||
| * @param currentTime Long 用以计算的当前时间 | |||
| * @return Boolean true:需要复习 false:不需要复习 | |||
| */ | |||
| fun calculateIsNeedInReview(calendar:Calendar,currentReviewNumber : Int, currentReviewTime : String, currentTime : Long) : Boolean { | |||
| fun calculateIsNeedInReview(calendar : Calendar, | |||
| currentReviewNumber : Int, | |||
| currentReviewTime : String, | |||
| currentTime : Long) : Boolean { | |||
| if (currentReviewNumber >= reviewTime.size) return false | |||
| try { | |||
| //转换设置当前的复习时间 | |||
| calendar.time = DateUtil.format(currentReviewTime,DateUtil.FORMAT_1) | |||
| calendar.time = DateUtil.format(currentReviewTime, DateUtil.FORMAT_1) | |||
| //与当前时间的间隔 | |||
| val intervalTime = currentTime - calendar.timeInMillis | |||
| //单位转换 : 给一个提前量的时间,提前一分钟 | |||
| return intervalTime >= when(reviewIntervalUnit){ | |||
| return intervalTime >= when (reviewIntervalUnit) { | |||
| "minute" -> reviewTime[currentReviewNumber] * 60000 | |||
| "hour" -> reviewTime[currentReviewNumber] * 60 * 60000 | |||
| else -> reviewTime[currentReviewNumber] * 24 * 60 * 60000 | |||
| else -> reviewTime[currentReviewNumber] * 24 * 60 * 60000 | |||
| } - 60000 | |||
| }catch (e:Exception){ | |||
| } catch (e : Exception) { | |||
| e.printStackTrace() | |||
| } | |||
| return false | |||
| @@ -113,13 +113,13 @@ object DBCourseManager { | |||
| correctNumber = detail.right.getOrElse(key, { 0 }).toInt() //正确数 | |||
| errorNumber = detail.wrong.getOrElse(key, { 0 }).toInt() //错误数 | |||
| beforeTestScore = detail.before.getOrElse(key, { AppConstants.NOT_DOING }) //课时学前测试成绩 | |||
| afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩 | |||
| afterTestScore = detail.after.getOrElse(key, { AppConstants.NOT_DOING }) //课时学后测试成绩,作文知识点测试时为知识点测试成绩 | |||
| this.lessonType = lessonType | |||
| } | |||
| // 学习位置 | |||
| // id集合中的学习entity的位置, 作文的视频、知识点测试,不以这个为标准 | |||
| val learnIndex = wordIds.indexOf(detail.lesson_learn_point.getOrElse(key, { -1 })) //学习位置,当前位置为已学 | |||
| lesson.learnedIndex = learnIndex | |||
| // 注:针对口语对话课时,如果没有学习完,是不会设置进度点的。 todo 分课程类型设置进度点 口语未做判断处理 | |||
| lesson.learnIsOver = when (base.coursePackType) { | |||
| //作文课时,需要根据课时属性,判断本课时是否学习完成 | |||
| @@ -157,9 +157,9 @@ object DBCourseManager { | |||
| positionIndex += 1 | |||
| // TODO: 2022/5/9 对课时数量进行限制,课时太多,开发人员不好进行测试 | |||
| val needBreak = when (base.coursePackType) { | |||
| AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 6 //保留十个课时 | |||
| AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 4 //保留六个课时 | |||
| else -> positionIndex == 1 //保留三个课时 | |||
| AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION -> positionIndex == 10 //保留十个课时 | |||
| AppConstants.COURSEPACK_TYPE_ENGLISH_SPOKEN -> positionIndex == 6 //保留六个课时 | |||
| else -> positionIndex == 3 //保留三个课时 | |||
| } | |||
| if (needBreak) { | |||
| break | |||
| @@ -0,0 +1,450 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import android.annotation.SuppressLint | |||
| import android.graphics.Color | |||
| import android.os.Bundle | |||
| import android.view.View | |||
| import androidx.core.content.ContextCompat | |||
| import androidx.lifecycle.ViewModelProvider | |||
| import androidx.recyclerview.widget.LinearLayoutManager | |||
| import com.github.mikephil.charting.components.XAxis | |||
| import com.github.mikephil.charting.data.BarData | |||
| import com.github.mikephil.charting.data.BarDataSet | |||
| import com.github.mikephil.charting.data.BarEntry | |||
| import com.github.mikephil.charting.formatter.ValueFormatter | |||
| import com.lxj.xpopup.XPopup | |||
| import com.suliang.common.base.activity.BaseActivityVM | |||
| import com.suliang.common.extension.click | |||
| import com.suliang.common.extension.setHtml | |||
| import com.suliang.common.widget.NumberUtils | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.adapter.AdapterStatisticsCourseChapterInfo | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.bean.course.CourseDetail | |||
| import com.xkl.cdl.databinding.ActivityCourseStatisticsDetailBinding | |||
| import com.xkl.cdl.widget.MyMarkerView | |||
| import com.xkl.cdl.widget.StatisticsICourseChangePopup | |||
| import java.util.* | |||
| import kotlin.collections.ArrayList | |||
| import kotlin.math.abs | |||
| /** | |||
| * author suliang | |||
| * create 2022/6/27 9:42 | |||
| * Describe: 课程的统计详情 | |||
| */ | |||
| class CourseStatisticsDetailActivity : BaseActivityVM<ActivityCourseStatisticsDetailBinding, CourseStatisticsDetailViewModel>() { | |||
| override fun initViewModel() : CourseStatisticsDetailViewModel { | |||
| return ViewModelProvider(this)[CourseStatisticsDetailViewModel::class.java] | |||
| } | |||
| private lateinit var pupCourseChangeView : StatisticsICourseChangePopup | |||
| override fun initActivity(savedInstanceState : Bundle?) { | |||
| binding.titleBar.onBackClick = { _ -> finish() } | |||
| binding.course = vm.currentStatisCourse | |||
| initChart() | |||
| binding.rvChapterInfo.apply { | |||
| layoutManager = LinearLayoutManager(this@CourseStatisticsDetailActivity,LinearLayoutManager.VERTICAL,false) | |||
| adapter = AdapterStatisticsCourseChapterInfo() | |||
| } | |||
| if (vm.statisticsCourseList.size <= 1){ | |||
| binding.ivChangeCourse.visibility = View.INVISIBLE | |||
| }else{ | |||
| // TODO: 2022/7/1 弹窗实例失败 | |||
| //课程切换弹窗 | |||
| binding.ivChangeCourse.click { | |||
| if (!this::pupCourseChangeView.isInitialized){ | |||
| pupCourseChangeView = StatisticsICourseChangePopup(this).apply { | |||
| data = vm.statisticsCourseList | |||
| checkPostion = vm.checkedPosition | |||
| onAdapterItemClick = { position,item -> | |||
| vm.checkedPosition = position | |||
| vm.currentStatisCourse = item | |||
| loadData() | |||
| } | |||
| } | |||
| } | |||
| XPopup.Builder(this).asCustom(pupCourseChangeView).show() | |||
| } | |||
| } | |||
| vm.courseDetailLiveData.observe(this) { | |||
| when { | |||
| //英语界面显示 | |||
| vm.isShowAsEnglish(vm.dbControlBase.subjectId, vm.dbControlBase.coursePackType) -> { | |||
| //已学课堂练习 进度布局隐藏 | |||
| binding.ranking3.visibility = View.GONE | |||
| //测试成绩对比 | |||
| initTestTotalScore(it.st_before, it.st_after) | |||
| //学习效率初始 | |||
| initEfficient(it.e, it.last_e) | |||
| //学习时长统计 | |||
| initDurationTime(it) | |||
| } | |||
| //作文界面显示 | |||
| else -> { | |||
| binding.layoutTestScore.visibility = View.GONE | |||
| binding.layoutEfficient.visibility = View.GONE | |||
| initDurationTime(it) | |||
| } | |||
| } | |||
| } | |||
| /**课程进度 章节学习情况显示*/ | |||
| vm.courseLessonLiveData.observe(this){ | |||
| when(vm.currentStatisCourse.course.courseType){ | |||
| AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> { | |||
| binding.ranking1.setProgressNumber(vm.learnLessonSize / vm.totalLessonSize.toFloat() * 100) | |||
| binding.tvRanking1Flag.text = "已学章节" | |||
| binding.tvRanking1Value.setHtml("<font color=\"#40A540\"><b>${vm.learnLessonSize}</b></font>/${vm.totalLessonSize}") | |||
| binding.ranking2.setProgressNumber(vm.learnWordNumber/vm.totalWordNumber.toFloat() * 100 ) | |||
| binding.tvRanking2Flag.text = "已学知识点" | |||
| binding.tvRanking2Value.setHtml("<font color=\"#5082E6\"><b>${vm.learnWordNumber}</b></font>/${vm.totalWordNumber}") | |||
| binding.ranking3.setProgressNumber(vm.learnRadingSize/vm.totalRadingSize.toFloat() * 100) | |||
| binding.tvRanking3Flag.text = "已学课堂练习" | |||
| binding.tvRanking3Value.setHtml("<font color=\"#8757E6\"><b>${vm.learnRadingSize}</b></font>/${vm.totalRadingSize}") | |||
| } | |||
| else -> { | |||
| binding.ranking1.setProgressNumber(vm.learnLessonSize / vm.totalLessonSize.toFloat() * 100) | |||
| binding.tvRanking1Flag.text = "已学课时" | |||
| binding.tvRanking1Value.setHtml("<font color=\"#40A540\"><b>${vm.learnLessonSize}</b></font>/${vm.totalLessonSize}") | |||
| binding.ranking2.setProgressNumber(vm.learnWordNumber/vm.totalWordNumber.toFloat() * 100) | |||
| binding.tvRanking2Flag.text = "已学词汇量" | |||
| binding.tvRanking2Value.setHtml("<font color=\"#5082E6\"><b>${vm.learnWordNumber}</b></font>/${vm.totalWordNumber}") | |||
| binding.ranking3.visibility = View.GONE | |||
| } | |||
| } | |||
| //根据生成的数据,进行选择器的实现 | |||
| when(vm.chapterList.size){ | |||
| 0,1 -> binding.tvChapterChoose.visibility = View.GONE | |||
| else -> binding.tvChapterChoose.visibility = if(vm.chapterList[0].size > 0){ | |||
| binding.tvChapterChoose.text = vm.chapterList[0][0].chapterName | |||
| View.VISIBLE | |||
| }else { | |||
| View.GONE | |||
| } | |||
| } | |||
| //通知刷新列表 | |||
| vm.rvChapterChangeLiveData.value = 0 | |||
| } | |||
| //章节切换 | |||
| vm.rvChapterChangeLiveData.observe(this){ | |||
| if(vm.chapterList.size > it){ | |||
| (binding.rvChapterInfo.adapter as AdapterStatisticsCourseChapterInfo).apply { | |||
| needShowEmptyView = false | |||
| setData(vm.chapterList[it]) | |||
| } | |||
| }else{ | |||
| (binding.rvChapterInfo.adapter as AdapterStatisticsCourseChapterInfo).apply { | |||
| needShowEmptyView = true | |||
| setData(null) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 初始化数据图 | |||
| */ | |||
| private fun initChart() { | |||
| binding.barChart.apply { | |||
| description.isEnabled = false | |||
| setMaxVisibleValueCount(40) | |||
| setScaleEnabled(false) //禁止缩放 | |||
| setDrawGridBackground(false) | |||
| setDrawBarShadow(false) | |||
| setDrawValueAboveBar(false) | |||
| isHighlightFullBarEnabled = true | |||
| xAxis.apply { | |||
| isEnabled = true | |||
| setDrawGridLines(false) | |||
| setDrawAxisLine(false) | |||
| //自定义设置x轴显示value | |||
| valueFormatter = object : ValueFormatter() { | |||
| override fun getFormattedValue(value : Float) : String { | |||
| return vm.getTimeNode(value.toInt()) } | |||
| } | |||
| //设置x坐标显示个数 | |||
| granularity = 1f | |||
| textSize = 11f | |||
| textColor = Color.parseColor("#8A8A99") | |||
| position = XAxis.XAxisPosition.BOTTOM_INSIDE | |||
| } | |||
| axisLeft.apply { | |||
| gridColor = Color.parseColor("#E6E2E2") | |||
| textColor = Color.parseColor("#8A8A99") | |||
| setDrawAxisLine(false) | |||
| // granularity = 1f | |||
| setLabelCount(4, false) // force 6 labels | |||
| valueFormatter = object : ValueFormatter() { | |||
| override fun getFormattedValue(value : Float) : String { | |||
| return NumberUtils.formatFloor(value.toDouble(),"0.#") | |||
| } | |||
| } | |||
| } | |||
| axisRight.isEnabled = false | |||
| legend.isEnabled = false | |||
| //标签 | |||
| setDrawMarkers(true) | |||
| val mv = MyMarkerView(this@CourseStatisticsDetailActivity, R.layout.custom_marker_view) | |||
| mv.chartView = this // For bounds control | |||
| this.marker = mv // Set the marker to the chart | |||
| mv.setValueCallback { tv, e -> | |||
| val dataSets = this.data.dataSets | |||
| for (iBarDataSet in dataSets) { | |||
| //设置value框的偏移量 | |||
| mv.offsetX = 240f | |||
| val entryIndex = iBarDataSet.getEntryIndex(e as BarEntry) | |||
| if (entryIndex >= 0) { | |||
| this.setDrawMarkers(true) | |||
| if (e.getX() >= 2) { | |||
| //放左边 | |||
| mv.offsetX = -400f | |||
| } else { | |||
| //放右边 | |||
| mv.offsetX =400f | |||
| } | |||
| //分钟数转换为毫秒 | |||
| val valid = (e.yVals[0] * 60000.0).toLong() | |||
| val review = (e.yVals[1]* 60000.0).toLong() | |||
| val free = (e.yVals[2] * 60000.0).toLong() | |||
| val str : String = "<b><font>${vm.getTimeNode(entryIndex)}<font></b>" + | |||
| "<br><font color=\'#8A8A99\'>有效学习时长:</font> <font color=\'#F7874F\'>${formatTime(valid)}</font><font color=\'#8A8A99\'>${formatTimeUnit(valid)}</font>" + | |||
| "<br><font color=\'#8A8A99\'>有效复习时长:</font> <font color=\'#40A540\'>${formatTime(review)}</font><font color=\'#8A8A99\'>${formatTimeUnit(review)}</font>\n" + | |||
| "<br><font color=\'#8A8A99\'>空闲时长:</font> <font color=\'#5082E6\'> ${formatTime(free)}</font><font color=\'#8A8A99\'>${formatTimeUnit(free)}</font>" | |||
| tv.setHtml(str) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| override fun loadData() { | |||
| vm.getCourseDetailInfo() | |||
| } | |||
| /** | |||
| * 测试成绩对比 | |||
| * @param stBefore Double | |||
| * @param stAfter Double | |||
| */ | |||
| private fun initTestTotalScore(stBefore : Double, stAfter : Double) { | |||
| var tc = R.color.green_1 //tvTestState文字显示颜色 | |||
| var res = R.drawable.ic_rise //tvTestState前缀图标 | |||
| var tv = "+?" //tvTestState显示文字 | |||
| var ivRes = 0 //ivTestState 背景资源 | |||
| var tvBefore = "" //学前值 | |||
| var tvAfter = "" //学后值 | |||
| when { | |||
| //学前学后总测试都没做 | |||
| stBefore == AppConstants.NOT_DOING && stAfter == AppConstants.NOT_DOING -> { | |||
| ivRes = R.mipmap.statistics_testscore_nodata_chart | |||
| tvBefore = "未测" | |||
| tvAfter = "未测" | |||
| } | |||
| //学前学后总测都做完 | |||
| stBefore != AppConstants.NOT_DOING && stAfter != AppConstants.NOT_DOING -> { | |||
| //差距 | |||
| val instance = stAfter - stBefore | |||
| when { | |||
| instance == 0.0 -> { | |||
| tv = "+0" | |||
| ivRes = R.mipmap.statistics_testscore_equality_chart | |||
| } | |||
| instance > 0 -> { | |||
| tv = "+${NumberUtils.formatFloor(instance, "0.#")}" | |||
| ivRes = R.mipmap.statistics_testscore_up_chart | |||
| } | |||
| else -> { | |||
| tc = R.color.red_1 | |||
| res = R.drawable.ic_decline | |||
| tv = "-${NumberUtils.formatFloor(abs(instance), "0.#")}" | |||
| ivRes = R.mipmap.statistics_testscore_lower_chart | |||
| } | |||
| } | |||
| tvBefore = "${stBefore.toInt()}分" | |||
| tvAfter = "${stAfter.toInt()}分" | |||
| } | |||
| //学前总测做了,学后总测没做 | |||
| stAfter == AppConstants.NOT_DOING -> { | |||
| ivRes = R.mipmap.statistics_testscore_noend_chart | |||
| tvBefore = "${stBefore.toInt()}分" | |||
| tvAfter = "未测" | |||
| } | |||
| } | |||
| binding.ivTestState.setBackgroundResource(ivRes) | |||
| binding.tvTestState.apply { | |||
| setTextColor(ContextCompat.getColor(this@CourseStatisticsDetailActivity, tc)) | |||
| setCompoundDrawablesWithIntrinsicBounds(res, 0, 0, 0) | |||
| text = tv | |||
| } | |||
| binding.tvBeforeTotal.text = tvBefore | |||
| binding.tvAfterTotal.text = tvAfter | |||
| } | |||
| /** | |||
| * 学习效率 | |||
| * @param e Double | |||
| * @param lastE Double | |||
| */ | |||
| private fun initEfficient(e : Double, lastE : Double) { | |||
| when (vm.currentStatisCourse.course.courseType) { | |||
| //口语隐藏效率 | |||
| AppConstants.COURSE_TYPE_ENGLISH_SPOKEN -> binding.layoutEfficient.visibility = View.GONE | |||
| else -> { | |||
| binding.rankingEfficient.setProgressNumber(e.toFloat()) | |||
| binding.tvEfficient.text = "${NumberUtils.formatHalfUp(e, "0.##")}%" | |||
| binding.tvEfficientState.apply { | |||
| val instance = lastE - e | |||
| when { | |||
| //增长 | |||
| instance >= 0 -> { | |||
| setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_rise, 0, 0, 0) | |||
| setTextColor(ContextCompat.getColor(this@CourseStatisticsDetailActivity, R.color.green_1)) | |||
| text = "+${NumberUtils.formatHalfUp(instance, "0.##")}%" | |||
| } | |||
| //下降 | |||
| else -> { | |||
| setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_decline, 0, 0, 0) | |||
| setTextColor(ContextCompat.getColor(this@CourseStatisticsDetailActivity, R.color.red_1)) | |||
| text = "-${NumberUtils.formatHalfUp(abs(instance), "0.##")}%" | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /**学习时长统计*/ | |||
| @SuppressLint("SetTextI18n") | |||
| private fun initDurationTime(detail : CourseDetail) { | |||
| //总时长趋势 | |||
| val value = "${formatTime(abs(detail.current_week_total_durations))}${ | |||
| formatTimeUnit(abs(detail.current_week_total_durations)) | |||
| }" | |||
| when { | |||
| detail.current_week_total_durations >= 0 -> { | |||
| binding.tvDurationState.apply { | |||
| setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_rise, 0, 0, 0) | |||
| text = "+$value" | |||
| } | |||
| } | |||
| else -> { | |||
| binding.tvDurationState.apply { | |||
| setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_rise, 0, 0, 0) | |||
| text = "-$value" | |||
| } | |||
| } | |||
| } | |||
| var totalTime = 0L | |||
| var validTotalTime = 0L | |||
| var reviewTotalTime = 0L | |||
| val chartValueList = mutableListOf<MutableList<Long>>() | |||
| for (i in 1 until 5L) { | |||
| val total = detail.total.getOrElse(i, { 0 }) | |||
| val valid = detail.valid.getOrElse(i, { 0 }) | |||
| val review = detail.review.getOrElse(i, { 0 }) | |||
| totalTime += total | |||
| validTotalTime += valid | |||
| reviewTotalTime += review | |||
| //图表模型值 | |||
| chartValueList.add(mutableListOf(valid, review, total - valid - review)) | |||
| } | |||
| val freeTotalTime = totalTime - validTotalTime - reviewTotalTime | |||
| //设置时长 | |||
| binding.tvTotalDuration.text = formatTime(totalTime) | |||
| binding.tvTotalDurationUnit.text = formatTimeUnit(totalTime) | |||
| binding.tvValidTime.text = formatTime(validTotalTime) | |||
| binding.tvValidTimeUnit.text = formatTimeUnit(validTotalTime) | |||
| binding.tvReviewTime.text = formatTime(reviewTotalTime) | |||
| binding.tvReviewTimeUnit.text = formatTimeUnit(reviewTotalTime) | |||
| binding.tvFreeTime.text = formatTime(freeTotalTime) | |||
| binding.tvFreeTimeUnit.text = formatTimeUnit(freeTotalTime) | |||
| //更新图表值 | |||
| setChartDate(chartValueList) | |||
| } | |||
| /** 格式化时间值 */ | |||
| private fun formatTime(time : Long) : String { | |||
| val hour_1 = time / 3600000.0 | |||
| val minute_1 = time / 60000.0 | |||
| return when { | |||
| //大于1小时 | |||
| hour_1 > 1 -> "${NumberUtils.formatFloor(hour_1, "0.##")}" | |||
| //大于1分钟 | |||
| minute_1 > 1 -> "${NumberUtils.formatFloor(minute_1, "0.##")}" | |||
| //小于1分钟 秒数 | |||
| else -> "" | |||
| } | |||
| } | |||
| /**格式化单位*/ | |||
| private fun formatTimeUnit(time : Long) : String { | |||
| val hour_1 = time / 3600000.0 | |||
| val minute_1 = time / 60000.0 | |||
| return when { | |||
| //大于1小时 | |||
| hour_1 > 1 -> "小时" | |||
| //大于1分钟 | |||
| minute_1 > 1 -> "分钟" | |||
| //小于1分钟 秒数 | |||
| else -> "不足1分钟" | |||
| } | |||
| } | |||
| /** | |||
| * 设置统计图的数据 | |||
| * @param list | |||
| */ | |||
| private fun setChartDate(chartValueList : MutableList<MutableList<Long>>) { | |||
| val barChart = binding.barChart | |||
| val values = ArrayList<BarEntry>() | |||
| var max = 0f | |||
| chartValueList.forEachIndexed { i,it -> | |||
| val valid = if (it[0] >= 0) it[0]/60000.0f else 0f | |||
| val review = if (it[1] >= 0) it[1]/60000.0f else 0f | |||
| val free = if (it[2] >= 0) it[2]/60000.0f else 0f | |||
| val sum = valid + review + free | |||
| if (sum > max) { | |||
| max = sum | |||
| } | |||
| //数据由毫秒转换为了分钟数 | |||
| values.add(BarEntry(i.toFloat(), floatArrayOf(valid, review, free), null)) | |||
| } | |||
| if (max <= 0) { | |||
| barChart.setNoDataText("暂无数据") | |||
| barChart.clear() | |||
| return | |||
| } | |||
| val set1 : BarDataSet | |||
| if (barChart.data != null && barChart.data.dataSetCount > 0){ | |||
| set1 = barChart.data.getDataSetByIndex(0) as BarDataSet | |||
| set1.values = values | |||
| barChart.data.notifyDataChanged() | |||
| barChart.notifyDataSetChanged() | |||
| }else{ | |||
| set1 = BarDataSet(values,"") | |||
| set1.setDrawIcons(false) | |||
| set1.setDrawValues(false) | |||
| set1.colors = vm.getColors().toMutableList() | |||
| val data = BarData(set1) | |||
| data.barWidth = 0.5f | |||
| barChart.data = data | |||
| } | |||
| barChart.setFitBars(true) | |||
| barChart.invalidate() | |||
| } | |||
| } | |||
| @@ -0,0 +1,254 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import androidx.lifecycle.MutableLiveData | |||
| import com.github.mikephil.charting.utils.ColorTemplate | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.base.activity.ToastEvent | |||
| import com.suliang.common.base.viewmodel.BaseViewModel | |||
| import com.suliang.common.util.thread.AppExecutors | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.DataTransferHolder | |||
| import com.xkl.cdl.data.bean.StatisticsCourse | |||
| import com.xkl.cdl.data.bean.course.Course | |||
| import com.xkl.cdl.data.bean.course.CourseDetail | |||
| import com.xkl.cdl.data.bean.course.Lesson | |||
| 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 | |||
| import io.reactivex.rxjava3.schedulers.Schedulers | |||
| /** | |||
| * author suliang | |||
| * create 2022/6/28 11:29 | |||
| * Describe: | |||
| */ | |||
| class CourseStatisticsDetailViewModel : BaseViewModel() { | |||
| //课程列表 | |||
| val statisticsCourseList = DataTransferHolder.instance.getData<MutableList<StatisticsCourse>>(AppConfig.INTENT_1) | |||
| //选择课程 | |||
| var checkedPosition = DataTransferHolder.instance.getData<Int>(AppConfig.INTENT_2) | |||
| //当前操作课程 | |||
| var currentStatisCourse = statisticsCourseList[checkedPosition] | |||
| val dbControlBase : DbControlBase | |||
| get() { | |||
| val course : Course = currentStatisCourse.course | |||
| return DbControlBase(course.subjectId, course.coursePackId, course.coursePackType, course.courseId, course.courseType) | |||
| } | |||
| //当前课程统计信息 | |||
| val courseDetailLiveData = MutableLiveData<CourseDetail>() | |||
| //当前课程课时信息初始结束 | |||
| val courseLessonLiveData = MutableLiveData<Boolean>() | |||
| //rv选择章节position | |||
| val rvChapterChangeLiveData = MutableLiveData<Int>() | |||
| /**获取课程统计详情*/ | |||
| fun getCourseDetailInfo() { | |||
| val course = currentStatisCourse.course | |||
| DataRepository.getCourseDetails(course.subjectId, course.coursePackId, course.courseId).map { | |||
| //测试成绩对比、学习效率 和 学习时长统计中的总时间趋势 | |||
| courseDetailLiveData.postValue(it) | |||
| //获取课时信息 | |||
| DBCourseManager.queryAllLesson(dbControlBase, it) | |||
| }.subscribeOn(Schedulers.from(AppExecutors.diskIO)).subscribe({ | |||
| initLessons(it) | |||
| }, { | |||
| showToast(ToastEvent("数据查询失败")) | |||
| }) | |||
| } | |||
| /**是否显示为英语的界面 | |||
| * 语文的识字、拼音和英语显示是一致的 只有作文显示不一致 | |||
| * */ | |||
| fun isShowAsEnglish(subjectId : Int, coursePackType : Int) : Boolean { | |||
| return subjectId == AppConstants.SUBJECT_ENGLISH || coursePackType != AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION | |||
| } | |||
| /**透过下标获取不同的时间点*/ | |||
| fun getTimeNode(entryIndex : Int) : String { | |||
| return when (entryIndex) { | |||
| 0 -> "6:00-12:00" | |||
| 1 -> "12:00-18:00" | |||
| 2 -> "18:00-24:00" | |||
| else -> "0:00-6:00" | |||
| } | |||
| } | |||
| /*----------------------设置统计图表中需要的一些方法 Start-------------------------------------------------------------------------------------------*/ | |||
| /** | |||
| * 获取最大值 | |||
| * | |||
| * @return | |||
| */ | |||
| fun getMaxY(max : Int) : Float { | |||
| //设置坐标轴最大值和最小值 | |||
| var temp = max / 5 | |||
| temp = if (temp >= 1000) { | |||
| (temp / 1000 + if (temp % 1000 > 0) 1 else 0) * 1000 | |||
| } else if (temp >= 100) { | |||
| (temp / 100 + if (temp % 100 > 0) 1 else 0) * 100 | |||
| } else if (temp >= 10) { | |||
| (temp / 10 + if (temp % 10 > 0) 1 else 0) * 10 | |||
| } else { | |||
| (temp + if (max % 5 > 0) 1 else 0) * 1 | |||
| } | |||
| return (temp * 5 + temp / 2.0).toFloat() | |||
| } | |||
| /** | |||
| * 获取最小值 | |||
| * | |||
| * @return | |||
| */ | |||
| fun getMinY(max : Int) : Float { | |||
| //设置坐标轴最大值和最小值 | |||
| var temp = max / 5 | |||
| temp = if (temp >= 1000) { | |||
| (temp / 1000 + if (temp % 1000 > 0) 1 else 0) * 1000 | |||
| } else if (temp >= 100) { | |||
| (temp / 100 + if (temp % 100 > 0) 1 else 0) * 100 | |||
| } else if (temp >= 10) { | |||
| (temp / 10 + if (temp % 10 > 0) 1 else 0) * 10 | |||
| } else { | |||
| (temp + if (max % 5 > 0) 1 else 0) * 1 | |||
| } | |||
| return (-(temp - temp / 2.0) - if (temp / 2.5 == 0.0) 0.3 else temp / 2.5).toFloat() | |||
| } | |||
| val XUEKAOLE_COLORS_STATISTIC = intArrayOf(ColorTemplate.rgb("#F7874F"), ColorTemplate.rgb("#40A540"), | |||
| ColorTemplate.rgb("#5082E6")) | |||
| /** | |||
| * 获取柱状图的颜色 | |||
| * @return | |||
| */ | |||
| fun getColors() : IntArray { | |||
| // have as many colors as stack-values per entry | |||
| val colors = IntArray(3) | |||
| System.arraycopy(XUEKAOLE_COLORS_STATISTIC, 0, colors, 0, 3) | |||
| return colors | |||
| } | |||
| var learnLessonSize = 0 //已学课时 作文:已学章节数 | |||
| var totalLessonSize = 0 //总课时 作文:总章节数 | |||
| var learnWordNumber = 0 //已学词汇数 作文:已学知识点数量 | |||
| var totalWordNumber = 0 //总词汇量 作文: 总知识点个数 | |||
| var learnRadingSize = 0 //已学课堂练习数 | |||
| var totalRadingSize = 0 //课堂练习总数 | |||
| val chapterList = mutableListOf<MutableList<Lesson>>() //用于显示的章节课时的学习情况 | |||
| private fun initLessons(list : List<Lesson>) { | |||
| learnLessonSize = 0 //已学课时 作文:已学章节数 | |||
| totalLessonSize = 0 //总课时 作文:总章节数 | |||
| learnWordNumber = 0 //已学词汇数 作文:已学知识点数量 | |||
| totalWordNumber = 0 //总词汇量 作文: 总知识点个数 | |||
| learnRadingSize = 0 //已学课堂练习数 | |||
| totalRadingSize = 0 //课堂练习总数 | |||
| chapterList.clear() | |||
| var previousChapterId = -1L //记录上一个章节的id | |||
| var currentChapterList = mutableListOf<Lesson>() //记录一个章节的课时 | |||
| when(dbControlBase.courseType){ | |||
| AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> { | |||
| for (i in list.size -1 downTo 0) { | |||
| val lesson = list[i] | |||
| //转换为章节集合 | |||
| when(lesson.chapterId){ | |||
| //同一章节 | |||
| previousChapterId -> { | |||
| currentChapterList.add(lesson) | |||
| } | |||
| //不同章节 | |||
| else -> { | |||
| currentChapterList = mutableListOf() | |||
| currentChapterList.add(lesson) | |||
| chapterList.add(currentChapterList) | |||
| previousChapterId = lesson.chapterId | |||
| } | |||
| } | |||
| } | |||
| //总章节数 | |||
| totalLessonSize = chapterList.size | |||
| val chapterListNew = mutableListOf<Lesson>() | |||
| //组合章节结果, 转换为lesson用于显示 | |||
| chapterList.forEach { | |||
| val lesson = it[0] | |||
| //将课时组装为课时 | |||
| val newLesson = Lesson(lesson.subjectId,lesson.coursePackId,lesson.coursePackType,lesson.courseId,lesson.courseType,lesson.chapterId,lesson.chapterName,lesson.lessonId,lesson.lessonName) | |||
| //计算各种数量 | |||
| it.forEach { | |||
| when(it.lessonType){ | |||
| AppConstants.LESSON_TYPE_COMPOSITION_KNOWLEDGE -> { //知识点学习 | |||
| newLesson.totalNumber = it.totalNumber | |||
| newLesson.errorNumber = it.errorNumber | |||
| newLesson.correctNumber = it.correctNumber | |||
| totalWordNumber += it.totalNumber | |||
| learnWordNumber += if (it.learnIsOver) it.totalNumber else lesson.wordIds.subList(0,lesson.learnedIndex+1).size | |||
| } | |||
| AppConstants.LESSON_TYPE_COMPOSITION_EXAM -> { //知识点测试,成绩在detail的after中 | |||
| newLesson.afterTestScore = it.afterTestScore | |||
| } | |||
| AppConstants.LESSON_TYPE_COMPOSITION_READING -> { //课堂练习 | |||
| totalRadingSize += it.totalNumber | |||
| learnRadingSize += if (it.learnIsOver) it.totalNumber else it.wordIds.subList(0,it.learnedIndex + 1).size //已学数 | |||
| } | |||
| } | |||
| } | |||
| //计算该章节的进度 | |||
| val chapterProgress = CourseManager.calculateCompositionChapterProgress(it) | |||
| if (chapterProgress !=0.0){ | |||
| learnLessonSize += 1 | |||
| newLesson.beforeTestScore = chapterProgress //使用学前测试字段记录 章节进度 | |||
| chapterListNew.add(newLesson) | |||
| } | |||
| } | |||
| chapterList.clear() | |||
| chapterList.addAll(mutableListOf(chapterListNew)) | |||
| } | |||
| //英语 | |||
| else -> { | |||
| totalLessonSize = list.size //总课时数 | |||
| //使用倒叙循环 | |||
| for (i in list.size -1 downTo 0) { | |||
| val lesson = list.get(i) | |||
| if (lesson.lessonType != AppConstants.LESSON_TYPE_DIALOGUE) { //口语对话练习不添加为总数,不添加到列表显示 | |||
| totalWordNumber += lesson.totalNumber //累加总数 | |||
| //做了学前测试,则学习课时+1 | |||
| if (lesson.beforeTestScore != AppConstants.NOT_DOING) { | |||
| learnLessonSize += 1 | |||
| //做了学前测试,才有可能进行了课时单词的学习,计算已学单词数 | |||
| if (lesson.learnedIndex != -1) { | |||
| learnWordNumber += if (lesson.learnIsOver) lesson.totalNumber else lesson.wordIds.subList(0, | |||
| lesson.learnedIndex + 1).size | |||
| } | |||
| //统计已学章节的内容,用于显示章节的学习情况 | |||
| when (lesson.chapterId) { | |||
| //同一章节 | |||
| previousChapterId -> currentChapterList.add(lesson) | |||
| else -> { | |||
| currentChapterList = mutableListOf() | |||
| currentChapterList.add(lesson) | |||
| chapterList.add(currentChapterList) | |||
| previousChapterId = lesson.chapterId | |||
| } | |||
| } | |||
| } | |||
| }else if (lesson.learnIsOver){ //口语对话课时,学习完成,则添加一个已学课时数 | |||
| learnLessonSize += 1 | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //通知更新 | |||
| courseLessonLiveData.postValue(true) | |||
| } | |||
| } | |||
| @@ -1,14 +1,11 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import android.graphics.Typeface | |||
| import android.text.Html | |||
| import android.text.SpannableString | |||
| import android.text.Spanned | |||
| import android.text.style.StyleSpan | |||
| import androidx.core.content.ContextCompat | |||
| import androidx.lifecycle.ViewModelProvider | |||
| import androidx.recyclerview.widget.GridLayoutManager | |||
| import appApi.AppApi.EntityCount | |||
| import com.github.mikephil.charting.components.* | |||
| import com.github.mikephil.charting.data.Entry | |||
| import com.github.mikephil.charting.data.LineData | |||
| @@ -18,7 +15,6 @@ import com.github.mikephil.charting.highlight.Highlight | |||
| import com.github.mikephil.charting.interfaces.datasets.ILineDataSet | |||
| import com.github.mikephil.charting.listener.OnChartValueSelectedListener | |||
| import com.github.mikephil.charting.renderer.LineChartRenderer | |||
| import com.google.android.material.tabs.TabLayout | |||
| import com.lxj.xpopup.XPopup | |||
| import com.lxj.xpopup.enums.PopupPosition | |||
| import com.suliang.common.base.fragment.BaseFragmentVM | |||
| @@ -26,12 +22,11 @@ import com.suliang.common.extension.click | |||
| import com.suliang.common.extension.loadFragment | |||
| import com.suliang.common.extension.setHtml | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.adapter.AdapterStaticsTime | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.databinding.FragmentStaticsBinding | |||
| import com.xkl.cdl.widget.MyMarkerView | |||
| class StaticsFragment : BaseFragmentVM<FragmentStaticsBinding, StaticsFragmentViewModel>() { | |||
| class StaticsFragment : BaseFragmentVM<FragmentStaticsBinding, StatisticsFragmentViewModel>() { | |||
| companion object { | |||
| @JvmStatic | |||
| @@ -39,18 +34,20 @@ class StaticsFragment : BaseFragmentVM<FragmentStaticsBinding, StaticsFragmentVi | |||
| } | |||
| override fun initViewModel() : StaticsFragmentViewModel { | |||
| return ViewModelProvider(this)[StaticsFragmentViewModel::class.java] | |||
| override fun initViewModel() : StatisticsFragmentViewModel { | |||
| return ViewModelProvider(this)[StatisticsFragmentViewModel::class.java] | |||
| } | |||
| override fun initFragment() { | |||
| //添加碎片 | |||
| loadFragment(R.id.fragment_top_time,0,StatisticsTimeTopFragment()) | |||
| loadFragment(R.id.fragment_top_time,0,StatisticsTimeTopFragment.newInstance(0)) | |||
| //detail | |||
| binding.layoutEnglish.click { | |||
| // TODO: 2022/6/24 进入详情 | |||
| SubjectStatisticsDetailActivity.newInstance(requireContext(),AppConstants.SUBJECT_ENGLISH) | |||
| } | |||
| binding.layoutChinese.click { | |||
| SubjectStatisticsDetailActivity.newInstance(requireContext(),AppConstants.SUBJECT_CHINESE) | |||
| } | |||
| binding.layoutChinese.click { } | |||
| //chart change | |||
| binding.tvChartSubjectChoose.click { | |||
| XPopup.Builder(requireContext()).apply { | |||
| @@ -124,7 +121,7 @@ class StaticsFragment : BaseFragmentVM<FragmentStaticsBinding, StaticsFragmentVi | |||
| ///设置时间统计的选中项 | |||
| childFragmentManager.findFragmentByTag(StatisticsTimeTopFragment::javaClass.name)?.run { | |||
| //设置时间统计的选中项 | |||
| (this as StatisticsTimeTopFragment).binding.tabLayoutTime.getTabAt(0)?.select() | |||
| (this as StatisticsTimeTopFragment).loadData() | |||
| } | |||
| //图表设置 | |||
| vm.chartChooseProjectIdLiveData.value = vm.chartChooseProjectIdLiveData.value | |||
| @@ -0,0 +1,146 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import android.graphics.Color | |||
| import androidx.lifecycle.MutableLiveData | |||
| import appApi.AppApi | |||
| import com.github.mikephil.charting.components.YAxis | |||
| import com.github.mikephil.charting.data.Entry | |||
| import com.github.mikephil.charting.data.LineDataSet | |||
| import com.github.mikephil.charting.utils.ColorTemplate | |||
| import com.suliang.common.base.activity.ToastEvent | |||
| import com.suliang.common.base.viewmodel.BaseViewModel | |||
| import com.suliang.common.extension.diskIo2Main | |||
| import com.suliang.common.util.thread.AppExecutors | |||
| import com.suliang.common.widget.NumberUtils | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.bean.TimeStatisticItem | |||
| import com.xkl.cdl.data.manager.CourseManager | |||
| import com.xkl.cdl.data.repository.DataRepository | |||
| import com.xkl.cdl.module.XKLApplication | |||
| import io.reactivex.rxjava3.core.Observable | |||
| import io.reactivex.rxjava3.schedulers.Schedulers | |||
| import kotlin.math.abs | |||
| import kotlin.math.max | |||
| class StatisticsFragmentViewModel : BaseViewModel() { | |||
| //chart选中 itemId | |||
| var chartChooseProjectIdLiveData = MutableLiveData<Int>(0) | |||
| //图标初始完成 | |||
| val chartLineInitLiveData = MutableLiveData<Boolean>() | |||
| /**获取词条数据,进行图标数据实例 */ | |||
| fun getStatisticsChartData(projectId:Int) { | |||
| DataRepository.getStatisticsChartData(projectId) | |||
| .subscribeOn(Schedulers.from(AppExecutors.diskIO)) | |||
| .observeOn(Schedulers.from(AppExecutors.io)) | |||
| .subscribe({ | |||
| //进行图标数据处理 | |||
| entityCountList.clear() | |||
| entityCountList.addAll(it.entityCountListList) | |||
| initChartLine(it.entityCountListList) | |||
| },{ | |||
| it.printStackTrace() | |||
| showToast(ToastEvent("统计数据获取失败")) | |||
| }) | |||
| } | |||
| val entityCountList: MutableList<AppApi.EntityCount> = mutableListOf() | |||
| var maxChart = 0L | |||
| var linDataSet_new : LineDataSet? = null //折线1 : 新学词条 | |||
| var linDataSet_review : LineDataSet? = null //折线2 : 复习词条 | |||
| private fun initChartLine(list: List<AppApi.EntityCount>){ | |||
| maxChart = 0L | |||
| linDataSet_new = null | |||
| linDataSet_review = null | |||
| val entrys_new = mutableListOf<Entry>() //新学点集合 | |||
| val entrys_review = mutableListOf<Entry>() //复习点集合 | |||
| list.forEachIndexed { index, it -> | |||
| entrys_new.add(Entry(index.toFloat(),it.n.toFloat())) | |||
| entrys_review.add(Entry(index.toFloat(),it.r.toFloat())) | |||
| maxChart = when{ | |||
| it.n > it.r -> max(maxChart, it.n) | |||
| else -> max(maxChart, it.r) | |||
| } | |||
| } | |||
| if (list.isEmpty()){ | |||
| chartLineInitLiveData.postValue(false) | |||
| return | |||
| } | |||
| linDataSet_new = LineDataSet(entrys_new, "新学词条数").apply { | |||
| axisDependency = YAxis.AxisDependency.RIGHT | |||
| color = Color.parseColor("#5082E6") | |||
| lineWidth = 2f | |||
| circleRadius = 5f | |||
| circleHoleRadius = 3f | |||
| setCircleColor(Color.parseColor("#5082E6")) | |||
| fillAlpha = 65 | |||
| fillColor = ColorTemplate.getHoloBlue() | |||
| highLightColor = Color.parseColor("#1A5082E6") | |||
| highlightLineWidth = 18f | |||
| setDrawCircleHole(true) | |||
| setDrawHorizontalHighlightIndicator(false) | |||
| setDrawVerticalHighlightIndicator(true) | |||
| } | |||
| linDataSet_review = LineDataSet(entrys_review, "复习词条数").apply { | |||
| axisDependency = YAxis.AxisDependency.RIGHT | |||
| color = Color.parseColor("#F25255") | |||
| lineWidth = 2f | |||
| circleRadius = 5f | |||
| circleHoleRadius = 3f | |||
| setCircleColor(Color.parseColor("#F25255")) | |||
| fillAlpha = 65 | |||
| fillColor = Color.RED | |||
| highLightColor = Color.parseColor("#1A5082E6") | |||
| highlightLineWidth = 18f | |||
| setDrawCircleHole(true) | |||
| setDrawHorizontalHighlightIndicator(false) | |||
| } | |||
| chartLineInitLiveData.postValue(true) | |||
| } | |||
| /** 获取y轴的最大值 */ | |||
| fun getMaxY(): Float{ | |||
| //设置坐标轴最大值和最小值 | |||
| var temp : Int = (maxChart / 5).toInt() | |||
| temp = if (temp >= 1000) { | |||
| (temp / 1000 + if (temp % 1000 > 0) 1 else 0) * 1000 | |||
| } else if (temp >= 100) { | |||
| (temp / 100 + if (temp % 100 > 0) 1 else 0) * 100 | |||
| } else if (temp >= 10) { | |||
| (temp / 10 + if (temp % 10 > 0) 1 else 0) * 10 | |||
| } else { | |||
| (temp + if (maxChart % 5 > 0) 1 else 0) * 1 | |||
| } | |||
| return (temp * 5 + temp / 2.0).toFloat() | |||
| } | |||
| /**获取y轴的最小值*/ | |||
| fun getMinY():Float{ | |||
| //设置坐标轴最大值和最小值 | |||
| var temp : Int = (maxChart / 5).toInt() | |||
| temp = if (temp >= 1000) { | |||
| (temp / 1000 + if (temp % 1000 > 0) 1 else 0) * 1000 | |||
| } else if (temp >= 100) { | |||
| (temp / 100 + if (temp % 100 > 0) 1 else 0) * 100 | |||
| } else if (temp >= 10) { | |||
| (temp / 10 + if (temp % 10 > 0) 1 else 0) * 10 | |||
| } else { | |||
| (temp + if (maxChart % 5 > 0) 1 else 0) * 1 | |||
| } | |||
| return (-(temp - temp / 2.0) - if (temp / 2.5 == 0.0) 0.3 else temp / 2.5).toFloat() | |||
| } | |||
| /** 获取为x显示的值 */ | |||
| fun getxAxisValue(value : Float) : String { | |||
| if (entityCountList.size > value){ | |||
| val entity = entityCountList[value.toInt()] | |||
| val split = entity.day.split("-") | |||
| return if (split[2].equals("01")) "${split[1]}月" else split[2] | |||
| } | |||
| return "" | |||
| } | |||
| } | |||
| @@ -1,10 +1,13 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import android.os.Bundle | |||
| import androidx.lifecycle.ViewModelProvider | |||
| import androidx.recyclerview.widget.GridLayoutManager | |||
| import com.google.android.material.tabs.TabLayout | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.base.fragment.BaseFragmentVM | |||
| import com.xkl.cdl.adapter.AdapterStaticsTime | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.databinding.FragmentStatisticsTimeTopBinding | |||
| /** | |||
| @@ -12,22 +15,33 @@ import com.xkl.cdl.databinding.FragmentStatisticsTimeTopBinding | |||
| * create 2022/6/24 17:24 | |||
| * Describe: | |||
| */ | |||
| class StatisticsTimeTopFragment : BaseFragmentVM<FragmentStatisticsTimeTopBinding, StaticsFragmentViewModel>() { | |||
| class StatisticsTimeTopFragment : BaseFragmentVM<FragmentStatisticsTimeTopBinding, StatisticsTimeTopFragmentViewModel>() { | |||
| override fun initViewModel() : StaticsFragmentViewModel { | |||
| return ViewModelProvider(requireParentFragment())[StaticsFragmentViewModel::class.java] | |||
| companion object{ | |||
| fun newInstance(projectId : Int) : StatisticsTimeTopFragment { | |||
| val args = Bundle() | |||
| args.putInt(AppConfig.INTENT_1, projectId) | |||
| val fragment = StatisticsTimeTopFragment() | |||
| fragment.arguments = args | |||
| return fragment | |||
| } | |||
| } | |||
| override fun initViewModel() : StatisticsTimeTopFragmentViewModel { | |||
| return ViewModelProvider(this)[StatisticsTimeTopFragmentViewModel::class.java] | |||
| } | |||
| override fun initFragment() { | |||
| vm.projectId = requireArguments().getInt(AppConfig.INTENT_1,0) | |||
| initTabLayout() | |||
| //rv | |||
| binding.rvTime.apply { | |||
| layoutManager = GridLayoutManager(requireContext(), 2) | |||
| adapter = AdapterStaticsTime() | |||
| } | |||
| } | |||
| override fun loadData() { | |||
| //监听时间选项变化 | |||
| vm.timeStatisticsPositionLiveData.observe(this) { position -> | |||
| if (!vm.isInitStatisticsResponse()) { | |||
| @@ -42,9 +56,15 @@ class StatisticsTimeTopFragment : BaseFragmentVM<FragmentStatisticsTimeTopBindin | |||
| } | |||
| } | |||
| override fun loadData() { | |||
| //设置选中加载数据 | |||
| vm.timeStatisticsPositionLiveData.value = binding.tabLayoutTime.selectedTabPosition | |||
| } | |||
| private fun initTabLayout() { | |||
| binding.tabLayoutTime.run { | |||
| addTab(this.newTab().setText("全部时间"), false) | |||
| addTab(this.newTab().setText("全部时间"), true) | |||
| addTab(this.newTab().setText("今日"), false) | |||
| addTab(this.newTab().setText("本周"), false) | |||
| addTab(this.newTab().setText("本月"), false) | |||
| @@ -1,39 +1,26 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import android.graphics.Color | |||
| import androidx.lifecycle.MutableLiveData | |||
| import appApi.AppApi | |||
| import com.github.mikephil.charting.components.YAxis | |||
| import com.github.mikephil.charting.data.Entry | |||
| import com.github.mikephil.charting.data.LineDataSet | |||
| import com.github.mikephil.charting.utils.ColorTemplate | |||
| import com.suliang.common.base.activity.ToastEvent | |||
| import com.suliang.common.base.viewmodel.BaseViewModel | |||
| import com.suliang.common.extension.diskIo2Main | |||
| import com.suliang.common.util.thread.AppExecutors | |||
| import com.suliang.common.widget.NumberUtils | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.bean.TimeStatisticItem | |||
| import com.xkl.cdl.data.manager.CourseManager | |||
| import com.xkl.cdl.data.repository.DataRepository | |||
| import com.xkl.cdl.module.XKLApplication | |||
| import io.reactivex.rxjava3.core.Observable | |||
| import io.reactivex.rxjava3.schedulers.Schedulers | |||
| import kotlin.math.abs | |||
| import kotlin.math.max | |||
| class StaticsFragmentViewModel : BaseViewModel() { | |||
| class StatisticsTimeTopFragmentViewModel : BaseViewModel() { | |||
| /**顶部统计信息*/ | |||
| /** 时间统计选择位置 */ | |||
| var projectId = 0 //默认全部 | |||
| val timeStatisticsPositionLiveData = MutableLiveData<Int>() | |||
| lateinit var statisticsResponse : AppApi.StatisticsCenterResponse | |||
| var timeValuesList : MutableList<MutableList<TimeStatisticItem>> = mutableListOf() | |||
| //chart选中 itemId | |||
| var chartChooseProjectIdLiveData = MutableLiveData<Int>(0) | |||
| //图标初始完成 | |||
| val chartLineInitLiveData = MutableLiveData<Boolean>() | |||
| fun isInitStatisticsResponse() : Boolean { | |||
| return this::statisticsResponse.isInitialized | |||
| } | |||
| @@ -42,7 +29,7 @@ class StaticsFragmentViewModel : BaseViewModel() { | |||
| fun getStatistics() : MutableLiveData<Boolean> { | |||
| val result = MutableLiveData<Boolean>() | |||
| Observable.fromCallable { | |||
| val statisticsCenter = XKLApplication.mobileCache.statisticsCenter(0, 0) | |||
| val statisticsCenter = XKLApplication.mobileCache.statisticsCenter(projectId.toLong(), 0) | |||
| val parseFrom = AppApi.StatisticsCenterResponse.parseFrom(statisticsCenter) | |||
| return@fromCallable parseFrom | |||
| }.compose(diskIo2Main()).subscribe({ | |||
| @@ -184,119 +171,4 @@ class StaticsFragmentViewModel : BaseViewModel() { | |||
| } | |||
| } | |||
| /**获取词条数据,进行图标数据实例 */ | |||
| fun getStatisticsChartData(projectId:Int) { | |||
| DataRepository.getStatisticsChartData(projectId) | |||
| .subscribeOn(Schedulers.from(AppExecutors.diskIO)) | |||
| .observeOn(Schedulers.from(AppExecutors.io)) | |||
| .subscribe({ | |||
| //进行图标数据处理 | |||
| entityCountList.clear() | |||
| entityCountList.addAll(it.entityCountListList) | |||
| initChartLine(it.entityCountListList) | |||
| },{ | |||
| it.printStackTrace() | |||
| showToast(ToastEvent("统计数据获取失败")) | |||
| }) | |||
| } | |||
| val entityCountList: MutableList<AppApi.EntityCount> = mutableListOf() | |||
| var maxChart = 0L | |||
| var linDataSet_new : LineDataSet? = null //折线1 : 新学词条 | |||
| var linDataSet_review : LineDataSet? = null //折线2 : 复习词条 | |||
| private fun initChartLine(list: List<AppApi.EntityCount>){ | |||
| maxChart = 0L | |||
| linDataSet_new = null | |||
| linDataSet_review = null | |||
| val entrys_new = mutableListOf<Entry>() //新学点集合 | |||
| val entrys_review = mutableListOf<Entry>() //复习点集合 | |||
| list.forEachIndexed { index, it -> | |||
| entrys_new.add(Entry(index.toFloat(),it.n.toFloat())) | |||
| entrys_review.add(Entry(index.toFloat(),it.r.toFloat())) | |||
| maxChart = when{ | |||
| it.n > it.r -> max(maxChart, it.n) | |||
| else -> max(maxChart, it.r) | |||
| } | |||
| } | |||
| if (list.isEmpty()){ | |||
| chartLineInitLiveData.postValue(false) | |||
| return | |||
| } | |||
| linDataSet_new = LineDataSet(entrys_new, "新学词条数").apply { | |||
| axisDependency = YAxis.AxisDependency.RIGHT | |||
| color = Color.parseColor("#5082E6") | |||
| lineWidth = 2f | |||
| circleRadius = 5f | |||
| circleHoleRadius = 3f | |||
| setCircleColor(Color.parseColor("#5082E6")) | |||
| fillAlpha = 65 | |||
| fillColor = ColorTemplate.getHoloBlue() | |||
| highLightColor = Color.parseColor("#1A5082E6") | |||
| highlightLineWidth = 18f | |||
| setDrawCircleHole(true) | |||
| setDrawHorizontalHighlightIndicator(false) | |||
| setDrawVerticalHighlightIndicator(true) | |||
| } | |||
| linDataSet_review = LineDataSet(entrys_review, "复习词条数").apply { | |||
| axisDependency = YAxis.AxisDependency.RIGHT | |||
| color = Color.parseColor("#F25255") | |||
| lineWidth = 2f | |||
| circleRadius = 5f | |||
| circleHoleRadius = 3f | |||
| setCircleColor(Color.parseColor("#F25255")) | |||
| fillAlpha = 65 | |||
| fillColor = Color.RED | |||
| highLightColor = Color.parseColor("#1A5082E6") | |||
| highlightLineWidth = 18f | |||
| setDrawCircleHole(true) | |||
| setDrawHorizontalHighlightIndicator(false) | |||
| } | |||
| chartLineInitLiveData.postValue(true) | |||
| } | |||
| /** 获取y轴的最大值 */ | |||
| fun getMaxY(): Float{ | |||
| //设置坐标轴最大值和最小值 | |||
| var temp : Int = (maxChart / 5).toInt() | |||
| temp = if (temp >= 1000) { | |||
| (temp / 1000 + if (temp % 1000 > 0) 1 else 0) * 1000 | |||
| } else if (temp >= 100) { | |||
| (temp / 100 + if (temp % 100 > 0) 1 else 0) * 100 | |||
| } else if (temp >= 10) { | |||
| (temp / 10 + if (temp % 10 > 0) 1 else 0) * 10 | |||
| } else { | |||
| (temp + if (maxChart % 5 > 0) 1 else 0) * 1 | |||
| } | |||
| return (temp * 5 + temp / 2.0).toFloat() | |||
| } | |||
| /**获取y轴的最小值*/ | |||
| fun getMinY():Float{ | |||
| //设置坐标轴最大值和最小值 | |||
| var temp : Int = (maxChart / 5).toInt() | |||
| temp = if (temp >= 1000) { | |||
| (temp / 1000 + if (temp % 1000 > 0) 1 else 0) * 1000 | |||
| } else if (temp >= 100) { | |||
| (temp / 100 + if (temp % 100 > 0) 1 else 0) * 100 | |||
| } else if (temp >= 10) { | |||
| (temp / 10 + if (temp % 10 > 0) 1 else 0) * 10 | |||
| } else { | |||
| (temp + if (maxChart % 5 > 0) 1 else 0) * 1 | |||
| } | |||
| return (-(temp - temp / 2.0) - if (temp / 2.5 == 0.0) 0.3 else temp / 2.5).toFloat() | |||
| } | |||
| /** 获取为x显示的值 */ | |||
| fun getxAxisValue(value : Float) : String { | |||
| if (entityCountList.size > value){ | |||
| val entity = entityCountList[value.toInt()] | |||
| val split = entity.day.split("-") | |||
| return if (split[2].equals("01")) "$split[1]月" else split[2] | |||
| } | |||
| return "" | |||
| } | |||
| } | |||
| @@ -0,0 +1,87 @@ | |||
| package com.xkl.cdl.module.m_statics | |||
| import android.content.Context | |||
| import android.content.Intent | |||
| import android.os.Bundle | |||
| import androidx.recyclerview.widget.LinearLayoutManager | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.base.activity.BaseActivity | |||
| import com.suliang.common.extension.loadFragment | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.adapter.AdapterSubjectCourseStatistics | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.DataTransferHolder | |||
| import com.xkl.cdl.data.bean.StatisticsCourse | |||
| import com.xkl.cdl.data.manager.CourseManager | |||
| import com.xkl.cdl.databinding.ActivitySubjectStatisticsBinding | |||
| /** | |||
| * author suliang | |||
| * create 2022/6/27 9:40 | |||
| * Describe: 统计项目下的课程列表详情 | |||
| */ | |||
| class SubjectStatisticsDetailActivity : BaseActivity<ActivitySubjectStatisticsBinding>() { | |||
| companion object { | |||
| fun newInstance(context : Context, projectId : Int) { | |||
| val intent = Intent(context,SubjectStatisticsDetailActivity::class.java).apply { | |||
| putExtra(AppConfig.INTENT_1,projectId) | |||
| } | |||
| context.startActivity(intent) | |||
| } | |||
| } | |||
| private var projectId = 0 | |||
| private lateinit var adapter: AdapterSubjectCourseStatistics | |||
| private var statisticsCourseList = mutableListOf<StatisticsCourse>() | |||
| override fun initActivity(savedInstanceState : Bundle?) { | |||
| projectId = intent.getIntExtra(AppConfig.INTENT_1,0) | |||
| when(projectId){ | |||
| AppConstants.SUBJECT_ENGLISH -> binding.titleBar.setTitleTextValue("英语统计") | |||
| AppConstants.SUBJECT_CHINESE -> binding.titleBar.setTitleTextValue("语文统计") | |||
| } | |||
| binding.titleBar.onBackClick = { _ -> finish()} | |||
| //添加time统计fragment | |||
| loadFragment(R.id.fragment_top_time,0,StatisticsTimeTopFragment.newInstance(projectId)) | |||
| //实例化课程Rv | |||
| binding.rvCourse.apply { | |||
| layoutManager = LinearLayoutManager(this@SubjectStatisticsDetailActivity,LinearLayoutManager.VERTICAL,false) | |||
| this@SubjectStatisticsDetailActivity.adapter = AdapterSubjectCourseStatistics() | |||
| this.adapter = this@SubjectStatisticsDetailActivity.adapter.apply { | |||
| onItemClick = { _, position,_ -> | |||
| DataTransferHolder.instance.putData(AppConfig.INTENT_1,statisticsCourseList) | |||
| DataTransferHolder.instance.putData(AppConfig.INTENT_2,position) | |||
| startActivity(CourseStatisticsDetailActivity::class.java) | |||
| } | |||
| } | |||
| this@SubjectStatisticsDetailActivity.adapter.needShowEmptyView | |||
| } | |||
| } | |||
| override fun loadData() { | |||
| val coursePackList = CourseManager.subjectWithCoursePackMap.get(projectId) | |||
| if (coursePackList.isNullOrEmpty()){ | |||
| adapter.setData(emptyList<StatisticsCourse>().toMutableList()) | |||
| }else{ | |||
| //组合统计课程实体病设置进度值 | |||
| val courseSortInfo = CourseManager.mSortInfoList.get(projectId) | |||
| coursePackList.forEach { coursePack -> | |||
| coursePack.childrenCourses.forEach { course -> | |||
| statisticsCourseList.add(StatisticsCourse(course,coursePack.cover)) | |||
| run breaking@{ | |||
| courseSortInfo?.forEach { courseInfo -> | |||
| if (course.coursePackId == courseInfo.packId && course.courseId == courseInfo.courseId){ | |||
| course.courseLearnProgress = courseInfo.s | |||
| return@breaking | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //设置列表数据 | |||
| adapter.setData(statisticsCourseList) | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,106 @@ | |||
| package com.xkl.cdl.widget | |||
| import android.content.Context | |||
| import android.graphics.Color | |||
| import android.view.ViewGroup | |||
| import android.widget.ImageView | |||
| import android.widget.TextView | |||
| import androidx.core.content.ContextCompat | |||
| import androidx.recyclerview.widget.LinearLayoutManager | |||
| import androidx.recyclerview.widget.RecyclerView | |||
| import androidx.viewpager.widget.ViewPager | |||
| import com.lxj.xpopup.core.BottomPopupView | |||
| import com.lxj.xpopup.util.XPopupUtils | |||
| import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||
| import com.suliang.common.base.adapter.BaseRVAdapter | |||
| import com.suliang.common.extension.click | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.bean.StatisticsCourse | |||
| import com.xkl.cdl.data.binding.BindingAdapter | |||
| import com.xkl.cdl.databinding.ItemStatisticsBottomCourseChangeBinding | |||
| /** | |||
| * author suliang | |||
| * create 2022/7/1 16:41 | |||
| * Describe: | |||
| */ | |||
| class StatisticsICourseChangePopup(context : Context) : BottomPopupView(context) { | |||
| lateinit var recyclerView: RecyclerView | |||
| var data = mutableListOf<StatisticsCourse>() | |||
| lateinit var onAdapterItemClick : (position:Int,item:StatisticsCourse) -> Unit | |||
| var checkPostion = - 1 | |||
| private lateinit var rvAdapter: Adapter | |||
| override fun getImplLayoutId() : Int { | |||
| return R.layout.dialog_bottom_auto_play_select | |||
| } | |||
| override fun onCreate() { | |||
| super.onCreate() | |||
| findViewById<TextView>(R.id.tv_title).text = "请选择你要查看的课程" | |||
| recyclerView = findViewById(R.id.rv_repeat) | |||
| findViewById<ImageView>(R.id.iv_cancel).click { | |||
| dismiss() | |||
| } | |||
| rvAdapter = Adapter().apply { | |||
| checkPosition = checkPostion | |||
| onItemClick = {_,p,item -> | |||
| if (this@StatisticsICourseChangePopup::onAdapterItemClick.isInitialized){ | |||
| onAdapterItemClick.invoke(p,item) | |||
| } | |||
| } | |||
| } | |||
| recyclerView.apply { | |||
| layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false) | |||
| adapter = rvAdapter | |||
| } | |||
| rvAdapter.setData(data) | |||
| } | |||
| override fun getMaxHeight() : Int { | |||
| return (XPopupUtils.getScreenHeight(context) * .7f).toInt() | |||
| } | |||
| } | |||
| // TODO: 2022/7/1 适配器inflate 失败 | |||
| internal class Adapter : BaseRVAdapter<StatisticsCourse>(){ | |||
| var checkPosition = 0 | |||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
| return BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_statistics_bottom_course_change)) | |||
| } | |||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
| (holder.binding as ItemStatisticsBottomCourseChangeBinding).run { | |||
| getItem(position).let { | |||
| tvName.text = it.course.courseTitle | |||
| BindingAdapter.courseStatisticsLearnProgressTextViewFormat(tvValue,it.course.courseLearnProgress) | |||
| } | |||
| when(position){ | |||
| checkPosition -> { | |||
| layout.setBackgroundColor(Color.parseColor("0D5082E6")) | |||
| tvName.setTextColor(ContextCompat.getColor(context,R.color.theme_color)) | |||
| tvValue.setTextColor(ContextCompat.getColor(context,R.color.theme_color)) | |||
| } | |||
| else -> { | |||
| layout.setBackgroundColor(ContextCompat.getColor(context,R.color.translation)) | |||
| tvName.setTextColor(ContextCompat.getColor(context,R.color.main_text_color)) | |||
| tvValue.setTextColor(ContextCompat.getColor(context,R.color.gray_2)) | |||
| } | |||
| } | |||
| } | |||
| holder.binding.root.click { | |||
| if (checkPosition != position){ | |||
| notifyDataSetChanged() | |||
| if (onItemClickIsInitialized()){ | |||
| onItemClick.invoke(it,position,getItem(position)) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,681 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <layout 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"> | |||
| <data> | |||
| <variable | |||
| name="course" | |||
| type="com.xkl.cdl.data.bean.StatisticsCourse" /> | |||
| </data> | |||
| <androidx.appcompat.widget.LinearLayoutCompat | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:background="@color/white_1" | |||
| android:orientation="vertical" | |||
| tools:context=".module.m_statics.CourseStatisticsDetailActivity"> | |||
| <com.suliang.common.widget.TitleBar | |||
| android:id="@+id/title_bar" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="@dimen/title_bar_height" | |||
| app:barBackground="@color/white" | |||
| app:titleTextValue="@{course.course.courseTitle}"/> | |||
| <androidx.core.widget.NestedScrollView | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent"> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:layout_margin="@dimen/global_spacing"> | |||
| <!--top title--> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_top" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintTop_toTopOf="parent"> | |||
| <com.google.android.material.imageview.ShapeableImageView | |||
| android:id="@+id/img_course" | |||
| android:layout_width="52dp" | |||
| android:layout_height="0dp" | |||
| android:scaleType="fitXY" | |||
| app:imageByteArray="@{course.cover}" | |||
| app:layout_constraintDimensionRatio="52:72" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:shapeAppearance="@style/roundedCornerStyle" | |||
| tools:src="@color/main_text_color" /> | |||
| <ImageView | |||
| android:id="@+id/iv_change_course" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:src="@drawable/ic_change" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <TextView | |||
| android:id="@+id/tv_course_name" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="8dp" | |||
| android:layout_marginTop="4dp" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:ellipsize="end" | |||
| android:maxLines="2" | |||
| android:text="@{course.course.courseTitle}" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintEnd_toStartOf="@+id/iv_change_course" | |||
| app:layout_constraintStart_toEndOf="@+id/img_course" | |||
| app:layout_constraintTop_toTopOf="@+id/img_course" | |||
| tools:text="kjdkjsdkgjaskdlgjsadgjasldgddddffddfdfdffddjsakldgjsdakg" /> | |||
| <ProgressBar | |||
| android:id="@+id/progress_course" | |||
| style="@style/Widget.AppCompat.ProgressBar.Horizontal" | |||
| android:layout_width="120dp" | |||
| android:layout_height="4dp" | |||
| android:layout_marginBottom="8dp" | |||
| android:max="1000" | |||
| android:progressDrawable="@drawable/progress_center" | |||
| android:progressTint="@color/theme_color" | |||
| app:courseStatisticsLearnProgressFormat="@{(int)course.course.courseLearnProgress}" | |||
| app:layout_constraintBottom_toBottomOf="@+id/img_course" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_course_name" | |||
| tools:progress="40" /> | |||
| <TextView | |||
| android:id="@+id/tv_progress" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| android:layout_marginBottom="4dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:courseStatisticsLearnProgressTextViewFormat="@{(int)course.course.courseLearnProgress}" | |||
| app:layout_constraintBottom_toBottomOf="@+id/img_course" | |||
| app:layout_constraintStart_toEndOf="@+id/progress_course" | |||
| tools:text="10%" /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| <!--测试成绩--> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_test_score" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="12dp" | |||
| android:layout_marginEnd="12dp" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintEnd_toStartOf="@+id/layout_efficient" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/layout_top" | |||
| app:layout_goneMarginEnd="0dp"> | |||
| <TextView | |||
| android:id="@+id/tv_test_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="测试成绩对比" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/bigSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <ImageView | |||
| android:id="@+id/iv_test_state" | |||
| android:layout_width="60dp" | |||
| android:layout_height="0dp" | |||
| android:layout_marginTop="@dimen/global_spacing" | |||
| app:layout_constraintDimensionRatio="60:92" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_test_flag" /> | |||
| <TextView | |||
| android:id="@+id/tv_test_state" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="8dp" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintStart_toEndOf="@+id/iv_test_state" | |||
| app:layout_constraintTop_toTopOf="@+id/iv_test_state" | |||
| tools:text="+12" /> | |||
| <TextView | |||
| android:id="@+id/tv_before_total_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="学前总测试" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_test_state" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_test_state" /> | |||
| <TextView | |||
| android:id="@+id/tv_before_total" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="12分" | |||
| android:textColor="@color/theme_color" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_before_total_flag" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_before_total_flag" /> | |||
| <TextView | |||
| android:id="@+id/tv_after_total_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="学前总测试" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_before_total" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_before_total" /> | |||
| <TextView | |||
| android:id="@+id/tv_after_total" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="14分" | |||
| android:textColor="@color/red_1" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_after_total_flag" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_after_total_flag" /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| <!--学习效率--> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_efficient" | |||
| android:layout_width="0dp" | |||
| android:layout_height="0dp" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintBottom_toBottomOf="@+id/layout_test_score" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/layout_test_score" | |||
| app:layout_constraintTop_toTopOf="@+id/layout_test_score"> | |||
| <TextView | |||
| android:id="@+id/tv_efficient_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="学习效率" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/bigSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <com.xkl.cdl.widget.MyRankingView | |||
| android:id="@+id/ranking_efficient" | |||
| android:layout_width="80dp" | |||
| android:layout_height="80dp" | |||
| android:gravity="center" | |||
| android:orientation="vertical" | |||
| app:backgroundColor="@color/translation" | |||
| app:borderWidth="4dp" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@id/tv_efficient_flag" | |||
| app:normalColor="@color/gray_1" | |||
| app:progressColor="@color/red_2" | |||
| tools:progressNumber="20"> | |||
| <TextView | |||
| android:id="@+id/tv_efficient" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| tools:text="12%" /> | |||
| <TextView | |||
| android:id="@+id/tv_efficient_state" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| tools:drawableStart="@drawable/ic_rise" | |||
| tools:text="+3.4%" /> | |||
| </com.xkl.cdl.widget.MyRankingView> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| <!--学习时长分析--> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_duration" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="12dp" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/layout_test_score"> | |||
| <TextView | |||
| android:id="@+id/tv_duration_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="学习时长分析" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/bigSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <TextView | |||
| android:id="@+id/tv_duration_flag_1" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="12dp" | |||
| android:text="总学习时长" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_duration_flag" /> | |||
| <!--总学习时长--> | |||
| <TextView | |||
| android:id="@+id/tv_total_duration" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="36dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_duration_flag_1" | |||
| tools:text="12.1" /> | |||
| <TextView | |||
| android:id="@+id/tv_total_duration_unit" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="4dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/normalSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_total_duration" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_total_duration" | |||
| tools:text="小时" /> | |||
| <TextView | |||
| android:id="@+id/tv_duration_state" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="8dp" | |||
| android:gravity="center" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/normalSize" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_total_duration_unit" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_total_duration_unit" | |||
| tools:drawableStart="@drawable/ic_rise" | |||
| tools:text="+3.4%" /> | |||
| <TextView | |||
| android:id="@+id/tv_duration_range" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="8dp" | |||
| android:gravity="center" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_duration_state" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_duration_state" | |||
| android:text="(本周)" /> | |||
| <TextView | |||
| android:id="@+id/tv_valid_time_flag" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="20dp" | |||
| android:text="有效学习时长" | |||
| android:textColor="@color/red_2" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_review_time_flag" | |||
| app:layout_constraintHorizontal_chainStyle="spread" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_total_duration" /> | |||
| <TextView | |||
| android:id="@+id/tv_review_time_flag" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:text="有效复习时长" | |||
| android:textColor="@color/green_1" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_free_time_flag" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_valid_time_flag" | |||
| app:layout_constraintTop_toTopOf="@+id/tv_valid_time_flag" /> | |||
| <TextView | |||
| android:id="@+id/tv_free_time_flag" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:text="空闲时长" | |||
| android:textColor="@color/theme_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_review_time_flag" | |||
| app:layout_constraintTop_toTopOf="@+id/tv_valid_time_flag" /> | |||
| <TextView | |||
| android:id="@+id/tv_valid_time" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/red_2" | |||
| android:textSize="22dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_valid_time_flag" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_valid_time_flag" | |||
| tools:text="12.1" /> | |||
| <TextView | |||
| android:id="@+id/tv_valid_time_unit" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="4dp" | |||
| android:textColor="@color/red_2" | |||
| android:textSize="@dimen/smallerSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_valid_time" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_valid_time" | |||
| tools:text="小时" /> | |||
| <TextView | |||
| android:id="@+id/tv_review_time" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/green_1" | |||
| android:textSize="22dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_review_time_flag" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_review_time_flag" | |||
| tools:text="12.1" /> | |||
| <TextView | |||
| android:id="@+id/tv_review_time_unit" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="4dp" | |||
| android:textColor="@color/green_1" | |||
| android:textSize="@dimen/smallerSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_review_time" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_review_time" | |||
| tools:text="小时" /> | |||
| <TextView | |||
| android:id="@+id/tv_free_time" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/theme_color" | |||
| android:textSize="22dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_free_time_flag" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_free_time_flag" | |||
| tools:text="12.1" /> | |||
| <TextView | |||
| android:id="@+id/tv_free_time_unit" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="4dp" | |||
| android:textColor="@color/theme_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_free_time" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_free_time" | |||
| tools:text="小时" /> | |||
| <com.github.mikephil.charting.charts.BarChart | |||
| android:id="@+id/bar_chart" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="0dp" | |||
| app:layout_constraintDimensionRatio="343:200" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_valid_time" | |||
| android:layout_marginTop="10dp"/> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| <!--课程进度--> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_course_progress" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="12dp" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/layout_duration"> | |||
| <TextView | |||
| android:id="@+id/tv_course_progress_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="课程进度" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/bigSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:paddingTop="20dp" | |||
| android:paddingBottom="20dp" | |||
| android:layout_marginTop="12dp" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_course_progress_flag" | |||
| android:background="@drawable/shape_rounder_8_stroke_gray1" | |||
| > | |||
| <com.xkl.cdl.widget.MyRankingView | |||
| android:id="@+id/ranking_1" | |||
| android:layout_width="80dp" | |||
| android:layout_height="80dp" | |||
| android:gravity="center" | |||
| android:orientation="vertical" | |||
| app:backgroundColor="@color/translation" | |||
| app:borderWidth="4dp" | |||
| app:normalColor="@color/gray_1" | |||
| app:progressColor="@color/green_1" | |||
| tools:progressNumber="20" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toStartOf="@+id/ranking_2"> | |||
| <TextView | |||
| android:id="@+id/tv_ranking_1_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="11dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:text="已学章节" /> | |||
| <TextView | |||
| android:id="@+id/tv_ranking_1_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="11dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| tools:text="1/12" /> | |||
| </com.xkl.cdl.widget.MyRankingView> | |||
| <com.xkl.cdl.widget.MyRankingView | |||
| android:id="@+id/ranking_2" | |||
| android:layout_width="80dp" | |||
| android:layout_height="80dp" | |||
| android:gravity="center" | |||
| android:orientation="vertical" | |||
| app:backgroundColor="@color/translation" | |||
| app:borderWidth="4dp" | |||
| app:normalColor="@color/gray_1" | |||
| app:progressColor="@color/theme_color" | |||
| tools:progressNumber="20" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/ranking_1" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toStartOf="@+id/ranking_3"> | |||
| <TextView | |||
| android:id="@+id/tv_ranking_2_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="11dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| tools:text="已学词汇量/已学知识点" /> | |||
| <TextView | |||
| android:id="@+id/tv_ranking_2_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="11dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| tools:text="1/12" /> | |||
| </com.xkl.cdl.widget.MyRankingView> | |||
| <com.xkl.cdl.widget.MyRankingView | |||
| android:id="@+id/ranking_3" | |||
| android:layout_width="80dp" | |||
| android:layout_height="80dp" | |||
| android:gravity="center" | |||
| android:orientation="vertical" | |||
| app:backgroundColor="@color/translation" | |||
| app:borderWidth="4dp" | |||
| app:normalColor="@color/gray_1" | |||
| app:progressColor="#8757E6" | |||
| tools:progressNumber="20" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/ranking_2" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent"> | |||
| <TextView | |||
| android:id="@+id/tv_ranking_3_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="11dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:text="已学课堂练习" /> | |||
| <TextView | |||
| android:id="@+id/tv_ranking_3_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="11dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| tools:text="1/12" /> | |||
| </com.xkl.cdl.widget.MyRankingView> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| <!--章节学习情况--> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_chapter_info" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="12dp" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/layout_course_progress"> | |||
| <TextView | |||
| android:id="@+id/tv_chapter_info_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="章节学习情况" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/bigSize" | |||
| android:textStyle="bold" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <com.google.android.material.button.MaterialButton | |||
| android:id="@+id/tv_chapter_choose" | |||
| style="@style/Widget.MaterialComponents.Button.UnelevatedButton" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="24dp" | |||
| android:drawableEnd="@drawable/ic_down" | |||
| android:gravity="center" | |||
| android:insetTop="0dp" | |||
| android:insetBottom="0dp" | |||
| android:minWidth="124dp" | |||
| tools:text="第一章" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/tv_chapter_info_flag" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/tv_chapter_info_flag" | |||
| app:strokeColor="@color/gray_1" | |||
| app:strokeWidth="@dimen/line_height" | |||
| app:cornerRadius="4dp"/> | |||
| <androidx.recyclerview.widget.RecyclerView | |||
| android:id="@+id/rv_chapter_info" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_chapter_info_flag" | |||
| android:layout_marginTop="6dp"/> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </androidx.core.widget.NestedScrollView> | |||
| </androidx.appcompat.widget.LinearLayoutCompat> | |||
| </layout> | |||
| @@ -0,0 +1,69 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <androidx.appcompat.widget.LinearLayoutCompat 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="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:background="@color/white_1" | |||
| android:orientation="vertical" | |||
| tools:context=".module.m_statics.SubjectStatisticsDetailActivity"> | |||
| <com.suliang.common.widget.TitleBar | |||
| android:id="@+id/title_bar" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="@dimen/title_bar_height" | |||
| app:barBackground="@color/white"/> | |||
| <androidx.core.widget.NestedScrollView | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:background="@color/white_1"> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent"> | |||
| <androidx.fragment.app.FragmentContainerView | |||
| android:id="@+id/fragment_top_time" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="0dp" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginTop="@dimen/global_spacing" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| app:layout_constraintDimensionRatio="343:275" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <androidx.appcompat.widget.LinearLayoutCompat | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:layout_margin="@dimen/global_spacing" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:orientation="vertical" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/fragment_top_time"> | |||
| <TextView | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:layout_margin="12dp" | |||
| android:text="课程综合分析" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/bigSize" | |||
| android:textStyle="bold" /> | |||
| <androidx.recyclerview.widget.RecyclerView | |||
| android:id="@+id/rv_course" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="0dp" | |||
| android:layout_weight="1" /> | |||
| </androidx.appcompat.widget.LinearLayoutCompat> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </androidx.core.widget.NestedScrollView> | |||
| </androidx.appcompat.widget.LinearLayoutCompat> | |||
| @@ -27,7 +27,7 @@ | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginTop="@dimen/global_spacing" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| app:layout_constraintDimensionRatio="h,343:275" | |||
| app:layout_constraintDimensionRatio="343:275" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_title" /> | |||
| <!-- android:name="com.xkl.cdl.module.m_statics.StatisticsTimeTopFragment"--> | |||
| @@ -140,7 +140,7 @@ | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:padding="12dp" | |||
| app:layout_constraintDimensionRatio="h,343:243" | |||
| app:layout_constraintDimensionRatio="343:243" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/layout_english"> | |||
| @@ -193,14 +193,15 @@ | |||
| android:insetTop="0dp" | |||
| android:insetBottom="0dp" | |||
| android:minWidth="124dp" | |||
| android:text="全部科目" | |||
| android:text="全部项目" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/chart_title" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/chart_title" | |||
| app:strokeColor="@color/gray_1" | |||
| app:strokeWidth="@dimen/line_height" /> | |||
| app:strokeWidth="@dimen/line_height" | |||
| app:cornerRadius="4dp"/> | |||
| <com.github.mikephil.charting.charts.LineChart | |||
| android:id="@+id/chart" | |||
| @@ -23,7 +23,7 @@ | |||
| app:layout_constraintHeight_min="128dp" | |||
| android:layout_marginEnd="8dp" | |||
| android:scaleType="centerCrop" | |||
| app:layout_constraintDimensionRatio="h,102:136" | |||
| app:layout_constraintDimensionRatio="102:136" | |||
| app:layout_constraintEnd_toStartOf="@+id/barrier" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| @@ -20,7 +20,7 @@ | |||
| android:layout_height="0dp" | |||
| android:orientation="vertical" | |||
| android:paddingStart="12dp" | |||
| app:layout_constraintDimensionRatio="h,154:100" | |||
| app:layout_constraintDimensionRatio="154:100" | |||
| app:layout_constraintTop_toTopOf="parent"> | |||
| <TextView | |||
| @@ -0,0 +1,42 @@ | |||
| <?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> | |||
| </data> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="48dp" | |||
| android:paddingStart="@dimen/global_spacing" | |||
| android:paddingEnd="@dimen/global_spacing"> | |||
| <TextView | |||
| android:id="@+id/tv_name" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:textSize="@dimen/normalSize" | |||
| android:textColor="@color/main_text_color" | |||
| tools:text="课程名称" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_value" /> | |||
| <TextView | |||
| android:id="@+id/tv_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textSize="@dimen/normalSize" | |||
| android:textColor="@color/gray_2" | |||
| tools:text="12%" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </layout> | |||
| @@ -0,0 +1,86 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <layout 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"> | |||
| <data> | |||
| <variable | |||
| name="course" | |||
| type="com.xkl.cdl.data.bean.StatisticsCourse" /> | |||
| </data> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="12dp"> | |||
| <com.google.android.material.imageview.ShapeableImageView | |||
| android:id="@+id/img_course" | |||
| android:layout_width="54dp" | |||
| android:layout_height="0dp" | |||
| app:layout_constraintDimensionRatio="54:72" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| android:layout_marginStart="14dp" | |||
| android:scaleType="fitXY" | |||
| tools:src="@color/main_text_color" | |||
| app:shapeAppearance="@style/roundedCornerStyle" | |||
| app:imageByteArray="@{course.cover}"/> | |||
| <androidx.constraintlayout.utils.widget.ImageFilterView | |||
| android:id="@+id/iv_arrow" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:src="@drawable/ic_arrow_right" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| android:layout_marginEnd="12dp" /> | |||
| <TextView | |||
| android:id="@+id/tv_course_name" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:maxLines="2" | |||
| android:ellipsize="end" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textColor="@color/main_text_color" | |||
| app:layout_constraintTop_toTopOf="@+id/img_course" | |||
| app:layout_constraintStart_toEndOf="@+id/img_course" | |||
| app:layout_constraintEnd_toStartOf="@+id/iv_arrow" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:layout_marginTop="4dp" | |||
| android:layout_marginStart="8dp" | |||
| tools:text="kjdkjsdkgjaskdlgjsadgjasldgddddffddfdfdffddjsakldgjsdakg" | |||
| android:text="@{course.course.courseTitle}"/> | |||
| <ProgressBar | |||
| android:id="@+id/progress_course" | |||
| android:layout_width="120dp" | |||
| android:layout_height="4dp" | |||
| style="@style/Widget.AppCompat.ProgressBar.Horizontal" | |||
| android:progressDrawable="@drawable/progress_center" | |||
| android:progressTint="@color/theme_color" | |||
| android:max="1000" | |||
| tools:progress="40" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_course_name" | |||
| app:layout_constraintBottom_toBottomOf="@+id/img_course" | |||
| android:layout_marginBottom="8dp" | |||
| app:courseStatisticsLearnProgressFormat="@{(int)course.course.courseLearnProgress}"/> | |||
| <TextView | |||
| android:id="@+id/tv_progress" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| app:layout_constraintBottom_toBottomOf="@+id/img_course" | |||
| app:layout_constraintStart_toEndOf="@+id/progress_course" | |||
| android:textSize="@dimen/smallerSize" | |||
| android:textColor="@color/main_text_color" | |||
| android:layout_marginStart="12dp" | |||
| android:layout_marginBottom="4dp" | |||
| tools:text="10%" | |||
| app:courseStatisticsLearnProgressTextViewFormat="@{(int)course.course.courseLearnProgress}"/> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </layout> | |||
| @@ -0,0 +1,208 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <layout 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"> | |||
| <data> | |||
| </data> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:background="@drawable/shape_rounder_8_stroke_gray1"> | |||
| <View | |||
| android:id="@+id/v_1" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="38dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <View | |||
| android:id="@+id/v_2" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="38dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/v_1" /> | |||
| <View | |||
| android:id="@+id/v_3" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="38dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/v_2" /> | |||
| <View | |||
| android:id="@+id/v_4" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="38dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/v_3" /> | |||
| <View | |||
| android:id="@+id/v_5" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="38dp" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/v_4" /> | |||
| <TextView | |||
| android:id="@+id/tv_1" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| android:text="课时" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/v_1" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_1_value" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/v_1" /> | |||
| <TextView | |||
| android:id="@+id/tv_2" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| tools:text="课时" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/v_2" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/v_2" /> | |||
| <TextView | |||
| android:id="@+id/tv_3" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| tools:text="课时" | |||
| tools:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/v_3" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/v_3" /> | |||
| <TextView | |||
| android:id="@+id/tv_4" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| tools:text="课时" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/v_4" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/v_4" /> | |||
| <TextView | |||
| android:id="@+id/tv_5" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| tools:text="课时" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@+id/v_5" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="@+id/v_5" /> | |||
| <TextView | |||
| android:id="@+id/tv_1_value" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginEnd="12dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_1" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_1" | |||
| app:layout_constraintTop_toTopOf="@+id/v_1" | |||
| tools:text="课时名称" /> | |||
| <TextView | |||
| android:id="@+id/tv_2_value" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginEnd="12dp" | |||
| android:textColor="@color/theme_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_2" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="@id/tv_1_value" | |||
| app:layout_constraintTop_toTopOf="@+id/v_2" | |||
| tools:text="课时名称" /> | |||
| <TextView | |||
| android:id="@+id/tv_3_value" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginEnd="12dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_3" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="@id/tv_1_value" | |||
| app:layout_constraintTop_toTopOf="@+id/v_3" | |||
| tools:text="课时名称" /> | |||
| <ProgressBar | |||
| android:id="@+id/p_4" | |||
| android:layout_width="100dp" | |||
| android:layout_height="4dp" | |||
| style="@style/Widget.AppCompat.ProgressBar.Horizontal" | |||
| android:progressDrawable="@drawable/progress_center" | |||
| android:progressTint="@color/red_2" | |||
| android:max="100" | |||
| tools:progress="80" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_4" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_4_value" | |||
| app:layout_constraintStart_toStartOf="@id/tv_1_value" | |||
| app:layout_constraintTop_toTopOf="@+id/v_4" /> | |||
| <TextView | |||
| android:id="@+id/tv_4_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| app:layout_goneMarginStart="0dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_4" | |||
| app:layout_constraintStart_toEndOf="@+id/p_4" | |||
| app:layout_constraintTop_toTopOf="@+id/v_4" | |||
| tools:text="20" /> | |||
| <ProgressBar | |||
| android:id="@+id/p_5" | |||
| android:layout_width="100dp" | |||
| android:layout_height="4dp" | |||
| style="@style/Widget.AppCompat.ProgressBar.Horizontal" | |||
| android:progressDrawable="@drawable/progress_center" | |||
| android:progressTint="@color/theme_color" | |||
| android:max="100" | |||
| tools:progress="80" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_5" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_5_value" | |||
| app:layout_constraintStart_toStartOf="@id/tv_1_value" | |||
| app:layout_constraintTop_toTopOf="@+id/v_5" /> | |||
| <TextView | |||
| android:id="@+id/tv_5_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="12dp" | |||
| app:layout_goneMarginStart="0dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBottom_toBottomOf="@id/v_5" | |||
| app:layout_constraintStart_toEndOf="@+id/p_5" | |||
| app:layout_constraintTop_toTopOf="@+id/v_5" | |||
| tools:text="20" /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </layout> | |||
| @@ -0,0 +1,20 @@ | |||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
| android:width="20dp" | |||
| android:height="20dp" | |||
| android:viewportWidth="20" | |||
| android:viewportHeight="20"> | |||
| <path | |||
| android:pathData="M0,0h20v20h-20z" | |||
| android:strokeAlpha="0" | |||
| android:strokeWidth="1" | |||
| android:fillColor="#000000" | |||
| android:fillType="nonZero" | |||
| android:strokeColor="#00000000" | |||
| android:fillAlpha="0"/> | |||
| <path | |||
| android:pathData="M16.6251,11.9417L3.3749,11.9417C2.9836,11.9417 2.6667,12.2586 2.6667,12.6499C2.6667,12.7986 2.7127,12.9367 2.7906,13.05C2.8242,13.1297 2.8756,13.204 2.9393,13.2695L6.0607,16.3891C6.1988,16.5272 6.3794,16.5963 6.5617,16.5963C6.7441,16.5963 6.9247,16.5272 7.0628,16.3891C7.339,16.1129 7.339,15.665 7.0628,15.387L5.0303,13.3581L16.6234,13.3581C17.0146,13.3581 17.3316,13.0411 17.3316,12.6499C17.3316,12.2586 17.0164,11.9417 16.6251,11.9417L16.6251,11.9417ZM3.3749,8.6557L16.6251,8.6557C17.0164,8.6557 17.3333,8.3387 17.3333,7.9475C17.3333,7.7987 17.2873,7.6606 17.2094,7.5473C17.1846,7.4889 17.1492,7.4323 17.1067,7.3791L14.5537,4.2595C14.3058,3.9568 13.8596,3.9125 13.5569,4.1604C13.2541,4.4083 13.2099,4.8544 13.4578,5.1572L15.1627,7.2393L3.3749,7.2393C2.9836,7.2393 2.6667,7.5562 2.6667,7.9475C2.6667,8.3387 2.9836,8.6557 3.3749,8.6557L3.3749,8.6557Z" | |||
| android:strokeWidth="1" | |||
| android:fillColor="#323233" | |||
| android:fillType="evenOdd" | |||
| android:strokeColor="#00000000"/> | |||
| </vector> | |||