| <entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_discern.xml" value="0.3223958333333333" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_discern.xml" value="0.3223958333333333" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_nav_learn_center.xml" value="0.3223958333333333" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_nav_learn_center.xml" value="0.3223958333333333" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_nav_my.xml" value="0.3223958333333333" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_nav_my.xml" value="0.3223958333333333" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/detail_example_flag.xml" value="0.4546296296296296" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/detail_phrase_flag.xml" value="0.4546296296296296" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/detail_reference_flag.xml" value="0.4546296296296296" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/dialog_bottom_bg.xml" value="0.5140625" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/dialog_bottom_bg.xml" value="0.5140625" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/et_search_bg.xml" value="0.175" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/et_search_bg.xml" value="0.175" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_delete.xml" value="0.30520833333333336" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_delete.xml" value="0.30520833333333336" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/progress_statistics.xml" value="0.4425925925925926" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/progress_statistics.xml" value="0.4425925925925926" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/progressbar_countdown_time.xml" value="0.45740740740740743" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/progressbar_countdown_time.xml" value="0.45740740740740743" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_12_white.xml" value="0.5140625" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_12_white.xml" value="0.5140625" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_8_stroke_gray1.xml" value="0.503125" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_8_stroke_gray2.xml" value="0.48055555555555557" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_8_stroke_gray2.xml" value="0.48055555555555557" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_toplr_24_white.xml" value="0.5061538461538462" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_toplr_24_white.xml" value="0.5061538461538462" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_rounder_toplr_8_white.xml" value="0.5010416666666667" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_switch_thumb.xml" value="0.14074074074074075" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/shape_switch_thumb.xml" value="0.14074074074074075" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/spoken_autoplay_btn_text_color_.xml" value="0.5140625" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/spoken_autoplay_btn_text_color_.xml" value="0.5140625" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/switch_thumb_selector.xml" value="0.3768518518518518" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/switch_thumb_selector.xml" value="0.3768518518518518" /> | ||||
| <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_main.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_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_base.xml" value="0.4979166666666667" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam.xml" value="0.23632218844984804" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam.xml" value="0.25" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam_word.xml" value="0.33" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_exam_word.xml" value="0.33" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_spell.xml" value="0.47690217391304346" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_spell.xml" value="0.47690217391304346" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word.xml" value="0.33" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word2.xml" value="0.4979166666666667" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_word2.xml" value="0.4979166666666667" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_main.xml" value="0.5" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_main.xml" value="0.5" /> | ||||
| <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_splash.xml" value="0.4921875" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_first.xml" value="0.4979166666666667" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_first.xml" value="0.4979166666666667" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_learn_center.xml" value="0.25" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_learn_center.xml" value="0.25" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_my.xml" value="0.28229166666666666" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_my.xml" value="0.28229166666666666" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_control_button.xml" value="0.46467391304347827" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_spell_content.xml" value="0.45153985507246375" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_spell_content.xml" value="0.45153985507246375" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_word_choose_content.xml" value="0.45153985507246375" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_exam_word_choose_content.xml" value="0.33" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_spell.xml" value="0.4859375" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_test_statistic.xml" value="0.5" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_test_statistic.xml" value="0.5" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_title.xml" value="0.2373353596757852" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_title.xml" value="0.5784919653893696" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_learn_word.xml" value="0.390625" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_over_number.xml" value="0.4979166666666667" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_over_number.xml" value="0.4979166666666667" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_word.xml" value="0.390625" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/inc_word_detail.xml" value="0.30538922155688625" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/includ_test_option_item.xml" value="0.45153985507246375" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/includ_test_option_item.xml" value="0.45153985507246375" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_main_learn_center_course_progress.xml" value="0.503125" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_main_learn_center_course_progress.xml" value="0.503125" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_main_learn_center_course_type_title.xml" value="0.67" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_main_learn_center_course_type_title.xml" value="0.67" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_title_bar.xml" value="0.25052083333333336" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/include_title_bar.xml" value="0.25052083333333336" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_course_lesson.xml" value="0.4785615491009682" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_course_lesson.xml" value="0.4785615491009682" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_empty.xml" value="0.4979166666666667" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_empty.xml" value="0.4979166666666667" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_historical_route.xml" value="0.4859375" /> | |||||
| <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_spell_single_word.xml" value="0.23632218844984804" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/main_item_course_progress.xml" value="0.25" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/main_item_course_progress.xml" value="0.25" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/main_item_coursepack.xml" value="0.43500866551126516" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/main_item_coursepack.xml" value="0.43500866551126516" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_my.xml" value="0.44166666666666665" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_my.xml" value="0.44166666666666665" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_service.xml" value="0.21574074074074073" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_service.xml" value="0.21574074074074073" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_statistics.xml" value="0.44166666666666665" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_statistics.xml" value="0.44166666666666665" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_play.xml" value="0.503125" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_play_pause.xml" value="0.503125" /> | |||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_right.xml" value="0.4425925925925926" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_right.xml" value="0.4425925925925926" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spell.xml" value="0.5061538461538462" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spell.xml" value="0.5061538461538462" /> | ||||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spoken.xml" value="0.5061538461538462" /> | <entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_spoken.xml" value="0.5061538461538462" /> |
| package com.xkl.cdl | |||||
| import android.util.TypedValue | |||||
| import android.view.View | |||||
| import android.widget.LinearLayout | |||||
| import android.widget.TextView | |||||
| import androidx.core.content.ContextCompat | |||||
| import com.xkl.cdl.databinding.IncWordDetailBinding | |||||
| /** | |||||
| * author suliang | |||||
| * create 2022/4/25 19:14 | |||||
| * Describe: 扩展 | |||||
| */ | |||||
| /** | |||||
| * IncWordDetailBinding 扩展,设置词组、例句、参考 | |||||
| * @receiver IncWordDetailBinding | |||||
| * @param phrase String? 词组 | |||||
| * @param example String? 例句 | |||||
| * @param refrence String? 参考 | |||||
| */ | |||||
| fun IncWordDetailBinding.initValue(phrase : String?, example : String?, refrence : String?){ | |||||
| phrase?.let { | |||||
| tvPhraseFlag.visibility = View.VISIBLE | |||||
| layoutPhrase.run { | |||||
| visibility = View.VISIBLE | |||||
| removeAllViews() | |||||
| val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) | |||||
| it.split("\n").forEach { | |||||
| val child = TextView(context).apply { | |||||
| layoutParams = params | |||||
| text = it | |||||
| setTextColor(ContextCompat.getColor(context, R.color.main_text_color)) | |||||
| setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14f) | |||||
| } | |||||
| addView(child) | |||||
| } | |||||
| } | |||||
| }?:let { | |||||
| tvPhraseFlag.visibility = View.GONE | |||||
| layoutPhrase.visibility = View.GONE | |||||
| } | |||||
| example?.let { | |||||
| tvExampleFlag.visibility = View.VISIBLE | |||||
| tvExample.visibility = View.VISIBLE | |||||
| }?:let { | |||||
| tvExampleFlag.visibility = View.GONE | |||||
| tvExample.visibility = View.GONE | |||||
| } | |||||
| refrence?.let { | |||||
| tvReferenceFlag.visibility = View.VISIBLE | |||||
| tvReference.visibility = View.VISIBLE | |||||
| }?:let { | |||||
| tvReferenceFlag.visibility = View.GONE | |||||
| tvReference.visibility = View.GONE | |||||
| } | |||||
| } |
| package com.xkl.cdl.adapter | |||||
| import android.graphics.Color | |||||
| import android.view.ViewGroup | |||||
| import android.widget.TextView | |||||
| import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||||
| import com.suliang.common.base.adapter.BaseRVAdapter | |||||
| import com.suliang.common.extension.click | |||||
| import com.suliang.common.util.StringUtil | |||||
| import com.xkl.cdl.R | |||||
| import com.xkl.cdl.data.AppConstants | |||||
| import com.xkl.cdl.data.bean.LearnWord | |||||
| import com.xkl.cdl.databinding.ItemHistoricalRouteBinding | |||||
| /** | |||||
| * author suliang | |||||
| * create 2022/4/25 16:00 | |||||
| * Describe: 历史轨迹 | |||||
| * @param courseType 课程类型 | |||||
| */ | |||||
| class AdapterHistoricalRoute(val courseType : Int) : BaseRVAdapter<LearnWord>() { | |||||
| //正常颜色 | |||||
| private val normalColor = Color.parseColor("#606060") | |||||
| //选中颜色 | |||||
| private val selectColor = Color.parseColor("#5082E6") | |||||
| //选中背景 | |||||
| private val selectBackground = R.drawable.shape_rounder_toplr_8_white | |||||
| //记录当前显示位置 | |||||
| private var currentSelectPosition = 0 | |||||
| //记录上一个选中item的位置 | |||||
| private var previousSelectPosition = -1 | |||||
| //是否是历史轨迹最后一个 | |||||
| val currentIsFirstSelect : Boolean | |||||
| get() { | |||||
| return currentSelectPosition == itemCount - 1 | |||||
| } | |||||
| /*----------下面三属性为辨音、拼写独有---------------------------------------------------------------------*/ | |||||
| //当前选中项是否需要显示为横线 | |||||
| private var currentIsShowLine = false | |||||
| //上一条选中项是否需要显示为横线 | |||||
| private var previousIsShowLine = false | |||||
| //标记历史记录中最后添加的项是否学习完成 | |||||
| private var lastIsLearnOver = false | |||||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||||
| return BaseAdapterViewHolder(inflateBinding(parent, R.layout.item_historical_route)) | |||||
| } | |||||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||||
| when (courseType) { | |||||
| AppConstants.COURSE_TYPE_ENGLISH_VOICE, | |||||
| AppConstants.COURSE_TYPE_ENGLISH_SPELL -> bindSpellOrVoice(holder, position) | |||||
| else -> bindDiscern(holder, position) | |||||
| } | |||||
| } | |||||
| /**认读历史轨迹*/ | |||||
| private fun bindDiscern(holder : BaseAdapterViewHolder, position : Int) { | |||||
| val item = getItem(position) | |||||
| (holder.binding as ItemHistoricalRouteBinding).run { | |||||
| //赋值 识字课程只显示括号包裹里面的中文内容 | |||||
| tvHistory.run { | |||||
| text = when (courseType) { | |||||
| AppConstants.COURSE_TYPE_CHINESE_LITERACY -> StringUtil.literacyGetWord(item.word) | |||||
| else -> item.word | |||||
| } | |||||
| //设置颜色背景 | |||||
| when (position) { | |||||
| currentSelectPosition -> setSelect(this) | |||||
| else -> setNormal(this) | |||||
| } | |||||
| } | |||||
| root.click { | |||||
| //选中项点击无效 | |||||
| if (currentSelectPosition == position) return@click | |||||
| //点击改变值,并更新显示 | |||||
| previousSelectPosition = currentSelectPosition | |||||
| currentSelectPosition = position | |||||
| notifyItemChanged(previousSelectPosition) | |||||
| notifyItemChanged(currentSelectPosition) | |||||
| //回调 | |||||
| onItemClick.invoke(it, position, item) | |||||
| } | |||||
| } | |||||
| } | |||||
| private fun bindSpellOrVoice(holder : BaseAdapterViewHolder, position : Int) { | |||||
| val item = getItem(position) | |||||
| (holder.binding as ItemHistoricalRouteBinding).run { | |||||
| //设置颜色背景 | |||||
| when (position) { | |||||
| currentSelectPosition -> setSelect(tvHistory) | |||||
| else -> setNormal(tvHistory) | |||||
| } | |||||
| //赋值 | |||||
| tvHistory.text = when (position) { | |||||
| currentSelectPosition -> when { | |||||
| currentIsShowLine -> " -- " | |||||
| else -> item.word | |||||
| } | |||||
| previousSelectPosition -> when { | |||||
| previousIsShowLine -> "--" | |||||
| else -> item.word | |||||
| } | |||||
| else -> item.word | |||||
| } | |||||
| //事件 | |||||
| root.click { | |||||
| if (currentSelectPosition == position) return@click | |||||
| //点击改变选中值与显示 | |||||
| previousSelectPosition = currentSelectPosition | |||||
| previousIsShowLine = !(previousSelectPosition == itemCount-1 && lastIsLearnOver) | |||||
| currentSelectPosition = position | |||||
| currentIsShowLine = true | |||||
| notifyItemChanged(previousSelectPosition) | |||||
| notifyItemChanged(currentSelectPosition) | |||||
| //事件 | |||||
| onItemClick.invoke(it,position,item) | |||||
| } | |||||
| } | |||||
| } | |||||
| /** 设置正常颜色 */ | |||||
| private fun setNormal(textView : TextView) { | |||||
| textView.run { | |||||
| setBackgroundResource(0) | |||||
| setTextColor(normalColor) | |||||
| } | |||||
| } | |||||
| /**设置选中颜色*/ | |||||
| private fun setSelect(textView : TextView) { | |||||
| textView.run { | |||||
| setBackgroundResource(selectBackground) | |||||
| setTextColor(selectColor) | |||||
| } | |||||
| } | |||||
| /**辨音、拼写显示的答案后,轨迹显示值*/ | |||||
| fun showHistoricalRouteValue(){ | |||||
| currentIsShowLine = false | |||||
| notifyItemChanged(currentSelectPosition) | |||||
| } | |||||
| /** 当前项学习完成 ,判断标记最后一项是否学习完成*/ | |||||
| fun currentLearnOver(){ | |||||
| if (currentSelectPosition == itemCount -1 ) lastIsLearnOver = true | |||||
| } | |||||
| /** 拼写错误,需要在点击下一条时,移除当前item项 */ | |||||
| fun spellLastLearnError(){ | |||||
| removeData(currentSelectPosition) | |||||
| } | |||||
| override fun setData(data : MutableList<LearnWord>?) { | |||||
| currentSelectPosition = 0 | |||||
| previousSelectPosition = -1 | |||||
| //拼写值 | |||||
| currentIsShowLine = true | |||||
| previousIsShowLine = false | |||||
| super.setData(data) | |||||
| } | |||||
| override fun addData(data : LearnWord?, position : Int?) { | |||||
| previousSelectPosition = currentSelectPosition | |||||
| currentSelectPosition = position ?: itemCount | |||||
| //添加数据拼写辨音初始值 | |||||
| currentIsShowLine = true | |||||
| previousIsShowLine = false | |||||
| lastIsLearnOver = false | |||||
| super.addData(data, position) | |||||
| notifyItemChanged(previousSelectPosition) | |||||
| } | |||||
| } |
| */ | */ | ||||
| class SpellItemDecoration : ItemDecoration() { | class SpellItemDecoration : ItemDecoration() { | ||||
| private val mDividerWidth = ScreenUtil.dp2px(1f) | |||||
| private val mDividerWidth = ScreenUtil.dp2px(1f).toInt() | |||||
| override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) { | override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) { | ||||
| val chilidCount = parent.adapter!!.itemCount | val chilidCount = parent.adapter!!.itemCount |
| const val COURSEPACK_TYPE_CHINESE_LITERACY = 3 | const val COURSEPACK_TYPE_CHINESE_LITERACY = 3 | ||||
| /**课程包类型: categoryId -> 语文拼音 */ | /**课程包类型: categoryId -> 语文拼音 */ | ||||
| const val COURSEPACK_TYPE_CHINESE_PINYIN = 1 | |||||
| const val COURSEPACK_TYPE_CHINESE_PINYIN = 5 | |||||
| /**课程类型: typeId 英语认读*/ | /**课程类型: typeId 英语认读*/ |
| * @property courseType Int 课程类型 | * @property courseType Int 课程类型 | ||||
| * @property chapterId Long 章节id | * @property chapterId Long 章节id | ||||
| * @property lessonId Long 课时id | * @property lessonId Long 课时id | ||||
| * @property wordId Long 内容id | |||||
| * @property wordId Long 内容id 作文知识点时为"knowledge_id" | |||||
| * @property first Boolean 是否第一次出现 | * @property first Boolean 是否第一次出现 | ||||
| * @property reviewNum Int 大七次次数 | * @property reviewNum Int 大七次次数 | ||||
| * @property reviewTime Long 复习时间 | * @property reviewTime Long 复习时间 | ||||
| val chapterId: Long, | val chapterId: Long, | ||||
| val lessonId: Long, | val lessonId: Long, | ||||
| val wordId: Long, | val wordId: Long, | ||||
| val first: Boolean, | |||||
| var first: Boolean, | |||||
| val lessonType: Int) { | val lessonType: Int) { | ||||
| /** 大复习次数 */ | /** 大复习次数 */ | ||||
| var reviewNum: Int = 0 | var reviewNum: Int = 0 |
| lessonType) { | lessonType) { | ||||
| var word: String? = null //词汇 | |||||
| var word: String = "" //词汇 | |||||
| var phonetic_uk: String? = null // 英式音标 | var phonetic_uk: String? = null // 英式音标 | ||||
| var phonetic_us: String? = null // 美式音标 | var phonetic_us: String? = null // 美式音标 | ||||
| var phonetic_cn: String? = null // 中文拼音 | var phonetic_cn: String? = null // 中文拼音 |
| import io.reactivex.rxjava3.annotations.NonNull | import io.reactivex.rxjava3.annotations.NonNull | ||||
| import net.sqlcipher.database.SQLiteDatabase | import net.sqlcipher.database.SQLiteDatabase | ||||
| import java.io.File | import java.io.File | ||||
| import java.lang.StringBuilder | |||||
| /** | /** | ||||
| * author suliang | * author suliang | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * 查询学习数据,结果为单词类型,其中作文知识点为单独查询 | |||||
| * @param dbcb DbControlBase | |||||
| * @param lesson Lesson | |||||
| * @return List<LearnWord> | |||||
| */ | |||||
| fun queryLearnData(dbcb : DbControlBase, lesson : Lesson) : List<LearnWord> { | |||||
| return when (dbcb.courseType) { | |||||
| AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> queryLearnDataForCompositionKnowledge(dbcb, lesson) | |||||
| else -> queryLearnDataForWord(dbcb, lesson) | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * 查询学习数据 | * 查询学习数据 | ||||
| * @return List<LearnWord>? | * @return List<LearnWord>? | ||||
| */ | */ | ||||
| @SuppressLint("Range") | @SuppressLint("Range") | ||||
| fun queryLearnDataForWord(dbcb : DbControlBase, lesson : Lesson) : @NonNull List<LearnWord> { | |||||
| private fun queryLearnDataForWord(dbcb : DbControlBase, lesson : Lesson) : @NonNull List<LearnWord> { | |||||
| val result = mutableListOf<LearnWord>() | val result = mutableListOf<LearnWord>() | ||||
| open(dbcb) | open(dbcb) | ||||
| //从lesson已学位置开始获取数据 | //从lesson已学位置开始获取数据 | ||||
| word = getString(getColumnIndex("word")) | word = getString(getColumnIndex("word")) | ||||
| basic_explanation = getString(getColumnIndex("basic_explaination")) | basic_explanation = getString(getColumnIndex("basic_explaination")) | ||||
| extend_explanation = getString(getColumnIndex("all_explaination")) | extend_explanation = getString(getColumnIndex("all_explaination")) | ||||
| phrase = getString(getColumnIndex("pharse")) | |||||
| phrase = getString(getColumnIndex("phrase")) | |||||
| example = getString(getColumnIndex("example")) | example = getString(getColumnIndex("example")) | ||||
| reference = getString(getColumnIndex("reference")) | reference = getString(getColumnIndex("reference")) | ||||
| return result | return result | ||||
| } | } | ||||
| /** | |||||
| * 作文知识点数据学习数据查询 | |||||
| * @param dbcb DbControlBase | |||||
| * @param lesson Lesson | |||||
| * @return List<LearnWord> | |||||
| */ | |||||
| @SuppressLint("Range") | |||||
| private fun queryLearnDataForCompositionKnowledge(dbcb : DbControlBase, lesson : Lesson) : List<LearnWord> { | |||||
| val result = mutableListOf<LearnWord>() | |||||
| open(dbcb) | |||||
| //从lesson已学位置开始获取数据 | |||||
| val needLearnIds = lesson.wordIds.subList(lesson.learnedIndex + 1, lesson.wordIds.size) | |||||
| val wordIds = StringBuilder() //ids集合 | |||||
| val sortSqlBuilder = StringBuilder() //排序value | |||||
| needLearnIds.forEachIndexed { index, value -> | |||||
| sortSqlBuilder.append(" when knowledge_id = $value then $index ") | |||||
| when (needLearnIds.lastIndex) { | |||||
| index -> wordIds.append(value) | |||||
| else -> wordIds.append(value).append(",") | |||||
| } | |||||
| } | |||||
| val sql = | |||||
| "SELECT * FROM knowledge WHERE chapter_id = ${lesson.chapterId} AND knowledge_id in ($wordIds) ORDER by CASE $sortSqlBuilder END" | |||||
| mDataBase?.rawQuery(sql, null)?.run { | |||||
| while (moveToNext()) { | |||||
| //学习单词实体 | |||||
| result.add(LearnWord(dbcb.subjectId, | |||||
| dbcb.coursePackId, | |||||
| dbcb.courseId, | |||||
| dbcb.coursePackType, | |||||
| dbcb.courseType, | |||||
| lesson.chapterId, | |||||
| lesson.lessonId, | |||||
| getLong(getColumnIndex("knowledge_id")), | |||||
| true, | |||||
| lesson.lessonType).apply { | |||||
| word = getString(getColumnIndex("title")) | |||||
| basic_explanation = getString(getColumnIndex("explaination")) | |||||
| example = getString(getColumnIndex("example")) | |||||
| reference = getString(getColumnIndex("reference")) | |||||
| }) | |||||
| } | |||||
| close() | |||||
| } | |||||
| return result | |||||
| } | |||||
| /**获取图片*/ | /**获取图片*/ | ||||
| fun queryPhoto(dbControlBase : DbControlBase, wordId : Long) : ByteArray? { | fun queryPhoto(dbControlBase : DbControlBase, wordId : Long) : ByteArray? { | ||||
| open(dbControlBase) | open(dbControlBase) | ||||
| var result : ByteArray? = null | var result : ByteArray? = null | ||||
| val sql = "SELECT photo FROM res WHERE word_id = $wordId" | val sql = "SELECT photo FROM res WHERE word_id = $wordId" | ||||
| mDataBase?.rawQuery(sql,null)?.apply { | |||||
| while (moveToNext()){ | |||||
| mDataBase?.rawQuery(sql, null)?.apply { | |||||
| while (moveToNext()) { | |||||
| result = getBlob(0) | result = getBlob(0) | ||||
| } | } | ||||
| close() | close() |
| AppConstants.COURSE_TYPE_ENGLISH_SPELL, AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> { | AppConstants.COURSE_TYPE_ENGLISH_SPELL, AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> { | ||||
| } | } | ||||
| else -> { | else -> { | ||||
| wordChooseBinding.tvWord.setOnClickListener(ivVoiceClick) | |||||
| wordChooseBinding.incWord.tvWord.setOnClickListener(ivVoiceClick) | |||||
| wordChooseBinding.ivVoice.setOnClickListener(ivVoiceClick) | wordChooseBinding.ivVoice.setOnClickListener(ivVoiceClick) | ||||
| } | } | ||||
| } | } | ||||
| //未答和正确都更新显示正确的项 错误同时显示正确和错误的项 | //未答和正确都更新显示正确的项 错误同时显示正确和错误的项 | ||||
| wordChooseBinding.run { | wordChooseBinding.run { | ||||
| ivVoice.visibility = View.GONE | ivVoice.visibility = View.GONE | ||||
| tvWord.visibility = View.VISIBLE | |||||
| incWord.tvWord.visibility = View.VISIBLE | |||||
| //显示正确项 | //显示正确项 | ||||
| vm.optionList[vm.currentCorrectPosition].run { | vm.optionList[vm.currentCorrectPosition].run { | ||||
| root.setBackgroundResource(vm.correctBg) | root.setBackgroundResource(vm.correctBg) | ||||
| } | } | ||||
| private fun initChooseUIData(examBean : ExamBean) { | private fun initChooseUIData(examBean : ExamBean) { | ||||
| wordChooseBinding.tvWord.text = examBean.word | |||||
| wordChooseBinding.incWord.tvWord.text = examBean.word | |||||
| wordChooseBinding.progressWordCountdownTime.run { | wordChooseBinding.progressWordCountdownTime.run { | ||||
| max = vm.currentMaxTime | max = vm.currentMaxTime | ||||
| progress = vm.currentMaxTime | progress = vm.currentMaxTime | ||||
| } | } | ||||
| private fun initVoiceUIData(examBean : ExamBean) { | private fun initVoiceUIData(examBean : ExamBean) { | ||||
| wordChooseBinding.tvWord.run { | |||||
| wordChooseBinding.incWord.tvWord.run { | |||||
| text = examBean.word | text = examBean.word | ||||
| visibility = View.GONE | visibility = View.GONE | ||||
| } | } | ||||
| } | } | ||||
| private fun initSpokenUIData(examBean : ExamBean) { | private fun initSpokenUIData(examBean : ExamBean) { | ||||
| wordChooseBinding.tvWord.text = examBean.word | |||||
| wordChooseBinding.incWord.tvWord.text = examBean.word | |||||
| if (examBean.questionIsAudio) { | if (examBean.questionIsAudio) { | ||||
| wordChooseBinding.tvWord.visibility = View.GONE | |||||
| wordChooseBinding.incWord.tvWord.visibility = View.GONE | |||||
| wordChooseBinding.ivVoice.visibility = View.VISIBLE | wordChooseBinding.ivVoice.visibility = View.VISIBLE | ||||
| } else { | } else { | ||||
| wordChooseBinding.tvWord.visibility = View.VISIBLE | |||||
| wordChooseBinding.incWord.tvWord.visibility = View.VISIBLE | |||||
| wordChooseBinding.ivVoice.visibility = View.GONE | wordChooseBinding.ivVoice.visibility = View.GONE | ||||
| } | } | ||||
| wordChooseBinding.progressWordCountdownTime.visibility = View.GONE | wordChooseBinding.progressWordCountdownTime.visibility = View.GONE | ||||
| private fun initSpellUIData(examBean : ExamBean) { | private fun initSpellUIData(examBean : ExamBean) { | ||||
| spellBinding.tvExplain.text = examBean.correct //释义 | spellBinding.tvExplain.text = examBean.correct //释义 | ||||
| //初始显示word的时候,文字置空,颜色修改为黑色 | //初始显示word的时候,文字置空,颜色修改为黑色 | ||||
| spellBinding.tvWord.run { | |||||
| spellBinding.incWord.tvWord.run { | |||||
| text = "" | text = "" | ||||
| setTextColor(ContextCompat.getColor(this@LearnExamActivity, R.color.main_text_color)) | setTextColor(ContextCompat.getColor(this@LearnExamActivity, R.color.main_text_color)) | ||||
| } | } | ||||
| when { | when { | ||||
| isOver -> { //拼写结束 | isOver -> { //拼写结束 | ||||
| //设置正确的值 | //设置正确的值 | ||||
| spellBinding.tvWord.setTextColor(ContextCompat.getColor(this, R.color.red_1)) | |||||
| spellBinding.tvWord.text = correctValue | |||||
| spellBinding.incWord.tvWord.setTextColor(ContextCompat.getColor(this, R.color.red_1)) | |||||
| spellBinding.incWord.tvWord.text = correctValue | |||||
| //拼写结束 | //拼写结束 | ||||
| vm.spellOver(selectedValue.toString(), errorSize) | vm.spellOver(selectedValue.toString(), errorSize) | ||||
| //执行当前的单词发音 延迟发音 | //执行当前的单词发音 延迟发音 | ||||
| spellBinding.tvWord.postDelayed({ ivVoiceClick(spellBinding.tvWord) }, 200) | |||||
| spellBinding.incWord.tvWord.postDelayed({ ivVoiceClick(spellBinding.incWord.tvWord) }, 200) | |||||
| } | } | ||||
| else -> { | else -> { | ||||
| //设置为选中的值 | //设置为选中的值 | ||||
| spellBinding.tvWord.text = selectedValue | |||||
| spellBinding.incWord.tvWord.text = selectedValue | |||||
| } | } | ||||
| } | } | ||||
| spellBinding.spellRecyclerView.scrollToPosition(nextPosition) | spellBinding.spellRecyclerView.scrollToPosition(nextPosition) |
| package com.xkl.cdl.module.learn | package com.xkl.cdl.module.learn | ||||
| import androidx.appcompat.app.AppCompatActivity | |||||
| import android.content.Context | |||||
| import android.os.Bundle | import android.os.Bundle | ||||
| import android.text.SpannableStringBuilder | |||||
| import android.text.TextUtils | |||||
| import android.util.TypedValue | |||||
| import android.view.View | |||||
| import android.widget.LinearLayout | |||||
| import android.widget.TextView | |||||
| import androidx.core.content.ContextCompat | |||||
| import androidx.lifecycle.ViewModelProvider | |||||
| import androidx.recyclerview.widget.GridLayoutManager | |||||
| import androidx.recyclerview.widget.LinearLayoutManager | |||||
| import androidx.recyclerview.widget.SimpleItemAnimator | |||||
| import com.suliang.common.base.activity.BaseActivityVM | |||||
| import com.suliang.common.extension.click | |||||
| import com.xkl.cdl.R | import com.xkl.cdl.R | ||||
| import com.xkl.cdl.adapter.AdapterHistoricalRoute | |||||
| import com.xkl.cdl.adapter.AdapterSpell | |||||
| import com.xkl.cdl.adapter.itemdecoration.SpellItemDecoration | |||||
| import com.xkl.cdl.data.AppConstants | |||||
| import com.xkl.cdl.data.bean.LearnWord | |||||
| import com.xkl.cdl.data.manager.UserInfoManager | |||||
| import com.xkl.cdl.databinding.ActivityLearnWordBinding | |||||
| import com.xkl.cdl.databinding.IncLearnSpellBinding | |||||
| import com.xkl.cdl.databinding.IncLearnWordBinding | |||||
| import com.xkl.cdl.databinding.IncWordDetailBinding | |||||
| import com.xkl.cdl.initValue | |||||
| /** | /** | ||||
| * 正常的学习、复习、自动播放 | * 正常的学习、复习、自动播放 | ||||
| */ | */ | ||||
| class LearnWordActivity : AppCompatActivity() { | |||||
| override fun onCreate(savedInstanceState: Bundle?) { | |||||
| super.onCreate(savedInstanceState) | |||||
| setContentView(R.layout.activity_learn_word) | |||||
| class LearnWordActivity : BaseActivityVM<ActivityLearnWordBinding, LearnWordViewModel>() { | |||||
| //历史轨迹适配器 | |||||
| private lateinit var adapterHistorical : AdapterHistoricalRoute | |||||
| //历史轨迹布局管理器 | |||||
| private val historicalLayoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true) | |||||
| //认读、辨音的内容布局 | |||||
| private lateinit var bindingWord : IncLearnWordBinding | |||||
| //拼写内容布局 | |||||
| private lateinit var bindingSpell : IncLearnSpellBinding | |||||
| //拼写单词RecyclerView适配器 | |||||
| private lateinit var spellAdapter : AdapterSpell | |||||
| //拼写是否可纠错的状态值 | |||||
| private val SPELL_TAG_ALL_RIGHT = 1 //全对 | |||||
| private val SPELL_TAG_IN_ERROR = 2 //可纠错 | |||||
| override fun initViewModel() : LearnWordViewModel { | |||||
| return ViewModelProvider(this)[LearnWordViewModel::class.java] | |||||
| } | } | ||||
| override fun initActivity(savedInstanceState : Bundle?) { | |||||
| initTitle() | |||||
| initBanner() | |||||
| initHistoricalRoute() | |||||
| initContentBinding() | |||||
| initControlButton() | |||||
| } | |||||
| /** 横幅 自动播放 与 复习时使用 */ | |||||
| private fun initBanner() { | |||||
| // TODO: 2022/4/25 初始化横幅,默认隐藏 | |||||
| } | |||||
| /** 标题初始 */ | |||||
| private fun initTitle() { | |||||
| binding.incLearnTitle.run { | |||||
| showProgress = false | |||||
| imgBack.click { onBackPressed() } | |||||
| tvTitle.text = vm.learnData.lesson.lessonName | |||||
| when (vm.learnData.lesson.coursePackType) { | |||||
| AppConstants.COURSEPACK_TYPE_CHINESE_COMPOSITION, AppConstants.COURSEPACK_TYPE_CHINESE_LITERACY, AppConstants.COURSEPACK_TYPE_CHINESE_PINYIN -> { | |||||
| voiceSwitch.visibility = View.GONE | |||||
| vm.defaultSoundWay = AppConstants.SOUND_TYPE_CN | |||||
| } | |||||
| else -> { | |||||
| voiceSwitch.setSoundWay(UserInfoManager.getDefaultSoundWay()) | |||||
| voiceSwitch.soundWayChange.observe(this@LearnWordActivity) { | |||||
| vm.defaultSoundWay = it | |||||
| UserInfoManager.putDefaultSoundWay(it) | |||||
| if (this@LearnWordActivity::spellAdapter.isInitialized) { | |||||
| spellAdapter.defaultSoundWay = it | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /** 历史轨迹初始 */ | |||||
| private fun initHistoricalRoute() { | |||||
| adapterHistorical = AdapterHistoricalRoute(vm.learnData.lesson.courseType).apply { | |||||
| onItemClick = { v : View, position : Int, item : LearnWord -> | |||||
| } | |||||
| } | |||||
| binding.rvHistoricalRoute.apply { | |||||
| layoutManager = historicalLayoutManager | |||||
| adapter = adapterHistorical | |||||
| } | |||||
| } | |||||
| /** 布局内容初始 */ | |||||
| private fun initContentBinding() { | |||||
| when (vm.learnData.lesson.courseType) { | |||||
| AppConstants.COURSE_TYPE_ENGLISH_SPELL -> { | |||||
| bindingSpell = IncLearnSpellBinding.inflate(layoutInflater, binding.containerLayout, true) | |||||
| //初始拼写列表 | |||||
| bindingSpell.spellRecyclerView.run { | |||||
| layoutManager = GridLayoutManager(this@LearnWordActivity, 2, GridLayoutManager.HORIZONTAL, false) | |||||
| addItemDecoration(SpellItemDecoration()) | |||||
| setHasFixedSize(true) | |||||
| (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false | |||||
| spellAdapter = AdapterSpell().apply { | |||||
| onItemSpellingClickListener = itemSpellingClick | |||||
| defaultSoundWay = vm.defaultSoundWay | |||||
| } | |||||
| adapter = spellAdapter | |||||
| } | |||||
| //拼写纠错按钮 | |||||
| bindingSpell.tvControlError.click { | |||||
| when (it.tag as Int) { | |||||
| SPELL_TAG_ALL_RIGHT -> vm.currentSpellIsCorrect = false | |||||
| SPELL_TAG_IN_ERROR -> { | |||||
| vm.currentSpellIsCorrect = true | |||||
| //下一条显示 | |||||
| binding.incControlButton.tvLeft.visibility = View.VISIBLE | |||||
| //取消纠错点击 | |||||
| spellAdapter.isErrorRecovery = false | |||||
| } | |||||
| } | |||||
| it.visibility = View.INVISIBLE | |||||
| } | |||||
| } | |||||
| else -> { | |||||
| bindingWord = IncLearnWordBinding.inflate(layoutInflater, binding.containerLayout, true) | |||||
| } | |||||
| } | |||||
| } | |||||
| //初始化按钮 | |||||
| private fun initControlButton() { | |||||
| when (vm.learnData.lesson.courseType) { | |||||
| AppConstants.COURSE_TYPE_ENGLISH_SPELL -> { | |||||
| binding.incControlButton.run { | |||||
| tvLeft.visibility = View.INVISIBLE | |||||
| tvCenter.visibility = View.INVISIBLE | |||||
| tvRight.visibility = View.INVISIBLE | |||||
| //设置为下一条 | |||||
| tvLeft.setText(R.string.control_next) | |||||
| } | |||||
| } | |||||
| //知识点学习的时候,需要将重读按钮隐藏 | |||||
| AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> binding.incControlButton.tvRight.visibility = View.INVISIBLE | |||||
| //其他课程默认按钮全部显示 | |||||
| } | |||||
| } | |||||
| override fun loadData() { | |||||
| binding.incDetail.initValue("","","") | |||||
| } | |||||
| /** | |||||
| * 拼写时的点击事件 | |||||
| * @param selectedValue 选择的值 | |||||
| * @param nextPosition 下一个item的位置,拼写未完成时为下一列的位置,拼写完成为错误的第一个位置 | |||||
| * @param isOver 拼写是否完成 为true时则判断为拼写完成 | |||||
| * @param correctValue 正确的拼写值,但未选中的赋值了颜色 | |||||
| * @param errorSize 错误的个数,拼写完成后才会有这个值 | |||||
| */ | |||||
| private val itemSpellingClick = | |||||
| { selectedValue : SpannableStringBuilder, nextPosition : Int, isOver : Boolean, correctValue : SpannableStringBuilder, errorSize : Int -> | |||||
| when { | |||||
| isOver -> { //拼写结束 | |||||
| //设置正确的值 | |||||
| bindingSpell.incWord.tvWord.setTextColor(ContextCompat.getColor(this, R.color.red_1)) | |||||
| bindingSpell.incWord.tvWord.text = correctValue | |||||
| //拼写结束 | |||||
| // vm.spellOver(selectedValue.toString(), errorSize) | |||||
| //执行当前的单词发音 延迟发音 | |||||
| // bindingSpell.incWord.tvWord.postDelayed({ ivVoiceClick(spellBinding.incWord.tvWord) }, 200) | |||||
| } | |||||
| else -> { | |||||
| //设置为选中的值 | |||||
| bindingSpell.incWord.tvWord.text = selectedValue | |||||
| } | |||||
| } | |||||
| bindingSpell.spellRecyclerView.scrollToPosition(nextPosition) | |||||
| } | |||||
| /** 返回 */ | |||||
| override fun onBackPressed() { | |||||
| when { | |||||
| vm.isAllOver -> finish() | |||||
| else -> "" | |||||
| } | |||||
| } | |||||
| } | } |
| package com.xkl.cdl.module.learn | |||||
| import com.xkl.cdl.data.AppConstants | |||||
| import com.xkl.cdl.data.DataTransferHolder | |||||
| import com.xkl.cdl.data.bean.intentdata.LearnData | |||||
| /** | |||||
| * 正常的学习、复习、自动播放 | |||||
| */ | |||||
| class LearnWordViewModel : LearnBaseViewModel() { | |||||
| //默认发音 | |||||
| var defaultSoundWay : Int = 0 | |||||
| //操作数据 | |||||
| val learnData = DataTransferHolder.instance.getData<LearnData>().apply { | |||||
| DataTransferHolder.instance.clear() | |||||
| } | |||||
| //记录拼写的最终结果 | |||||
| var currentSpellIsCorrect = false | |||||
| } |
| import com.suliang.common.eventbus.LiveDataBus | import com.suliang.common.eventbus.LiveDataBus | ||||
| import com.xkl.cdl.adapter.AdapterLesson | import com.xkl.cdl.adapter.AdapterLesson | ||||
| import com.xkl.cdl.data.AppConstants | import com.xkl.cdl.data.AppConstants | ||||
| import com.xkl.cdl.data.DataTransferHolder | |||||
| import com.xkl.cdl.data.bean.LearnDialogBean | import com.xkl.cdl.data.bean.LearnDialogBean | ||||
| import com.xkl.cdl.data.bean.course.ExamBean | import com.xkl.cdl.data.bean.course.ExamBean | ||||
| import com.xkl.cdl.data.bean.course.Lesson | import com.xkl.cdl.data.bean.course.Lesson | ||||
| import com.xkl.cdl.data.bean.intentdata.ExamData | import com.xkl.cdl.data.bean.intentdata.ExamData | ||||
| import com.xkl.cdl.data.bean.intentdata.LearnData | |||||
| import com.xkl.cdl.data.event.LearnEventData | import com.xkl.cdl.data.event.LearnEventData | ||||
| import com.xkl.cdl.data.manager.CourseManager | import com.xkl.cdl.data.manager.CourseManager | ||||
| import com.xkl.cdl.databinding.FragmentCourseLessonBinding | import com.xkl.cdl.databinding.FragmentCourseLessonBinding | ||||
| import com.xkl.cdl.dialog.LearnDialog | import com.xkl.cdl.dialog.LearnDialog | ||||
| import com.xkl.cdl.module.learn.LearnWordActivity | |||||
| /** | /** | ||||
| * 课程章节目录 | * 课程章节目录 | ||||
| private val onLessonClick : (v:View,position: Int, lesson: Lesson) -> Unit = { view, position, entity -> | private val onLessonClick : (v:View,position: Int, lesson: Lesson) -> Unit = { view, position, entity -> | ||||
| when(entity.lessonType){ | when(entity.lessonType){ | ||||
| AppConstants.LESSON_TYPE_WORD -> { | AppConstants.LESSON_TYPE_WORD -> { | ||||
| if (entity.beforeTestScore == AppConstants.NOT_DOING){ //课时学前测试,没有做 | |||||
| startLearn(entity) | |||||
| /* if (entity.beforeTestScore == AppConstants.NOT_DOING){ //课时学前测试,没有做 | |||||
| //弹窗显示学前测试提示 | //弹窗显示学前测试提示 | ||||
| showLessonBeforeTestStartDialog(entity) | showLessonBeforeTestStartDialog(entity) | ||||
| }else if (!entity.learnIsOver){ | |||||
| }else if (!entity.learnIsOver){ //当前课时未学完,直接开始学习 | |||||
| startLearn(entity) | |||||
| }else if (entity.afterTestScore != AppConstants.NOT_DOING){ | }else if (entity.afterTestScore != AppConstants.NOT_DOING){ | ||||
| loadLessonAfterTest(entity) | loadLessonAfterTest(entity) | ||||
| }else{ //当前课时学习完成的弹窗 | }else{ //当前课时学习完成的弹窗 | ||||
| showLessonAllOverDialog(entity) | showLessonAllOverDialog(entity) | ||||
| } | |||||
| }*/ | |||||
| } | } | ||||
| AppConstants.LESSON_TYPE_SENTENCE -> { | AppConstants.LESSON_TYPE_SENTENCE -> { | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * 查询课时数据,并开始学习,进入学习界面 | |||||
| * @param lesson Lesson 课时 | |||||
| */ | |||||
| private fun startLearn(lesson : Lesson){ | |||||
| vm.loadLessonLearnData(lesson).observe(this){ | |||||
| DataTransferHolder.instance.putData(value = it) | |||||
| startActivity(LearnWordActivity::class.java) | |||||
| } | |||||
| } | |||||
| /** 开始课时学后测试 | /** 开始课时学后测试 | ||||
| * 请求数据后,直接开始跳转 | * 请求数据后,直接开始跳转 | ||||
| */ | */ |
| * @param lesson Lesson 课时 | * @param lesson Lesson 课时 | ||||
| * @return MutableLiveData<LearnData> | * @return MutableLiveData<LearnData> | ||||
| */ | */ | ||||
| fun loadLessonLearnForWord(lesson : Lesson): MutableLiveData<LearnData>{ | |||||
| fun loadLessonLearnData(lesson : Lesson): MutableLiveData<LearnData>{ | |||||
| val result = MutableLiveData<LearnData>() | val result = MutableLiveData<LearnData>() | ||||
| Observable.create<List<LearnWord>> { | Observable.create<List<LearnWord>> { | ||||
| it.onNext(DBCourseManager.queryLearnDataForWord(dbControlBase, lesson)) | |||||
| it.onNext(DBCourseManager.queryLearnData(dbControlBase, lesson)) | |||||
| it.onComplete() | it.onComplete() | ||||
| }.compose(diskIo2Main()) | }.compose(diskIo2Main()) | ||||
| .subscribe { | .subscribe { | ||||
| learnWordList = it | learnWordList = it | ||||
| } | } | ||||
| } | } | ||||
| return result | return result | ||||
| } | } | ||||
| package com.xkl.cdl.util | package com.xkl.cdl.util | ||||
| import com.xkl.cdl.data.bean.BaseWord | import com.xkl.cdl.data.bean.BaseWord | ||||
| import java.io.* | |||||
| import java.util.* | |||||
| /** | /** | ||||
| * author suliang | * author suliang | ||||
| * create 2022/4/22 17:12 | |||||
| * Describe: 规则 | |||||
| * @property originList 源数据 | |||||
| * @property cycleTime 循环次数 | |||||
| * create 2021/1/18 13:48 | |||||
| * Describe: 学习工具类 | |||||
| * @property originLearnList 源数据 | |||||
| * @property isSpellModel 是否是拼写模式 | |||||
| * @property examErrorsMap 学前总和章前错误列表 | |||||
| */ | */ | ||||
| class LearnRuleUtil<T : BaseWord>(private val originList : List<T>, private val cycleTime : Int) { | |||||
| //源数据学习位置 | |||||
| private var originLearnPosition : Int = 0 | |||||
| //学习列表位置 | |||||
| private var currentLearnPosition : Int = -1 | |||||
| //学习列表数据 | |||||
| private var currentList = mutableListOf<T?>(null) | |||||
| //当前答题是否错误 | |||||
| class LearnRuleUtil<T : BaseWord> constructor(val originLearnList : List<T>, | |||||
| isSpellModel : Boolean, | |||||
| val examErrorsMap : Map<String, Boolean>?) { | |||||
| //错误时循环插入间隔 | |||||
| private var insertInterval : IntArray = when { | |||||
| isSpellModel -> intArrayOf(1, 2, 3, 5) | |||||
| else -> intArrayOf(1, 2, 3, 5, 7, 10, 15) | |||||
| } | |||||
| //当前用于学习的列表: 学习数据从该列表中获取, 并放入第一个数据为null | |||||
| private var currentLearnList : MutableList<T?> = mutableListOf(null) | |||||
| //源数据学习的位置 | |||||
| private var originLearnPosition = -1 | |||||
| //学习列表学习的位置 | |||||
| private var currentLearnPosition = -1 | |||||
| //外部选择结果的记录 记录当前点击是否错误: 点击后需修改,用于获取下一条时进行判断 | |||||
| private var currentIsError = false | private var currentIsError = false | ||||
| //学前总和课时前测试错误集合 | |||||
| var examErrorMap = hashMapOf<String,Boolean>() | |||||
| //新错误集合 | |||||
| var newErrorMap = hashMapOf<String,Boolean>() | |||||
| //第一次点击正确的集合, 如果后面再点击错误,需要移除并添加到错误集合中 | |||||
| var newCorrectMap = hashMapOf<String,Boolean>() | |||||
| //学习错误的集合: 本次点击的新错误(不包含 examErrorsMap 中的错误) | |||||
| var currentErrorMap : MutableMap<String, Boolean> = HashMap() | |||||
| private set | |||||
| //学习正确的集合: 存放学习中第一次正确的单词 | |||||
| var currentCorrectMap : MutableMap<String, Boolean> = HashMap() | |||||
| private set | |||||
| /** | |||||
| * 获取下一条数据,返回是否学习完成 | |||||
| */ | |||||
| val loadNext : T? | |||||
| get() { | |||||
| return when { | |||||
| currentIsError || isHaveNext -> nextWord() | |||||
| else -> null | |||||
| } | |||||
| } | |||||
| //1、如果当前学习下标为-1,即第一次进入学习,直接返true | |||||
| //2、判断源数据是否学习完成 | |||||
| //源数据学习未完,返回true ,还有未学单词 | |||||
| //源数据学完,判断学习列表是否学习结束 | |||||
| // 而在源数据没有获取到数据的时候,当前数据currentList的当前数据下坐标会走到下一个有效值位置 | |||||
| // 取出当前学习的单词,当前单词不会为null,会有源数据进行空位填充, | |||||
| //获取下一个不为null 且和当前数据不一样的数据 | |||||
| //如果获取不到下一个不为空的数据,则还有下一条,否则结束了 | |||||
| fun getNext(): T? { | |||||
| if (currentIsError || isHaveNext()){ | |||||
| return nextWord() | |||||
| //是否有下一条数据 | |||||
| val isHaveNext : Boolean | |||||
| get() { | |||||
| //1、如果当前学习下标为-1,即第一次进入学习,直接返true | |||||
| if (currentLearnPosition == -1) return true | |||||
| //2、判断源数据是否学习完成 | |||||
| val originLearnEnd = isLearnOverForOrigin() | |||||
| //源数据学习未完,返回true ,还有未学单词 | |||||
| if (!originLearnEnd) return true | |||||
| //源数据学完,判断学习列表是否学习结束 | |||||
| // 而在源数据没有获取到数据的时候,当前数据currentList的当前数据下坐标会走到下一个有效值位置 | |||||
| // 取出当前学习的单词,当前单词不会为null,会有源数据进行空位填充, | |||||
| val curentLearnWord = currentLearnList[currentLearnPosition]!! | |||||
| //获取下一个不为null 且和当前数据不一样的数据 | |||||
| var nextWord : T? = null | |||||
| for (i in currentLearnPosition until currentLearnList.size) { | |||||
| val word : T? = currentLearnList[i] | |||||
| if (word != null && word.wordId != curentLearnWord.wordId) { | |||||
| nextWord = word | |||||
| break | |||||
| } | |||||
| } | |||||
| //如果获取不到下一个不为空的数据,则还有下一条,否则结束了 | |||||
| return nextWord != null | |||||
| } | |||||
| //有下一条数据的情况下获取下一条 | |||||
| private fun nextWord() : T? { | |||||
| currentLearnPosition++ | |||||
| return currentLearnList.getOrNull(currentLearnPosition) ?: let { | |||||
| // //当前学习列表获取的下一条数据为空,则从源数据集合获取数据 | |||||
| originNexWord?.let { //源数据还有数据 | |||||
| val key = "${it.chapterId}_${it.lessonId}_${it.wordId}" | |||||
| when { | |||||
| //是否测试错误 | |||||
| isInExamErrorMap(key) -> insertErrorWord(it, true) //对当前集合进行插入,在学前测试错误集合中的内容,直接从第一次错误开始 | |||||
| else -> when { //源数据不为错误数据,需要将源数据添加到当前学习列表中,可能直接替换 或直接添加 | |||||
| currentLearnList.size > currentLearnPosition -> currentLearnList[currentLearnPosition] = it | |||||
| else -> currentLearnList.add(currentLearnPosition, it) | |||||
| } | |||||
| } | |||||
| //获取当前位置的值:重新获取的原因是:当为测试错误时,将修改数据的次数 | |||||
| currentLearnList[currentLearnPosition] | |||||
| } ?: getCurrentListNextWordForCycle() //源数据没有数据了,继续获取下一条数据,因下一条数据可能为null,所有需要走递归,找不为空的数据 | |||||
| } | } | ||||
| return null | |||||
| } | } | ||||
| /** | /** | ||||
| * 是否还有下一单词 | |||||
| * @return Boolean | |||||
| * 递归获取当前列表的下一条不为空的数据 | |||||
| */ | */ | ||||
| private fun isHaveNext():Boolean{ | |||||
| //如果当前学习下标为-1,即第一次进入学习,直接返true | |||||
| if (currentLearnPosition == -1) return true | |||||
| //元数据是否学习完成,未学习完成,则还有学习数据, 元数据学习完成,还需要判断当前学习列表是否学习完成 | |||||
| if (!isLearnOverForOrigin()) return true | |||||
| //判断当前列表是否学习完成 | |||||
| val currentLearnWord = currentList[currentLearnPosition]!! | |||||
| var nextWord: T? = null | |||||
| //循环找寻与当前word不一样的数据 | |||||
| if (currentLearnPosition + 1 < currentList.size-1) { | |||||
| for (i in currentLearnPosition + 1 .. currentList.size - 1) { | |||||
| currentList[i]?.let { | |||||
| if (currentLearnWord.wordId != it.wordId ){ | |||||
| nextWord = it | |||||
| private fun getCurrentListNextWordForCycle() : T? { | |||||
| currentLearnPosition++ | |||||
| return when { | |||||
| currentLearnList.size > currentLearnPosition -> currentLearnList[currentLearnPosition] | |||||
| ?: getCurrentListNextWordForCycle() | |||||
| else -> null | |||||
| } | |||||
| } | |||||
| /** 获取原数据集合的下一条数据 */ | |||||
| private val originNexWord : T? | |||||
| get() { | |||||
| originLearnPosition++ | |||||
| return originLearnList.getOrNull(originLearnPosition) | |||||
| } | |||||
| /** 替换错误单词数据位置,为空占位,将后面的相同单词替换为空,便于错误后,重新排列错误数据 */ | |||||
| private fun replaceErrorToNull(item : T) { | |||||
| run cyclerFor@{ | |||||
| for (i in currentLearnPosition + 1 until currentLearnList.size) { | |||||
| currentLearnList[i]?.let { | |||||
| //为相同单词 | |||||
| if (item.wordId == it.wordId && item.lessonId == it.lessonId && item.chapterId == it.chapterId) { | |||||
| currentLearnList.set(i, null) | |||||
| if (it.repeatNum == insertInterval.size) { | |||||
| return@cyclerFor | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (nextWord != null) break | |||||
| } | } | ||||
| } | } | ||||
| //如果获取不到下一个不为空的数据,则还有下一条,否则结束了 | |||||
| return nextWord != null | |||||
| } | |||||
| /** 计算当前列表值,并进行扩容 */ | |||||
| private fun dilatationSpace() { | |||||
| var spaceCount = currentLearnPosition //需要扩容的空间 | |||||
| for (i in insertInterval.indices) { | |||||
| spaceCount += insertInterval[i] + 1 //扩容需用空间 当前 + 间隔 + 1,最后的1即位需要替换的位置 | |||||
| } | |||||
| dilatationToPosition(spaceCount) | |||||
| } | |||||
| /** 扩容至指定位置,并添加空数据占位 */ | |||||
| private fun dilatationToPosition(insertPosition : Int) { | |||||
| for (i in currentLearnList.size .. insertPosition) { | |||||
| currentLearnList.add(null) | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 插入错误单词 | |||||
| * @param item 原型单词 | |||||
| * @param isExamError 是否时长测试列表中的错误 | |||||
| */ | |||||
| private fun insertErrorWord(item : T, isExamError : Boolean) { | |||||
| var stat : Int | |||||
| //测试第一次添加时,为当前位置,所以减二用于获取插入前的位置 | |||||
| stat = if (isExamError) currentLearnPosition - insertInterval[0] - 1 else currentLearnPosition | |||||
| dilatationSpace() | |||||
| for (i in 1 .. insertInterval.size) { | |||||
| val insert = copyWord(item, i) | |||||
| if (i == 1 && isExamError) { //如果是测试错误的加入,第一次应特殊设置,用于判断为学习进度 | |||||
| insert?.first = true | |||||
| } | |||||
| val insertPosition = stat + insertInterval[i - 1] + 1 //需要插入的位置 | |||||
| val nextWordPosition = getNextValidPosition(stat) //下一个有效单词的下标 : 开始值为现在传入的值 | |||||
| val finalInsertPosition = getFinalInsertPosition(stat, insertPosition, nextWordPosition) //获取最终需要插入的位置 | |||||
| //操作判断是否需要扩容,如果需要则自动扩容 | |||||
| dilatationToPosition(finalInsertPosition) | |||||
| //取出当前内容,进行操作: 插入 或 替换 | |||||
| val t : T? = currentLearnList[finalInsertPosition] | |||||
| if (null == t) { | |||||
| currentLearnList[finalInsertPosition] = insert | |||||
| } else { | |||||
| currentLearnList.add(finalInsertPosition, insert) | |||||
| } | |||||
| stat = finalInsertPosition | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| * 源数据是否学习完成 | |||||
| * 生成一个新的单词对象 | |||||
| * @param old 用来copy的单词 | |||||
| * @param num 设置错误次数 | |||||
| * @return | |||||
| */ | */ | ||||
| fun isLearnOverForOrigin():Boolean{ | |||||
| return originLearnPosition >= originList.size -1 | |||||
| private fun copyWord(old : T, num : Int) : T? { | |||||
| var newWord : T? = null | |||||
| try { | |||||
| newWord = copy(old) | |||||
| newWord.repeatNum = num | |||||
| newWord.first = false //设置为不为第一次出现的内容 | |||||
| return newWord | |||||
| } catch (e : IOException) { | |||||
| e.printStackTrace() | |||||
| } catch (e : ClassNotFoundException) { | |||||
| e.printStackTrace() | |||||
| } | |||||
| return null | |||||
| } | } | ||||
| /** | /** | ||||
| * 获取下一条数据 | |||||
| * 获取从指定位置向后的有效单词位置 | |||||
| */ | */ | ||||
| private fun nextWord(): T?{ | |||||
| //声明下一条数据 | |||||
| var next : T? = null | |||||
| //当前列表 学习位置+1 | |||||
| currentLearnPosition ++ | |||||
| when{ | |||||
| //从当前集合获取 | |||||
| currentList.size > currentLearnPosition -> { | |||||
| private fun getNextValidPosition(startPosition : Int) : Int { | |||||
| var nextWordPositon = -1 | |||||
| val currentListSize = currentLearnList.size | |||||
| for (j in startPosition + 1 until currentListSize) { | |||||
| if (j < 0) continue //如果在学习时,第一个就是测试的错误,则startPosition为-2,j < 0时,为无效,所以直接跳过 | |||||
| if (currentLearnList[j] != null) { | |||||
| nextWordPositon = j | |||||
| break | |||||
| } | } | ||||
| //从源数据据列表获取 | |||||
| else -> { | |||||
| originLearnPosition++ | |||||
| if (!isLearnOverForOrigin()) { | |||||
| next = originList[originLearnPosition] | |||||
| //元数据如果是课时学前或总测的错误数据,那么就需要按照小循环插入 | |||||
| } | |||||
| return nextWordPositon | |||||
| } | |||||
| /** | |||||
| * 获取最终获取的插入位置 | |||||
| * @param insertPosition 应该插入的位置 | |||||
| * @param nextWordPositon 下一个单词的所在的位置 | |||||
| */ | |||||
| private fun getFinalInsertPosition(currentPosition : Int, insertPosition : Int, nextWordPositon : Int) : Int { | |||||
| if (nextWordPositon != -1 && insertPosition <= nextWordPositon) { //下一个单词的位置有效,且 插入位置小于或等于下一个的位置 | |||||
| val isChangePosition = isChangeInsertPosition(insertPosition) | |||||
| if (isChangePosition) { //如果需要改变位置 | |||||
| val startItem = currentLearnList[currentPosition]!! //开始位置的单词 | |||||
| val nextItem = currentLearnList[nextWordPositon]!! //下一个有效位置的单词 | |||||
| return if (startItem.wordId == nextItem.wordId) { //如果需要插入的单词和下一个有效位置的单词是同一个 | |||||
| nextWordPositon //如果是同一个,将插入单词放在下一个有效单词之前 | |||||
| } else { | |||||
| nextWordPositon + 1 //如果不是同一个单词,则将单词插入下一个单词之后 | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return next | |||||
| return insertPosition | |||||
| } | } | ||||
| /** | |||||
| * 是否需要改变插入位置 | |||||
| */ | |||||
| private fun isChangeInsertPosition(currentInsertPosition : Int) : Boolean { | |||||
| //如果有下一个有效单词 且 有效单词位置 大于等于 需要插入的位置 (处理是否需要修改插入位置的改变,避免中间为空数据时不能从原数据中获取数据,而导致出现连续错误单词) | |||||
| var isChangePosition = false //记录是否需要改变插入位置,避免中间为空数据时不能从原数据中获取数据,而导致出现连续错误单词 | |||||
| //原数据中剩余学习单词,originlearnPosition为已显示或学习的下坐标 | |||||
| val originSurplusNum = originLearnList.size - (originLearnPosition + 1) | |||||
| isChangePosition = if (originSurplusNum > 0) { //原数据中有未学习完的单词 | |||||
| var nullNum = 0 //记录空数据数量,从当前学习单词的下一个开始,到需要插入的数据中间的空数据数量 | |||||
| for (k in currentLearnPosition + 1 until currentInsertPosition) { | |||||
| if (null == currentLearnList[k]) { | |||||
| nullNum++ | |||||
| } | |||||
| } | |||||
| nullNum > originSurplusNum //如果空数据数量大于源数据未学数据的大小,则需要改变数据位置,改变到下一个有效单词的后面 | |||||
| } else { | |||||
| true | |||||
| } | |||||
| return isChangePosition | |||||
| } | |||||
| /** | |||||
| * 复制一个对象 | |||||
| * @param old 原对象 | |||||
| * @param <T> 泛型 | |||||
| * @return | |||||
| * @throws IOException | |||||
| * @throws ClassNotFoundException | |||||
| </T> */ | |||||
| @Throws(IOException::class, ClassNotFoundException::class) | |||||
| fun <T> copy(old : T) : T { | |||||
| // 写入字节流 | |||||
| val baos = ByteArrayOutputStream() | |||||
| val oos = ObjectOutputStream(baos) | |||||
| oos.writeObject(old) | |||||
| // 读取字节流 | |||||
| val bais = ByteArrayInputStream(baos.toByteArray()) | |||||
| val ois = ObjectInputStream(bais) | |||||
| return ois.readObject() as T | |||||
| } | |||||
| /** 是否在错误列表中 */ | |||||
| /** | |||||
| * 点击正确 | |||||
| * @param item T 点击单词 | |||||
| * @param isLearnFirst 是否是新出来的单词,非从历史轨迹中点击的已答过的单词 | |||||
| */ | |||||
| fun correct(item : T, isLearnFirst : Boolean) { | |||||
| val key : String = "${item.chapterId}_${item.lessonId}_${item.wordId}" | |||||
| if (!isInExamErrorMap(key) && !isInCurrentErrorMap(key) && !currentCorrectMap.containsKey(key)) { //不为已有错误,操作正确数+1 | |||||
| currentCorrectMap[key] = true | |||||
| } | |||||
| if (isLearnFirst) currentIsError = false | |||||
| } | |||||
| /** | |||||
| * 点击错误 | |||||
| */ | |||||
| fun error(item : T) { | |||||
| currentIsError = true //定义为学习错误,代表后面还有错误值 | |||||
| //如果在正确列表中,从正确列表中移除,并添加到错误列表中,正确数-1 ,错误数+1 | |||||
| //如果是测试前错误不操作正确错误数 | |||||
| //如果是学习中的错误 ,错误数+1 | |||||
| val key = "${item.chapterId}_${item.lessonId}_${item.wordId}" | |||||
| if (currentCorrectMap.containsKey(key)) { | |||||
| currentCorrectMap.remove(key) | |||||
| currentErrorMap[key] = true | |||||
| } else if (!isInExamErrorMap(key) && !isInCurrentErrorMap(key)) { //测试中数据的错误 | |||||
| currentErrorMap[key] = true | |||||
| } | |||||
| //开始进行数据扩容替换 | |||||
| replaceErrorToNull(item) //替换已有错误为空 | |||||
| insertErrorWord(item, false) //开始插入 | |||||
| } | |||||
| /** | |||||
| * 是否在学前章前测试的错误集合中 | |||||
| * @return true在 false不存在 | |||||
| */ | |||||
| fun isInExamErrorMap(key : String) : Boolean { | |||||
| return examErrorsMap?.getOrDefault(key, null) ?: false | |||||
| } | |||||
| /** 是否是在学习的错误列表中 */ | |||||
| fun isInCurrentErrorMap(key : String) : Boolean { | |||||
| return currentErrorMap.getOrDefault(key, false) | |||||
| } | |||||
| /** 判断源数据中的数据是否已经学习完成:由外部调用,originLearnPosition下标的数据为已经取出来学习的数据,只有为第一个的时候,才判断此方法 */ | |||||
| fun isLearnOverForOrigin() : Boolean { | |||||
| return originLearnPosition >= originLearnList.size - 1 | |||||
| } | |||||
| } | } |
| private void initView() { | private void initView() { | ||||
| defaultSize = ScreenUtil.INSTANCE.dp2px(122); //默认大小 | |||||
| defaultSize = (int) ScreenUtil.INSTANCE.dp2px(122); //默认大小 | |||||
| mRectF = new RectF(); | mRectF = new RectF(); | ||||
| if (normalColor != -2 && progressWidth != -2) { | if (normalColor != -2 && progressWidth != -2) { |
| init { | init { | ||||
| orientation = VERTICAL | orientation = VERTICAL | ||||
| gravity = Gravity.CENTER | gravity = Gravity.CENTER | ||||
| setPadding(0,ScreenUtil.dp2px(8f),0,ScreenUtil.dp2px(8f)) | |||||
| setPadding(0,ScreenUtil.dp2px(8f).toInt(),0,ScreenUtil.dp2px(8f).toInt()) | |||||
| LayoutInflater.from(context).inflate(R.layout.smart_refresh_header, this, true) | LayoutInflater.from(context).inflate(R.layout.smart_refresh_header, this, true) | ||||
| } | } | ||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <solid android:color="#40A540" /> | |||||
| <corners android:radius="2dp" /> | |||||
| <size | |||||
| android:width="4dp" | |||||
| android:height="16dp" /> | |||||
| </shape> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <solid android:color="#F7874F" /> | |||||
| <corners android:radius="2dp" /> | |||||
| <size | |||||
| android:width="4dp" | |||||
| android:height="16dp" /> | |||||
| </shape> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <solid android:color="#5082E6" /> | |||||
| <corners android:radius="2dp" /> | |||||
| <size | |||||
| android:width="4dp" | |||||
| android:height="16dp" /> | |||||
| </shape> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <corners android:topLeftRadius="8dp" | |||||
| android:topRightRadius="8dp"/> | |||||
| <solid android:color="@color/white"/> | |||||
| </shape> |
| <include | <include | ||||
| android:id="@+id/inc_learn_title" | android:id="@+id/inc_learn_title" | ||||
| layout="@layout/inc_learn_title" /> | |||||
| layout="@layout/inc_learn_title" | |||||
| app:showProgress="@{true}" | |||||
| /> | |||||
| <include | <include | ||||
| android:id="@+id/inc_statistics" | android:id="@+id/inc_statistics" |
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:app="http://schemas.android.com/apk/res-auto" | 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" | |||||
| tools:context=".module.learn.LearnWordActivity"> | |||||
| xmlns:tools="http://schemas.android.com/tools"> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||||
| <data> | |||||
| </data> | |||||
| <androidx.constraintlayout.widget.ConstraintLayout | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="match_parent" | |||||
| android:background="@color/white" | |||||
| tools:context=".module.learn.LearnWordActivity"> | |||||
| <!--标题头部导航--> | |||||
| <include | |||||
| android:id="@+id/inc_learn_title" | |||||
| layout="@layout/inc_learn_title" | |||||
| app:showProgress="@{false}" /> | |||||
| <View | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="30dp" | |||||
| android:background="@color/gray_1" | |||||
| app:layout_constraintTop_toTopOf="@id/rv_historical_route"/> | |||||
| <!--历史轨迹--> | |||||
| <androidx.recyclerview.widget.RecyclerView | |||||
| android:id="@+id/rv_historical_route" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="30dp" | |||||
| android:orientation="horizontal" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| app:layout_constraintTop_toBottomOf="@+id/inc_learn_title" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| tools:reverseLayout="true" | |||||
| tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | |||||
| tools:itemCount="1" | |||||
| android:onItemSelected="1" | |||||
| app:layout_constraintHorizontal_bias="0" | |||||
| tools:listitem="@layout/item_historical_route" | |||||
| /> | |||||
| <TextView | |||||
| android:id="@+id/tv_banner" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="28dp" | |||||
| android:background="#1A5082E6" | |||||
| android:gravity="center" | |||||
| android:text="@string/auto_playing" | |||||
| android:textColor="@color/theme_color" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| app:layout_constraintTop_toBottomOf="@+id/rv_historical_route" | |||||
| android:visibility="gone"/> | |||||
| <!--内容布局--> | |||||
| <FrameLayout | |||||
| android:id="@+id/container_layout" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="wrap_content" | |||||
| android:minHeight="190dp" | |||||
| android:paddingBottom="10dp" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_banner"> | |||||
| <!-- <include layout="@layout/inc_learn_word" />--> | |||||
| <!-- <include layout="@layout/inc_learn_spell" />--> | |||||
| </FrameLayout> | |||||
| <!--按钮--> | |||||
| <include | |||||
| android:id="@+id/inc_control_button" | |||||
| layout="@layout/inc_control_button" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="56dp" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@id/container_layout" /> | |||||
| <!--详情--> | |||||
| <include | |||||
| android:id="@+id/inc_detail" | |||||
| layout="@layout/inc_word_detail" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="0dp" | |||||
| app:layout_constraintBottom_toTopOf="@+id/tv_use_total_time" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/inc_control_button" | |||||
| android:visibility="gone" | |||||
| tools:visibility="visible"/> | |||||
| <!--总时间--> | |||||
| <TextView | |||||
| android:id="@+id/tv_use_total_time" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="30dp" | |||||
| android:gravity="center" | |||||
| android:text="本次学习 00:00:00" | |||||
| android:textColor="@color/gray_2" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent"/> | |||||
| <Button | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="22dp" | |||||
| style="@style/Widget.MaterialComponents.Button.UnelevatedButton" | |||||
| tools:text="暂停播放" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| android:insetTop="0dp" | |||||
| android:insetBottom="0dp" | |||||
| android:textColor="@color/theme_color" | |||||
| app:strokeColor = "@color/theme_color" | |||||
| app:strokeWidth="@dimen/line_height" | |||||
| android:layout_marginBottom="4dp" | |||||
| android:layout_marginStart="@dimen/global_spacing" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| app:icon="@drawable/ic_play_pause" | |||||
| app:iconSize="16dp" | |||||
| app:iconTint="@color/theme_color" | |||||
| app:rippleColor="@color/white"/> | |||||
| <Button | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="22dp" | |||||
| style="@style/Widget.MaterialComponents.Button.UnelevatedButton" | |||||
| tools:text="停止播放" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| android:insetTop="0dp" | |||||
| android:insetBottom="0dp" | |||||
| android:textColor="@color/theme_color" | |||||
| app:strokeColor = "@color/theme_color" | |||||
| app:strokeWidth="@dimen/line_height" | |||||
| android:layout_marginBottom="4dp" | |||||
| android:layout_marginEnd="@dimen/global_spacing" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| app:icon="@drawable/ic_play_stop" | |||||
| app:iconSize="10dp" | |||||
| app:iconTint="@color/theme_color" | |||||
| app:rippleColor="@color/white" | |||||
| /> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||||
| </layout> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||||
| xmlns:tools="http://schemas.android.com/tools" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing"> | |||||
| <Button | |||||
| android:id="@+id/tv_left" | |||||
| style="@style/common_button" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/common_button_height" | |||||
| android:backgroundTint="@color/control_left" | |||||
| android:gravity="center" | |||||
| app:cornerRadius="8dp" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toStartOf="@id/tv_center" | |||||
| app:layout_constraintHorizontal_chainStyle="spread" | |||||
| app:layout_constraintHorizontal_weight="3" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:text="@string/control_answer" | |||||
| tools:visibility="visible" /> | |||||
| <Button | |||||
| android:id="@+id/tv_center" | |||||
| style="@style/common_button" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/common_button_height" | |||||
| android:layout_marginStart="8dp" | |||||
| android:layout_marginEnd="8dp" | |||||
| android:backgroundTint="@color/control_center" | |||||
| android:text="@string/control_error" | |||||
| app:cornerRadius="8dp" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toStartOf="@+id/tv_right" | |||||
| app:layout_constraintHorizontal_weight="3" | |||||
| app:layout_constraintStart_toEndOf="@+id/tv_left" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:visibility="visible" /> | |||||
| <Button | |||||
| android:id="@+id/tv_right" | |||||
| style="@style/common_button" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/common_button_height" | |||||
| android:backgroundTint="@color/control_right" | |||||
| android:text="@string/control_reRead" | |||||
| app:cornerRadius="8dp" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintHorizontal_weight="2" | |||||
| app:layout_constraintStart_toEndOf="@+id/tv_center" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:visibility="visible" /> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> |
| app:layout_constraintTop_toBottomOf="@+id/tv_explain" | app:layout_constraintTop_toBottomOf="@+id/tv_explain" | ||||
| tools:progress="20" /> | tools:progress="20" /> | ||||
| <androidx.appcompat.widget.AppCompatTextView | |||||
| android:id="@+id/tv_word" | |||||
| android:layout_width="match_parent" | |||||
| <!-- <androidx.appcompat.widget.AppCompatTextView--> | |||||
| <!-- android:id="@+id/tv_word"--> | |||||
| <!-- android:layout_width="match_parent"--> | |||||
| <!-- android:layout_height="@dimen/height_word"--> | |||||
| <!-- android:layout_marginTop="@dimen/global_spacing"--> | |||||
| <!-- android:gravity="center"--> | |||||
| <!-- tools:text="gradual"--> | |||||
| <!-- tools:textColor="@color/num0"--> | |||||
| <!-- android:textSize="@dimen/size_max_word"--> | |||||
| <!-- app:autoSizeMaxTextSize="@dimen/size_max_word"--> | |||||
| <!-- app:autoSizeMinTextSize="@dimen/miniSize"--> | |||||
| <!-- app:autoSizeStepGranularity="1dp"--> | |||||
| <!-- app:autoSizeTextType="uniform"--> | |||||
| <!-- app:layout_constraintStart_toStartOf="parent"--> | |||||
| <!-- app:layout_constraintEnd_toEndOf="parent"--> | |||||
| <!-- app:layout_constraintTop_toBottomOf="@+id/progress_word_countdown_time" />--> | |||||
| <include | |||||
| android:id="@+id/inc_word" | |||||
| layout="@layout/inc_word" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/height_word" | android:layout_height="@dimen/height_word" | ||||
| android:layout_marginTop="@dimen/global_spacing" | |||||
| android:gravity="center" | |||||
| tools:text="gradual" | |||||
| tools:textColor="@color/num0" | |||||
| android:textSize="@dimen/size_max_word" | |||||
| app:autoSizeMaxTextSize="@dimen/size_max_word" | |||||
| app:autoSizeMinTextSize="@dimen/miniSize" | |||||
| app:autoSizeStepGranularity="1dp" | |||||
| app:autoSizeTextType="uniform" | |||||
| app:layout_constraintStart_toStartOf="parent" | app:layout_constraintStart_toStartOf="parent" | ||||
| app:layout_constraintEnd_toEndOf="parent" | app:layout_constraintEnd_toEndOf="parent" | ||||
| app:layout_constraintTop_toBottomOf="@+id/progress_word_countdown_time" /> | |||||
| app:layout_constraintTop_toBottomOf="@+id/progress_word_countdown_time" | |||||
| android:layout_marginTop="@dimen/global_spacing"/> | |||||
| <TextView | <TextView | ||||
| android:id="@+id/tv_rule_tip" | android:id="@+id/tv_rule_tip" | ||||
| android:textSize="@dimen/smallerSize" | android:textSize="@dimen/smallerSize" | ||||
| app:layout_constraintStart_toStartOf="parent" | app:layout_constraintStart_toStartOf="parent" | ||||
| app:layout_constraintEnd_toEndOf="parent" | app:layout_constraintEnd_toEndOf="parent" | ||||
| app:layout_constraintTop_toBottomOf="@+id/tv_word" /> | |||||
| app:layout_constraintTop_toBottomOf="@+id/inc_word" /> | |||||
| <androidx.recyclerview.widget.RecyclerView | <androidx.recyclerview.widget.RecyclerView | ||||
| android:id="@+id/spell_recyclerView" | android:id="@+id/spell_recyclerView" | ||||
| app:layout_constraintEnd_toEndOf="parent" | app:layout_constraintEnd_toEndOf="parent" | ||||
| app:layout_constraintTop_toBottomOf="@+id/tv_rule_tip" | app:layout_constraintTop_toBottomOf="@+id/tv_rule_tip" | ||||
| android:layout_marginTop="12dp" | android:layout_marginTop="12dp" | ||||
| app:layout_constrainedWidth="true"/> | |||||
| app:layout_constrainedWidth="true" | |||||
| tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager" | |||||
| tools:itemCount="4" | |||||
| tools:listitem="@layout/item_spell_single_word" | |||||
| tools:orientation="horizontal"/> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> | </androidx.constraintlayout.widget.ConstraintLayout> |
| app:layout_constraintTop_toTopOf="parent" | app:layout_constraintTop_toTopOf="parent" | ||||
| android:visibility="gone"/> | android:visibility="gone"/> | ||||
| <androidx.appcompat.widget.AppCompatTextView | |||||
| android:id="@+id/tv_word" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/height_word" | |||||
| android:layout_marginTop="10dp" | |||||
| android:gravity="center" | |||||
| tools:text="gradual" | |||||
| android:textColor="@color/num0" | |||||
| android:textSize="@dimen/size_max_word" | |||||
| app:autoSizeMaxTextSize="@dimen/size_max_word" | |||||
| app:autoSizeMinTextSize="@dimen/miniSize" | |||||
| app:autoSizeStepGranularity="1dp" | |||||
| app:autoSizeTextType="uniform" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" /> | |||||
| <include | |||||
| android:id="@+id/inc_word" | |||||
| layout="@layout/inc_word" /> | |||||
| <androidx.constraintlayout.widget.Barrier | <androidx.constraintlayout.widget.Barrier | ||||
| android:id="@+id/barrier" | android:id="@+id/barrier" | ||||
| android:layout_width="wrap_content" | android:layout_width="wrap_content" | ||||
| android:layout_height="wrap_content" | android:layout_height="wrap_content" | ||||
| app:barrierDirection="bottom" | app:barrierDirection="bottom" | ||||
| app:constraint_referenced_ids="tv_word,iv_voice"/> | |||||
| app:constraint_referenced_ids="inc_word,iv_voice"/> | |||||
| <!--单词测试时间--> | <!--单词测试时间--> | ||||
| <ProgressBar | <ProgressBar |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto" | |||||
| xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:tools="http://schemas.android.com/tools" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| > | |||||
| <!-- tools:showIn="@layout/activity_learn_word"--> | |||||
| <TextView | |||||
| android:id="@+id/tv_explain" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginTop="14dp" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/bigSize" | |||||
| android:textStyle="bold" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:text="逐渐的;逐步的逐渐的;逐步的逐渐逐渐的;逐步的逐渐的;逐步的逐渐" /> | |||||
| <include | |||||
| android:id="@+id/inc_word" | |||||
| layout="@layout/inc_word" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/height_word" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_explain" /> | |||||
| <androidx.recyclerview.widget.RecyclerView | |||||
| android:id="@+id/spell_recyclerView" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_gravity="center" | |||||
| android:layout_marginStart="10dp" | |||||
| android:layout_marginTop="12dp" | |||||
| android:layout_marginEnd="10dp" | |||||
| android:background="@color/red_3" | |||||
| app:layout_constrainedWidth="true" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/inc_word" | |||||
| tools:itemCount="4" | |||||
| tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager" | |||||
| tools:listitem="@layout/item_spell_single_word" | |||||
| tools:orientation="horizontal" /> | |||||
| <!--操作失误tip--> | |||||
| <Button | |||||
| android:id="@+id/tv_control_error" | |||||
| style="@style/Widget.MaterialComponents.Button.UnelevatedButton" | |||||
| android:insetTop="0dp" | |||||
| android:insetBottom="0dp" | |||||
| app:cornerRadius="8dp" | |||||
| app:strokeWidth="@dimen/line_height" | |||||
| app:strokeColor="@color/theme_color" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="32dp" | |||||
| android:layout_marginTop="10dp" | |||||
| android:layout_marginEnd="30dp" | |||||
| android:backgroundTint="@color/white" | |||||
| android:gravity="center" | |||||
| android:text="操作失误,我不会" | |||||
| android:textColor="@color/theme_color" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| android:visibility="invisible" | |||||
| app:layout_constraintRight_toRightOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/spell_recyclerView" | |||||
| tools:visibility="visible" /> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> |
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:app="http://schemas.android.com/apk/res-auto" | xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
| xmlns:tools="http://schemas.android.com/tools" | xmlns:tools="http://schemas.android.com/tools" | ||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/title_bar_height" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:showIn="@layout/activity_learn_exam"> | tools:showIn="@layout/activity_learn_exam"> | ||||
| <ImageView | |||||
| android:id="@+id/img_back" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="match_parent" | |||||
| android:layout_centerVertical="true" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:scaleType="centerInside" | |||||
| android:src="@drawable/ic_back" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" /> | |||||
| <data> | |||||
| <import type="android.view.View"/> | |||||
| <variable | |||||
| name="showProgress" | |||||
| type="Boolean" /> | |||||
| </data> | |||||
| <TextView | |||||
| android:id="@+id/tv_title" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="66dp" | |||||
| android:ellipsize="end" | |||||
| android:maxLines="1" | |||||
| tools:text="小学英语单词课程小学英语单词课程小学英语单词课程" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/bigSize" | |||||
| android:textStyle="bold" | |||||
| app:layout_constrainedWidth="true" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toStartOf="@+id/tv_num_progress" | |||||
| app:layout_constraintHorizontal_chainStyle="packed" | |||||
| <androidx.constraintlayout.widget.ConstraintLayout | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/title_bar_height" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | app:layout_constraintStart_toStartOf="parent" | ||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| app:layout_goneMarginEnd="66dp" /> | |||||
| app:layout_constraintTop_toTopOf="parent"> | |||||
| <TextView | |||||
| android:id="@+id/tv_num_progress" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="2dp" | |||||
| android:layout_marginEnd="70dp" | |||||
| tools:text="90/100" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/normalSize" | |||||
| android:textStyle="bold" | |||||
| android:visibility="visible" | |||||
| app:layout_constrainedWidth="true" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toEndOf="@id/tv_title" | |||||
| app:layout_constraintTop_toTopOf="parent" /> | |||||
| <ImageView | |||||
| android:id="@+id/img_back" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="match_parent" | |||||
| android:layout_centerVertical="true" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:scaleType="centerInside" | |||||
| android:src="@drawable/ic_back" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_title" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="70dp" | |||||
| android:ellipsize="end" | |||||
| android:maxLines="1" | |||||
| tools:text="小学" | |||||
| android:gravity="center" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/bigSize" | |||||
| android:textStyle="bold" | |||||
| app:layout_constrainedWidth="true" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toStartOf="@+id/tv_num_progress" | |||||
| app:layout_constraintHorizontal_chainStyle="packed" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| app:layout_goneMarginEnd="70dp" /> | |||||
| <com.xkl.cdl.widget.VoiceSwitch | |||||
| android:id="@+id/voice_switch" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| android:visibility="visible" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" /> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||||
| <TextView | |||||
| android:id="@+id/tv_num_progress" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="2dp" | |||||
| android:layout_marginEnd="70dp" | |||||
| tools:text="90/100" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/normalSize" | |||||
| android:textStyle="bold" | |||||
| android:visibility="@{showProgress?View.VISIBLE:View.GONE}" | |||||
| app:layout_constrainedWidth="true" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toEndOf="@id/tv_title" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:visibility="gone"/> | |||||
| <com.xkl.cdl.widget.VoiceSwitch | |||||
| android:id="@+id/voice_switch" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:visibility="visible" | |||||
| android:layout_marginEnd="@dimen/global_spacing" | |||||
| app:layout_constraintBottom_toBottomOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" /> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||||
| </layout> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto" | |||||
| xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:tools="http://schemas.android.com/tools" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| > | |||||
| <!-- tools:showIn="@layout/activity_learn_word"--> | |||||
| <com.airbnb.lottie.LottieAnimationView | |||||
| android:id="@+id/iv_voice" | |||||
| android:layout_width="74dp" | |||||
| android:layout_height="74dp" | |||||
| app:lottie_fileName="data.json" | |||||
| app:lottie_loop="true" | |||||
| app:lottie_autoPlay="false" | |||||
| app:lottie_repeatMode="restart" | |||||
| app:layout_constraintLeft_toLeftOf="parent" | |||||
| app:layout_constraintRight_toRightOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| app:layout_constraintBottom_toBottomOf="parent" /> | |||||
| <!--图片--> | |||||
| <com.google.android.material.imageview.ShapeableImageView | |||||
| android:id="@+id/img_word" | |||||
| android:layout_width="75dp" | |||||
| android:layout_height="75dp" | |||||
| android:layout_marginTop="12dp" | |||||
| android:scaleType="centerInside" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| app:shapeAppearance="@style/roundedCornerStyle" | |||||
| tools:src="@mipmap/img_default" /> | |||||
| <include | |||||
| android:id="@+id/inc_word" | |||||
| layout="@layout/inc_word" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/height_word" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/img_word" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_phonetic" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginTop="10dp" | |||||
| android:gravity="center" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingRight="@dimen/global_spacing" | |||||
| android:textColor="@color/gray_2" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| app:layout_constraintTop_toBottomOf="@+id/inc_word" | |||||
| tools:text="英 [gud:bai] 美 [gud:bai] " /> | |||||
| <TextView | |||||
| android:id="@+id/tv_explain" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginTop="10dp" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/normalSize" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_phonetic" | |||||
| tools:text="基本释义" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_expand_explain" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginTop="8dp" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| android:textColor="@color/gray_2" | |||||
| android:textSize="@dimen/smallSize" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_explain" | |||||
| tools:text="扩展释义>" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_pattern" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| android:layout_marginTop="8dp" | |||||
| android:textColor="@color/gray_2" | |||||
| android:textSize="@dimen/smallSize" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_expand_explain" | |||||
| tools:text="句型>" /> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <androidx.appcompat.widget.AppCompatTextView 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:id="@+id/tv_word" | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="@dimen/height_word" | |||||
| android:layout_marginTop="10dp" | |||||
| android:gravity="center" | |||||
| android:paddingStart="10dp" | |||||
| android:paddingEnd="10dp" | |||||
| android:textColor="@color/num0" | |||||
| android:textSize="@dimen/size_max_word" | |||||
| app:autoSizeMaxTextSize="@dimen/size_max_word" | |||||
| app:autoSizeMinTextSize="@dimen/miniSize" | |||||
| app:autoSizeStepGranularity="1dp" | |||||
| app:autoSizeTextType="uniform" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" | |||||
| tools:showIn="@layout/inc_exam_word_choose_content" | |||||
| tools:text="gradual" /> |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||||
| android:id="@+id/scrollview" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="match_parent" | |||||
| xmlns:tools="http://schemas.android.com/tools"> | |||||
| <androidx.constraintlayout.widget.ConstraintLayout | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="match_parent"> | |||||
| <View | |||||
| android:id="@+id/v_space" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="@dimen/line_height" | |||||
| android:background="@color/gray_1" | |||||
| app:layout_constraintEnd_toEndOf="parent" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toTopOf="parent" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_phrase_flag" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="@dimen/global_spacing" | |||||
| android:layout_marginTop="8dp" | |||||
| android:drawablePadding="8dp" | |||||
| android:gravity="center_vertical" | |||||
| android:text="@string/phrase" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/normalSize" | |||||
| android:textStyle="bold" | |||||
| app:drawableStartCompat="@drawable/detail_phrase_flag" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/v_space"/> | |||||
| <com.suliang.common.widget.TagLinearLayout | |||||
| android:id="@+id/layout_phrase" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:paddingStart="@dimen/global_spacing" | |||||
| android:paddingEnd="@dimen/global_spacing" | |||||
| app:item_horizontal_space="20dp" | |||||
| app:item_vertical_space="2dp" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_phrase_flag"> | |||||
| </com.suliang.common.widget.TagLinearLayout> | |||||
| <TextView | |||||
| android:id="@+id/tv_example_flag" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="@dimen/global_spacing" | |||||
| android:layout_marginTop="8dp" | |||||
| android:drawablePadding="8dp" | |||||
| android:gravity="center_vertical" | |||||
| android:text="@string/example" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/normalSize" | |||||
| android:textStyle="bold" | |||||
| app:drawableStartCompat="@drawable/detail_example_flag" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/layout_phrase"/> | |||||
| <TextView | |||||
| android:id="@+id/tv_example" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="@dimen/global_spacing" | |||||
| android:layout_marginEnd="@dimen/global_spacing" | |||||
| android:gravity="center_vertical" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/smallSize" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_example_flag" | |||||
| tools:text="例句"/> | |||||
| <TextView | |||||
| android:id="@+id/tv_reference_flag" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="@dimen/global_spacing" | |||||
| android:layout_marginTop="8dp" | |||||
| android:drawablePadding="8dp" | |||||
| android:gravity="center_vertical" | |||||
| android:text="@string/reference" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/normalSize" | |||||
| android:textStyle="bold" | |||||
| app:drawableStartCompat="@drawable/detail_reference_flag" | |||||
| app:layout_constraintStart_toStartOf="parent" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_example"/> | |||||
| <TextView | |||||
| android:id="@+id/tv_reference" | |||||
| tools:text="参考内容" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginStart="@dimen/global_spacing" | |||||
| android:layout_marginEnd="@dimen/global_spacing" | |||||
| android:gravity="center_vertical" | |||||
| android:textColor="@color/main_text_color" | |||||
| android:textSize="@dimen/smallSize" | |||||
| app:layout_constraintTop_toBottomOf="@+id/tv_reference_flag"/> | |||||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||||
| </androidx.core.widget.NestedScrollView> |
| <?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"> | |||||
| <LinearLayout | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="match_parent" | |||||
| android:orientation="vertical" | |||||
| android:paddingTop="4dp" | |||||
| android:gravity="bottom"> | |||||
| <TextView | |||||
| android:id="@+id/tv_history" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="match_parent" | |||||
| android:gravity="center" | |||||
| android:paddingLeft="4dp" | |||||
| android:paddingRight="4dp" | |||||
| android:textSize="@dimen/smallerSize" | |||||
| android:maxLines="1" | |||||
| android:text="hello" | |||||
| tools:background="@drawable/shape_rounder_toplr_8_white"/> | |||||
| </LinearLayout> | |||||
| </layout> |
| <color name="red_3">#FF0000</color> | <color name="red_3">#FF0000</color> | ||||
| <color name="control_left">#687A69</color> | |||||
| <color name="control_center">#85766D</color> | |||||
| <color name="control_right">#666D80</color> | |||||
| <color name="num0">#E50213</color> | <color name="num0">#E50213</color> | ||||
| <color name="num1">#E50251</color> | <color name="num1">#E50251</color> |
| <string name="continue_learn">继续学习</string> | <string name="continue_learn">继续学习</string> | ||||
| <string name="test_again">再测一次</string> | <string name="test_again">再测一次</string> | ||||
| <string name="test_type_after_total">学后总测试</string> | <string name="test_type_after_total">学后总测试</string> | ||||
| <string name="title_activity_learn_base">LearnBaseActivity</string> | |||||
| <!-- Strings used for fragments for navigation --> | |||||
| <string name="first_fragment_label">First Fragment</string> | |||||
| <string name="second_fragment_label">Second Fragment</string> | |||||
| <string name="next">Next</string> | |||||
| <string name="previous">Previous</string> | |||||
| <string name="hello_first_fragment">Hello first fragment</string> | <string name="hello_first_fragment">Hello first fragment</string> | ||||
| <string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string> | <string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string> | ||||
| <string name="lesson_relearn_content">但不会清空测试与备忘本数据</string> | <string name="lesson_relearn_content">但不会清空测试与备忘本数据</string> | ||||
| <string name="sure">确定</string> | <string name="sure">确定</string> | ||||
| <string name="control_answer">答案</string> | |||||
| <string name="control_correct">正确</string> | |||||
| <string name="control_error">错误/不认识</string> | |||||
| <string name="control_reRead">重读</string> | |||||
| <string name="control_next">下一条</string> | |||||
| <string name="phrase">词组</string> | |||||
| <string name="example">例句</string> | |||||
| <string name="reference">参考</string> | |||||
| <string name="auto_playing">自动播放中···</string> | |||||
| </resources> | </resources> |
| <item name="android:layout_marginTop">66dp</item> | <item name="android:layout_marginTop">66dp</item> | ||||
| <item name="android:backgroundTint">@color/theme_color</item> | <item name="android:backgroundTint">@color/theme_color</item> | ||||
| </style> | </style> | ||||
| <!--只有button的属性,没有布局的属性--> | |||||
| <style name="common_button" parent="Widget.MaterialComponents.Button.UnelevatedButton"> | |||||
| <item name="android:layout_width">0dp</item> | |||||
| <item name="android:layout_height">@dimen/common_button_height</item> | |||||
| <item name="android:gravity">center</item> | |||||
| <item name="android:insetTop">0dp</item> | |||||
| <item name="android:insetBottom">0dp</item> | |||||
| <item name="android:textSize">@dimen/smallSize</item> | |||||
| <item name="android:textColor">@color/white</item> | |||||
| <item name="android:backgroundTint">@color/theme_color</item> | |||||
| </style> | |||||
| <!--dialog style --> | <!--dialog style --> | ||||
| <style name="dialog_style" parent="@android:style/Theme.Dialog"> | <style name="dialog_style" parent="@android:style/Theme.Dialog"> |
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:width="16dp" | |||||
| android:height="16dp" | |||||
| android:viewportWidth="16" | |||||
| android:viewportHeight="16"> | |||||
| <path | |||||
| android:pathData="M12.5708,8.8575L5.5145,13.0913C5.0409,13.3755 4.4267,13.2219 4.1425,12.7483C4.0493,12.5929 4,12.4151 4,12.2338L4,3.7662C4,3.2139 4.4477,2.7662 5,2.7662C5.1812,2.7662 5.3591,2.8154 5.5145,2.9087L12.5708,7.1425C13.0444,7.4267 13.198,8.0409 12.9138,8.5145C12.8294,8.6552 12.7116,8.773 12.5708,8.8575Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| </vector> |
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:width="16dp" | |||||
| android:height="16dp" | |||||
| android:viewportWidth="16" | |||||
| android:viewportHeight="16"> | |||||
| <path | |||||
| android:pathData="M5,3C5.5523,3 6,3.4477 6,4L6,12C6,12.5523 5.5523,13 5,13C4.4477,13 4,12.5523 4,12L4,4C4,3.4477 4.4477,3 5,3ZM12,3C12.5523,3 13,3.4477 13,4L13,12C13,12.5523 12.5523,13 12,13C11.4477,13 11,12.5523 11,12L11,4C11,3.4477 11.4477,3 12,3Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| </vector> |
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:width="16dp" | |||||
| android:height="16dp" | |||||
| android:viewportWidth="16" | |||||
| android:viewportHeight="16"> | |||||
| <path | |||||
| android:pathData="M-283,-755h375v812h-375z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#FFFFFF" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| <path | |||||
| android:pathData="M4,3L12,3C12.5523,3 13,3.4477 13,4L13,12C13,12.5523 12.5523,13 12,13L4,13C3.4477,13 3,12.5523 3,12L3,4C3,3.4477 3.4477,3 4,3Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| <path | |||||
| android:pathData="M-4,-3L72,-3A4,4 0,0 1,76 1L76,15A4,4 0,0 1,72 19L-4,19A4,4 0,0 1,-8 15L-8,1A4,4 0,0 1,-4 -3z" | |||||
| android:strokeLineJoin="round" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#00000000" | |||||
| android:strokeColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeLineCap="round"/> | |||||
| <path | |||||
| android:pathData="M-263,-3L-187,-3A4,4 0,0 1,-183 1L-183,15A4,4 0,0 1,-187 19L-263,19A4,4 0,0 1,-267 15L-267,1A4,4 0,0 1,-263 -3z" | |||||
| android:strokeLineJoin="round" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#00000000" | |||||
| android:strokeColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeLineCap="round"/> | |||||
| <path | |||||
| android:pathData="M-246.4292,8.8575L-253.4855,13.0913C-253.9591,13.3755 -254.5733,13.2219 -254.8575,12.7483C-254.9507,12.5929 -255,12.4151 -255,12.2338L-255,3.7662C-255,3.2139 -254.5523,2.7662 -254,2.7662C-253.8188,2.7662 -253.6409,2.8154 -253.4855,2.9087L-246.4292,7.1425C-245.9556,7.4267 -245.802,8.0409 -246.0862,8.5145C-246.1706,8.6552 -246.2884,8.773 -246.4292,8.8575Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| <path | |||||
| android:pathData="M4,3L12,3C12.5523,3 13,3.4477 13,4L13,12C13,12.5523 12.5523,13 12,13L4,13C3.4477,13 3,12.5523 3,12L3,4C3,3.4477 3.4477,3 4,3Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| <path | |||||
| android:pathData="M-4,-3L72,-3A4,4 0,0 1,76 1L76,15A4,4 0,0 1,72 19L-4,19A4,4 0,0 1,-8 15L-8,1A4,4 0,0 1,-4 -3z" | |||||
| android:strokeLineJoin="round" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#00000000" | |||||
| android:strokeColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeLineCap="round"/> | |||||
| <path | |||||
| android:pathData="M4,3L12,3C12.5523,3 13,3.4477 13,4L13,12C13,12.5523 12.5523,13 12,13L4,13C3.4477,13 3,12.5523 3,12L3,4C3,3.4477 3.4477,3 4,3Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| <path | |||||
| android:pathData="M4,3L12,3C12.5523,3 13,3.4477 13,4L13,12C13,12.5523 12.5523,13 12,13L4,13C3.4477,13 3,12.5523 3,12L3,4C3,3.4477 3.4477,3 4,3Z" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeColor="#00000000"/> | |||||
| <path | |||||
| android:pathData="M-4,-3L72,-3A4,4 0,0 1,76 1L76,15A4,4 0,0 1,72 19L-4,19A4,4 0,0 1,-8 15L-8,1A4,4 0,0 1,-4 -3z" | |||||
| android:strokeLineJoin="round" | |||||
| android:strokeWidth="1" | |||||
| android:fillColor="#00000000" | |||||
| android:strokeColor="#5082E6" | |||||
| android:fillType="evenOdd" | |||||
| android:strokeLineCap="round"/> | |||||
| </vector> |
| min_sdk_version : 21, | min_sdk_version : 21, | ||||
| target_sdk_version : 31, | target_sdk_version : 31, | ||||
| version_code : 100, | version_code : 100, | ||||
| version_name : "100", | |||||
| version_name : "1.0.0", | |||||
| applicationId : "com.xkl.cdl" | applicationId : "com.xkl.cdl" | ||||
| ] | ] | ||||
| versions = [ | versions = [ |
| notifyDataSetChanged() | notifyDataSetChanged() | ||||
| } | } | ||||
| /** | /** | ||||
| * 添加单条数据 | * 添加单条数据 | ||||
| * @param data T? | * @param data T? | ||||
| * @param position Int? | * @param position Int? | ||||
| */ | */ | ||||
| fun addData(data: T?, position: Int?) { | |||||
| open fun addData(data: T?, position: Int?) { | |||||
| data?.let { | data?.let { | ||||
| val size = mData.size | val size = mData.size | ||||
| val startPosition = position?.let { it -> | val startPosition = position?.let { it -> | ||||
| } | } | ||||
| } | } | ||||
| fun removeData(position:Int){ | |||||
| if(mData.size >= position){ | |||||
| mData.removeAt(position) | |||||
| notifyItemRangeChanged(position,mData.size-position) | |||||
| } | |||||
| } | |||||
| package com.suliang.common.util | |||||
| /** | |||||
| * author suliang | |||||
| * create 2022/4/25 16:29 | |||||
| * Describe: | |||||
| */ | |||||
| class ColorUtil { | |||||
| companion object{ | |||||
| /** | |||||
| * 把一个int颜色值转换成String | |||||
| * @param color | |||||
| * @return | |||||
| */ | |||||
| fun getHexWebString(color : Int) : String { | |||||
| var s = "#" | |||||
| val colorStr = color and -0x1000000 or (color and 0x00ff0000) or (color and 0x0000ff00) or (color and 0x000000ff) | |||||
| var s1 = Integer.toHexString(colorStr) | |||||
| s1 = s1.substring(2, s1.length) | |||||
| s = s + s1 | |||||
| return s | |||||
| } | |||||
| } | |||||
| } |
| package com.suliang.common.util | package com.suliang.common.util | ||||
| import android.text.Html | |||||
| import android.text.Spanned | |||||
| import androidx.annotation.ColorInt | |||||
| import java.util.regex.Pattern | |||||
| /** | /** | ||||
| * author suliang | |||||
| * create 2022/4/11 17:16 | |||||
| * Describe: | |||||
| */class StringUtil { | |||||
| * author suliang | |||||
| * create 2022/4/11 17:16 | |||||
| * Describe: | |||||
| */ | |||||
| class StringUtil { | |||||
| companion object { | |||||
| /** | |||||
| * 识字课程认读显示转换: (你)好,括号中的字变大,并设置颜色 | |||||
| * @param word 处理的文字 | |||||
| * @param colorId 处理的颜色id color.xml | |||||
| * @return | |||||
| */ | |||||
| fun literacyToHtmlWord(word : String, @ColorInt colorId : Int) : Spanned? { | |||||
| val m = Pattern.compile("\\([\u4e00-\u9fa5]\\)").matcher(word) | |||||
| val builder = StringBuffer() | |||||
| val color : String = ColorUtil.getHexWebString(colorId) | |||||
| var isFind = false | |||||
| var previousEndPosition = 0 //下次循环开始的位置 | |||||
| while (m.find()) { | |||||
| isFind = true | |||||
| val startPosition = m.start() //左括号的下一位置 | |||||
| val endPosition = m.end() //右括号位置 | |||||
| if (startPosition - previousEndPosition > 0) { | |||||
| builder.append("<font color=\"#FF323233\">").append(word.substring(previousEndPosition, startPosition)) | |||||
| .append("</font>") | |||||
| } | |||||
| //直接匹配添加 | |||||
| builder.append("<font color= \"").append(color).append("\"><big><big>") | |||||
| .append(word.substring(startPosition + 1, endPosition - 1)).append("</big></big></font>") | |||||
| previousEndPosition = endPosition | |||||
| } | |||||
| if (isFind) { | |||||
| if (previousEndPosition != word.length) { | |||||
| builder.append("<font color=\"#FF323233\">").append(word.substring(previousEndPosition)).append("</font>") | |||||
| } | |||||
| } else { | |||||
| builder.append("<font color=\"").append(color).append("\">").append(word).append("</font>") | |||||
| } | |||||
| println(builder.toString()) | |||||
| return Html.fromHtml(builder.toString()) | |||||
| } | |||||
| /** | |||||
| * 识字课程,获取括号包裹的中文内容 (你)好 --> 你 | |||||
| * @param word 识字的词语 | |||||
| * @return 英文括号中的内容 | |||||
| */ | |||||
| fun literacyGetWord(word : String) : String { | |||||
| val m = Pattern.compile("\\([\u4e00-\u9fa5]\\)").matcher(word) | |||||
| return if (m.find()) { | |||||
| word.substring(m.start() + 1, m.end() - 1) | |||||
| } else word | |||||
| } | |||||
| /** 识字课程,备忘本使用,去掉括号包裹内容的中文括号 */ | |||||
| fun literacyGetMemoWord(word : String) : String { | |||||
| val m = Pattern.compile("\\([\u4e00-\u9fa5]\\)").matcher(word) | |||||
| val buffer = StringBuffer() | |||||
| while (m.find()) { | |||||
| val startPosition = m.start() //左括号的下一位置 | |||||
| val endPosition = m.end() //右括号位置 | |||||
| m.appendReplacement(buffer, word.substring(startPosition + 1, endPosition - 1)) | |||||
| } | |||||
| m.appendTail(buffer) | |||||
| return buffer.toString() | |||||
| } | |||||
| } | |||||
| } | } |
| import android.content.res.Resources | import android.content.res.Resources | ||||
| import android.os.Build | import android.os.Build | ||||
| import android.util.DisplayMetrics | import android.util.DisplayMetrics | ||||
| import android.util.TypedValue | |||||
| import android.view.ViewConfiguration | import android.view.ViewConfiguration | ||||
| import android.view.WindowManager | import android.view.WindowManager | ||||
| import com.suliang.common.util.AppGlobals | import com.suliang.common.util.AppGlobals | ||||
| /** | /** | ||||
| * dp2px | * dp2px | ||||
| */ | */ | ||||
| fun dp2px(dpValue: Float): Int { | |||||
| val scale = AppGlobals.application.resources.displayMetrics.density | |||||
| return (dpValue * scale + 0.5f).toInt() | |||||
| fun dp2px(dpValue: Float): Float { | |||||
| // 参数1:输入的value类型 | |||||
| // 参数2:要转换的数值 | |||||
| // 参数3:Resources.getSystem().displayMetrics(很多人用的是Context.displayMetrics,需要把context传递过来,这里可以用Resources.getSystem方法会更好) | |||||
| return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpValue,Resources.getSystem().displayMetrics) | |||||
| // val scale = AppGlobals.application.resources.displayMetrics.density | |||||
| // return (dpValue * scale + 0.5f).toInt() | |||||
| } | } | ||||
| /** | /** |
| package com.suliang.common.widget; | |||||
| import android.content.Context; | |||||
| import android.content.res.TypedArray; | |||||
| import android.util.AttributeSet; | |||||
| import android.view.View; | |||||
| import android.widget.LinearLayout; | |||||
| import androidx.annotation.Nullable; | |||||
| import androidx.constraintlayout.widget.ConstraintLayout; | |||||
| import com.suliang.common.R; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * author suliang | |||||
| * create 2021/12/31 12:45 | |||||
| * Describe: 自适应的流式布局 ,子控件一行放不下的情况下,需要换行为两行,子控件不需要设置margin | |||||
| * 使用方式: 定义布局,width = “match_parent" or 固定值 ,height = "wrap_content" 可设置左右的内间距 | |||||
| * 子控件: 不设置margin,width 和 height 均使用wrap_content | |||||
| * 每一行的第一个控件与父控件没有间隔,即不添加 itemHorizontalSpace。当前行的第二个控件开始与前一个控件间隔 itemHorizontalSpace | |||||
| * 第一行的第一个控件与父控件没有间隔,即不添加 itemVerticalSpace ,第二行后与前一项的才添加itemVerticalSpace | |||||
| * 整体布局相当于: | |||||
| * leftPadding View1 itemHorizontalSpace View2 rightPadding | |||||
| * itemVerticalSpace | |||||
| * leftPadding View3 itemHorizontalSpace View4 rightPadding | |||||
| */ | |||||
| public class TagLinearLayout extends LinearLayout { | |||||
| private final int itemVerticalSpace; //垂直方向的控件间距, 默认12px | |||||
| private final int itemHorizontalSpace; //横置方向的控件间距,默认12px | |||||
| private final List<List<View>> allView = new ArrayList<>() ; //所有控件每一行的集合和一行中有几个View的集合 | |||||
| private final List<Integer> lineHeights = new ArrayList<>() ; //每一行的最高控件的高度 | |||||
| private boolean needLayout = true ; | |||||
| public TagLinearLayout(Context context) { | |||||
| this(context,null); | |||||
| } | |||||
| public TagLinearLayout(Context context, @Nullable AttributeSet attrs) { | |||||
| this(context, attrs,0); | |||||
| } | |||||
| public TagLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | |||||
| super(context, attrs, defStyleAttr); | |||||
| TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TagLinearLayout); | |||||
| itemVerticalSpace = (int) typedArray.getDimension(R.styleable.TagLinearLayout_item_vertical_space, 12); | |||||
| itemHorizontalSpace = (int) typedArray.getDimension(R.styleable.TagLinearLayout_item_horizontal_space, 12); | |||||
| typedArray.recycle(); | |||||
| } | |||||
| @Override | |||||
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |||||
| allView.clear(); | |||||
| lineHeights.clear(); | |||||
| //控件宽度 | |||||
| int width = MeasureSpec.getSize(widthMeasureSpec); | |||||
| measureChildren(widthMeasureSpec,heightMeasureSpec); | |||||
| //父控件横向和垂直的间距 | |||||
| int horizontalPadding = getPaddingLeft() + getPaddingRight(); | |||||
| int verticalPadding = getPaddingTop() + getPaddingBottom(); | |||||
| //用于计算的父控件宽度 | |||||
| int widthNotHavePadding = width - horizontalPadding; | |||||
| int childWidth ; //当前行 宽度 | |||||
| int childHeight ; //当前行 高度 | |||||
| int lineTotalWidth = 0 ; //当前行 总宽度 | |||||
| int lineMaxHeight = 0 ; //当前行 最大高度 | |||||
| int totalHeight = 0 ; //记录总高度 | |||||
| int line = 0 ; //总行数 | |||||
| int childCount = getChildCount(); | |||||
| //一行的内容 | |||||
| List<View> lineViews = null ; | |||||
| for (int i = 0 ; i < childCount ; i++){ | |||||
| View child = getChildAt(i); | |||||
| //子控件的宽 = 自生宽 ,子控件布局为 wrap_content | |||||
| childWidth = child.getMeasuredWidth(); | |||||
| childHeight = child.getMeasuredHeight(); | |||||
| if (lineViews != null && lineViews.size() != 0 && lineTotalWidth + childWidth + itemHorizontalSpace <= widthNotHavePadding){ //非当前行的第一个控件 | |||||
| lineMaxHeight = Math.max(lineMaxHeight,childHeight); | |||||
| lineTotalWidth += (childWidth+itemHorizontalSpace); | |||||
| }else{ //当前行的第一个控件,即新行开始 | |||||
| //行数加1 | |||||
| line++; | |||||
| //累加总高 | |||||
| //1、只有一行,则会在最后一个item进行累加 | |||||
| //2、有多行,则在下一行开始时累加上一行的高,最后一行在最后一个的时候进行累加 | |||||
| if ( line > 1 ){ | |||||
| totalHeight += lineMaxHeight ; | |||||
| lineHeights.add(lineMaxHeight); | |||||
| } | |||||
| //当前行基础值 | |||||
| lineTotalWidth = childWidth ; | |||||
| lineMaxHeight = childHeight ; | |||||
| //赋值处理 | |||||
| lineViews = new ArrayList<>(); //赋值新行集合 | |||||
| allView.add(lineViews); //添加到总集合 | |||||
| } | |||||
| //最后一行,添加总高 | |||||
| if (i == childCount -1){ | |||||
| totalHeight += lineMaxHeight ; | |||||
| lineHeights.add(lineMaxHeight); | |||||
| } | |||||
| //添加到行集合 | |||||
| lineViews.add(child); | |||||
| } | |||||
| //总高 = 高度 + 内间距 + 行间距 | |||||
| //控件高度 | |||||
| int height = totalHeight + verticalPadding; | |||||
| if (line > 1){ | |||||
| height = height + (line -1) * itemVerticalSpace ; | |||||
| } | |||||
| //如果设置了最小高度,则判断设置 | |||||
| if (this.getVisibility() != View.GONE ){ | |||||
| if (getParent() instanceof ConstraintLayout){ | |||||
| ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) getLayoutParams(); | |||||
| height = Math.max(height,params.matchConstraintMinHeight); | |||||
| }else{ | |||||
| height = Math.max(height,getMinimumHeight()); | |||||
| } | |||||
| } | |||||
| //设值 | |||||
| setMeasuredDimension(width,Math.max(height,getMinimumHeight())); | |||||
| } | |||||
| @Override | |||||
| protected void onLayout(boolean changed, int l, int t, int r, int b) { | |||||
| // super.onLayout(changed, l, t, r, b); | |||||
| if (needLayout || changed){ | |||||
| needLayout = false ; | |||||
| } | |||||
| int top = getPaddingTop(); | |||||
| int left = getPaddingLeft(); | |||||
| int right ; | |||||
| int bottom ; | |||||
| //循环行 | |||||
| for (int i = 0 , lines = allView.size(); i < lines ; i++){ | |||||
| //当前行 | |||||
| List<View> lineViews = allView.get(i); | |||||
| int currentLineMaxHeight = lineHeights.get(i); | |||||
| for (int j = 0 , count = lineViews.size() ; j < count ; j++ ){ | |||||
| View view = lineViews.get(j); | |||||
| int measuredWidth = view.getMeasuredWidth(); | |||||
| int measuredHeight = view.getMeasuredHeight(); | |||||
| int currentTop = (currentLineMaxHeight - measuredHeight)/2 + top ; | |||||
| bottom = currentTop + measuredHeight; | |||||
| right = left + measuredWidth; | |||||
| view.layout(left,currentTop,right,bottom); | |||||
| //重置left | |||||
| left = right + itemHorizontalSpace; | |||||
| } | |||||
| //新行 | |||||
| top = top + lineHeights.get(i) + itemVerticalSpace; | |||||
| left = getPaddingLeft(); | |||||
| } | |||||
| } | |||||
| } |
| context.obtainStyledAttributes(attrs, R.styleable.TitleBar).run { | context.obtainStyledAttributes(attrs, R.styleable.TitleBar).run { | ||||
| val barBackground = getColor(R.styleable.TitleBar_barBackground, 0) | val barBackground = getColor(R.styleable.TitleBar_barBackground, 0) | ||||
| val titleTextValue = getString(R.styleable.TitleBar_titleTextValue) | val titleTextValue = getString(R.styleable.TitleBar_titleTextValue) | ||||
| val titleTextSize = getDimension(R.styleable.TitleBar_titleTextSize, ScreenUtil.dp2px(18f).toFloat()) | |||||
| val titleTextSize = getDimension(R.styleable.TitleBar_titleTextSize, ScreenUtil.dp2px(18f)) | |||||
| val titleTextColor = getColor(R.styleable.TitleBar_titleTextColor, Color.parseColor("#323232")) | val titleTextColor = getColor(R.styleable.TitleBar_titleTextColor, Color.parseColor("#323232")) | ||||
| val rightTextValue = getString(R.styleable.TitleBar_rightTextValue) | val rightTextValue = getString(R.styleable.TitleBar_rightTextValue) | ||||
| val rightTextColor = getColor(R.styleable.TitleBar_rightTextColor, 0) | val rightTextColor = getColor(R.styleable.TitleBar_rightTextColor, 0) |
| <attr name="backTextColor" format="color"/> <!--左侧大小--> | <attr name="backTextColor" format="color"/> <!--左侧大小--> | ||||
| <attr name="backSrc" format="reference|integer"/> <!--左侧图片--> | <attr name="backSrc" format="reference|integer"/> <!--左侧图片--> | ||||
| <attr name="drawPadding" format="dimension"/> <!--图片drawPadding--> | <attr name="drawPadding" format="dimension"/> <!--图片drawPadding--> | ||||
| </declare-styleable> | |||||
| <!--自定义标签容器--> | |||||
| <declare-styleable name="TagLinearLayout"> | |||||
| <attr name="item_vertical_space" format="dimension"/> <!--垂直方向的控件间距--> | |||||
| <attr name="item_horizontal_space" format="dimension"/> <!--横向方向的控件间距--> | |||||
| </declare-styleable> | </declare-styleable> | ||||
| </resources> | </resources> |