| @@ -68,6 +68,7 @@ | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_course_main.xml" value="0.33" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_course_statistics_detail.xml" value="0.33" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_course_test.xml" value="0.31567028985507245" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_dictionary.xml" value="0.5" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_exam_learn_spell.xml" value="0.47690217391304346" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_base.xml" value="0.4979166666666667" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/activity_learn_creading.xml" value="0.4304093567251462" /> | |||
| @@ -95,6 +96,7 @@ | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/dialog_common.xml" value="0.30978260869565216" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/dialog_item_select_repeat.xml" value="0.4979166666666667" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/dialog_lesson_learn.xml" value="0.4144927536231884" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/dialog_vocabulary.xml" value="0.370018115942029" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_lesson.xml" value="0.33" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_main.xml" value="0.4979166666666667" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/fragment_course_pack.xml" value="0.34427083333333336" /> | |||
| @@ -127,6 +129,12 @@ | |||
| <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_checkbox.xml" value="1.0" /> | |||
| <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_dic_detail_chinese.xml" value="0.33242753623188404" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_dic_detail_chinese_for_english_item.xml" value="0.75" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_dic_detail_english.xml" value="0.5" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_dic_history_chinese.xml" value="0.23632218844984804" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_dic_history_english.xml" value="0.23632218844984804" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_dic_search.xml" value="0.75" /> | |||
| <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_memo.xml" value="0.67" /> | |||
| @@ -136,7 +144,7 @@ | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_memo_detail_list_for_literacy.xml" value="0.67" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_memo_detail_list_literacy.xml" value="0.5" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_memo_detail_list_pinyin.xml" value="0.67" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_service_center_item.xml" value="0.717816683831102" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_service_center_item.xml" value="0.6604774535809018" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_spell_single_word.xml" value="0.23632218844984804" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_statics_for_time.xml" value="0.27717391304347827" /> | |||
| <entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/item_statistics_bottom_course_change.xml" value="0.30842391304347827" /> | |||
| @@ -12,6 +12,9 @@ | |||
| android:supportsRtl="true" | |||
| android:theme="@style/Theme.XklLocal" | |||
| tools:ignore="LockedOrientationActivity"> | |||
| <activity | |||
| android:name=".module.m_service_center.DictionaryActivity" | |||
| android:exported="true" /> | |||
| <activity | |||
| android:name=".module.m_service_center.TestVocabularyActivity" | |||
| android:exported="true" /> | |||
| @@ -33,7 +33,7 @@ class AdapterTestScore(vm : TestScoreViewModel) : BaseRVAdapterVM<Exam,TestScore | |||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
| return BaseAdapterViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_test_score,parent,false)) | |||
| return BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_test_score)) | |||
| } | |||
| @SuppressLint("SetTextI18n") | |||
| @@ -0,0 +1,83 @@ | |||
| package com.xkl.cdl.adapter | |||
| import android.view.ViewGroup | |||
| import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||
| import com.suliang.common.base.adapter.BaseRVAdapterVM | |||
| import com.suliang.common.databinding.ItemEmptyBinding | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.bean.DictionaryItem | |||
| import com.xkl.cdl.module.m_service_center.DictionaryViewModel | |||
| /** | |||
| * author suliang | |||
| * create 2022/8/4 10:41 | |||
| * Describe: | |||
| */ | |||
| class DictionaryAdapter(vm:DictionaryViewModel) : BaseRVAdapterVM<DictionaryItem,DictionaryViewModel>(vm) { | |||
| override fun getItemCount() : Int { | |||
| //历史记录,不显示空状态 | |||
| return if (vm.detailState == 1) { | |||
| //大于页数显示的总数量,则显示指定数量,否则显示全部 | |||
| val showCount = vm.histroyShowPage * vm.historyPageSize | |||
| if (getData().size >= showCount){ | |||
| showCount | |||
| }else{ | |||
| getData().size | |||
| } | |||
| }else if (needShowEmptyView && getData().isEmpty()) 1 else getData().size | |||
| } | |||
| override fun onBindEmptyViewHolder(holder : BaseAdapterViewHolder) { | |||
| (holder.binding as ItemEmptyBinding).run { | |||
| //根据监听,显示具体的内容 | |||
| vm.etSearchLiveData.value?.let { | |||
| if (it.isNotEmpty()) { | |||
| imgEmpty.setImageResource(R.mipmap.empty_nothing_search) | |||
| tvContent.text = "没有搜索到任何内容" | |||
| } else { | |||
| imgEmpty.setImageResource(R.mipmap.empty_nothing) | |||
| tvContent.text = "没有数据" | |||
| } | |||
| } | |||
| } | |||
| } | |||
| override fun getItemViewType(position : Int) : Int { | |||
| return when{ | |||
| enableEmptyPosition(position) -> TYPE_EMPTY | |||
| //单条详情 | |||
| vm.searchItemDetailAble -> { | |||
| val item = getItem(position) | |||
| item.form //1英文 2 中文 | |||
| } | |||
| //多条记录 | |||
| else -> 3 | |||
| } | |||
| } | |||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
| return when(viewType){ | |||
| 1 -> BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_dic_detail_english)) //英文搜索结果详情 | |||
| 2 -> BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_dic_detail_chinese)) //中文搜索结果详情 | |||
| else -> BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_dic_search)) //记录列表 历史和搜索记录列表 | |||
| } | |||
| } | |||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
| val itemViewType = getItemViewType(position) | |||
| when(itemViewType){ | |||
| 1 -> { | |||
| } | |||
| 2 -> { | |||
| } | |||
| 3 -> { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| package com.xkl.cdl.data.bean | |||
| /** | |||
| * author suliang | |||
| * create 2022/8/4 10:47 | |||
| * Describe: | |||
| */ | |||
| open class DictionaryBean() { | |||
| var id : Long = 0 | |||
| var word : String? = null | |||
| var form : Int = 0 // 1 英文 2中文 | |||
| var pre_index : String? = null //前缀 | |||
| var phonetic_uk : String? = null | |||
| var phonectic_us : String? = null | |||
| var phonectic_cn : String? = null | |||
| var basic_explaination : String? = null | |||
| var all_explaination : String? = null | |||
| var phrase : String? = null | |||
| var example : String? = null | |||
| var reference : String? = null | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package com.xkl.cdl.data.bean | |||
| import androidx.room.Entity | |||
| import androidx.room.Ignore | |||
| import androidx.room.PrimaryKey | |||
| /** | |||
| * author suliang | |||
| * create 2022/8/4 10:42 | |||
| * Describe: | |||
| */ | |||
| @Entity(tableName = "dic_history",primaryKeys = ["word"]) | |||
| class DictionaryItem : DictionaryBean() { | |||
| //课程查询时的课程id | |||
| var courseId : Long = 0 | |||
| //查询时间 | |||
| var queryTime: Long = 0 | |||
| //词典联想结果判断其是否在历史记录中,如果在历史记录中,则进行时间重排序 | |||
| @Ignore | |||
| var inHistory : Boolean = false | |||
| } | |||
| @@ -5,4 +5,4 @@ package com.xkl.cdl.data.bean | |||
| * create 2022/7/13 10:48 | |||
| * Describe: | |||
| */ | |||
| class VocabularyItem(val icon: Int, val level: String,val level_e: String) | |||
| class VocabularyItem(val icon: Int, val level: String,val level_e: String,val stage : Int,val size:Int) | |||
| @@ -1,5 +1,6 @@ | |||
| package com.xkl.cdl.data.bean.intentdata | |||
| import com.xkl.cdl.data.bean.VocabularyItem | |||
| import com.xkl.cdl.data.bean.course.ExamBean | |||
| import com.xkl.cdl.data.bean.course.Lesson | |||
| @@ -16,6 +17,7 @@ import com.xkl.cdl.data.bean.course.Lesson | |||
| * | |||
| * 课时学后测: coursePackType, courseType, testData ,lesson. | |||
| * | |||
| * 词汇量测试: testData, vocabularyItem | |||
| */ | |||
| data class ExamData( | |||
| val subjectId : Int, //项目类型 | |||
| @@ -36,5 +38,8 @@ data class ExamData( | |||
| // 课程/课时学前测试正确错误列表 key=> {chapter_id}_{lesson_id}_{entity_id} value=>正确 true;错误 false | |||
| var mExamWRMap : HashMap<String, Boolean>? = null | |||
| //测词汇量item | |||
| var vocabularyItem : VocabularyItem? = null | |||
| } | |||
| @@ -18,6 +18,7 @@ abstract class AppDatabase : RoomDatabase() { | |||
| abstract fun examDao() : ExamDao | |||
| abstract fun examItemDao(): ExamItemDao | |||
| abstract fun examMiddleDao(): ExamMiddleDao | |||
| abstract fun dictionaryDao(): DictionaryDao | |||
| companion object{ | |||
| private var DATABASE_NAME :String = "${FileUtil.getSaveDirPath("db")}${File.separator}app.db" | |||
| @@ -0,0 +1,22 @@ | |||
| package com.xkl.cdl.data.exam_record | |||
| import androidx.room.Dao | |||
| import androidx.room.Insert | |||
| import androidx.room.OnConflictStrategy | |||
| import androidx.room.Query | |||
| import com.xkl.cdl.data.bean.DictionaryItem | |||
| @Dao | |||
| interface DictionaryDao { | |||
| @Insert(onConflict = OnConflictStrategy.REPLACE) | |||
| fun insert(item:DictionaryItem) | |||
| @Query("SELECT * FROM dic_history WHERE courseId = :courseId ORDER BY queryTime DESC LIMIT 30") | |||
| fun query(courseId : Long): MutableList<DictionaryItem> | |||
| @Query("DELETE FROM dic_history WHERE courseId = :courseId") | |||
| fun clear(courseId: Long) | |||
| } | |||
| @@ -0,0 +1,224 @@ | |||
| package com.xkl.cdl.data.manager.db | |||
| import android.widget.Toast | |||
| import com.google.common.base.Joiner | |||
| import com.suliang.common.util.AppGlobals | |||
| import com.suliang.common.util.LogUtil | |||
| import com.suliang.common.util.thread.AppExecutors | |||
| import com.xkl.cdl.data.bean.DictionaryItem | |||
| import com.xkl.cdl.data.manager.FilePathManager | |||
| import net.sqlcipher.database.SQLiteDatabase | |||
| import java.lang.StringBuilder | |||
| import java.util.* | |||
| /** | |||
| * author suliang | |||
| * create 2022/7/14 17:28 | |||
| * Describe: | |||
| */ | |||
| object DictionaryManager { | |||
| private var mDataBase : SQLiteDatabase? = null | |||
| private var pwd : String = "XKL_XECD_DICT_DATA_KEY" | |||
| private const val DATA_DICT = "dict" | |||
| private const val MAX_COUNT = 50 | |||
| private fun open() { | |||
| synchronized(DictionaryManager) { | |||
| if (mDataBase == null) { | |||
| mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getDictionaryDbPath().path, pwd, null, SQLiteDatabase.OPEN_READONLY) | |||
| } | |||
| if (mDataBase == null) { | |||
| AppExecutors.mainThread.run { | |||
| Toast.makeText(AppGlobals.application, "词典数据获取失败,请重启应用或联系业务员", Toast.LENGTH_LONG) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //避免进行多次的查询 | |||
| private var count: Int = 0 | |||
| /** | |||
| * 简单版词典联想查询 | |||
| * @param keyWord 关键字 | |||
| * @param courseId 课程id,用于课程查询时用以记录历史记录的的courseId | |||
| * @return 查询结果 | |||
| */ | |||
| fun queryKeyWordLikeMatchSimple(keyWord : String, courseId : Long = 0) : MutableList<DictionaryItem> { | |||
| //打开数据库 | |||
| open() | |||
| val currentCount : Int = count | |||
| LogUtil.e("开始查询 $count") | |||
| count++ | |||
| //1、完全匹配 | |||
| //2 、完全包含步变形匹配 | |||
| //3 、完全包含的变形完整匹配 | |||
| //4 、非完全包含的匹配 | |||
| val result : MutableList<DictionaryItem> = mutableListOf() | |||
| val titleMap : MutableMap<String, Int> = mutableMapOf() | |||
| var sql = "" | |||
| //1: wor | |||
| sql = "SELECT word FROM $DATA_DICT WHERE word = $keyWord" | |||
| query(sql, titleMap) | |||
| //2: wor% %wor% | |||
| sql = "SELECT word FROM $DATA_DICT WHERE word LIKE $keyWord || '%' LIMIT $MAX_COUNT" //完全匹配搜索:以关键字开头的搜索 | |||
| query(sql, titleMap) | |||
| if (titleMap.size < MAX_COUNT) { | |||
| sql = "SELECT word FROM $DATA_DICT WHERE word LIKE '%' || $keyWord || '%' LIMIT $MAX_COUNT" //查找任意位置包含keyword的值 | |||
| query(sql, titleMap) | |||
| if (titleMap.size < MAX_COUNT) { | |||
| //3:% w % o % r % | |||
| var newKeyWord : String = insertPercent(keyWord) | |||
| val length = keyWord.length | |||
| for (i in 0 until length - 1) { | |||
| val max : Int = MAX_COUNT - titleMap.size | |||
| newKeyWord = substringEnd(newKeyWord) | |||
| sql = "SELECT word FROM $DATA_DICT WHERE word LIKE \"$newKeyWord\" LIMIT $max" | |||
| query(sql, titleMap) | |||
| if (titleMap.size >= MAX_COUNT) break | |||
| } | |||
| } | |||
| } | |||
| //查询最终结果 | |||
| val list = ArrayList(titleMap.entries) | |||
| list.sortedWith{ o1, o2 -> o1.value.compareTo(o2.value) } | |||
| for (i in list.indices) { | |||
| if (count - currentCount != 1) break | |||
| query(list[i].key,courseId)?.let { | |||
| result.add(it) | |||
| } | |||
| } | |||
| LogUtil.e("结束查询 $count") | |||
| return result | |||
| } | |||
| /** | |||
| * 联想查询,仅查询title,并将title放入hashmap中 | |||
| * @param sql 查询数据 | |||
| * @param hashMap | |||
| */ | |||
| private fun query(sql : String, hashMap : MutableMap<String, Int>) { | |||
| mDataBase?.rawQuery(sql, null)?.let { | |||
| while (it.moveToNext()) { | |||
| if (hashMap.size == MAX_COUNT) break //数量15则打断 | |||
| val title = it.getString(0) | |||
| if (hashMap[title] == null) { | |||
| hashMap[title] = hashMap.size + 1 | |||
| } | |||
| } | |||
| } | |||
| } | |||
| private val builder = StringBuilder() | |||
| /** | |||
| * 字符串添加% | |||
| * @param value 字符串 | |||
| * @return | |||
| */ | |||
| private fun insertPercent(value : String) : String { | |||
| val length = value.length | |||
| if (length == 0) { | |||
| return "" | |||
| } | |||
| builder.setLength(0) | |||
| builder.append("%") | |||
| for (i in 0 until length) { | |||
| builder.append(value[i]).append("%") | |||
| } | |||
| return builder.toString() | |||
| } | |||
| /** | |||
| * 去掉字符串最后一位 | |||
| * @param value 字符串 | |||
| * @return | |||
| */ | |||
| private fun substringEnd(value : String) : String { | |||
| val length = value.length | |||
| return if (length == 0) { | |||
| "" | |||
| } else value.substring(0, length - 1) | |||
| } | |||
| /** | |||
| * 查询返回数据 精准匹配 | |||
| * @param word | |||
| * @return | |||
| */ | |||
| fun query(keyWord : String,courseId : Long) : DictionaryItem?{ | |||
| var mDictionaryItem : DictionaryItem? = null | |||
| val sql = "SELECT * FROM $DATA_DICT WHERE word = \"$keyWord\"" | |||
| open() | |||
| mDataBase?.rawQuery(sql,null)?.let { | |||
| if (it.count > 0) { | |||
| it.moveToNext() | |||
| mDictionaryItem = DictionaryItem().apply { | |||
| this.courseId = courseId | |||
| id = it.getLong(0) //id | |||
| word = it.getString(1) //单词 | |||
| form = it.getInt(2) | |||
| pre_index = it.getString(3) | |||
| phonetic_uk = it.getString(4) | |||
| phonectic_us = it.getString(5) | |||
| phonectic_cn = it.getString(6) | |||
| // setPhoto(it.getString(7)) | |||
| // setAudio(it.getString(8)) | |||
| // setDifficulty(it.getInt(9)) | |||
| basic_explaination = it.getString(10) | |||
| all_explaination = it.getString(11) | |||
| phrase = it.getString(12) | |||
| example = it.getString(13) | |||
| reference = it.getString(14) | |||
| // setVersion(it.getInt(15)) //version版本 | |||
| } | |||
| } | |||
| it.close() | |||
| } | |||
| return mDictionaryItem | |||
| } | |||
| /** | |||
| * 查询返回数据 批量精准匹配 | |||
| * @param words | |||
| * @param courseId 课程查询的courseId | |||
| * @return | |||
| */ | |||
| fun querys(words : List<String>,courseId : Long = 0 ) : List<DictionaryItem> { | |||
| val list : MutableList<DictionaryItem> = ArrayList<DictionaryItem>() | |||
| val sql = "SELECT * FROM " + DATA_DICT + " WHERE word IN (" + Joiner.on(",").join(words) + ")" | |||
| open() | |||
| mDataBase?.rawQuery(sql, null)?.let { | |||
| while (it.moveToNext()) { | |||
| val mDictionaryItem = DictionaryItem().apply { | |||
| this.courseId = courseId | |||
| id = it.getLong(0) //id | |||
| word = it.getString(1) //单词 | |||
| form = it.getInt(2) | |||
| pre_index = it.getString(3) | |||
| phonetic_uk = it.getString(4) | |||
| phonectic_us = it.getString(5) | |||
| phonectic_cn = it.getString(6) | |||
| // setPhoto(it.getString(7)) | |||
| // setAudio(it.getString(8)) | |||
| // setDifficulty(it.getInt(9)) | |||
| basic_explaination = it.getString(10) | |||
| all_explaination = it.getString(11) | |||
| phrase = it.getString(12) | |||
| example = it.getString(13) | |||
| reference = it.getString(14) | |||
| } | |||
| list.add(mDictionaryItem) | |||
| } | |||
| it.close() | |||
| } | |||
| //去重 排序 | |||
| return list.distinctBy { it.word }.sortedBy { | |||
| words.indexOf(it.word) | |||
| } | |||
| } | |||
| } | |||
| @@ -1,14 +1,18 @@ | |||
| package com.xkl.cdl.data.manager.db | |||
| import android.widget.Toast | |||
| import androidx.core.database.getBlobOrNull | |||
| import appApi.AppApi | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.util.AppGlobals | |||
| import com.suliang.common.util.file.FileUtil | |||
| import com.suliang.common.util.thread.AppExecutors | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.bean.VocabularyItem | |||
| import com.xkl.cdl.data.bean.course.ExamBean | |||
| import com.xkl.cdl.data.manager.FilePathManager | |||
| import net.sqlcipher.database.SQLiteDatabase | |||
| import java.io.File | |||
| /** | |||
| * author suliang | |||
| @@ -17,12 +21,15 @@ import net.sqlcipher.database.SQLiteDatabase | |||
| */ | |||
| object VocabularyManager { | |||
| private var mDataBase : SQLiteDatabase? = null | |||
| private var mDataBase : SQLiteDatabase? = null | |||
| private var pwd : String = "XKL_VOCABULARY_DATA_KEY" //https://cdn.xuekaole.com/dict/vocabulary.db | |||
| // XKL_XECD_DICT_DATA_KEY https://cdn.xuekaole.com/dict/xecd_dict.db | |||
| private fun open() { | |||
| synchronized(VocabularyManager) { | |||
| if (mDataBase == null) { | |||
| mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getVocabularyDbPath().path, "", null, SQLiteDatabase.OPEN_READONLY) | |||
| mDataBase = SQLiteDatabase.openDatabase(FilePathManager.getVocabularyDbPath().path, pwd, null, | |||
| SQLiteDatabase.OPEN_READONLY) | |||
| } | |||
| if (mDataBase == null) { | |||
| AppExecutors.mainThread.run { | |||
| @@ -37,12 +44,75 @@ object VocabularyManager { | |||
| * @param level VocabularyItem | |||
| * @return List<ExamBean> | |||
| */ | |||
| fun queryVocabularyTestData(level : VocabularyItem) : List<ExamBean>{ | |||
| fun queryVocabularyTestData(level : VocabularyItem) : List<ExamBean> { | |||
| open() | |||
| val result = mutableListOf<ExamBean>() | |||
| val sql = "SELECT * FROM " | |||
| val sql = "SELECT * FROM exam WHERE stage <= ${level.stage} ORDER BY random() LIMIT 100" | |||
| mDataBase?.rawQuery(sql, null)?.let { | |||
| while (it.moveToNext()) { | |||
| result.add(ExamBean().apply { | |||
| id = it.getLong(0) | |||
| word_id = it.getLong(1) | |||
| word = it.getString(2) | |||
| correct = it.getString(3) | |||
| error1 = it.getString(4) | |||
| error2 = it.getString(5) | |||
| error3 = it.getString(6) | |||
| }) | |||
| } | |||
| it.close() | |||
| } | |||
| return result; | |||
| } | |||
| /** | |||
| * 查询单词的发音音频 | |||
| * @param base DbControlBase | |||
| * @param wordId Long 查询单词 | |||
| * @param soundWay Int 发音方式 | |||
| * @return String 发音保存的文件名称 要不为空,要不为文件地址, 为空,则表明没有发音文件,如果是英美发音方式,则表示英美都没有发音内容 | |||
| */ | |||
| fun queryAudio(wordId : Long, soundWay : Int) : String? { | |||
| val sql = "SELECT audio_us,audio_uk FROM exam WHERE word_id = $wordId" | |||
| var audio_us : ByteArray? = null | |||
| var audio_uk : ByteArray? = null | |||
| open() | |||
| mDataBase?.rawQuery(sql, null)?.let { | |||
| while (it.moveToNext()) { | |||
| //写入文件 | |||
| audio_us = it.getBlobOrNull(0) | |||
| audio_uk = it.getBlobOrNull(1) | |||
| } | |||
| it.close() | |||
| } | |||
| //不为空,写入本身,如果为空,用另外的发音方式写入 | |||
| val parentPath = FileUtil.getSaveDirPath(AppConfig.VOICE) | |||
| val audio_us_file_path = audio_us?.let { | |||
| val audioFileNameUS = "${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
| val file = File(parentPath, audioFileNameUS) | |||
| FileUtil.writeBytesToFile(file, it) | |||
| file.path | |||
| } ?: audio_uk?.let { | |||
| val audioFileNameUS = "${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
| val file = File(parentPath, audioFileNameUS) | |||
| FileUtil.writeBytesToFile(file, it) | |||
| file.path | |||
| } | |||
| //不为空,写入本身,如果为空,用另外的发音方式写入 | |||
| val audio_uk_file_path = audio_uk?.let { | |||
| val audioFileNameUk = "${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
| val file = File(parentPath, audioFileNameUk) | |||
| FileUtil.writeBytesToFile(file, it) | |||
| file.path | |||
| } ?: audio_us?.let { | |||
| val audioFileNameUk = "${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
| val file = File(parentPath, audioFileNameUk) | |||
| FileUtil.writeBytesToFile(file, it) | |||
| file.path | |||
| } | |||
| return if (soundWay == AppConstants.SOUND_TYPE_UK) audio_uk_file_path else audio_us_file_path | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ import com.suliang.common.util.file.FileUtil | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.manager.db.DBCourseManager | |||
| import com.xkl.cdl.data.manager.db.DbControlBase | |||
| import com.xkl.cdl.data.manager.db.VocabularyManager | |||
| import io.reactivex.rxjava3.core.Observable | |||
| import java.io.File | |||
| import java.util.* | |||
| @@ -94,4 +95,46 @@ object AudioCache { | |||
| } | |||
| return lruCache[fileName] | |||
| } | |||
| fun getVocabulary(wordId : Long,soundWay : Int){ | |||
| //父文件路劲 | |||
| val parentPath = FileUtil.getSaveDirPath(AppConfig.VOICE) | |||
| //先查对应的文件路径 : 项目id_课程包id_课程id_单词id_发音方式 | |||
| val defaultKey = "${wordId}_$soundWay" | |||
| Observable.fromCallable { | |||
| //不为空,直接取值 | |||
| if (lruCache.get(defaultKey) == null){ | |||
| //为空 查文件 | |||
| val file = File(parentPath, defaultKey) | |||
| when { | |||
| file.exists() -> lruCache.put(defaultKey, file.path) //文件存在,则表示以前进行过数据库查询,且进行了保存文件,英美发音本地都有文件 | |||
| else -> { //文件不存在,则查询数据库进行查询,返回的为保存的文件地址 | |||
| val filePath = VocabularyManager.queryAudio(wordId, soundWay) | |||
| filePath?.also { | |||
| when(soundWay){ | |||
| AppConstants.SOUND_TYPE_UK -> { | |||
| lruCache.put(defaultKey, it) //保存路径 | |||
| val audioFileNameUS = "${wordId}_${AppConstants.SOUND_TYPE_US}" | |||
| lruCache.put(audioFileNameUS, File(parentPath,audioFileNameUS).path) //保存路径 | |||
| } | |||
| AppConstants.SOUND_TYPE_US -> { | |||
| lruCache.put(defaultKey, it) //保存路径 | |||
| val audioFileNameUS = "${wordId}_${AppConstants.SOUND_TYPE_UK}" | |||
| lruCache.put(audioFileNameUS, File(parentPath,audioFileNameUS).path) //保存路径 | |||
| } | |||
| else -> lruCache.put(defaultKey, it) //保存路径 | |||
| } | |||
| } //如果查询返回路径为空,则说明,没有发音内容 | |||
| } | |||
| } | |||
| } | |||
| return@fromCallable lruCache.get(defaultKey) ?: "" | |||
| }.compose(diskIo2Main()).subscribe { | |||
| if (this::audioLiveData.isInitialized) | |||
| audioLiveData.value = it | |||
| } | |||
| } | |||
| } | |||
| @@ -95,5 +95,5 @@ class CommonDialog private constructor() : BaseDialogFragment<DialogCommonBindin | |||
| } | |||
| override fun resizeDialog(){} | |||
| } | |||
| @@ -71,6 +71,8 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
| //备忘本测试 课程测试 | |||
| AppConstants.TEST_TYPE_SERVICE_CENTER, | |||
| AppConstants.TEST_TYPE_MEMO -> initMemoTestOver() | |||
| //词汇量测试结束 | |||
| } | |||
| //学习结束弹窗 | |||
| AppConstants.DIALOG_TYPE_LEARNING_OVER -> initLessonLearningOver(false) | |||
| @@ -87,18 +89,6 @@ class LearnDialog private constructor() : BaseDialogFragment<DialogLessonLearnBi | |||
| } | |||
| } | |||
| override fun onStart() { | |||
| super.onStart() | |||
| //设置dialog位置 | |||
| requireDialog().window?.let { | |||
| it.attributes.run { | |||
| y = ScreenUtil.getScreenHeight() / 5 | |||
| it.attributes = this | |||
| } | |||
| it.setGravity(Gravity.TOP) | |||
| } | |||
| } | |||
| /** 初始化分数 */ | |||
| private fun initScore() { | |||
| //插画 | |||
| @@ -0,0 +1,97 @@ | |||
| package com.xkl.cdl.dialog | |||
| import android.annotation.SuppressLint | |||
| import android.os.Bundle | |||
| import android.view.Gravity | |||
| import android.view.View | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.base.BaseDialogFragment | |||
| import com.suliang.common.extension.click | |||
| import com.suliang.common.extension.setHtml | |||
| import com.suliang.common.util.os.ScreenUtil | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.databinding.DialogVocabularyBinding | |||
| /** | |||
| * author suliang | |||
| * create 2022/4/2 15:47 | |||
| * Describe: 词汇量测试结束弹窗 | |||
| */ | |||
| class VocabularyDialog private constructor() : BaseDialogFragment<DialogVocabularyBinding>() { | |||
| companion object { | |||
| /** | |||
| * @param levelName String 测试级别 | |||
| * @param testTime String 测试时间 | |||
| * @param testCount Int 词汇量 | |||
| * @param coverRange Int 覆盖率 | |||
| * @return VocabularyDialog | |||
| */ | |||
| fun newInstance( levelName:String, testTime : String, testCount: Int, coverRange:Int) : VocabularyDialog { | |||
| val args = Bundle() | |||
| args.putString(AppConfig.INTENT_1, levelName) | |||
| args.putString(AppConfig.INTENT_2, testTime) | |||
| args.putInt(AppConfig.INTENT_3, testCount) | |||
| args.putInt(AppConfig.INTENT_4, coverRange) | |||
| val fragment = VocabularyDialog() | |||
| fragment.arguments = args | |||
| return fragment | |||
| } | |||
| } | |||
| /** 事件动作 */ | |||
| lateinit var onDialogListener : (action : Int, dialog : VocabularyDialog) -> Unit | |||
| @SuppressLint("SetTextI18n") | |||
| override fun initFragment() { | |||
| val levelName = requireArguments().getString(AppConfig.INTENT_1) | |||
| val testTime = requireArguments().getString(AppConfig.INTENT_2) | |||
| val testCount = requireArguments().getInt(AppConfig.INTENT_3) | |||
| val coverRange = requireArguments().getInt(AppConfig.INTENT_4) | |||
| binding.tvVocabulary.text = "学考乐词汇量测试:$levelName" | |||
| binding.tvVocabularyTestTime.text = testTime | |||
| when(testCount){ | |||
| 0 -> { | |||
| binding.groupVocabularyNormal.visibility = View.GONE | |||
| binding.tvVocabularyLittle.visibility = View.VISIBLE | |||
| binding.tvLeft.apply { | |||
| text = getString(R.string.done) | |||
| click { | |||
| onDialogListener(AppConstants.DIALOG_OVER,this@VocabularyDialog) | |||
| } | |||
| } | |||
| binding.tvRight.apply { | |||
| text = getString(R.string.test_again) | |||
| click { | |||
| onDialogListener(AppConstants.DIALOG_START_TEST,this@VocabularyDialog) | |||
| } | |||
| } | |||
| } | |||
| else -> { | |||
| binding.groupVocabularyNormal.visibility = View.VISIBLE | |||
| binding.tvVocabularyLittle.visibility = View.GONE | |||
| binding.tvVocabularyCount.text = "$testCount" | |||
| binding.ratingBar.setStar(coverRange / 20f) | |||
| binding.tvVocabularyCoverRateStage.setHtml("覆盖率<font color = \'#F7874F\'>${coverRange}%</font>($levelName)") | |||
| binding.tvLeft.apply { | |||
| text = getString(R.string.test_again) | |||
| click { | |||
| onDialogListener(AppConstants.DIALOG_START_TEST,this@VocabularyDialog) | |||
| } | |||
| } | |||
| binding.tvRight.apply { | |||
| text = getString(R.string.done) | |||
| click { | |||
| onDialogListener(AppConstants.DIALOG_OVER,this@VocabularyDialog) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -27,6 +27,7 @@ import com.xkl.cdl.adapter.itemdecoration.SpellItemDecoration | |||
| 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.VocabularyItem | |||
| import com.xkl.cdl.data.bean.course.ExamBean | |||
| import com.xkl.cdl.data.event.LearnEventData | |||
| import com.xkl.cdl.data.manager.CourseManager | |||
| @@ -36,6 +37,7 @@ import com.xkl.cdl.databinding.* | |||
| import com.xkl.cdl.dialog.CommonDialog | |||
| import com.xkl.cdl.dialog.CommonDialogBean | |||
| import com.xkl.cdl.dialog.LearnDialog | |||
| import com.xkl.cdl.dialog.VocabularyDialog | |||
| import com.xkl.cdl.module.m_service_center.TestDetailActivity | |||
| /** | |||
| @@ -137,6 +139,11 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| /** 新单词来了的发音事件 */ | |||
| private fun initNewWordRead(it : ExamBean) { | |||
| //词汇量测试,新单词发音 | |||
| if (vm.intentData.examType == AppConstants.TEST_TYPE_NORMAL){ | |||
| wordChooseBinding.ivVoice.performClick() | |||
| return | |||
| } | |||
| when (vm.intentData.courseType) { | |||
| //拼写初始不发音,作文不发音, 音标课程测试不发音 | |||
| AppConstants.COURSE_TYPE_ENGLISH_SOUNDMARK, AppConstants.COURSE_TYPE_ENGLISH_SPELL, AppConstants.COURSE_TYPE_CHINESE_COMPOSITION -> { | |||
| @@ -147,7 +154,6 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| AppConstants.COURSE_TYPE_ENGLISH_VOICE -> { | |||
| wordChooseBinding.ivVoice.performClick() | |||
| } | |||
| } | |||
| } | |||
| @@ -188,7 +194,11 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| private fun initStatistics() { | |||
| binding.incStatistics.run { | |||
| progressTestNumber.max = vm.testData.size | |||
| tvTestTypeName.text = CourseManager.getExamTypeName(vm.intentData.examType) | |||
| //测试类型 | |||
| tvTestTypeName.text = when (vm.intentData.examType) { | |||
| AppConstants.TEST_TYPE_NORMAL -> vm.intentData.vocabularyItem?.level | |||
| else -> CourseManager.getExamTypeName(vm.intentData.examType) | |||
| } | |||
| //监听总时间的改变 | |||
| vm.totalUseTime.observe(this@LearnExamActivity) { | |||
| tvTestTime.text = DateUtil.formatGMT(it, DateUtil.FORMAT_2) | |||
| @@ -200,6 +210,7 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| vm.errorLiveData.observe(this@LearnExamActivity) { | |||
| tvErrorNumber.text = "错误: $it" | |||
| } | |||
| } | |||
| } | |||
| @@ -497,7 +508,7 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| } | |||
| } | |||
| } | |||
| } ?: let { //取消事件 | |||
| } ?: let { //取消选项答案选择事件,设置选项发音事件 | |||
| vm.optionList.forEachIndexed { index, optionItemBinding -> | |||
| optionItemBinding.root.click(null) | |||
| optionItemBinding.tvOption.click { optionItemBinding.ivOption.performClick() } | |||
| @@ -523,12 +534,13 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| //口语的音频,有问题,与选项 直接获取其tag获取音频 | |||
| currentPlayView?.cancelAnimation() | |||
| if (view is LottieAnimationView && view.visibility == View.VISIBLE) currentPlayView = view | |||
| if (isSpokenTotalTest()) { | |||
| //口语课程学前学后总的点击事件取tag | |||
| AudioCache.get(view.tag as String) | |||
| } else { | |||
| AudioCache.get(vm.dbBaseControl, it.word_id, vm.defaultSoundWay) | |||
| when{ | |||
| //词汇量测试 | |||
| vm.intentData.examType == AppConstants.TEST_TYPE_NORMAL -> AudioCache.getVocabulary(it.word_id,vm.defaultSoundWay) | |||
| //口语 | |||
| isSpokenTotalTest() -> AudioCache.get(view.tag as String) | |||
| //测试 | |||
| else -> AudioCache.get(vm.dbBaseControl, it.word_id, vm.defaultSoundWay) | |||
| } | |||
| } | |||
| } | |||
| @@ -593,14 +605,37 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| /** 测试完成 : 弹窗显示 */ | |||
| private fun testOver() { | |||
| if (vm.intentData.examType == AppConstants.TEST_TYPE_NORMAL){ | |||
| vm.intentData.vocabularyItem?.let { | |||
| val createTime = DateUtil.format(System.currentTimeMillis(), DateUtil.FORMAT_1) | |||
| VocabularyDialog.newInstance(it.level,createTime,vm.scoreValue,vm.vocabularyCoverange).apply { | |||
| onDialogListener = { action, dialog -> | |||
| when(action){ | |||
| //完成 | |||
| AppConstants.DIALOG_OVER -> { | |||
| dialog.dismissAllowingStateLoss() | |||
| finish() | |||
| } | |||
| //再测一次 | |||
| AppConstants.DIALOG_START_TEST -> { | |||
| dialog.dismissAllowingStateLoss() | |||
| finish() | |||
| LiveEventBus.get<VocabularyItem>("vocabulary_test").post(vm.intentData.vocabularyItem) | |||
| } | |||
| } | |||
| } | |||
| }.show(supportFragmentManager,"vocabulary") | |||
| } | |||
| return | |||
| } | |||
| //对话框信息实体 | |||
| val learnDialogBean = LearnDialogBean(AppConstants.DIALOG_TYPE_EXAM_OVER).apply { | |||
| examType = vm.intentData.examType | |||
| score = vm.scoreValue | |||
| correctNumber = vm.correctLiveData.value!! | |||
| errorNumber = vm.errorLiveData.value!! | |||
| //备忘本需要显示课程名称 | |||
| if (examType == AppConstants.TEST_TYPE_MEMO || examType == AppConstants.TEST_TYPE_SERVICE_CENTER){ | |||
| //备忘本、课程测试需要显示课程名称 | |||
| if (examType == AppConstants.TEST_TYPE_MEMO || examType == AppConstants.TEST_TYPE_SERVICE_CENTER) { | |||
| showTimeCount = vm.intentData.showTitle | |||
| } | |||
| } | |||
| @@ -717,8 +752,7 @@ class LearnExamActivity : BaseActivityVM<ActivityLearnExamBinding, LearnExamView | |||
| } | |||
| } | |||
| //备忘本测试 | |||
| AppConstants.TEST_TYPE_SERVICE_CENTER, | |||
| AppConstants.TEST_TYPE_MEMO -> when(action){ | |||
| AppConstants.TEST_TYPE_SERVICE_CENTER, AppConstants.TEST_TYPE_MEMO -> when (action) { | |||
| //完成 | |||
| AppConstants.DIALOG_OVER -> { | |||
| dialog.dismissAllowingStateLoss() | |||
| @@ -380,8 +380,10 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
| createErrorRecord() | |||
| } | |||
| } | |||
| //添加到记录集合 | |||
| mExamRecordList.add(currentExamRecord!!.build()) | |||
| //添加到记录集合 词汇量测试不添加记录 | |||
| if (intentData.examType != AppConstants.TEST_TYPE_NORMAL) { | |||
| mExamRecordList.add(currentExamRecord!!.build()) | |||
| } | |||
| currentChooseResultLiveData.value = chooseResult | |||
| } | |||
| @@ -484,8 +486,8 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
| //分数 与 覆盖率 只有测试成功才进行计算 | |||
| if (isVocablularySuccess) { | |||
| val converange = (calculateCorrectNumber - 25) / 75 //被测试题的占比重 | |||
| // TODO: 2022/4/14 16000应该为该词汇量测试对应的词汇数 | |||
| scoreValue = (16000 * converange).toInt() | |||
| // 用了了100只是预防取值错误时不好看 | |||
| scoreValue = ( intentData.vocabularyItem?.size ?: 100 * converange).toInt() | |||
| vocabularyCoverange = (converange * 100).toInt() //测试类型的覆盖率 | |||
| } | |||
| } | |||
| @@ -580,16 +582,11 @@ class LearnExamViewModel : LearnBaseViewModel() { | |||
| stage = "小学" | |||
| coverRate = vocabularyCoverange.toFloat() | |||
| } | |||
| //添加测试题 | |||
| addAllRecord(mExamRecordList) | |||
| //添加测试题 词汇量测试不添加测试题 | |||
| if (intentData.examType != AppConstants.TEST_TYPE_NORMAL) { | |||
| addAllRecord(mExamRecordList) | |||
| } | |||
| } | |||
| // //是否完成 | |||
| // if (examType == SyncStateContract.Constants.TEST_TYPE_AFTER_TOTAL && mData.getScore() >= SyncStateContract.Constants.SCORE_1 && !mData.isCourseFinished()) { | |||
| // learnExam.setIsFinished(true) | |||
| // } | |||
| //最终上传数据 | |||
| record.addExam(learnExam) //测试试卷 | |||
| //时间 学习中心的测试需要添加为学习时间 | |||
| @@ -0,0 +1,165 @@ | |||
| package com.xkl.cdl.module.m_service_center | |||
| import android.annotation.SuppressLint | |||
| import android.content.Context | |||
| import android.content.Intent | |||
| import android.os.Bundle | |||
| import android.view.View | |||
| import androidx.lifecycle.ViewModelProvider | |||
| import androidx.recyclerview.widget.LinearLayoutManager | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.base.activity.BaseActivityVM | |||
| import com.suliang.common.extension.click | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.databinding.ActivityDictionaryBinding | |||
| import com.xkl.cdl.dialog.CommonDialog | |||
| import com.xkl.cdl.dialog.CommonDialogBean | |||
| import com.xkl.cdl.module.m_service_center.DictionaryViewModel.Companion.STATE_HISTROY | |||
| import com.xkl.cdl.module.m_service_center.DictionaryViewModel.Companion.STATE_SEARCH | |||
| class DictionaryActivity : BaseActivityVM<ActivityDictionaryBinding, DictionaryViewModel>() { | |||
| companion object { | |||
| fun newInstance(context : Context, courseId : Long = 0) { | |||
| val intent = Intent(context, DictionaryActivity::class.java) | |||
| intent.putExtra(AppConfig.INTENT_1, courseId) | |||
| context.startActivity(intent) | |||
| } | |||
| } | |||
| override fun initViewModel() : DictionaryViewModel { | |||
| return ViewModelProvider(this)[DictionaryViewModel::class.java] | |||
| } | |||
| @SuppressLint("NotifyDataSetChanged") | |||
| override fun initActivity(savedInstanceState : Bundle?) { | |||
| vm.courseId = intent.getLongExtra(AppConfig.INTENT_1, 0) | |||
| initHistoryRecyclerView() | |||
| //点击 查看更多 | |||
| binding.tvSeeMore.click { | |||
| when (vm.detailState) { | |||
| STATE_HISTROY -> { | |||
| //显示页面加1, 列表更新 | |||
| vm.histroyShowPage++ | |||
| vm.adapter.notifyDataSetChanged() | |||
| //根据数量判断,是否还要显示 | |||
| binding.tvSeeMore.visibility = if (vm.historyList.size > vm.histroyShowPage * vm.historyPageSize) View.VISIBLE else View.GONE | |||
| } | |||
| STATE_SEARCH -> { | |||
| //显示页面加1, 列表更新 | |||
| vm.searchShowPage++ | |||
| vm.adapter.notifyDataSetChanged() | |||
| //根据数量判断,是否还要显示 | |||
| binding.tvSeeMore.visibility = if (vm.searchDetailList.size > vm.searchShowPage * vm.historyPageSize) View.VISIBLE else View.GONE | |||
| } | |||
| } | |||
| } | |||
| //删除历史记录,弹窗提示 | |||
| binding.ivDelete.click { | |||
| val commonDialogBean = CommonDialogBean(titleText = R.string.dialog_delete_history, leftText = R.string.delete, | |||
| rightText = R.string.cancel) | |||
| CommonDialog.newInstance(commonDialogBean).apply { | |||
| onCommonDialogButtonClickListener = { dialog : CommonDialog, isRightClick : Boolean -> | |||
| if (!isRightClick) { //删除 | |||
| vm.clearHistory() | |||
| } | |||
| dialog.dismissAllowingStateLoss() | |||
| } | |||
| }.show(supportFragmentManager, javaClass.name) | |||
| } | |||
| } | |||
| private fun initHistoryRecyclerView() { | |||
| binding.rv.apply { | |||
| layoutManager = LinearLayoutManager(this@DictionaryActivity, LinearLayoutManager.VERTICAL, false) | |||
| adapter = vm.adapter | |||
| } | |||
| } | |||
| @SuppressLint("NotifyDataSetChanged") | |||
| override fun loadData() { | |||
| //更新历史记录适配器 | |||
| vm.updateHistoryAdapterLiveData.observe(this) { | |||
| when { | |||
| //历史记录 | |||
| !it -> vm.adapter.apply { | |||
| //如果非历史列表记录,则更新列表布局的状态 | |||
| if (vm.detailState != STATE_HISTROY) { | |||
| vm.detailState = STATE_HISTROY | |||
| binding.apply { | |||
| tvHistoryTitle.visibility = View.VISIBLE | |||
| ivDelete.visibility = View.VISIBLE | |||
| } | |||
| } | |||
| binding.layoutDetail.visibility = if (vm.historyList.isEmpty()) View.GONE else View.VISIBLE | |||
| binding.tvSeeMore.visibility = if (vm.historyList.size > vm.histroyShowPage * vm.historyPageSize) View.VISIBLE else View.GONE | |||
| //更新值 | |||
| needShowEmptyView = false | |||
| setData(vm.historyList) | |||
| } | |||
| //搜索结果 | |||
| else -> vm.adapter.apply { | |||
| if (vm.detailState != STATE_SEARCH) { | |||
| vm.detailState = STATE_SEARCH | |||
| binding.apply { | |||
| tvHistoryTitle.visibility = View.GONE | |||
| ivDelete.visibility = View.GONE | |||
| } | |||
| } | |||
| binding.layoutDetail.visibility = View.VISIBLE | |||
| binding.tvSeeMore.visibility = if (vm.searchDetailList.size > vm.searchShowPage * vm.historyPageSize) View.VISIBLE else View.GONE | |||
| needShowEmptyView = vm.searchDetailList.size == 0 | |||
| setData(vm.searchDetailList) | |||
| } | |||
| } | |||
| } | |||
| /**搜索监听*/ | |||
| vm.etSearchLiveData.observe(this) { | |||
| when { | |||
| //直接历史记录更新 | |||
| it.isNullOrEmpty() -> vm.updateHistoryAdapterLiveData.value = false | |||
| //历史记录的详情 | |||
| vm.searchItemDetailAble -> { | |||
| } | |||
| //关键字搜索 | |||
| else -> vm.searchKeyWord(it) | |||
| } | |||
| } | |||
| //查询历史记录 | |||
| vm.loadHistory() | |||
| } | |||
| /** | |||
| * 详情中的状态 | |||
| * @param state Int 1历史记录 2搜索详情 | |||
| */ | |||
| private fun initLayoutDetailState(state : Int) { | |||
| when (state) { | |||
| //历史布局 | |||
| STATE_HISTROY -> binding.apply { | |||
| tvHistoryTitle.visibility = View.VISIBLE | |||
| ivDelete.visibility = View.VISIBLE | |||
| } | |||
| //查询详情 | |||
| STATE_SEARCH -> binding.apply { | |||
| tvHistoryTitle.visibility = View.GONE | |||
| ivDelete.visibility = View.GONE | |||
| } | |||
| } | |||
| binding.tvSeeMore.visibility = View.GONE | |||
| } | |||
| } | |||
| @@ -0,0 +1,111 @@ | |||
| package com.xkl.cdl.module.m_service_center | |||
| import android.os.Handler | |||
| import android.os.Looper | |||
| import androidx.lifecycle.MutableLiveData | |||
| import com.suliang.common.base.viewmodel.BaseViewModel | |||
| import com.suliang.common.util.thread.AppExecutors | |||
| import com.xkl.cdl.adapter.DictionaryAdapter | |||
| import com.xkl.cdl.data.bean.DictionaryItem | |||
| import com.xkl.cdl.data.exam_record.AppDatabase | |||
| import com.xkl.cdl.data.manager.db.DictionaryManager | |||
| import io.reactivex.rxjava3.core.Observable | |||
| import io.reactivex.rxjava3.schedulers.Schedulers | |||
| class DictionaryViewModel : BaseViewModel() { | |||
| //courseId 记录为是否为课程查询,课程历史记录为课程,否则为0词典查询 | |||
| var courseId : Long = 0 | |||
| //记录状态 | |||
| companion object { | |||
| const val STATE_HISTROY = 1 | |||
| const val STATE_SEARCH = 2 | |||
| } | |||
| //适配器 | |||
| val adapter = DictionaryAdapter(this) | |||
| //输入框监听 | |||
| val etSearchLiveData = MutableLiveData<String>() | |||
| //状态 : 默认历史记录 | |||
| var detailState = STATE_HISTROY | |||
| //历史记录集合 | |||
| val historyList = mutableListOf<DictionaryItem>() | |||
| //更新详情适配器 false:历史记录 true:联想搜索结果 | |||
| val updateHistoryAdapterLiveData = MutableLiveData<Boolean>() | |||
| //历史记录,显示的页数 | |||
| var histroyShowPage = 1 | |||
| var searchShowPage = 1 | |||
| //一页显示的历史记录个数,最多历史记录显示30个 | |||
| val historyPageSize = 10 | |||
| //搜索结果,做多匹配显示50个 | |||
| //是否显示搜索单词的详情 true:显示单条记录的详情 false:搜索显示的为搜索结果的列表详情 | |||
| var searchItemDetailAble = false | |||
| //搜索结果 多条为联想结果 单条基本为搜索的详情结果 | |||
| var searchDetailList = mutableListOf<DictionaryItem>() | |||
| val handler : Handler = Handler(Looper.getMainLooper()) | |||
| //记录联想结果中为历史记录的位置 | |||
| val searchListForHistoryPositionMap = mutableMapOf<Int,Int>() | |||
| /** | |||
| * 查询搜索历史 | |||
| */ | |||
| fun loadHistory() { | |||
| Observable.fromCallable { | |||
| val history = AppDatabase.instance.dictionaryDao().query(courseId) | |||
| historyList.addAll(history) | |||
| updateHistoryAdapterLiveData.postValue(false) | |||
| }.observeOn(Schedulers.from(AppExecutors.io)).subscribe() | |||
| } | |||
| /**清空历史记录*/ | |||
| fun clearHistory() { | |||
| Observable.fromCallable { | |||
| historyList.clear() | |||
| updateHistoryAdapterLiveData.postValue(false) | |||
| AppDatabase.instance.dictionaryDao().clear(courseId) | |||
| }.observeOn(Schedulers.from(AppExecutors.io)).subscribe() | |||
| } | |||
| /** | |||
| * 关键字搜索 | |||
| * @param keyWord String | |||
| */ | |||
| fun searchKeyWord(keyWord : String) { | |||
| handler.removeCallbacksAndMessages(null) | |||
| handler.postDelayed({ | |||
| Observable.fromCallable { | |||
| //清空位置对应信息 | |||
| searchListForHistoryPositionMap.clear() | |||
| //联想查询结果 | |||
| searchDetailList = DictionaryManager.queryKeyWordLikeMatchSimple(keyWord) | |||
| //搜索记录中获取历史记录位置 需要判断是否有历史记录 | |||
| if (historyList.isNotEmpty() && searchDetailList.isNotEmpty()) { | |||
| historyList.forEachIndexed { historyIndex, historyItem -> | |||
| searchDetailList.forEachIndexed { searchIndex, searchItem -> | |||
| if (historyItem.word == searchItem.word) { | |||
| searchItem.inHistory = true | |||
| searchListForHistoryPositionMap.put(searchIndex, historyIndex) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //更新搜索结果 | |||
| updateHistoryAdapterLiveData.postValue(true) | |||
| }.observeOn(Schedulers.from(AppExecutors.io)).subscribe() | |||
| }, 300) | |||
| } | |||
| } | |||
| @@ -13,13 +13,14 @@ import com.suliang.common.base.adapter.BaseRVAdapter | |||
| import com.suliang.common.base.fragment.BaseFragmentVM | |||
| import com.suliang.common.extension.click | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.adapter.DictionaryAdapter | |||
| import com.xkl.cdl.databinding.FragmentMyBinding | |||
| import com.xkl.cdl.databinding.ItemServiceCenterItemBinding | |||
| import com.xkl.cdl.databinding.ServiceCenterFragmentBinding | |||
| import com.xkl.cdl.module.main.MainActivityViewModel | |||
| class ServiceCenterFragment : BaseFragmentVM<ServiceCenterFragmentBinding, MainActivityViewModel>() { | |||
| class ServiceCenterFragment : BaseFragmentVM<ServiceCenterFragmentBinding, MainActivityViewModel>() { | |||
| companion object { | |||
| @JvmStatic | |||
| fun newInstance() = ServiceCenterFragment() | |||
| @@ -27,16 +28,17 @@ class ServiceCenterFragment : BaseFragmentVM<ServiceCenterFragmentBinding, Main | |||
| override fun initFragment() { | |||
| val items = mutableListOf(Item("课程测试", R.drawable.ic_test_course, Color.parseColor("#1A58D4DB")), | |||
| Item("测词汇量", R.drawable.ic_test_count, Color.parseColor("#1AFF8B52")), | |||
| Item("测试成绩", R.drawable.ic_test_score, Color.parseColor("#1AF26255")), | |||
| Item("词典", R.drawable.ic_dictionary, Color.parseColor("#1A52CC52"))) | |||
| Item("测词汇量", R.drawable.ic_test_count, Color.parseColor("#1AFF8B52")), | |||
| Item("测试成绩", R.drawable.ic_test_score, Color.parseColor("#1AF26255")), | |||
| Item("词典", R.drawable.ic_dictionary, Color.parseColor("#1A52CC52"))) | |||
| binding.rv.apply { | |||
| layoutManager = GridLayoutManager(requireContext(),2,GridLayoutManager.VERTICAL,false) | |||
| layoutManager = GridLayoutManager(requireContext(), 2, GridLayoutManager.VERTICAL, false) | |||
| setHasFixedSize(true) | |||
| adapter = object : BaseRVAdapter<Item>() { | |||
| override fun coverViewHolder(parent : ViewGroup, viewType : Int) : BaseAdapterViewHolder { | |||
| return BaseAdapterViewHolder(inflateBinding(parent,R.layout.item_service_center_item)) | |||
| return BaseAdapterViewHolder(inflateBinding(parent, R.layout.item_service_center_item)) | |||
| } | |||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
| val item = getItem(position) | |||
| (holder.binding as ItemServiceCenterItemBinding).apply { | |||
| @@ -55,23 +57,25 @@ class ServiceCenterFragment : BaseFragmentVM<ServiceCenterFragmentBinding, Main | |||
| } | |||
| private fun startTo(position : Int) { | |||
| when(position){ | |||
| 0 -> startActivity(CourseTestActivity::class.java) | |||
| 1 -> startActivity(TestVocabularyActivity::class.java) | |||
| 2 -> startActivity(TestScoreActivity::class.java) | |||
| 3 -> "" | |||
| context?.let { | |||
| when (position) { | |||
| 0 -> startActivity(CourseTestActivity::class.java) | |||
| 1 -> startActivity(TestVocabularyActivity::class.java) | |||
| 2 -> startActivity(TestScoreActivity::class.java) | |||
| 3 -> DictionaryActivity.newInstance(it) | |||
| } | |||
| } | |||
| } | |||
| override fun loadData() { | |||
| } | |||
| override fun initViewModel(): MainActivityViewModel { | |||
| override fun initViewModel() : MainActivityViewModel { | |||
| return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
| } | |||
| data class Item(val name : String, val icon : Int, val backgroundColor:Int) | |||
| data class Item(val name : String, val icon : Int, val backgroundColor : Int) | |||
| } | |||
| @@ -36,7 +36,7 @@ class TestDetailActivity : BaseActivityVM<ActivityTestDetailBinding,TestDetailVi | |||
| if (vm.learnExam.score > 0){ | |||
| groupVocabularyLittle.visibility = View.GONE | |||
| groupVocabularyNormal.visibility = View.VISIBLE | |||
| tvVocabularyCount.text = "${vm.learnExam.score}" | |||
| tvVocabularyCount.text = "${vm.learnExam.score.toInt()}" | |||
| ratingBar.setStar(vm.learnExam.score/20) | |||
| tvVocabularyCoverRateStage.setHtml("覆盖率<font color=\'#F7874F\'>${vm.learnExam.coverRate}%</font>\t\t(${vm.learnExam.stage})") | |||
| }else{ | |||
| @@ -4,14 +4,20 @@ import android.os.Bundle | |||
| import android.view.ViewGroup | |||
| import androidx.lifecycle.ViewModelProvider | |||
| import androidx.recyclerview.widget.LinearLayoutManager | |||
| import com.jeremyliao.liveeventbus.LiveEventBus | |||
| import com.suliang.common.AppConfig | |||
| import com.suliang.common.base.activity.BaseActivityVM | |||
| import com.suliang.common.base.adapter.BaseAdapterViewHolder | |||
| import com.suliang.common.base.adapter.BaseRVAdapter | |||
| import com.suliang.common.extension.click | |||
| import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.AppConstants | |||
| import com.xkl.cdl.data.DataTransferHolder | |||
| import com.xkl.cdl.data.bean.VocabularyItem | |||
| import com.xkl.cdl.data.bean.intentdata.ExamData | |||
| import com.xkl.cdl.databinding.ActivityTestVocabularyBinding | |||
| import com.xkl.cdl.databinding.ItemVocabularyLevelBinding | |||
| import com.xkl.cdl.module.learn.LearnExamActivity | |||
| class TestVocabularyActivity : BaseActivityVM<ActivityTestVocabularyBinding, TestVocabularyViewModle>() { | |||
| @@ -31,13 +37,13 @@ class TestVocabularyActivity : BaseActivityVM<ActivityTestVocabularyBinding, Tes | |||
| return BaseAdapterViewHolder(inflateBinding(parent, R.layout.item_vocabulary_level)) | |||
| } | |||
| override fun onBindVH(holder : BaseAdapterViewHolder, position : Int) { | |||
| getItem(position).let { | |||
| getItem(position).let { item -> | |||
| (holder.binding as ItemVocabularyLevelBinding).run { | |||
| iv.setImageResource(it.icon) | |||
| tvLevel.text = it.level | |||
| tvLevelE.text = it.level_e | |||
| root.click { | |||
| // vm.queryVocabulary(it) | |||
| iv.setImageResource(item.icon) | |||
| tvLevel.text = item.level | |||
| tvLevelE.text = item.level_e | |||
| root.click { v -> | |||
| startTest(item) | |||
| } | |||
| } | |||
| } | |||
| @@ -47,8 +53,25 @@ class TestVocabularyActivity : BaseActivityVM<ActivityTestVocabularyBinding, Tes | |||
| } | |||
| override fun loadData() { | |||
| LiveEventBus.get<VocabularyItem>("vocabulary_test").observe(this){ | |||
| it?.let { | |||
| startTest(it) | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 开始测试 | |||
| * @param item VocabularyItem | |||
| */ | |||
| private fun startTest(item:VocabularyItem){ | |||
| vm.queryVocabularyTestData(item).observe(this@TestVocabularyActivity){ list -> | |||
| val examData = ExamData(AppConstants.SUBJECT_ENGLISH,AppConstants.TEST_TYPE_NORMAL,"词汇量测试","词汇量测试:${item.level}").apply { | |||
| testData = list | |||
| vocabularyItem = item | |||
| } | |||
| DataTransferHolder.instance.putData(key = AppConfig.INTENT_1,examData) | |||
| startActivity(LearnExamActivity::class.java) | |||
| } | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ import com.xkl.cdl.R | |||
| import com.xkl.cdl.data.bean.VocabularyItem | |||
| import com.xkl.cdl.data.bean.course.ExamBean | |||
| import com.xkl.cdl.data.manager.db.DBCourseManager | |||
| import com.xkl.cdl.data.manager.db.VocabularyManager | |||
| import io.reactivex.rxjava3.core.Observable | |||
| /** | |||
| @@ -15,16 +16,25 @@ import io.reactivex.rxjava3.core.Observable | |||
| * Describe: | |||
| */ | |||
| class TestVocabularyViewModle : BaseViewModel() { | |||
| // VOCABULARY":"{\"list\": | |||
| // [{\"id\":10,\"title\":\"综合测试\",\"title_en\":\"Comprehensive test\",\"vocabulary\":13000,}, | |||
| // {\"id\":2,\"title\":\"小学\",\"title_en\":\"Primary school\",\"vocabulary\":1000,}, | |||
| // {\"id\":3,\"title\":\"初中\",\"title_en\":\"Junior high school\",\"vocabulary\":2100,}, | |||
| // {\"id\":4,\"title\":\"高中\",\"title_en\":\"Senior high school\",\"vocabulary\":3500,}, | |||
| // {\"id\":5,\"title\":\"大学四级\",\"title_en\":\"College English Test Band 4\",\"vocabulary\":4200,}, | |||
| // {\"id\":6,\"title\":\"大学六级\",\"title_en\":\"College English Test Band 6\",\"vocabulary\":6000,}, | |||
| // {\"id\":7,\"title\":\"雅思\",\"title_en\":\"Lelts\",\"vocabulary\":7500,}, | |||
| // {\"id\":8,\"title\":\"托福\",\"title_en\":\"Toefl\",\"vocabulary\":8000,], | |||
| val data = mutableListOf( | |||
| VocabularyItem(R.mipmap.ic_comprehensive_test,"综合测试",""), | |||
| VocabularyItem(R.mipmap.ic_primary_school,"小学",""), | |||
| VocabularyItem(R.mipmap.ic_junior_school,"初中",""), | |||
| VocabularyItem(R.mipmap.ic_high_school,"高中",""), | |||
| VocabularyItem(R.mipmap.ic_university_4,"大学四级",""), | |||
| VocabularyItem(R.mipmap.ic_university_6,"大学六级",""), | |||
| VocabularyItem(R.mipmap.ic_lelts,"雅思",""), | |||
| VocabularyItem(R.mipmap.ic_toefl,"托福","") ) | |||
| VocabularyItem(R.mipmap.ic_comprehensive_test,"综合测试","Comprehensive test",10,13000), | |||
| VocabularyItem(R.mipmap.ic_primary_school,"小学","Primary school",2,1000), | |||
| VocabularyItem(R.mipmap.ic_junior_school,"初中","Junior high school",3,2100), | |||
| VocabularyItem(R.mipmap.ic_high_school,"高中","Senior high school",4,3500), | |||
| VocabularyItem(R.mipmap.ic_university_4,"大学四级","College English Test Band 4",5,4200), | |||
| VocabularyItem(R.mipmap.ic_university_6,"大学六级","College English Test Band 6",6,6000), | |||
| VocabularyItem(R.mipmap.ic_lelts,"雅思","Lelts",7,7500), | |||
| VocabularyItem(R.mipmap.ic_toefl,"托福","Toefl",8,8000) ) | |||
| /** | |||
| * 查询测试数据 | |||
| @@ -33,7 +43,7 @@ class TestVocabularyViewModle : BaseViewModel() { | |||
| fun queryVocabularyTestData(item : VocabularyItem) : MutableLiveData<List<ExamBean>> { | |||
| val result = MutableLiveData<List<ExamBean>>() | |||
| Observable.create<List<ExamBean>> { | |||
| it.onNext(DBCourseManager.queryLearnTest(dbControlBase, testType, lesson)) | |||
| it.onNext(VocabularyManager.queryVocabularyTestData(item)) | |||
| it.onComplete() | |||
| }.compose(diskIo2Main()).subscribe({ | |||
| result.value = it | |||
| @@ -55,11 +55,11 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>() { | |||
| //复制词典 和 词汇量测试 | |||
| val dictionary = FilePathManager.getDictionaryDbPath() | |||
| if (!dictionary.exists()) { | |||
| val copyAssetResult = FileUtil.copyAsset("dictionary.db", dictionary) | |||
| FileUtil.copyAsset("dictionary.db", dictionary) | |||
| } | |||
| val vocabulary = FilePathManager.getVocabularyDbPath() | |||
| if (!dictionary.exists()) { | |||
| val copyAssetResult = FileUtil.copyAsset("vocabulary.db", vocabulary) | |||
| if (!vocabulary.exists()) { | |||
| FileUtil.copyAsset("vocabulary.db", vocabulary) | |||
| } | |||
| // TODO: 2022/3/22 读取当前app绑定的课程数据, | |||
| @@ -0,0 +1,138 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||
| xmlns:tools="http://schemas.android.com/tools"> | |||
| <data> | |||
| <variable | |||
| name="vm" | |||
| type="com.xkl.cdl.module.m_service_center.DictionaryViewModel" /> | |||
| </data> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| tools:context=".module.m_service_center.DictionaryActivity" | |||
| android:background="@color/white"> | |||
| <com.suliang.common.widget.TitleBar | |||
| android:id="@+id/title_bar" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="@dimen/title_bar_height" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <com.google.android.material.imageview.ShapeableImageView | |||
| android:id="@+id/iv" | |||
| android:layout_width="72dp" | |||
| android:layout_height="72dp" | |||
| android:src="@drawable/ic_dictionary" | |||
| android:background="#1A58D4DB" | |||
| app:shapeAppearance="@style/CircleStyle" | |||
| app:contentPadding="@dimen/global_spacing" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/title_bar" /> | |||
| <TextView | |||
| android:id="@+id/tv_title" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="@string/xkl_dictionary" | |||
| app:layout_constraintTop_toBottomOf="@+id/iv" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| android:textSize="22dp" | |||
| android:textColor="@color/main_text_color" | |||
| android:textStyle="bold" | |||
| android:layout_marginTop="12dp" /> | |||
| <com.suliang.common.widget.InputSearchEditText | |||
| android:id="@+id/et_search" | |||
| android:layout_width="0dp" | |||
| android:layout_height="40dp" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_title" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| android:layout_marginTop="24dp" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:background="@drawable/shape_rounder_8_stroke_gray1" | |||
| android:paddingStart="@dimen/global_spacing" | |||
| android:paddingEnd="@dimen/global_spacing" | |||
| android:drawablePadding="4dp" | |||
| android:textSize="@dimen/smallSize" | |||
| android:gravity="center_vertical" | |||
| android:hint="输入你要搜索的内容…" | |||
| android:text="@{vm.etSearchLiveData}"/> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_detail" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/et_search" | |||
| android:layout_marginTop="12dp" | |||
| android:background="@drawable/shape_rounder_8_solid_green1" | |||
| android:backgroundTint="@color/gray_3" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginEnd="@dimen/global_spacing"> | |||
| <View | |||
| android:id="@+id/v_line" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="@dimen/line_height" | |||
| android:background="@color/gray_1" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:layout_marginTop="40dp" /> | |||
| <TextView | |||
| android:id="@+id/tv_history_title" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textColor="@color/gray_2" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintBottom_toTopOf="@+id/v_line" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| android:text="历史搜索记录" | |||
| android:layout_marginStart="@dimen/global_spacing" /> | |||
| <ImageView | |||
| android:id="@+id/iv_delete" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="0dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintBottom_toTopOf="@+id/v_line" | |||
| android:paddingEnd="8dp" | |||
| android:paddingStart="8dp" | |||
| android:layout_marginEnd="4dp" | |||
| android:src="@drawable/ic_rubbish" /> | |||
| <androidx.recyclerview.widget.RecyclerView | |||
| android:id="@+id/rv" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| app:layout_constraintTop_toBottomOf="@+id/v_line" /> | |||
| <TextView | |||
| android:id="@+id/tv_see_more" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="40dp" | |||
| android:textSize="@dimen/smallerSize" | |||
| android:textColor="@color/gray_2" | |||
| app:layout_constraintTop_toBottomOf="@+id/rv" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| android:text="查看更多" | |||
| android:drawableEnd="@drawable/ic_down" | |||
| android:drawableTint="@color/gray_2" | |||
| android:gravity="center_vertical" | |||
| android:drawablePadding="4dp" /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </layout> | |||
| @@ -40,6 +40,7 @@ | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/title_bar" | |||
| android:visibility="gone" | |||
| tools:visibility="visible" | |||
| > | |||
| @@ -213,6 +214,7 @@ | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/title_bar" | |||
| app:layout_constraintVertical_bias="0" | |||
| android:visibility="gone" | |||
| tools:visibility="gone"> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| @@ -20,10 +20,6 @@ | |||
| android:layout_height="@dimen/title_bar_height" | |||
| app:titleTextValue="@string/test_vocabulary"/> | |||
| <View | |||
| android:layout_width="match_parent" | |||
| android:layout_height="@dimen/line_height" | |||
| android:background="@color/gray_1"/> | |||
| <TextView | |||
| android:layout_width="wrap_content" | |||
| @@ -0,0 +1,198 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||
| xmlns:tools="http://schemas.android.com/tools"> | |||
| <data> | |||
| </data> | |||
| <LinearLayout | |||
| android:layout_width="300dp" | |||
| android:layout_height="wrap_content" | |||
| android:orientation="vertical"> | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:id="@+id/layout_vocabulary" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:background="@drawable/shape_rounder_12_white"> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="32dp" | |||
| tools:text="学考乐词汇量测试:小学" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:textStyle="bold"/> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary_test_time_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="8dp" | |||
| android:text="@string/test_time_1" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_vocabulary_test_time" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@id/tv_vocabulary" | |||
| app:layout_constraintHorizontal_chainStyle="packed"/> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary_test_time" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintBaseline_toBaselineOf="@+id/tv_vocabulary_test_time_flag" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_vocabulary_test_time_flag" | |||
| tools:text="时间" /> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary_count_flag" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="24dp" | |||
| android:text="@string/vocabulary" | |||
| 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_vocabulary_test_time_flag" /> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary_count" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:textColor="@color/red_1" | |||
| android:textSize="60dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_vocabulary_count_flag" | |||
| tools:text="1000" /> | |||
| <com.xl.ratingbar.MyRatingBar | |||
| android:id="@+id/rating_bar" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="24dp" | |||
| android:layout_marginTop="12dp" | |||
| android:clickable="false" | |||
| android:gravity="center" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_vocabulary_count" | |||
| app:starEmpty="@drawable/ic_star" | |||
| app:starFill="@drawable/ic_star_1" | |||
| app:starHalf="@drawable/ic_star_1" | |||
| app:starCount = "5" | |||
| tools:starNum = "2" | |||
| app:starImageWidth="24dp" | |||
| app:starImageHeight="24dp"/> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary_cover_rate_stage" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="8dp" | |||
| 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/rating_bar" | |||
| tools:text="覆盖率67%(高中)"/> | |||
| <TextView | |||
| android:id="@+id/tv_vocabulary_little" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="@dimen/global_spacing" | |||
| android:text="词汇量较少,建议再测一次" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/normalSize" | |||
| android:drawableTop="@mipmap/cry" | |||
| android:drawablePadding="12dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@id/tv_vocabulary_test_time" | |||
| android:visibility="gone" | |||
| tools:visibility="gone"/> | |||
| <androidx.constraintlayout.widget.Group | |||
| android:id="@+id/group_vocabulary_normal" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| app:constraint_referenced_ids="tv_vocabulary_count_flag,tv_vocabulary_count,rating_bar,tv_vocabulary_cover_rate_stage" | |||
| android:visibility="gone" | |||
| tools:visibility="visible"/> | |||
| <androidx.constraintlayout.widget.Barrier | |||
| android:id="@+id/barrier" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| app:barrierDirection="bottom" | |||
| app:barrierMargin="@dimen/smallerSize" | |||
| app:constraint_referenced_ids="tv_vocabulary_cover_rate_stage,tv_vocabulary_little"/> | |||
| <View | |||
| android:id="@+id/bottom_top_line" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="1dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| android:background="@color/gray_3" | |||
| app:layout_constraintTop_toBottomOf="@+id/barrier" | |||
| android:layout_marginTop="@dimen/global_spacing"/> | |||
| <TextView | |||
| android:id="@+id/tv_right" | |||
| android:layout_width="0dp" | |||
| android:layout_height="40dp" | |||
| android:gravity="center" | |||
| android:text="@string/test_again" | |||
| android:textColor="@color/theme_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/bottom_top_line" | |||
| app:layout_constraintStart_toEndOf="@id/vSplit" | |||
| /> | |||
| <View | |||
| android:id="@+id/vSplit" | |||
| android:layout_width="1dp" | |||
| android:layout_height="0dp" | |||
| app:layout_constraintEnd_toStartOf="@+id/tv_right" | |||
| app:layout_constraintStart_toEndOf="@+id/tv_left" | |||
| android:background="@color/gray_3" | |||
| app:layout_constraintTop_toTopOf="@+id/tv_right" | |||
| app:layout_constraintBottom_toBottomOf="@+id/tv_right"/> | |||
| <!--左边按钮--> | |||
| <TextView | |||
| android:id="@+id/tv_left" | |||
| android:layout_width="0dp" | |||
| android:layout_height="40dp" | |||
| android:gravity="center" | |||
| android:text="@string/done" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toStartOf="@+id/vSplit" | |||
| app:layout_constraintTop_toTopOf="@+id/vSplit" | |||
| app:layout_constraintBottom_toBottomOf="@+id/vSplit" | |||
| /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| </LinearLayout> | |||
| </layout> | |||
| @@ -0,0 +1,47 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||
| xmlns:tools="http://schemas.android.com/tools"> | |||
| <TextView | |||
| android:id="@+id/tv_word" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginTop="16dp" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:text="jaapjaapjaap" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="22dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintHorizontal_bias="0.0" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <TextView | |||
| android:id="@+id/tv_phonetic" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center_vertical" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallerSize" | |||
| app:layout_constraintEnd_toEndOf="@+id/tv_word" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_word" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_word" | |||
| tools:text="[gud:bai] " | |||
| app:layout_constraintHorizontal_bias="0" | |||
| android:layout_marginTop="8dp"/> | |||
| <androidx.recyclerview.widget.RecyclerView | |||
| android:id="@+id/recyclerView" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_phonetic" | |||
| /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| @@ -0,0 +1,55 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||
| android:orientation="horizontal" | |||
| android:gravity="center_vertical"> | |||
| <TextView | |||
| android:id="@+id/tv_word" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:singleLine="true" | |||
| tools:text="introduction" | |||
| android:textSize="@dimen/normalSize" | |||
| android:textColor="@color/main_text_color" | |||
| android:layout_marginTop="8dp" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintStart_toEndOf="@+id/iv_horuns" | |||
| android:gravity="center_vertical" | |||
| /> | |||
| <ImageView | |||
| android:id="@+id/iv_horuns" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| tools:src="@drawable/ic_horuns" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginEnd="8dp" | |||
| app:layout_constraintTop_toTopOf="@+id/tv_word" | |||
| app:layout_constraintBottom_toBottomOf="@+id/tv_word" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| android:paddingRight="4dp"/> | |||
| <TextView | |||
| android:id="@+id/tv_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:textSize="@dimen/smallerSize" | |||
| android:textColor="@color/gray_2" | |||
| tools:text="简介" | |||
| app:layout_constrainedWidth="true" | |||
| app:layout_constraintHorizontal_bias="0" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_word" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| android:layout_marginBottom="8dp" | |||
| android:layout_marginTop="4dp"/> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| @@ -0,0 +1,94 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||
| xmlns:tools="http://schemas.android.com/tools"> | |||
| <TextView | |||
| android:id="@+id/tv_word" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginTop="16dp" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:text="jaapjaapjaap" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="22dp" | |||
| android:textStyle="bold" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintHorizontal_bias="0.0" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <com.suliang.common.widget.TagLinearLayout | |||
| android:id="@+id/layout_phonetic" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| app:item_horizontal_space="16dp" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_word" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_word" | |||
| android:layout_marginTop="8dp" | |||
| android:layout_marginEnd="@dimen/global_spacing"> | |||
| <TextView | |||
| android:id="@+id/tv_phonetic_uk" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:drawableEnd="@drawable/ic_horuns" | |||
| android:gravity="center" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallerSize" | |||
| tools:text="英 [gud:bai] " /> | |||
| <TextView | |||
| android:id="@+id/tv_phonetic_us" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:drawableEnd="@drawable/ic_horuns" | |||
| android:gravity="center" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallerSize" | |||
| tools:text="美 [gud:bai] " /> | |||
| </com.suliang.common.widget.TagLinearLayout> | |||
| <TextView | |||
| android:id="@+id/tv_explain" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="10dp" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:textColor="@color/main_text_color" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintEnd_toEndOf="parent" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_word" | |||
| app:layout_constraintTop_toBottomOf="@+id/layout_phonetic" | |||
| app:layout_constraintHorizontal_bias="0" | |||
| android:textStyle="bold" | |||
| tools:text="基本释义"/> | |||
| <TextView | |||
| android:id="@+id/tv_expand_explain" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| android:layout_marginTop="2dp" | |||
| android:textColor="@color/gray_2" | |||
| android:textSize="@dimen/smallSize" | |||
| app:layout_constraintEnd_toEndOf="@+id/tv_word" | |||
| app:layout_constraintStart_toStartOf="@+id/tv_word" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_explain" | |||
| tools:text="扩展释义>"/> | |||
| <include | |||
| android:id="@+id/inc_word_detail" | |||
| android:layout_width="0dp" | |||
| android:layout_height="wrap_content" | |||
| layout="@layout/inc_word_detail" | |||
| app:layout_constraintTop_toBottomOf="@+id/tv_expand_explain" | |||
| app:layout_constraintStart_toStartOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent"/> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| @@ -0,0 +1,40 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="32dp" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| android:orientation="horizontal" | |||
| android:gravity="center_vertical"> | |||
| <ImageView | |||
| android:id="@+id/iv" | |||
| android:layout_width="16dp" | |||
| android:layout_height="16dp" | |||
| tools:src="@drawable/ic_clock" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:layout_marginEnd="8dp"/> | |||
| <TextView | |||
| android:id="@+id/tv_word" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:singleLine="true" | |||
| tools:text="jaap" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textColor="@color/main_text_color" | |||
| /> | |||
| <TextView | |||
| android:id="@+id/tv_value" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:singleLine="true" | |||
| android:ellipsize="end" | |||
| android:layout_marginEnd="@dimen/global_spacing" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textColor="@color/main_text_color" | |||
| tools:text="治疗"/> | |||
| </LinearLayout> | |||
| @@ -69,7 +69,7 @@ | |||
| tools:text="12:12:12" | |||
| android:textSize="@dimen/smallSize" | |||
| android:textColor="@color/gray_2" | |||
| android:layout_marginStart="24dp" /> | |||
| android:layout_marginStart="12dp" /> | |||
| <TextView | |||
| android:id="@+id/tv_score" | |||
| @@ -10,7 +10,7 @@ | |||
| <androidx.constraintlayout.widget.ConstraintLayout | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:background="@drawable/shape_rounder_12_white" | |||
| android:background="@drawable/shape_rounder_8_stroke_gray1" | |||
| android:padding="@dimen/global_spacing" | |||
| android:layout_marginBottom="12dp" | |||
| android:layout_marginStart="@dimen/global_spacing" | |||
| @@ -99,5 +99,9 @@ | |||
| <string name="vocabulary_level">测试等级:</string> | |||
| <string name="vocabulary">词汇量</string> | |||
| <string name="test_vocabulary">测词汇量</string> | |||
| <string name="done">完成</string> | |||
| <string name="xkl_dictionary">学考乐英汉双向词典</string> | |||
| <string name="dialog_delete_history">你确定要删除历史记录吗?</string> | |||
| <string name="delete">删除</string> | |||
| </resources> | |||
| @@ -0,0 +1,20 @@ | |||
| <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="M0,0h16v16h-16z" | |||
| android:strokeAlpha="0" | |||
| android:strokeWidth="1" | |||
| android:fillColor="#000000" | |||
| android:fillType="nonZero" | |||
| android:strokeColor="#00000000" | |||
| android:fillAlpha="0"/> | |||
| <path | |||
| android:pathData="M8,1.6C11.5346,1.6 14.4,4.4654 14.4,8C14.4,11.5346 11.5346,14.4 8,14.4C4.4654,14.4 1.6,11.5346 1.6,8C1.6,4.4654 4.4654,1.6 8,1.6ZM8,2.6667C5.0545,2.6667 2.6667,5.0545 2.6667,8C2.6667,10.9455 5.0545,13.3333 8,13.3333C10.9455,13.3333 13.3333,10.9455 13.3333,8C13.3333,5.0545 10.9455,2.6667 8,2.6667ZM8,4.2667C8.2735,4.2667 8.4989,4.4726 8.5297,4.7378L8.5333,4.8L8.5333,7.7141L10.8715,9.2699C11.0992,9.4214 11.1728,9.7177 11.0515,9.9556L11.0201,10.0094C10.8686,10.2371 10.5723,10.3107 10.3344,10.1895L10.2806,10.158L7.7046,8.444C7.5745,8.3575 7.4899,8.2191 7.4708,8.0663L7.4667,8L7.4667,4.8C7.4667,4.5054 7.7054,4.2667 8,4.2667Z" | |||
| android:strokeWidth="1" | |||
| android:fillColor="#8A8A99" | |||
| android:fillType="evenOdd" | |||
| android:strokeColor="#00000000"/> | |||
| </vector> | |||
| @@ -0,0 +1,12 @@ | |||
| <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="M9.9556,1.6C10.2055,1.6 10.422,1.7736 10.4762,2.0176L10.4762,2.0176L10.774,3.36L13.8667,3.36C14.1612,3.36 14.4,3.5988 14.4,3.8933C14.4,4.1668 14.1941,4.3923 13.9289,4.4231L13.8667,4.4267L12.64,4.426L12.64,12.8C12.64,13.6346 12.001,14.3199 11.1856,14.3935L11.04,14.4L4.96,14.4C4.0763,14.4 3.36,13.6837 3.36,12.8L3.36,12.8L3.359,4.426L2.1333,4.4267C1.8388,4.4267 1.6,4.1879 1.6,3.8933C1.6,3.6198 1.8059,3.3944 2.0711,3.3636L2.1333,3.36L5.225,3.36L5.5238,2.0176C5.5713,1.8041 5.7429,1.6445 5.9525,1.6079L6.0444,1.6ZM11.5733,4.4267L4.4267,4.4267L4.4267,12.8C4.4267,13.0735 4.6326,13.2989 4.8978,13.3297L4.8978,13.3297L4.96,13.3333L11.04,13.3333C11.3346,13.3333 11.5733,13.0946 11.5733,12.8L11.5733,12.8L11.5733,4.4267ZM6.8267,6.2933C7.1002,6.2933 7.3256,6.4992 7.3564,6.7645L7.36,6.8267L7.36,10.9333C7.36,11.2279 7.1212,11.4667 6.8267,11.4667C6.5532,11.4667 6.3277,11.2608 6.2969,10.9955L6.2933,10.9333L6.2933,6.8267C6.2933,6.5321 6.5321,6.2933 6.8267,6.2933ZM9.1733,6.2933C9.4468,6.2933 9.6723,6.4992 9.7031,6.7645L9.7067,6.8267L9.7067,10.9333C9.7067,11.2279 9.4679,11.4667 9.1733,11.4667C8.8998,11.4667 8.6744,11.2608 8.6436,10.9955L8.64,10.9333L8.64,6.8267C8.64,6.5321 8.8788,6.2933 9.1733,6.2933ZM9.5275,2.6667L6.472,2.6667L6.3179,3.36L9.6816,3.36L9.5275,2.6667Z" | |||
| android:strokeWidth="1" | |||
| android:fillColor="#9A9EB3" | |||
| android:fillType="evenOdd" | |||
| android:strokeColor="#00000000"/> | |||
| </vector> | |||
| @@ -1,22 +1,17 @@ | |||
| package com.suliang.common.base | |||
| import android.R.attr | |||
| import android.graphics.Color | |||
| import android.graphics.drawable.ColorDrawable | |||
| import android.os.Bundle | |||
| import android.view.Gravity | |||
| import android.view.LayoutInflater | |||
| import android.view.View | |||
| import android.view.ViewGroup | |||
| import androidx.fragment.app.DialogFragment | |||
| import androidx.fragment.app.FragmentManager | |||
| import androidx.viewbinding.ViewBinding | |||
| import com.suliang.common.extension.initBinding | |||
| import android.graphics.drawable.ColorDrawable | |||
| import android.util.DisplayMetrics | |||
| import androidx.fragment.app.FragmentManager | |||
| import androidx.fragment.app.FragmentTransaction | |||
| import android.R.attr.tag | |||
| import androidx.fragment.app.Fragment | |||
| import java.lang.IllegalStateException | |||
| import com.suliang.common.util.os.ScreenUtil | |||
| /** | |||
| @@ -33,6 +28,8 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment() { | |||
| override fun onCreateView( | |||
| inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? | |||
| ): View? { | |||
| //设置透明背景,避免DecorView的自带padding | |||
| dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) | |||
| _binding = initBinding(inflater, container) | |||
| return binding.root | |||
| } | |||
| @@ -52,6 +49,19 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment() { | |||
| //设置背景透明 | |||
| dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) | |||
| } | |||
| resizeDialog() | |||
| } | |||
| protected open fun resizeDialog() { | |||
| //设置dialog位置 | |||
| dialog?.window?.let { | |||
| it.attributes.run { | |||
| this.y = ScreenUtil.getScreenHeight() / 5 | |||
| this.width = ScreenUtil.dp2px(300f).toInt() | |||
| it.attributes = this | |||
| } | |||
| it.setGravity(Gravity.TOP) | |||
| } | |||
| } | |||
| abstract fun initFragment() | |||