<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> |