@@ -25,7 +25,6 @@ | |||
</formatting-settings> | |||
</DBN-SQL> | |||
<JetCodeStyleSettings> | |||
<option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="true" /> | |||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> | |||
</JetCodeStyleSettings> | |||
<DBN-PSQL> | |||
@@ -53,6 +52,7 @@ | |||
</formatting-settings> | |||
</DBN-SQL> | |||
<codeStyleSettings language="XML"> | |||
<option name="FORCE_REARRANGE_MODE" value="1" /> | |||
<indentOptions> | |||
<option name="CONTINUATION_INDENT_SIZE" value="4" /> | |||
</indentOptions> | |||
@@ -168,10 +168,6 @@ | |||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> | |||
<option name="RIGHT_MARGIN" value="120" /> | |||
<option name="KEEP_LINE_BREAKS" value="false" /> | |||
<option name="CALL_PARAMETERS_WRAP" value="0" /> | |||
<option name="METHOD_PARAMETERS_WRAP" value="1" /> | |||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" /> | |||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" /> | |||
</codeStyleSettings> | |||
</code_scheme> | |||
</component> |
@@ -3,7 +3,40 @@ | |||
<component name="DesignSurface"> | |||
<option name="filePathToZoomLevelMap"> | |||
<map> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/drawable/svg/drawable/ic_delete.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_my.xml" value="0.3223958333333333" /> | |||
<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_discern.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_launcher_background.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_nav_my.xml" value="0.3223958333333333" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_search.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_spell.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/ic_voice.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/nav_learn_center.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/nav_memo.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/nav_my.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/nav_service.xml" value="0.30520833333333336" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/nav_statistics.xml" value="0.1" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/drawable/theme_splash_bg.xml" value="0.30520833333333336" /> | |||
<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/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/textview.xml" value="0.49773550724637683" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/src/main/res/layout/textview_only.xml" value="0.49773550724637683" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/statedrawable/drawable/ic_nav_learn_center.xml" value="0.44166666666666665" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_learn_center.xml" value="0.44166666666666665" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/app/svg/drawable/ic_nav_memo.xml" value="0.21574074074074073" /> | |||
<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_statistics.xml" value="0.44166666666666665" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/lib/common/src/main/res/drawable/ic_search.xml" value="0.19814814814814816" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/lib/common/src/main/res/drawable/loading_bg.xml" value="0.5061538461538462" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/lib/common/src/main/res/layout/loadding_fragment.xml" value="0.4817708333333333" /> | |||
<entry key="..\:/Work/XKL/XKL/XklLocal/lib/common/src/main/res/layout/loading_fragment.xml" value="0.4979166666666667" /> | |||
<entry key="..\:/Work/XKL/XklLocal/app/src/main/res/drawable/theme_splash_bg.xml" value="0.22" /> | |||
<entry key="..\:/Work/XKL/XklLocal/app/src/main/res/layout/activity_fullscreen.xml" value="0.10144927536231885" /> | |||
<entry key="..\:/Work/XKL/XklLocal/app/src/main/res/layout/activity_main.xml" value="0.1" /> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="SQLAndroidSettings"> | |||
<option name="storageVersion" value="2" /> | |||
</component> | |||
</project> |
@@ -1,4 +1,22 @@ | |||
项目中遇到的错误: | |||
使用jectPack时的依赖: | |||
· androidx.appcompat: appcompat 依赖中已经带了livedata、viewmodel等,可以不用再依赖,再依赖时出现了异常编译错误,未解决 | |||
且 依赖版本不同,对应的livedata等版本也不同 | |||
项目中遇到的错误: 使用jectPack时的依赖: · androidx.appcompat: appcompat | |||
依赖中已经带了livedata、viewmodel等,可以不用再依赖,再依赖时出现了异常编译错误,未解决 且 依赖版本不同,对应的livedata等版本也不同 | |||
kotlin 泛型 单例实现 扩展实现 导航实现 | |||
svg | |||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:name="triangle" //定义矢量图的名称 | |||
android:height="64dp" //drawable的固定高度,支持所有的尺寸单位,一般使用dp | |||
android:width="64dp" //drawable的固定宽度,支持所有的尺寸单位,一般使用dp | |||
android:viewportHeight="600"//视图的高度,可以理解为画布的高度 | |||
android:viewportWidth="600" > //视图的宽度,下面的pathData中的内容便会在600宽高的画布内操作 | |||
<group //定义一个组,可以包含path 及子group, 同时可以定义转换信息,如旋转,伸缩,位移 | |||
android:name="rotationGroup"//组名 | |||
android:pivotX="300.0" //X坐标中心点,默认为0 | |||
android:pivotY="300.0" //Y坐标中心点,默认为0 | |||
android:rotation="45.0" > //旋转角度,顺时针 | |||
<path android:name="v" //路径的名称 | |||
android:fillColor="#000000"//填充颜色 | |||
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /> //路径的数据 | |||
</group> | |||
</vector> |
@@ -15,5 +15,21 @@ module : 按模块进行业务划分 | |||
common: | |||
app: 获取application | |||
base: 存放业务的基础类 | |||
activity、fragment、adapter的基类 | |||
eventbus: livedata的事件总线 | |||
util : 工具类,包含Kotlin的扩展属性 | |||
粘性事件: 不管先注册还是先发送消息,都能收到消息 | |||
非粘性事件: 必须先注册,再发送消息,如果先发送消息,再注册,则先发送的消息是监听不到 | |||
util : 工具类,包含Kotlin的扩展属性 | |||
media包: 实现音视频的播放封装 | |||
test音频: val source = listOf("http://ws.stream.qqmusic.qq.com/C4000017AcyB00D6Cr.m4a?guid=395745734&vkey=9CF75D8FDE45B1A7755C15F93C5F74CC18E444873F498DF3F5B9F0B41889778BE0EAC45305BD444F90D777C612BC5865DB0AF55A318854E1&uin=&fromtag=66" | |||
,"http://ws.stream.qqmusic.qq.com/C400001lePVO36SNSo.m4a?guid=748677131&vkey=A6077E31CB4A1C8A4F3D5D2BEDE8D3E16AB69FF2FEE95D9D48288EAD048AB89DC53B6846D184F842257617AF9714D97C864FB183BE05CC66&uin=&fromtag=66" | |||
,"http://ws.stream.qqmusic.qq.com/C400000ZFBf22vBvrf.m4a?guid=921100582&vkey=C9EA33972404E2AC1C80A479614AD44A9BF132A16F9B307D9515F595B41901AE53F6681E28BDA799FC047F30D624F8A19AE9C4856ADBEF4B&uin=&fromtag=66") | |||
image包: 实现了图片加载的封装 | |||
Kotlin的单例模式: https://developer.aliyun.com/article/642618 | |||
饿汉式(直接获取实例)、懒汉式(一次判空获取实例)、双重校验锁 | |||
object 声明: 为饿汉式单例,即直接实例 |
@@ -60,11 +60,18 @@ android { | |||
viewBinding true | |||
dataBinding true | |||
} | |||
sourceSets { | |||
main { | |||
res.srcDirs("svg") | |||
} | |||
} | |||
} | |||
dependencies { | |||
implementation 'androidx.legacy:legacy-support-v4:1.0.0' | |||
implementation project(path: ':lib:common') | |||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' | |||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' | |||
rootProject.ext.dependencies_required.each{ k, v -> implementation v} | |||
testImplementation rootProject.ext.dependencies_testImplementation.junit | |||
rootProject.ext.dependencies_androidTestImplementation.each{ k,v -> androidTestImplementation v} | |||
@@ -81,7 +88,7 @@ dependencies { | |||
// // Lifecycles only (without ViewModel or LiveData) | |||
// implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" | |||
// | |||
// // Saved state module for ViewModel | |||
// // Saved statedrawable module for ViewModel | |||
// implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" | |||
// | |||
// // Annotation processor |
@@ -1,5 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
package="com.xkl.cdl"> | |||
<application | |||
@@ -8,12 +9,13 @@ | |||
android:label="@string/app_name" | |||
android:roundIcon="@mipmap/ic_launcher_round" | |||
android:supportsRtl="true" | |||
android:theme="@style/Theme.XklLocal"> | |||
android:theme="@style/Theme.XklLocal" | |||
tools:ignore="LockedOrientationActivity"> | |||
<activity | |||
android:name=".module.splash.SplashActivity" | |||
android:configChanges="orientation|keyboardHidden|screenSize" | |||
android:screenOrientation="portrait" | |||
android:exported="true" | |||
android:screenOrientation="portrait" | |||
android:theme="@style/Theme.Splash"> | |||
<intent-filter> | |||
<action android:name="android.intent.action.MAIN" /> | |||
@@ -22,7 +24,10 @@ | |||
</activity> | |||
<activity | |||
android:name=".module.main.MainActivity" | |||
android:configChanges="orientation|keyboardHidden|screenSize"/> | |||
android:configChanges="layoutDirection|keyboard|screenSize|locale" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="portrait" | |||
android:windowSoftInputMode="adjustPan|adjustResize|stateHidden" /> | |||
</application> | |||
</manifest> |
@@ -0,0 +1,47 @@ | |||
package com.xkl.cdl.adapter | |||
import android.annotation.SuppressLint | |||
import androidx.fragment.app.Fragment | |||
import androidx.fragment.app.FragmentActivity | |||
import androidx.viewpager2.adapter.FragmentStateAdapter | |||
import java.util.* | |||
/** | |||
* author suliang | |||
* create 2022/3/18 10:41 | |||
* Describe: ViewPager适配器基类 | |||
*/ | |||
class ViewPagerAdapter : FragmentStateAdapter { | |||
constructor(fragmentActivity: FragmentActivity) : super(fragmentActivity) | |||
constructor(fragment: Fragment) : super(fragment) | |||
constructor(fragmentActivity: FragmentActivity, fragments: MutableList<Fragment>) : super(fragmentActivity) { | |||
mFragments.addAll(fragments) | |||
} | |||
constructor(fragment: Fragment, fragments: MutableList<Fragment>) : super(fragment) { | |||
mFragments.addAll(fragments) | |||
} | |||
private var mFragments = mutableListOf<Fragment>() | |||
override fun createFragment(position: Int): Fragment { | |||
return mFragments[position] | |||
} | |||
override fun getItemCount(): Int { | |||
return mFragments.size | |||
} | |||
fun getItem(position: Int): Fragment { | |||
return mFragments[position] | |||
} | |||
@SuppressLint("NotifyDataSetChanged") | |||
fun setData(fragments: MutableList<Fragment>) { | |||
mFragments.clear() | |||
mFragments.addAll(fragments) | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
package com.xkl.cdl.data | |||
/** | |||
* author suliang | |||
* create 2022/3/18 9:34 | |||
* Describe: | |||
*/ | |||
object Constants{ | |||
/** 项目: 英语 */ | |||
const val SUBJECT_ENGLISH = 3L | |||
/** 项目: 语文*/ | |||
const val SUBJECT_CHINESE = 1L | |||
/**课程包类型: categoryId -> 英语单词课程 */ | |||
const val COURSEPACK_TYPE_ENGLISH_WORD = 1 | |||
/**课程包类型: categoryId -> 英语音标课程 */ | |||
const val COURSEPACK_TYPE_ENGLISH_SOUNDMARK= 4 | |||
/**课程包类型: categoryId -> 英语口语 */ | |||
const val COURSEPACK_TYPE_ENGLISH_SPOKEN = 13 | |||
/**课程包类型: categoryId -> 语文作文 */ | |||
const val COURSEPACK_TYPE_CHINESE_COMPOSITION = 2 | |||
/**课程包类型: categoryId -> 语文识字 */ | |||
const val COURSEPACK_TYPE_CHINESE_LITERACY = 3 | |||
/**课程包类型: categoryId -> 语文拼音 */ | |||
const val COURSEPACK_TYPE_CHINESE_PINYIN = 1 | |||
/**课程类型: typeId 英语认读*/ | |||
const val COURSE_TYPE_ENGLISH_DISCERN = 1 | |||
/**课程类型: typeId 英语辨音*/ | |||
const val COURSE_TYPE_ENGLISH_VOICE= 2 | |||
/**课程类型: typeId 英语拼写*/ | |||
const val COURSE_TYPE_ENGLISH_SPELL = 3 | |||
/**课程类型: typeId 英语音标*/ | |||
const val COURSE_TYPE_ENGLISH_SOUNDMARK = 6 | |||
/**课程类型: typeId 英语口语*/ | |||
const val COURSE_TYPE_ENGLISH_SPOKEN = 8 | |||
/**课程类型: typeId 语文作文*/ | |||
const val COURSE_TYPE_CHINESE_COMPOSITION = 4 | |||
/**课程类型: typeId 语文识字*/ | |||
const val COURSE_TYPE_CHINESE_LITERACY = 5 | |||
/**课程类型: typeId 语文拼音*/ | |||
const val COURSE_TYPE_CHINESE_PINYIN = 7 | |||
/**课时类型: 作文视频*/ | |||
const val COMPOSITION_TYPE_VIDEO = 1 //视频 | |||
/**课时类型: 作文知识点学习*/ | |||
const val COMPOSITION_TYPE_KNOWLEDGE = 2 //知识点学习 | |||
/**课时类型: 作文知识点测试*/ | |||
const val COMPOSITION_TYPE_EXAM = 3 //知识点测试 | |||
/**课时类型: 作文课堂练习*/ | |||
const val COMPOSITION_TYPE_READING = 4 //课堂练习 | |||
/**课时类型: 作文课外练习*/ | |||
const val COMPOSITION_TYPE_TASK = 5 //课外练习 | |||
/**课时类型: 口语词汇*/ | |||
const val SPOKEN_LESSON_TYPE_WORD = 1 | |||
/**课时类型: 口语句型*/ | |||
const val SPOKEN_LESSON_TYPE_SENTENCE = 2 | |||
/**课时类型: 口语对话*/ | |||
const val SPOKEN_LESSON_TYPE_DIALOGUE = 3 | |||
} |
@@ -0,0 +1,11 @@ | |||
package com.xkl.cdl.data.course | |||
import androidx.databinding.BaseObservable | |||
import androidx.databinding.Bindable | |||
/** | |||
* author suliang | |||
* create 2022/3/17 18:12 | |||
* Describe: | |||
*/ | |||
data class CoursePack(@Bindable var id : Long) : BaseObservable() |
@@ -0,0 +1,18 @@ | |||
package com.xkl.cdl.data.manager | |||
import com.xkl.cdl.data.course.CoursePack | |||
/** | |||
* author suliang | |||
* create 2022/3/17 18:10 | |||
* Describe: 课程管理类 | |||
*/ | |||
object CourseManager { | |||
//所有课程 | |||
private var allCoursePack : HashMap<Long, CoursePack> = HashMap() | |||
} |
@@ -0,0 +1,61 @@ | |||
package com.xkl.cdl.module.m_center_learn | |||
import android.os.Bundle | |||
import androidx.lifecycle.ViewModel | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.Config | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.databinding.FragmentCoursePackBinding | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
/** | |||
* 学习中心 子tab 下的详情课程 | |||
* @property mainViewModel MainActivityViewModel | |||
*/ | |||
class CoursePackFragment : BaseFragmentVM<FragmentCoursePackBinding,CoursePackFragmentViewModel>() { | |||
companion object { | |||
@JvmStatic | |||
fun newInstance(projectId: Long) = CoursePackFragment().apply { | |||
arguments = Bundle().apply { | |||
putLong(Config.INTENT_1,projectId) | |||
} | |||
} | |||
} | |||
override fun initViewModel(): CoursePackFragmentViewModel { | |||
val projectId = requireArguments().getLong(Config.INTENT_1) | |||
return ViewModelProvider(this,ViewModelFactory(projectId)).get(CoursePackFragmentViewModel::class.java).apply { | |||
this.mainActivityViewModel = ViewModelProvider(requireActivity()).get(MainActivityViewModel::class.java) | |||
this.learnCenterFragmentViewModel = ViewModelProvider(requireParentFragment()).get(LearnCenterFragmentViewModel::class.java) | |||
} | |||
} | |||
override fun initFragment() { | |||
vm.learnCenterFragmentViewModel.mCoursePackSearchLiveData.observe(this){ | |||
LogUtil.e("课程包列表监听到搜索内容改变: ${vm.projectId}") | |||
} | |||
} | |||
override fun loadData() { | |||
} | |||
/** ViewModle 工厂类,提供传参 */ | |||
inner class ViewModelFactory(private val projectId:Long): ViewModelProvider.Factory{ | |||
@Suppress("UNCHECKED_CAST") | |||
override fun <T : ViewModel?> create(modelClass: Class<T>): T { | |||
return CoursePackFragmentViewModel(projectId) as T | |||
} | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
package com.xkl.cdl.module.m_center_learn | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
class CoursePackFragmentViewModel(val projectId : Long) : BaseViewModel() { | |||
lateinit var mainActivityViewModel: MainActivityViewModel | |||
lateinit var learnCenterFragmentViewModel: LearnCenterFragmentViewModel | |||
} |
@@ -1,60 +1,105 @@ | |||
package com.xkl.cdl.module.m_center_learn | |||
import android.os.Bundle | |||
import androidx.fragment.app.Fragment | |||
import android.graphics.Typeface | |||
import android.util.TypedValue | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import android.widget.TextView | |||
import androidx.core.content.ContextCompat | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.google.android.material.tabs.TabLayout | |||
import com.google.android.material.tabs.TabLayoutMediator | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.R | |||
// TODO: Rename parameter arguments, choose names that match | |||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER | |||
private const val ARG_PARAM1 = "param1" | |||
private const val ARG_PARAM2 = "param2" | |||
import com.xkl.cdl.adapter.ViewPagerAdapter | |||
import com.xkl.cdl.databinding.FragmentLearnCenterBinding | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
/** | |||
* A simple [Fragment] subclass. | |||
* Use the [LearnCenterFragment.newInstance] factory method to | |||
* create an instance of this fragment. | |||
* 学习中心Fragment: 包含 科目 | |||
*/ | |||
class LearnCenterFragment : Fragment() { | |||
// TODO: Rename and change types of parameters | |||
private var param1: String? = null | |||
private var param2: String? = null | |||
override fun onCreate(savedInstanceState: Bundle?) { | |||
super.onCreate(savedInstanceState) | |||
arguments?.let { | |||
param1 = it.getString(ARG_PARAM1) | |||
param2 = it.getString(ARG_PARAM2) | |||
} | |||
class LearnCenterFragment : BaseFragmentVM<FragmentLearnCenterBinding, LearnCenterFragmentViewModel>() { | |||
companion object { | |||
@JvmStatic | |||
fun newInstance() = LearnCenterFragment() | |||
} | |||
override fun onCreateView( | |||
inflater: LayoutInflater, container: ViewGroup?, | |||
savedInstanceState: Bundle? | |||
): View? { | |||
// Inflate the layout for this fragment | |||
return inflater.inflate(R.layout.fragment_learn_center, container, false) | |||
override fun initViewModel(): LearnCenterFragmentViewModel { | |||
return ViewModelProvider(this)[LearnCenterFragmentViewModel::class.java].apply { | |||
this.mainActivityViewModel = ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
} | |||
} | |||
companion object { | |||
/** | |||
* Use this factory method to create a new instance of | |||
* this fragment using the provided parameters. | |||
* | |||
* @param param1 Parameter 1. | |||
* @param param2 Parameter 2. | |||
* @return A new instance of fragment LearnCenterFragment. | |||
*/ | |||
// TODO: Rename and change types and number of parameters | |||
@JvmStatic | |||
fun newInstance(param1: String, param2: String) = | |||
LearnCenterFragment().apply { | |||
arguments = Bundle().apply { | |||
putString(ARG_PARAM1, param1) | |||
putString(ARG_PARAM2, param2) | |||
override fun initFragment() { | |||
//初始化项目子tab和标题 | |||
vm.initLearnCenterFragment() | |||
//必须先进行ViewPager2的适配器 设置 | |||
binding.viewPager2.adapter = ViewPagerAdapter(this, vm.mProjectFragments) | |||
//注册 tabLayout 监听 | |||
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { | |||
/** 选项卡被选中 */ | |||
override fun onTabSelected(tab: TabLayout.Tab?) { | |||
tab?.customView?.let { | |||
(it as TextView).apply { | |||
setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22f) | |||
typeface = Typeface.defaultFromStyle(Typeface.BOLD) | |||
setTextColor(ContextCompat.getColor(context, R.color.main_text_color)) | |||
} | |||
} | |||
//选中项恢复搜索框的数据 | |||
tab?.run { | |||
} | |||
LogUtil.e("选中tab ${tab?.position}") | |||
} | |||
/** 当选项卡被取消选中 */ | |||
override fun onTabUnselected(tab: TabLayout.Tab?) { | |||
LogUtil.e("未选中tab ${tab?.position}") | |||
tab?.customView?.let { | |||
(it as TextView).apply { | |||
setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16f) | |||
typeface = Typeface.defaultFromStyle(Typeface.NORMAL) | |||
setTextColor(ContextCompat.getColor(context, R.color.gray_2)) | |||
} | |||
} | |||
} | |||
/**当用户再次选择 已选中的Tab */ | |||
override fun onTabReselected(tab: TabLayout.Tab?) { | |||
} | |||
}) | |||
//TabLayout 与 ViewPager2 进行关联, 并进行tab设值 | |||
TabLayoutMediator(binding.tabLayout, binding.viewPager2) { tab, position -> | |||
tab.setCustomView(layoutInflater.inflate(R.layout.textview_only,binding.tabLayout,false)) | |||
tab.customView?.let { | |||
(it as TextView).setText(vm.mProjectTitles[position]) | |||
} | |||
}.attach() | |||
// /** 搜索框 输入监听 */ | |||
// binding.editTextSearch.setOnEditorActionListener { v, actionId, event -> | |||
// if (actionId == EditorInfo.IME_ACTION_SEARCH){ | |||
// //隐藏键盘 | |||
// KeyboardUtil.hideKeyboard(v) | |||
// //赋值,通知 子 Fragment 进行更新 | |||
// v.text.toString() | |||
// | |||
// true | |||
// }else { | |||
// false | |||
// } | |||
// } | |||
} | |||
override fun loadData() { | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
package com.xkl.cdl.module.m_center_learn | |||
import androidx.fragment.app.Fragment | |||
import androidx.lifecycle.MutableLiveData | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.data.Constants | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
/** | |||
* author suliang | |||
* create 2022/3/17 11:01 | |||
* Describe: 学习中心ViewModel | |||
*/ | |||
class LearnCenterFragmentViewModel : BaseViewModel() { | |||
lateinit var mainActivityViewModel: MainActivityViewModel | |||
/** 学习中心 子 tab fragment **/ | |||
var mProjectFragments = mutableListOf<Fragment>() | |||
/** 学习中心 子 tab title **/ | |||
var mProjectTitles = mutableListOf<Int>() | |||
/** 搜索框的双向数据 */ | |||
val mCoursePackSearchLiveData = MutableLiveData<String>() | |||
/** 实例化项目课程包Fragment和显示Title */ | |||
fun initLearnCenterFragment(){ | |||
if (mProjectFragments.isEmpty()){ | |||
mProjectFragments.add(CoursePackFragment.newInstance(Constants.SUBJECT_ENGLISH)) | |||
mProjectFragments.add(CoursePackFragment.newInstance(Constants.SUBJECT_CHINESE)) | |||
} | |||
if (mProjectTitles.isEmpty()){ | |||
mProjectTitles.add(R.string.project_english) | |||
mProjectTitles.add(R.string.project_chinese) | |||
} | |||
} | |||
} |
@@ -5,7 +5,12 @@ import androidx.fragment.app.Fragment | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.FragmentMemoBinding | |||
import com.xkl.cdl.databinding.FragmentMyBinding | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
// TODO: Rename parameter arguments, choose names that match | |||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER | |||
@@ -17,7 +22,7 @@ private const val ARG_PARAM2 = "param2" | |||
* Use the [MemoFragment.newInstance] factory method to | |||
* create an instance of this fragment. | |||
*/ | |||
class MemoFragment : Fragment() { | |||
class MemoFragment : BaseFragmentVM<FragmentMemoBinding, MainActivityViewModel>(){ | |||
// TODO: Rename and change types of parameters | |||
private var param1: String? = null | |||
private var param2: String? = null | |||
@@ -49,12 +54,18 @@ class MemoFragment : Fragment() { | |||
*/ | |||
// TODO: Rename and change types and number of parameters | |||
@JvmStatic | |||
fun newInstance(param1: String, param2: String) = | |||
MemoFragment().apply { | |||
arguments = Bundle().apply { | |||
putString(ARG_PARAM1, param1) | |||
putString(ARG_PARAM2, param2) | |||
} | |||
} | |||
fun newInstance() = MemoFragment() | |||
} | |||
override fun initFragment() { | |||
} | |||
override fun loadData() { | |||
} | |||
override fun initViewModel(): MainActivityViewModel { | |||
return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
} | |||
} |
@@ -5,7 +5,12 @@ import androidx.fragment.app.Fragment | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.base.activity.BaseActivityVM | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.FragmentMyBinding | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
// TODO: Rename parameter arguments, choose names that match | |||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER | |||
@@ -17,7 +22,7 @@ private const val ARG_PARAM2 = "param2" | |||
* Use the [MyFragment.newInstance] factory method to | |||
* create an instance of this fragment. | |||
*/ | |||
class MyFragment : Fragment() { | |||
class MyFragment : BaseFragmentVM<FragmentMyBinding,MainActivityViewModel>() { | |||
// TODO: Rename and change types of parameters | |||
private var param1: String? = null | |||
private var param2: String? = null | |||
@@ -49,12 +54,24 @@ class MyFragment : Fragment() { | |||
*/ | |||
// TODO: Rename and change types and number of parameters | |||
@JvmStatic | |||
fun newInstance(param1: String, param2: String) = | |||
MyFragment().apply { | |||
arguments = Bundle().apply { | |||
putString(ARG_PARAM1, param1) | |||
putString(ARG_PARAM2, param2) | |||
} | |||
} | |||
fun newInstance() = MyFragment() | |||
// .apply { | |||
// arguments = Bundle().apply { | |||
// putString(ARG_PARAM1, param1) | |||
// putString(ARG_PARAM2, param2) | |||
// } | |||
// } | |||
} | |||
override fun initFragment() { | |||
} | |||
override fun loadData() { | |||
} | |||
override fun initViewModel(): MainActivityViewModel { | |||
return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
package com.xkl.cdl.module.m_service_center | |||
import androidx.lifecycle.ViewModelProvider | |||
import android.os.Bundle | |||
import androidx.fragment.app.Fragment | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.FragmentMyBinding | |||
import com.xkl.cdl.databinding.ServiceCenterFragmentBinding | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
class ServiceCenterFragment : BaseFragmentVM<ServiceCenterFragmentBinding, MainActivityViewModel>() { | |||
companion object { | |||
@JvmStatic | |||
fun newInstance() = ServiceCenterFragment() | |||
} | |||
// override fun onActivityCreated(savedInstanceState: Bundle?) { | |||
// super.onActivityCreated(savedInstanceState) | |||
//// viewModel = ViewModelProvider(this).get(ServiceCenterViewModel::class.java) | |||
// // TODO: Use the ViewModel | |||
// } | |||
override fun initFragment() { | |||
} | |||
override fun loadData() { | |||
} | |||
override fun initViewModel(): MainActivityViewModel { | |||
return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
package com.xkl.cdl.module.m_service_center | |||
import androidx.lifecycle.ViewModel | |||
class ServiceCenterViewModel : ViewModel() { | |||
} |
@@ -5,7 +5,12 @@ import androidx.fragment.app.Fragment | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.base.fragment.BaseFragmentVM | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.FragmentMyBinding | |||
import com.xkl.cdl.databinding.FragmentStaticsBinding | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
// TODO: Rename parameter arguments, choose names that match | |||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER | |||
@@ -17,7 +22,7 @@ private const val ARG_PARAM2 = "param2" | |||
* Use the [StaticsFragment.newInstance] factory method to | |||
* create an instance of this fragment. | |||
*/ | |||
class StaticsFragment : Fragment() { | |||
class StaticsFragment : BaseFragmentVM<FragmentStaticsBinding, MainActivityViewModel>() { | |||
// TODO: Rename and change types of parameters | |||
private var param1: String? = null | |||
private var param2: String? = null | |||
@@ -49,12 +54,19 @@ class StaticsFragment : Fragment() { | |||
*/ | |||
// TODO: Rename and change types and number of parameters | |||
@JvmStatic | |||
fun newInstance(param1: String, param2: String) = | |||
StaticsFragment().apply { | |||
arguments = Bundle().apply { | |||
putString(ARG_PARAM1, param1) | |||
putString(ARG_PARAM2, param2) | |||
} | |||
} | |||
fun newInstance() = StaticsFragment() | |||
} | |||
override fun initFragment() { | |||
} | |||
override fun loadData() { | |||
} | |||
override fun initViewModel(): MainActivityViewModel { | |||
return ViewModelProvider(requireActivity())[MainActivityViewModel::class.java] | |||
} | |||
} |
@@ -1,12 +1,63 @@ | |||
package com.xkl.cdl.module.main | |||
import androidx.appcompat.app.AppCompatActivity | |||
import android.os.Bundle | |||
import androidx.lifecycle.ViewModelProvider | |||
import com.suliang.common.base.activity.BaseActivityVM | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.suliang.common.extension.loadFragment | |||
import com.suliang.common.extension.showHideFragment | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.ActivityMainBinding | |||
import com.xkl.cdl.module.m_center_learn.LearnCenterFragment | |||
import com.xkl.cdl.module.m_memo.MemoFragment | |||
import com.xkl.cdl.module.m_my.MyFragment | |||
import com.xkl.cdl.module.m_service_center.ServiceCenterFragment | |||
import com.xkl.cdl.module.m_statics.StaticsFragment | |||
import com.xkl.cdl.module.splash.SplashActivity | |||
class MainActivity : AppCompatActivity() { | |||
override fun onCreate(savedInstanceState: Bundle?) { | |||
super.onCreate(savedInstanceState) | |||
setContentView(R.layout.activity_main) | |||
/** | |||
* 主界面、控制切换子Fragment | |||
* ViewModle 进行初始化加载数据,只有等所有数据加载完全才进行页面的切换,子Fragment使用的是懒加载,不用担心生命周期、性能的问题 | |||
* @property mMemo MemoFragment | |||
* @property mStatistics StaticsFragment | |||
* @property mLearnCenter LearnCenterFragment | |||
* @property mServiceCenter ServiceCenterFragment | |||
* @property mMy MyFragment | |||
*/ | |||
class MainActivity : BaseActivityVM<ActivityMainBinding, MainActivityViewModel>() { | |||
private val mMemo by lazy { MemoFragment.newInstance() } | |||
private val mStatistics by lazy { StaticsFragment.newInstance() } | |||
private val mLearnCenter by lazy { LearnCenterFragment.newInstance() } | |||
private val mServiceCenter by lazy { ServiceCenterFragment.newInstance() } | |||
private val mMy by lazy { MyFragment.newInstance() } | |||
override fun initViewModel(): MainActivityViewModel { | |||
return ViewModelProvider(this)[MainActivityViewModel::class.java] | |||
} | |||
override fun initActivity(savedInstanceState: Bundle?) { | |||
//加载Fragment并显示 | |||
loadFragment(R.id.layout_container, -1, mMemo, mStatistics, mLearnCenter, mServiceCenter, mMy) | |||
//点击事件 | |||
binding.layoutNavContainer.setOnCheckedChangeListener { group, checkedId -> | |||
when (checkedId) { | |||
R.id.nav_memo -> showHideFragment(mMemo) | |||
R.id.nav_statistics -> showHideFragment(mStatistics) | |||
R.id.nav_learnCenter -> showHideFragment(mLearnCenter) | |||
R.id.nav_serviceCenter -> showHideFragment(mServiceCenter) | |||
R.id.nav_my -> showHideFragment(mMy) | |||
} | |||
} | |||
} | |||
override fun loadData() { | |||
//加载初始化数据 | |||
vm.loadInit().observe(this){ | |||
binding.layoutNavContainer.check(R.id.nav_learnCenter) | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
package com.xkl.cdl.module.main | |||
import androidx.fragment.app.Fragment | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.viewModelScope | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
import com.suliang.common.util.LogUtil | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.module.m_center_learn.LearnCenterFragment | |||
import kotlinx.coroutines.delay | |||
import kotlinx.coroutines.launch | |||
/** | |||
* author suliang | |||
* create 2022/3/15 14:39 | |||
* Describe: | |||
* 进行用户信息的判断,加载用户课程 | |||
*/ | |||
class MainActivityViewModel: BaseViewModel() { | |||
/** 进入程序加载完毕监听 */ | |||
val mainInitLiveData = MutableLiveData<Boolean>() | |||
/*** | |||
* 进行初始化加载 | |||
*/ | |||
fun loadInit(): MutableLiveData<Boolean> { | |||
//1、查询用户的课程数据 | |||
//设置进入课程管理类中 | |||
// viewModelScope.launch { | |||
// showHideLoading(true) | |||
// delay(2000) | |||
// LogUtil.e("launch delay") | |||
// LogUtil.e("launch后") | |||
// showHideLoading(false) | |||
// //通知Activity 加载完成 | |||
// } | |||
return MutableLiveData<Boolean>().apply { postValue(true) } | |||
} | |||
//2、计算用户的复习数据 | |||
//3、统计数据 | |||
//4、备忘本数据 | |||
} |
@@ -4,14 +4,24 @@ import android.annotation.SuppressLint | |||
import android.os.Bundle | |||
import android.os.Looper | |||
import android.view.View | |||
import androidx.lifecycle.LiveData | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.ViewModel | |||
import com.bumptech.glide.load.resource.bitmap.CircleCrop | |||
import com.suliang.common.base.activity.BaseActivity | |||
import com.suliang.common.eventbus.LiveDataBus | |||
import com.suliang.common.eventbus.NonStickyMutableLiveData | |||
import com.suliang.common.util.LogUtil | |||
import com.suliang.common.util.image.ImageLoader | |||
import com.suliang.common.util.image.ImageLoaderOption | |||
import com.suliang.common.util.media.* | |||
import com.suliang.common.util.thread.AppExecutors | |||
import com.xkl.cdl.R | |||
import com.xkl.cdl.databinding.ActivitySplashBinding | |||
import com.xkl.cdl.module.main.MainActivity | |||
import com.xkl.cdl.module.main.MainActivityViewModel | |||
import java.util.Calendar.SECOND | |||
import java.util.concurrent.TimeUnit | |||
@SuppressLint("CustomSplashScreen") | |||
class SplashActivity : BaseActivity<ActivitySplashBinding>() { | |||
@@ -28,57 +38,15 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>() { | |||
} | |||
override fun loadData() { | |||
ImageLoader.loadImage(ImageLoaderOption().apply { | |||
targetView = binding.img | |||
resourceId = R.drawable.illustration_splash | |||
centerInsideEnable = true | |||
circleEnable = true | |||
}) | |||
} | |||
val util:IMP = MPManager.apply { | |||
addPlayListener(object :IMPListener{ | |||
override fun onMpState(state: EMediaState) { | |||
LogUtil.e("SplashActivity 接收 ${Thread.currentThread() == Looper.getMainLooper().thread} ${Thread.currentThread()} --${state.name}") | |||
//定时跳跃到住主界面 | |||
AppExecutors.scheduledExecutor.schedule({ | |||
AppExecutors.mainThread.execute { | |||
startActivity(MainActivity::class.java) | |||
finish() | |||
} | |||
}) | |||
}, 2, TimeUnit.SECONDS) | |||
} | |||
var position = 0 | |||
var seekToPosition = 1000 | |||
val source = listOf("http://ws.stream.qqmusic.qq.com/C4000017AcyB00D6Cr.m4a?guid=395745734&vkey=9CF75D8FDE45B1A7755C15F93C5F74CC18E444873F498DF3F5B9F0B41889778BE0EAC45305BD444F90D777C612BC5865DB0AF55A318854E1&uin=&fromtag=66" | |||
,"http://ws.stream.qqmusic.qq.com/C400001lePVO36SNSo.m4a?guid=748677131&vkey=A6077E31CB4A1C8A4F3D5D2BEDE8D3E16AB69FF2FEE95D9D48288EAD048AB89DC53B6846D184F842257617AF9714D97C864FB183BE05CC66&uin=&fromtag=66" | |||
,"http://ws.stream.qqmusic.qq.com/C400000ZFBf22vBvrf.m4a?guid=921100582&vkey=C9EA33972404E2AC1C80A479614AD44A9BF132A16F9B307D9515F595B41901AE53F6681E28BDA799FC047F30D624F8A19AE9C4856ADBEF4B&uin=&fromtag=66") | |||
fun play(view: View) { | |||
seekToPosition = 1000 | |||
util.play(source[0]) | |||
} | |||
fun continuePlay(view: View) { | |||
util.continuePlay() | |||
} | |||
fun stop(view: View) { | |||
util.stopPlay() | |||
} | |||
fun pause(view: View) { | |||
util.pausePlay() | |||
} | |||
fun seekTo(view: View) { | |||
util.seekTo(seekToPosition) | |||
seekToPosition += 30000 | |||
} | |||
fun release(view: View) { | |||
util.destroyPlay() | |||
} | |||
fun nextPlay(view: View) { | |||
var i = position + 1 | |||
if (i > 3){ | |||
i = 0 | |||
} | |||
position = i | |||
util.play(source[position]) | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<!--主导韩颜色选择器--> | |||
<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |||
<item android:color="@color/theme_color" android:state_checked="true"/> | |||
<item android:color="#d0d0d0"/> | |||
</selector> |
@@ -0,0 +1,7 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<!--学习中心:搜索框--> | |||
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:shape="rectangle"> | |||
<solid android:color="@color/white_1"/> | |||
<corners android:radius="18dp"/> | |||
</shape> |
@@ -1,9 +1,120 @@ | |||
<?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:tools="http://schemas.android.com/tools" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
tools:context=".module.main.MainActivity"> | |||
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.main.MainActivity"> | |||
<FrameLayout | |||
android:id="@+id/layout_container" | |||
android:layout_width="0dp" | |||
android:layout_height="0dp" | |||
app:layout_constraintBottom_toTopOf="@+id/layout_nav_container" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<View | |||
android:layout_width="0dp" | |||
android:layout_height="@dimen/line_height" | |||
android:background="@color/gray_1" | |||
app:layout_constraintBottom_toTopOf="@+id/layout_nav_container" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" /> | |||
<RadioGroup | |||
android:id="@+id/layout_nav_container" | |||
android:layout_width="0dp" | |||
android:layout_height="@dimen/nav_bottom_height" | |||
app:layout_constraintTop_toBottomOf="@+id/layout_container" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
android:orientation="horizontal"> | |||
<RadioButton | |||
android:id="@+id/nav_memo" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/memo" | |||
app:drawableTopCompat="@drawable/ic_nav_memo" | |||
android:layout_weight="1" | |||
android:gravity="center" | |||
android:textSize="@dimen/miniSize" | |||
android:paddingTop="4dp" | |||
app:drawableTint="@color/nav_check_color" | |||
android:textColor="@color/nav_check_color" | |||
android:button="@null"/> | |||
<RadioButton | |||
android:id="@+id/nav_statistics" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/statistics" | |||
android:drawableTop="@drawable/ic_nav_statistics" | |||
android:layout_weight="1" | |||
android:gravity="center" | |||
android:textSize="@dimen/miniSize" | |||
android:paddingTop="4dp" | |||
android:button="@null" | |||
app:drawableTint="@color/nav_check_color" | |||
android:textColor="@color/nav_check_color"/> | |||
<RadioButton | |||
android:id="@+id/nav_learnCenter" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/learnCenter" | |||
app:drawableTopCompat="@drawable/ic_nav_learn_center" | |||
android:layout_weight="1" | |||
android:gravity="center" | |||
android:textSize="@dimen/miniSize" | |||
android:paddingTop="4dp" | |||
android:button="@null" | |||
app:drawableTint="@color/nav_check_color" | |||
android:textColor="@color/nav_check_color"/> | |||
<RadioButton | |||
android:id="@+id/nav_serviceCenter" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/memo" | |||
app:drawableTopCompat="@drawable/ic_nav_memo" | |||
android:layout_weight="1" | |||
android:gravity="center" | |||
android:textSize="@dimen/miniSize" | |||
android:paddingTop="4dp" | |||
android:button="@null" | |||
app:drawableTint="@color/nav_check_color" | |||
android:textColor="@color/nav_check_color"/> | |||
<RadioButton | |||
android:id="@+id/nav_my" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/my" | |||
app:drawableTopCompat="@drawable/ic_nav_my" | |||
android:layout_weight="1" | |||
android:gravity="center" | |||
android:textSize="@dimen/miniSize" | |||
android:paddingTop="4dp" | |||
android:button="@null" | |||
app:drawableTint="@color/nav_check_color" | |||
android:textColor="@color/nav_check_color"/> | |||
</RadioGroup> | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
</layout> |
@@ -24,83 +24,5 @@ | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintVertical_bias="0.6" /> | |||
<Button | |||
android:id="@+id/button5" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginTop="8dp" | |||
android:text="下一曲" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/button" | |||
android:onClick="nextPlay"/> | |||
<Button | |||
android:id="@+id/button6" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="104dp" | |||
android:layout_marginTop="8dp" | |||
android:text="拖动" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/button" | |||
android:onClick="seekTo"/> | |||
<Button | |||
android:id="@+id/button7" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="204dp" | |||
android:layout_marginTop="8dp" | |||
android:onClick="release" | |||
android:text="释放" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/button" /> | |||
<Button | |||
android:id="@+id/button" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="16dp" | |||
android:layout_marginTop="24dp" | |||
android:text="播放" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/img" | |||
android:onClick="play"/> | |||
<Button | |||
android:id="@+id/button2" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="24dp" | |||
android:layout_marginTop="24dp" | |||
android:text="暂停" | |||
app:layout_constraintStart_toEndOf="@+id/button" | |||
app:layout_constraintTop_toBottomOf="@+id/img" | |||
android:onClick="pause"/> | |||
<Button | |||
android:id="@+id/button3" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="8dp" | |||
android:layout_marginTop="24dp" | |||
android:text="停止" | |||
app:layout_constraintStart_toEndOf="@+id/button2" | |||
app:layout_constraintTop_toBottomOf="@+id/img" | |||
android:onClick="stop"/> | |||
<Button | |||
android:id="@+id/button4" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginTop="24dp" | |||
android:text="继续" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.555" | |||
app:layout_constraintStart_toEndOf="@+id/button3" | |||
app:layout_constraintTop_toBottomOf="@+id/img" | |||
android:onClick="continuePlay"/> | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
</layout> |
@@ -0,0 +1,14 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
tools:context=".module.m_center_learn.CoursePackFragment"> | |||
<!-- TODO: Update blank fragment layout --> | |||
<TextView | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/hello_blank_fragment" /> | |||
</FrameLayout> |
@@ -1,14 +1,67 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
tools:context=".module.m_center_learn.LearnCenterFragment"> | |||
<!-- TODO: Update blank fragment layout --> | |||
<TextView | |||
<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="viewmodel" | |||
type="com.xkl.cdl.module.m_center_learn.LearnCenterFragmentViewModel" /> | |||
</data> | |||
<LinearLayout | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="@string/hello_blank_fragment" /> | |||
android:background="@color/white" | |||
android:orientation="vertical" | |||
tools:context=".module.m_center_learn.LearnCenterFragment"> | |||
<com.google.android.material.tabs.TabLayout | |||
android:id="@+id/tab_layout" | |||
android:layout_width="match_parent" | |||
android:layout_height="@dimen/title_bar_height" | |||
android:layout_marginStart="16dp" | |||
app:tabContentStart="4dp" | |||
app:tabIndicatorHeight="0dp" | |||
app:tabMode="scrollable" | |||
app:tabRippleColor="@android:color/transparent" | |||
app:tabMaxWidth="200dp" | |||
app:tabMinWidth="10dp" | |||
app:tabPaddingEnd="16dp" | |||
app:tabPaddingStart="0dp"/> | |||
<com.suliang.common.widget.InputSearchEditText | |||
android:id="@+id/edit_text_search" | |||
android:layout_width="match_parent" | |||
android:layout_height="36dp" | |||
android:layout_marginStart="16dp" | |||
android:layout_marginTop="6dp" | |||
android:layout_marginEnd="16dp" | |||
android:layout_marginBottom="14dp" | |||
android:background="@drawable/et_search_bg" | |||
android:drawablePadding="4dp" | |||
android:gravity="center_vertical" | |||
android:hint="@string/search_hint_input_course_name" | |||
android:maxLines="1" | |||
android:singleLine="true" | |||
android:paddingLeft="16dp" | |||
android:paddingRight="16dp" | |||
android:textColor="@color/main_text_color" | |||
android:textSize="@dimen/normalSize" | |||
android:focusable="true" | |||
android:focusableInTouchMode="true" | |||
/> | |||
<androidx.viewpager2.widget.ViewPager2 | |||
android:id="@+id/view_pager_2" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:background="@color/white_1" | |||
android:overScrollMode="never" /> | |||
</FrameLayout> | |||
</LinearLayout> | |||
</layout> |
@@ -0,0 +1,14 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<FrameLayout | |||
xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
tools:context=".module.m_service_center.ServiceCenterFragment"> | |||
<TextView | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:text="Hello" /> | |||
</FrameLayout> |
@@ -0,0 +1,10 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<TextView xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:maxLines="1" | |||
android:gravity="bottom|center" | |||
android:textSize="@dimen/normalSize" | |||
android:textColor="@color/gray_2" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
tools:text = "项目"/> |
@@ -1,6 +1,3 @@ | |||
<resources> | |||
<declare-styleable name="FullscreenAttrs"> | |||
<attr name="fullscreenBackgroundColor" format="color" /> | |||
<attr name="fullscreenTextColor" format="color" /> | |||
</declare-styleable> | |||
</resources> |
@@ -6,10 +6,19 @@ | |||
<color name="teal_200">#FF03DAC5</color> | |||
<color name="teal_700">#FF018786</color> | |||
<color name="black">#FF000000</color> | |||
<color name="white">#FFFFFFFF</color> | |||
<color name="light_blue_600">#FF039BE5</color> | |||
<color name="light_blue_900">#FF01579B</color> | |||
<color name="light_blue_A200">#FF40C4FF</color> | |||
<color name="light_blue_A400">#FF00B0FF</color> | |||
<color name="black_overlay">#66000000</color> | |||
<color name="theme_color">#FF5082E6</color> | |||
<color name="main_text_color">#323233</color> | |||
<color name="gray_1">#FFE6E6E6</color> | |||
<color name="gray_2">#8A8A99</color> | |||
<color name="white">#FFFFFFFF</color> | |||
<color name="white_1">#FFF5F5F5</color> | |||
</resources> |
@@ -0,0 +1,14 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<resources> | |||
<dimen name="nav_bottom_height">50dp</dimen> | |||
<dimen name="line_height">0.5dp</dimen> | |||
<dimen name="miniSize">12dp</dimen> | |||
<dimen name="smallSize">14dp</dimen> | |||
<dimen name="normalSize">16dp</dimen> | |||
<dimen name="bigSize">18dp</dimen> | |||
<dimen name="biggerSize">20dp</dimen> | |||
<dimen name="title_bar_height">48dp</dimen> | |||
</resources> |
@@ -1,7 +1,18 @@ | |||
<resources> | |||
<string name="app_name">XklLocal</string> | |||
<string name="dummy_button">Dummy Button</string> | |||
<string name="dummy_content">DUMMY\nCONTENT</string> | |||
<!-- TODO: Remove or change this placeholder text --> | |||
<string name="app_name">学考乐本地版</string> | |||
<string name="hello_blank_fragment">Hello blank fragment</string> | |||
<string name="memo">备忘本</string> | |||
<string name="statistics">统计</string> | |||
<string name="learnCenter">学习中心</string> | |||
<string name="serviceCenter">服务中心</string> | |||
<string name="my">我的</string> | |||
<string name="project_english">英语</string> | |||
<string name="project_chinese">语文</string> | |||
<string name="search_hint_input_course_name">输入课程内容...</string> | |||
</resources> |
@@ -1,3 +1,2 @@ | |||
<resources> | |||
</resources> |
@@ -0,0 +1,12 @@ | |||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:width="24dp" | |||
android:height="23.272728dp" | |||
android:viewportWidth="33" | |||
android:viewportHeight="32"> | |||
<path | |||
android:fillColor="#8A8A99" | |||
android:fillType="evenOdd" | |||
android:pathData="M18.1314,28.2353C18.5819,28.2353 18.9471,28.6005 18.9471,29.051C18.9471,29.5015 18.5819,29.8667 18.1314,29.8667L14.8686,29.8667C14.4181,29.8667 14.0529,29.5015 14.0529,29.051C14.0529,28.6005 14.4181,28.2353 14.8686,28.2353L18.1314,28.2353ZM20.5784,24.9725C21.0289,24.9725 21.3941,25.3377 21.3941,25.7882C21.3941,26.2387 21.0289,26.6039 20.5784,26.6039L12.4216,26.6039C11.9711,26.6039 11.6059,26.2387 11.6059,25.7882C11.6059,25.3377 11.9711,24.9725 12.4216,24.9725L20.5784,24.9725ZM16.5,7.0275C21.0049,7.0275 24.6569,10.6794 24.6569,15.1843C24.6569,19.6892 21.0049,23.3412 16.5,23.3412C11.9951,23.3412 8.3431,19.6892 8.3431,15.1843C8.3431,10.6794 11.9951,7.0275 16.5,7.0275ZM29.551,15.1843C30.0015,15.1843 30.3667,15.5495 30.3667,16C30.3667,16.4505 30.0015,16.8157 29.551,16.8157L27.9196,16.8157C27.4691,16.8157 27.1039,16.4505 27.1039,16C27.1039,15.5495 27.4691,15.1843 27.9196,15.1843L29.551,15.1843ZM5.0804,15.1843C5.5309,15.1843 5.8961,15.5495 5.8961,16C5.8961,16.4505 5.5309,16.8157 5.0804,16.8157L3.449,16.8157C2.9985,16.8157 2.6333,16.4505 2.6333,16C2.6333,15.5495 2.9985,15.1843 3.449,15.1843L5.0804,15.1843ZM26.3052,6.1948C26.6238,6.5133 26.6238,7.0298 26.3052,7.3483L25.1517,8.5019C24.8331,8.8204 24.3167,8.8204 23.9981,8.5019C23.6796,8.1833 23.6796,7.6669 23.9981,7.3483L25.1517,6.1948C25.4702,5.8762 25.9867,5.8762 26.3052,6.1948ZM7.8483,6.1948L9.0019,7.3483C9.3204,7.6669 9.3204,8.1833 9.0019,8.5019C8.6833,8.8204 8.1669,8.8204 7.8483,8.5019L6.6948,7.3483C6.3762,7.0298 6.3762,6.5133 6.6948,6.1948C7.0133,5.8762 7.5298,5.8762 7.8483,6.1948ZM16.5,2.1333C16.9505,2.1333 17.3157,2.4985 17.3157,2.949L17.3157,4.5804C17.3157,5.0309 16.9505,5.3961 16.5,5.3961C16.0495,5.3961 15.6843,5.0309 15.6843,4.5804L15.6843,2.949C15.6843,2.4985 16.0495,2.1333 16.5,2.1333Z" | |||
android:strokeWidth="1" | |||
android:strokeColor="#00000000" /> | |||
</vector> |
@@ -0,0 +1,12 @@ | |||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:width="24dp" | |||
android:height="24dp" | |||
android:viewportWidth="24" | |||
android:viewportHeight="24"> | |||
<path | |||
android:pathData="M6.3429,5.0857L6.3429,6.0286C6.3429,7.2436 7.3278,8.2286 8.5429,8.2286C9.7579,8.2286 10.7429,7.2436 10.7429,6.0286L10.7429,5.0857L13.2571,5.0857L13.2571,6.0286C13.2571,7.2436 14.2421,8.2286 15.4571,8.2286C16.6722,8.2286 17.6571,7.2436 17.6571,6.0286L17.6571,5.0857L19.5429,5.0857C20.2372,5.0857 20.8,5.6486 20.8,6.3429L20.8,18.9143C20.8,19.6086 20.2372,20.1714 19.5429,20.1714L4.4571,20.1714C3.7628,20.1714 3.2,19.6086 3.2,18.9143L3.2,6.3429C3.2,5.6486 3.7628,5.0857 4.4571,5.0857L6.3429,5.0857ZM16.7143,14.5143L7.2857,14.5143C6.765,14.5143 6.3429,14.9364 6.3429,15.4571C6.3429,15.9472 6.7168,16.35 7.1949,16.3957L7.2857,16.4L16.7143,16.4C17.235,16.4 17.6571,15.9779 17.6571,15.4571C17.6571,14.967 17.2832,14.5643 16.8051,14.5186L16.7143,14.5143ZM16.7143,10.6643L7.2857,10.6643C6.765,10.6643 6.3429,11.0864 6.3429,11.6071C6.3429,12.0972 6.7168,12.5 7.1949,12.5457L7.2857,12.55L16.7143,12.55C17.235,12.55 17.6571,12.1279 17.6571,11.6071C17.6571,11.117 17.2832,10.7143 16.8051,10.6686L16.7143,10.6643ZM8.5429,3.2C9.0636,3.2 9.4857,3.6221 9.4857,4.1429L9.4857,6.0286C9.4857,6.5121 9.1217,6.9106 8.6528,6.9651L8.5429,6.9714L8.5429,6.9714C8.0221,6.9714 7.6,6.5493 7.6,6.0286L7.6,4.1429C7.6,3.6221 8.0221,3.2 8.5429,3.2ZM14.5143,4.1429C14.5143,3.6221 14.9364,3.2 15.4571,3.2C15.9779,3.2 16.4,3.6221 16.4,4.1429L16.4,6.0286C16.4,6.5121 16.036,6.9106 15.5671,6.9651L15.4571,6.9714L15.4571,6.9714C14.9364,6.9714 14.5143,6.5493 14.5143,6.0286L14.5143,4.1429Z" | |||
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="24dp" | |||
android:height="24dp" | |||
android:viewportWidth="24" | |||
android:viewportHeight="24"> | |||
<path | |||
android:pathData="M12,12C16.4183,12 20,15.5817 20,20C20,20.4418 19.6418,20.8 19.2,20.8L4.8,20.8C4.3922,20.8 4.0556,20.4948 4.0062,20.1003L4,20C4,15.5817 7.5817,12 12,12ZM12,3.2C14.2091,3.2 16,4.9909 16,7.2C16,9.4091 14.2091,11.2 12,11.2C9.7909,11.2 8,9.4091 8,7.2C8,4.9909 9.7909,3.2 12,3.2Z" | |||
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="24dp" | |||
android:height="24dp" | |||
android:viewportWidth="24" | |||
android:viewportHeight="24"> | |||
<path | |||
android:pathData="M11.2,12.8L11.2,19.2C11.2,20.0837 10.4837,20.8 9.6,20.8L9.6,20.8L4.8,20.8C3.9163,20.8 3.2,20.0837 3.2,19.2L3.2,19.2L3.2,14.4C3.2,13.5163 3.9163,12.8 4.8,12.8L4.8,12.8L11.2,12.8ZM19.2,12.8C20.0837,12.8 20.8,13.5163 20.8,14.4L20.8,19.2C20.8,20.0837 20.0837,20.8 19.2,20.8L14.4,20.8C13.5163,20.8 12.8,20.0837 12.8,19.2L12.8,12.8L19.2,12.8ZM9.6,3.2C10.4837,3.2 11.2,3.9163 11.2,4.8L11.2,11.2L4.8,11.2C3.9163,11.2 3.2,10.4837 3.2,9.6L3.2,4.8C3.2,3.9163 3.9163,3.2 4.8,3.2L9.6,3.2ZM19.2,3.2C20.0837,3.2 20.8,3.9163 20.8,4.8L20.8,4.8L20.8,9.6C20.8,10.4837 20.0837,11.2 19.2,11.2L19.2,11.2L12.8,11.2L12.8,4.8C12.8,3.9163 13.5163,3.2 14.4,3.2L14.4,3.2Z" | |||
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="24dp" | |||
android:height="24dp" | |||
android:viewportWidth="24" | |||
android:viewportHeight="24"> | |||
<path | |||
android:pathData="M21,19.2C21.6627,19.2 22.2,19.7373 22.2,20.4C22.2,21.0627 21.6627,21.6 21,21.6L3,21.6C2.3373,21.6 1.8,21.0627 1.8,20.4C1.8,19.7373 2.3373,19.2 3,19.2L21,19.2ZM7.2,9C7.8627,9 8.4,9.5373 8.4,10.2L8.4,16.8C8.4,17.4627 7.8627,18 7.2,18L4.8,18C4.1373,18 3.6,17.4627 3.6,16.8L3.6,10.2C3.6,9.5373 4.1373,9 4.8,9L7.2,9ZM13.2,2.4C13.8627,2.4 14.4,2.9373 14.4,3.6L14.4,16.8C14.4,17.4627 13.8627,18 13.2,18L10.8,18C10.1373,18 9.6,17.4627 9.6,16.8L9.6,3.6C9.6,2.9373 10.1373,2.4 10.8,2.4L13.2,2.4ZM19.2,7.2C19.8627,7.2 20.4,7.7373 20.4,8.4L20.4,16.8C20.4,17.4627 19.8627,18 19.2,18L16.8,18C16.1373,18 15.6,17.4627 15.6,16.8L15.6,8.4C15.6,7.7373 16.1373,7.2 16.8,7.2L19.2,7.2Z" | |||
android:strokeWidth="1" | |||
android:fillColor="#8A8A99" | |||
android:fillType="evenOdd" | |||
android:strokeColor="#00000000"/> | |||
</vector> |
@@ -31,7 +31,7 @@ ext { | |||
applicationId : "com.xkl.cdl" | |||
] | |||
versions = [ | |||
core_ktx_version : "1.3.2", | |||
core_ktx_version : "1.7.0", | |||
appcompat_version: "1.4.1", | |||
material_version : "1.3.0", | |||
lifecycle_version: "2.5.0-alpha02", | |||
@@ -73,7 +73,9 @@ ext { | |||
Glide : "com.github.bumptech.glide:glide:${versions.glide_version}", | |||
GlideCompiler : "com.github.bumptech.glide:compiler:${versions.glide_version}", | |||
//Glide translation | |||
GlideTranslation: "jp.wasabeef:glide-transformations:4.3.0" | |||
GlideTranslation: "jp.wasabeef:glide-transformations:4.3.0", | |||
//Activity_ktx Activity扩展 | |||
Activity_ktx : "androidx.activity:activity-ktx:1.4.0", | |||
] | |||
@@ -43,7 +43,8 @@ android { | |||
} | |||
dependencies { | |||
rootProject.ext.dependencies_required.each{ k,v -> implementation v} | |||
implementation 'androidx.legacy:legacy-support-v4:1.0.0' | |||
rootProject.ext.dependencies_required.each{ k, v -> implementation v} | |||
testImplementation rootProject.ext.dependencies_testImplementation.junit | |||
rootProject.ext.dependencies_androidTestImplementation.each{ k,v -> androidTestImplementation v} | |||
@@ -57,6 +58,9 @@ dependencies { | |||
//glide translation | |||
api customDependencies.GlideTranslation | |||
//Activity-ktx 扩展 | |||
api customDependencies.Activity_ktx | |||
} |
@@ -0,0 +1,13 @@ | |||
package com.suliang.common | |||
/** | |||
* author suliang | |||
* create 2022/3/17 17:04 | |||
* Describe: | |||
*/ | |||
object Config { | |||
const val INTENT_1 = "intent_1" | |||
const val INTENT_2 = "intent_2" | |||
const val INTENT_3 = "intent_3" | |||
const val INTENT_4 = "intent_4" | |||
} |
@@ -0,0 +1,58 @@ | |||
package com.suliang.common.base | |||
import android.graphics.Color | |||
import android.os.Bundle | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import androidx.fragment.app.DialogFragment | |||
import androidx.viewbinding.ViewBinding | |||
import com.suliang.common.extension.initBinding | |||
import android.graphics.drawable.ColorDrawable | |||
import android.util.DisplayMetrics | |||
/** | |||
* author suliang | |||
* create 2022/3/17 16:30 | |||
* Describe: | |||
*/ | |||
abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment() { | |||
private var _binding: VB? = null | |||
val binding: VB | |||
get() = _binding!! | |||
override fun onCreateView( | |||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? | |||
): View? { | |||
_binding = initBinding(inflater, container) | |||
return binding.root | |||
} | |||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |||
super.onViewCreated(view, savedInstanceState) | |||
requireDialog().apply { | |||
setCanceledOnTouchOutside(false) // 点击屏幕不消失 | |||
setCancelable(false) //返回键不取消 | |||
} | |||
initFragment() | |||
} | |||
override fun onStart() { | |||
super.onStart() | |||
if (isTransparent()){ | |||
//设置背景透明 | |||
dialog!!.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) | |||
} | |||
} | |||
abstract fun initFragment() | |||
/** 是否布局背景透明 */ | |||
abstract fun isTransparent():Boolean | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.suliang.common.base | |||
import android.graphics.drawable.AnimationDrawable | |||
import com.suliang.common.Config | |||
import com.suliang.common.databinding.LoadingFragmentBinding | |||
/** | |||
* author suliang | |||
* create 2022/3/17 15:53 | |||
* Describe: 加载等待弹窗 | |||
*/ | |||
class LoadingDialog : BaseDialogFragment<LoadingFragmentBinding>() { | |||
companion object{ | |||
@JvmStatic | |||
fun newInstance(msg : String): LoadingDialog { | |||
return LoadingDialog().apply { | |||
arguments?.putString(Config.INTENT_1,msg) | |||
} | |||
} | |||
} | |||
override fun initFragment() { | |||
//外部设置 | |||
arguments?.getString(Config.INTENT_1,null)?.let { | |||
binding.loadText.text = it | |||
} | |||
//开始动画 | |||
(binding.loadImage.drawable as AnimationDrawable).start() | |||
} | |||
override fun isTransparent(): Boolean { | |||
return true | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package com.suliang.common.base | |||
import com.suliang.common.base.activity.ToastEvent | |||
/** | |||
* author suliang | |||
* create 2022/3/17 11:41 | |||
* Describe: 页面常用操作行为 | |||
*/ | |||
interface ViewBehavior { | |||
/** 是否显示视图 */ | |||
fun showHideLoading(isShow: Boolean) | |||
/** 显示toast */ | |||
fun showToast(msg: ToastEvent) | |||
/** 无参数跳转activity */ | |||
fun startActivity(clazz: Class<*>) | |||
/** 返回键点击*/ | |||
// fun backPress(arg: Any?); | |||
/** 关闭页面 */ | |||
// fun finishPage(arg: Any?) | |||
} |
@@ -1,22 +1,33 @@ | |||
package com.suliang.common.base.activity | |||
import android.content.Intent | |||
import android.graphics.Color | |||
import android.os.Bundle | |||
import android.widget.Toast | |||
import androidx.annotation.StringRes | |||
import androidx.viewbinding.ViewBinding | |||
import com.suliang.common.base.LoadingDialog | |||
import com.suliang.common.extension.initBinding | |||
import com.suliang.common.util.ActivityStackManager | |||
import com.zackratos.ultimatebarx.ultimatebarx.statusBarOnly | |||
import com.suliang.common.base.ViewBehavior | |||
import com.suliang.common.util.LogUtil | |||
/** | |||
* 基类Activity | |||
*/ | |||
abstract class BaseActivity<VB : ViewBinding> : LifecycleLogActivity() { | |||
abstract class BaseActivity<VB : ViewBinding> : LifecycleLogActivity(), ViewBehavior { | |||
private var _binding: VB? = null | |||
val binding : VB | |||
val binding: VB | |||
get() = _binding!! | |||
// val binding : VB by lazy { | |||
// initViewBinding() | |||
// } | |||
override fun onCreate(savedInstanceState: Bundle?) { | |||
super.onCreate(savedInstanceState) | |||
//入栈 | |||
@@ -76,6 +87,41 @@ abstract class BaseActivity<VB : ViewBinding> : LifecycleLogActivity() { | |||
/** onCreate()最后实现的数据加载调用 */ | |||
abstract fun loadData() | |||
private val loadingDialog by lazy { | |||
LoadingDialog() | |||
} | |||
/** toast提示 */ | |||
override fun showToast(event: ToastEvent) { | |||
val showTime = if (event.showLong) Toast.LENGTH_LONG else Toast.LENGTH_LONG | |||
event.content?.let { | |||
Toast.makeText(this, it, showTime).show() | |||
} ?: let { | |||
Toast.makeText(this, event.contentResId!!, showTime).show() | |||
} | |||
} | |||
protected fun showToast(text: String, showLong: Boolean = false) { | |||
showToast(ToastEvent(content = text, showLong = showLong)) | |||
} | |||
protected fun showToast(@StringRes resId: Int, showLong: Boolean = false) { | |||
showToast(ToastEvent(contentResId = resId, showLong = showLong)) | |||
} | |||
override fun showHideLoading(isShow: Boolean) { | |||
if (isShow) { | |||
loadingDialog.show(supportFragmentManager, javaClass.name) | |||
} else if (loadingDialog.activity != null){ | |||
loadingDialog.dismiss() | |||
} | |||
} | |||
override fun startActivity(clazz: Class<*>) { | |||
startActivity(Intent(this,clazz)) | |||
} | |||
// 反射实现 ViewBinding | |||
// @SuppressWarnings("unchecked") | |||
// fun initBinding(inflater: LayoutInflater): VB { | |||
@@ -85,4 +131,35 @@ abstract class BaseActivity<VB : ViewBinding> : LifecycleLogActivity() { | |||
// return inflate.invoke(null, inflater) as VB | |||
// } | |||
// https://juejin.cn/post/6926007534188773384#heading-1 | |||
// private fun initViewBinding(): VB{ | |||
// return reflexViewBinding(this::class.java,layoutInflater) | |||
// } | |||
// private fun <V : ViewBinding?> reflexViewBinding(aClass: Class<*>, from: LayoutInflater?): V { | |||
// return try { | |||
// val genericSuperclass: Type = aClass.genericSuperclass | |||
// if (genericSuperclass is ParameterizedType) { | |||
// val actualTypeArguments: Array<Type> = | |||
// (Objects.requireNonNull(genericSuperclass) as ParameterizedType).getActualTypeArguments() | |||
// for (actualTypeArgument in actualTypeArguments) { | |||
// var tClass: Class<*> | |||
// tClass = try { | |||
// actualTypeArgument as Class<*> | |||
// } catch (e: Exception) { | |||
// continue | |||
// } | |||
// if (ViewBinding::class.java.isAssignableFrom(tClass)) { | |||
// val inflate: Method = tClass.getMethod("inflate", LayoutInflater::class.java) | |||
// return inflate.invoke(null, from) as V | |||
// } | |||
// } | |||
// } | |||
// reflexViewBinding(aClass.superclass, from) | |||
// } catch (e: Exception) { | |||
// e.printStackTrace() | |||
// throw RuntimeException(e.message, e) | |||
// } | |||
// } | |||
} |
@@ -1,25 +1,36 @@ | |||
package com.suliang.common.base.activity | |||
import android.os.Bundle | |||
import androidx.databinding.ViewDataBinding | |||
import androidx.lifecycle.ViewModel | |||
import androidx.viewbinding.ViewBinding | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
/** | |||
* Activity DataBinding 与 ViewModel基类,封装DataBinding和ViewModel | |||
* @property VB : ViewDataBinding | |||
* @property VM: ViewModel | |||
*/ | |||
abstract class BaseActivityVM<VB : ViewDataBinding, VM:ViewModel> : BaseActivity<VB>() { | |||
//abstract class BaseActivityVM<VB : ViewDataBinding, VM : AndroidViewModel> : BaseActivity<VB>() { | |||
abstract class BaseActivityVM<VB : ViewBinding, VM : BaseViewModel> : BaseActivity<VB>() { | |||
private lateinit var vm : VM | |||
lateinit var vm: VM | |||
private set | |||
override fun initFirst() { | |||
override fun setLayoutBefore() { | |||
super.setLayoutBefore() | |||
vm = initViewModel() | |||
super.initFirst() | |||
binding.lifecycleOwner = this | |||
} | |||
abstract fun initViewModel() : VM | |||
override fun setLayoutAfter() { | |||
super.setLayoutAfter() | |||
lifecycle.addObserver(vm) | |||
vm.pageEvent.observe(this) { startActivity(it) } | |||
vm.toastEvent.observe(this){ showToast(it) } | |||
vm.loadingEvent.observe(this) { showHideLoading(it)} | |||
} | |||
abstract fun initViewModel(): VM | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.suliang.common.base.activity | |||
import androidx.annotation.StringRes | |||
/** | |||
* author suliang | |||
* create 2022/3/17 15:07 | |||
* Describe: | |||
*/ | |||
data class ToastEvent( | |||
var content: String? = null, //Toast字符串 | |||
@StringRes var contentResId: Int? = null, //Toast字符串 | |||
var showLong: Boolean = false //是否长时间显示,默认false) | |||
) |
@@ -1,11 +1,18 @@ | |||
package com.suliang.common.base.fragment | |||
import android.content.Intent | |||
import android.os.Bundle | |||
import android.view.LayoutInflater | |||
import android.view.View | |||
import android.view.ViewGroup | |||
import android.widget.Toast | |||
import androidx.annotation.StringRes | |||
import androidx.databinding.ViewDataBinding | |||
import androidx.fragment.app.Fragment | |||
import androidx.viewbinding.ViewBinding | |||
import com.suliang.common.base.LoadingDialog | |||
import com.suliang.common.base.ViewBehavior | |||
import com.suliang.common.base.activity.ToastEvent | |||
import com.suliang.common.extension.initBinding | |||
/** | |||
@@ -20,23 +27,21 @@ import com.suliang.common.extension.initBinding | |||
* | |||
* @property VB : ViewDataBinding | |||
*/ | |||
abstract class BaseFragment<VB : ViewDataBinding> : Fragment() { | |||
abstract class BaseFragment<VB : ViewBinding> : Fragment(),ViewBehavior { | |||
private var _binding: VB? = null | |||
val binding : VB | |||
val binding: VB | |||
get() = _binding!! | |||
//是否加载数据 | |||
private var isLoaded = false | |||
override fun onCreateView( | |||
inflater: LayoutInflater, | |||
container: ViewGroup?, | |||
savedInstanceState: Bundle? | |||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? | |||
): View? { | |||
_binding = initBinding(inflater, container) | |||
return super.onCreateView(inflater, container, savedInstanceState) | |||
return binding.root | |||
} | |||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |||
@@ -73,4 +78,40 @@ abstract class BaseFragment<VB : ViewDataBinding> : Fragment() { | |||
*/ | |||
abstract fun loadData() | |||
private val loadingDialog by lazy { | |||
LoadingDialog() | |||
} | |||
/** toast提示 */ | |||
override fun showToast(event: ToastEvent) { | |||
val showTime = if (event.showLong) Toast.LENGTH_LONG else Toast.LENGTH_LONG | |||
event.content?.let { | |||
Toast.makeText(requireContext(), it, showTime).show() | |||
} ?: let { | |||
Toast.makeText(requireContext(), event.contentResId!!, showTime).show() | |||
} | |||
} | |||
protected fun showToast(text: String, showLong: Boolean = false) { | |||
showToast(ToastEvent(content = text, showLong = showLong)) | |||
} | |||
protected fun showToast(@StringRes resId: Int, showLong: Boolean = false) { | |||
showToast(ToastEvent(contentResId = resId, showLong = showLong)) | |||
} | |||
override fun showHideLoading(isShow: Boolean) { | |||
if (isShow) { | |||
loadingDialog.show(childFragmentManager, javaClass.name) | |||
} else if (loadingDialog.activity != null){ | |||
loadingDialog.dismiss() | |||
} | |||
} | |||
override fun startActivity(clazz: Class<*>) { | |||
startActivity(Intent(requireActivity(),clazz)) | |||
} | |||
} |
@@ -2,17 +2,22 @@ package com.suliang.common.base.fragment | |||
import androidx.databinding.ViewDataBinding | |||
import androidx.lifecycle.ViewModel | |||
import androidx.viewbinding.ViewBinding | |||
import com.suliang.common.base.viewmodel.BaseViewModel | |||
/** | |||
* Fragment DataBinding 与 ViewModel基类,封装DataBinding和ViewModel 自带懒加载 | |||
* @property VB : ViewDataBinding | |||
* @property VM: ViewModel | |||
*/ | |||
abstract class BaseFragmentVM<VB : ViewDataBinding, VM : ViewModel> : BaseFragment<VB>() { | |||
lateinit var viewModel: VM | |||
abstract class BaseFragmentVM<VB : ViewBinding, VM : BaseViewModel> : BaseFragment<VB>() { | |||
lateinit var vm: VM | |||
override fun initFirst() { | |||
viewModel = initViewModel() | |||
vm = initViewModel() | |||
vm.pageEvent.observe(this) { startActivity(it) } | |||
vm.toastEvent.observe(this){ showToast(it) } | |||
vm.loadingEvent.observe(this) { showHideLoading(it)} | |||
} | |||
abstract fun initViewModel(): VM |
@@ -39,10 +39,10 @@ class LifecycleLogFragment : Fragment() { | |||
LogUtil.d("onViewCreated") | |||
} | |||
override fun onActivityCreated(savedInstanceState: Bundle?) { | |||
super.onActivityCreated(savedInstanceState) | |||
LogUtil.d("onActivityCreated") | |||
} | |||
// override fun onActivityCreated(savedInstanceState: Bundle?) { | |||
// super.onActivityCreated(savedInstanceState) | |||
// LogUtil.d("onActivityCreated") | |||
// } | |||
override fun onStart() { | |||
super.onStart() |
@@ -0,0 +1,74 @@ | |||
package com.suliang.common.base.viewmodel | |||
import android.widget.Toast | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.ViewModel | |||
import com.suliang.common.base.ViewBehavior | |||
import com.suliang.common.base.activity.ToastEvent | |||
import com.suliang.common.util.LogUtil | |||
/** | |||
* author suliang | |||
* create 2022/3/17 11:03 | |||
* Describe: | |||
*/ | |||
open class BaseViewModel : ViewModel(), ViewModelLifecycle, ViewBehavior { | |||
override fun onCreate(owner: LifecycleOwner) { | |||
super.onCreate(owner) | |||
LogUtil.i("${javaClass.name} onCreate() ") | |||
} | |||
override fun onStart(owner: LifecycleOwner) { | |||
super.onStart(owner) | |||
LogUtil.i("${javaClass.name} onStart() ") | |||
} | |||
override fun onResume(owner: LifecycleOwner) { | |||
super.onResume(owner) | |||
LogUtil.i("${javaClass.name} onResume() ") | |||
} | |||
override fun onPause(owner: LifecycleOwner) { | |||
super.onPause(owner) | |||
LogUtil.i("${javaClass.name} onPause() ") | |||
} | |||
override fun onStop(owner: LifecycleOwner) { | |||
super.onStop(owner) | |||
LogUtil.i("${javaClass.name} onStop() ") | |||
} | |||
override fun onDestroy(owner: LifecycleOwner) { | |||
super.onDestroy(owner) | |||
LogUtil.i("${javaClass.name} onDestroy() ") | |||
} | |||
override fun onCleared() { | |||
super.onCleared() | |||
LogUtil.i("${javaClass.name} onCleared() ") | |||
} | |||
var loadingEvent = MutableLiveData<Boolean>() | |||
private set | |||
var toastEvent = MutableLiveData<ToastEvent>() | |||
private set | |||
var pageEvent = MutableLiveData<Class<*>>() | |||
private set | |||
override fun showHideLoading(isShow: Boolean) { | |||
loadingEvent.postValue(isShow) | |||
} | |||
override fun showToast(msg: ToastEvent) { | |||
toastEvent.postValue(msg) | |||
} | |||
override fun startActivity(clazz: Class<*>) { | |||
pageEvent.postValue(clazz) | |||
} | |||
} | |||
@@ -0,0 +1,12 @@ | |||
package com.suliang.common.base.viewmodel | |||
import androidx.lifecycle.DefaultLifecycleObserver | |||
/** | |||
* author suliang | |||
* create 2022/3/17 11:22 | |||
* Describe: ViewModel生命周期 与 Activity/Fragment同步 | |||
*/ | |||
interface ViewModelLifecycle : DefaultLifecycleObserver { | |||
} |
@@ -0,0 +1,79 @@ | |||
package com.suliang.common.eventbus | |||
import androidx.lifecycle.MutableLiveData | |||
/** | |||
* author suliang | |||
* create 2022/3/15 10:01 | |||
* Describe: 事件总线,这条总线把任何类中的数据直接传递到activity 或是 fragment 上 | |||
*/ | |||
object LiveDataBus { | |||
private val bus = mutableMapOf<String,MutableLiveData<Any>>() | |||
// /** | |||
// * 注册订阅者 | |||
// * @param key String key | |||
// * @param type Class<T> 类型 | |||
// * @param sticky Boolean 是否需要粘性,true:需要 false:不需要 | |||
// * @return MutableLiveData<T> | |||
// */ | |||
// @Synchronized | |||
// fun <T> with(key: String, type: Class<T>, sticky: Boolean): MutableLiveData<T>{ | |||
// if (!bus.containsKey(key)) { | |||
// bus[key] = if (sticky) MutableLiveData<Any>() else NonStickyMutableLiveData<Any>() | |||
// } | |||
// return bus[key] as MutableLiveData<T> | |||
// } | |||
//java使用传入泛型class, kotlin使用可直接传入泛型,不需要class类型 | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> with(key:String) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = MutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> withNonSticky(key:String) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = NonStickyMutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> with(key:String,type: Class<T>) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = MutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
@Synchronized | |||
fun <T> withNonSticky(key:String,type: Class<T>) : MutableLiveData<T>{ | |||
if (!bus.containsKey(key)){ | |||
bus[key] = NonStickyMutableLiveData() | |||
} | |||
return bus[key] as MutableLiveData<T> | |||
} | |||
fun remove(key:String){ | |||
bus.remove(key) | |||
} | |||
fun clearAll(){ | |||
bus.clear() | |||
} | |||
} |
@@ -0,0 +1,65 @@ | |||
package com.suliang.common.eventbus | |||
import androidx.lifecycle.LifecycleOwner | |||
import androidx.lifecycle.LiveData | |||
import androidx.lifecycle.MutableLiveData | |||
import androidx.lifecycle.Observer | |||
import java.lang.Exception | |||
import java.lang.NullPointerException | |||
import java.lang.reflect.Method | |||
/** | |||
* author suliang | |||
* create 2022/3/15 10:51 | |||
* Describe: 非粘性LiveData | |||
*/ | |||
class NonStickyMutableLiveData<T> : MutableLiveData<T>() { | |||
private var stickFlag : Boolean = false | |||
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { | |||
super.observe(owner, observer) | |||
if (!stickFlag){ | |||
hook(observer) | |||
stickFlag = true | |||
} | |||
} | |||
private fun hook(observer: Observer<in T>) = try { | |||
//得到 mLastVersion | |||
//获取到LiveData的类中的mObserver对象 | |||
// SafeIterableMap<Observer<? super T> , ObserverWrapper> mObservers | |||
val liveDataClass = LiveData::class.java | |||
val declaredField = liveDataClass.getDeclaredField("mObservers") | |||
declaredField.isAccessible = true | |||
//获取到这个成员变量的对象 | |||
val mObserVersObject = declaredField.get(this) | |||
//得到map对应的class对象 | |||
val mObserversClass = mObserVersObject.javaClass | |||
//获取到mObservers对象的get方法 | |||
val get:Method = mObserversClass.getDeclaredMethod("get", Any::class.java) | |||
get.isAccessible = true | |||
//执行get方法,获取到对象 | |||
val invokeEntry = get.invoke(mObserVersObject, observer) | |||
//定义一个空的对象 | |||
val observerWrapper = if (invokeEntry is Map.Entry<*, *>) { | |||
invokeEntry.value | |||
} else throw NullPointerException("observerWrapper is null") | |||
//得到observerWrapper的对象,编译擦除问题会引起多态冲突,所以用getSupperClass | |||
//getClass 返回对应的当前正在运行是的类所对应的 | |||
val mLastVersion = observerWrapper!!::class.java.superclass.getDeclaredField("mLastVersion") | |||
mLastVersion.isAccessible = true | |||
//得到mVersion | |||
val mVersion = liveDataClass.getDeclaredField("mVersion") | |||
mVersion.isAccessible = true | |||
//把mVersion数据填入到mLastVersion中 | |||
val mVersionValue = mVersion.get(this) | |||
mLastVersion.set(observerWrapper,mVersionValue) | |||
}catch (e: Exception){ | |||
e.printStackTrace() | |||
} | |||
} |
@@ -1,5 +1,6 @@ | |||
package com.suliang.common.extension | |||
import android.content.Intent | |||
import androidx.annotation.IdRes | |||
import androidx.fragment.app.Fragment | |||
import androidx.fragment.app.FragmentActivity | |||
@@ -13,6 +14,13 @@ import androidx.lifecycle.Lifecycle | |||
*/ | |||
/*----------------------------------Activity Start-------------------------------------*/ | |||
fun <T> FragmentActivity.startActivity(newActivityClass:Class<T>){ | |||
this.startActivity(Intent(this,newActivityClass)) | |||
} | |||
/** | |||
* Activity 级别 加载Fragment集合进入容器,并显示 | |||
* @receiver FragmentActivity |
@@ -12,7 +12,7 @@ import java.lang.reflect.ParameterizedType | |||
* @param vbPosition Int 在超类泛型中的vb位置 | |||
* @return VB | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
inline fun <VB : ViewBinding> Any.initBinding(inflater: LayoutInflater,vbPosition:Int = 0): VB { | |||
val vbClass = | |||
(javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance<Class<VB>>() | |||
@@ -28,9 +28,9 @@ inline fun <VB : ViewBinding> Any.initBinding(inflater: LayoutInflater,vbPositio | |||
* @param vbPosition Int | |||
* @return VB | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
@SuppressWarnings("UNCHECKED_CAST") | |||
inline fun <VB: ViewBinding> Any.initBinding(inflater: LayoutInflater, container:ViewGroup?,vbPosition:Int = 0):VB{ | |||
val vbClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance<Class<VB>>() | |||
val inflater = vbClass[vbPosition].getDeclaredMethod("inflate",LayoutInflater::class.java,ViewGroup::class.java,Boolean::class.java) | |||
return inflater.invoke(null,inflater,container,false) as VB | |||
val inflate = vbClass[vbPosition].getDeclaredMethod("inflate",LayoutInflater::class.java,ViewGroup::class.java,Boolean::class.java) | |||
return inflate.invoke(null,inflater,container,false) as VB | |||
} |
@@ -0,0 +1,28 @@ | |||
package com.suliang.common.extension | |||
import android.net.Uri | |||
import android.widget.ImageView | |||
import com.suliang.common.util.image.ImageLoader | |||
import com.suliang.common.util.image.ImageLoaderOption | |||
import java.io.File | |||
/** | |||
* author suliang | |||
* create 2022/3/15 9:48 | |||
* Describe: 图片加载扩展 | |||
*/ | |||
fun ImageView.loadImage(option:ImageLoaderOption){ | |||
ImageLoader.loadImage(option) | |||
} | |||
fun ImageView.loadImage(url: String){ | |||
ImageLoader.loadImage(this,url) | |||
} | |||
fun ImageView.loadImage(file: File){ | |||
ImageLoader.loadImage(this,file) | |||
} | |||
fun ImageView.loadImage(resourceId:Int){ | |||
ImageLoader.loadImage(this,resourceId) | |||
} | |||
fun ImageView.loadImage(uri: Uri){ | |||
ImageLoader.loadImage(this,uri) | |||
} |
@@ -29,30 +29,30 @@ object LogUtil { | |||
fun v(message: String) { | |||
if (!LogEnable) return | |||
val stackTrace = Throwable().stackTrace[1] | |||
Log.v(stackTrace.fileName,createLog(stackTrace,message)) | |||
Log.v(stackTrace.className,createLog(stackTrace,message)) | |||
} | |||
fun d(message: String) { | |||
if (!LogEnable) return | |||
val stackTrace = Throwable().stackTrace[1] | |||
Log.d(stackTrace.fileName,createLog(stackTrace,message)) | |||
Log.d(stackTrace.className,createLog(stackTrace,message)) | |||
} | |||
fun e(message: String) { | |||
if (!LogEnable) return | |||
val stackTrace = Throwable().stackTrace[1] | |||
Log.e(stackTrace.fileName,createLog(stackTrace,message)) | |||
Log.e(stackTrace.className,createLog(stackTrace,message)) | |||
} | |||
fun i(message: String) { | |||
if (!LogEnable) return | |||
val stackTrace = Throwable().stackTrace[1] | |||
Log.i(stackTrace.fileName,createLog(stackTrace,message)) | |||
Log.i(stackTrace.className,createLog(stackTrace,message)) | |||
} | |||
fun w(message: String) { | |||
if (!LogEnable) return | |||
val stackTrace = Throwable().stackTrace[1] | |||
Log.w(stackTrace.fileName,createLog(stackTrace,message)) | |||
Log.w(stackTrace.className,createLog(stackTrace,message)) | |||
} | |||
} |
@@ -167,7 +167,7 @@ object FileUtil { | |||
*/ | |||
private fun writeStream(inputStream: InputStream, outputStream: OutputStream) { | |||
try { | |||
var len = 0 | |||
var len: Int | |||
val buffer: ByteArray = ByteArray(1024) | |||
while ((inputStream.read(buffer).also { len = it }) != -1) { | |||
outputStream.write(buffer, 0, len) | |||
@@ -282,7 +282,7 @@ object FileUtil { | |||
try { | |||
val fileInputStream = FileInputStream(file) | |||
val outputStream = ByteArrayOutputStream() | |||
var len = 0 | |||
var len: Int | |||
var byteArray = ByteArray(1024) | |||
while (fileInputStream.read(byteArray).also { len = it } != -1) { | |||
outputStream.write(byteArray, 0, len) | |||
@@ -389,7 +389,7 @@ object FileUtil { | |||
// 使用默认缓冲区大小创建新的输入流 | |||
val gzip = GZIPInputStream(inputStream) | |||
val buffer = ByteArray(256) | |||
var n = 0 | |||
var n:Int | |||
// 将未压缩数据读入字节数组 | |||
while (gzip.read(buffer).also { n = it } >= 0) { | |||
out.write(buffer, 0, n) |
@@ -20,7 +20,7 @@ import kotlin.concurrent.thread | |||
* Describe: Glide 加载的具体实现 | |||
* https://juejin.cn/post/6844903463134953479#heading-3 | |||
*/ | |||
class GlideLoaderStrategy : IBaseLoaderStrategy { | |||
internal class GlideLoaderStrategy : IBaseLoaderStrategy { | |||
override fun clearMemoryCache() { | |||
Glide.get(AppGlobals.application).clearMemory() |
@@ -9,8 +9,6 @@ import java.io.File | |||
* create 2022/3/11 16:10 | |||
* Describe: 外部调用,图片加载库 | |||
* | |||
* 使用: ImageLoader.load(url).into(View) | |||
* | |||
* 顺序判断,自动限制,前面参数有值了,则后面的值无效,如果都没有值,则抛异常 | |||
* url > file > drawableRes > uri | |||
* | |||
@@ -19,6 +17,14 @@ import java.io.File | |||
* https://www.jianshu.com/p/19420b151507 | |||
* https://blog.csdn.net/EthanCo/article/details/102587193 | |||
* https://www.jianshu.com/p/e26130a93289 | |||
* | |||
* 使用: | |||
* ImageLoader.loadImage(option) | |||
* ImageLoader.loadImage(view,source) | |||
* ImageLoaderOption : 为加载的参数配置,必须要有上下文或者targetView,暂时没有设置listener监听和目标target,如果后期有使用,再在此类中进行配置,并在对应的strategy中进行实现 | |||
* | |||
* | |||
* | |||
*/ | |||
object ImageLoader { | |||
@@ -0,0 +1,110 @@ | |||
package com.suliang.common.widget | |||
import android.annotation.SuppressLint | |||
import android.content.Context | |||
import android.graphics.Color | |||
import android.graphics.Rect | |||
import android.graphics.drawable.Drawable | |||
import android.util.AttributeSet | |||
import android.view.MotionEvent | |||
import android.view.inputmethod.EditorInfo | |||
import androidx.appcompat.widget.AppCompatEditText | |||
import androidx.core.content.res.ResourcesCompat | |||
import com.suliang.common.R | |||
import com.suliang.common.util.os.KeyboardUtil | |||
/** | |||
* author suliang | |||
* create 2022/3/16 18:07 | |||
* Describe: 带关闭按钮的输入框 : 左侧为搜索图标, 右侧为删除图标 ,需自动设置监听改变 | |||
*/ | |||
@SuppressLint("UseCompatLoadingForDrawables") | |||
class InputSearchEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, | |||
defStyleAttr: Int = 0) : | |||
AppCompatEditText(context, attrs, defStyleAttr) { | |||
private val clearRect by lazy { Rect() } | |||
private var ic_delete_src: Int = 0 | |||
private var ic_delete_color: Int = 0 | |||
private var ic_search_src: Int = 0 | |||
private var ic_search_color: Int = 0 | |||
private var drawableStart: Drawable? = null | |||
private var drawableEnd: Drawable? = null | |||
init { | |||
attrs?.let { | |||
context.obtainStyledAttributes(attrs, R.styleable.InputSearchEditText, defStyleAttr, 0).let { | |||
ic_search_src = it.getResourceId(R.styleable.InputSearchEditText_ic_search_src, R.drawable.ic_search) | |||
ic_search_color = | |||
it.getColor(R.styleable.InputSearchEditText_ic_search_color, Color.parseColor("#8A8A8A")) | |||
ic_delete_color = | |||
it.getColor(R.styleable.InputSearchEditText_ic_delete_color, Color.parseColor("#8A8A8A")) | |||
ic_delete_src = it.getColor(R.styleable.InputSearchEditText_ic_delete_src, R.drawable.ic_delete) | |||
it.recycle() | |||
drawableStart = resources.getDrawable(ic_search_src, null).apply { | |||
setTint(ic_search_color) | |||
} | |||
setCompoundDrawablesWithIntrinsicBounds(drawableStart, null, null, null) | |||
drawableEnd = resources.getDrawable(ic_delete_src, null).apply { | |||
setTint(ic_delete_color) | |||
} | |||
} | |||
} | |||
} | |||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { | |||
super.onSizeChanged(w, h, oldw, oldh) | |||
//计算删除按钮所在的矩形区域 | |||
clearRect.apply { | |||
left = w - paddingRight - w.coerceAtMost(h) | |||
top = paddingTop | |||
right = w - paddingRight | |||
bottom = w.coerceAtLeast(h) | |||
} | |||
} | |||
override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) { | |||
super.onFocusChanged(focused, direction, previouslyFocusedRect) | |||
checkEditFocus(focused) | |||
} | |||
/** 根据焦点改变,显示对应的图标内容 | |||
* @param focused Boolean 有焦点为true 否则false | |||
*/ | |||
@SuppressLint("UseCompatLoadingForDrawables") | |||
private fun checkEditFocus(focused: Boolean) { | |||
if (text.isNullOrEmpty() || !focused) { //如果没有内容 或者没有焦点,则设置搜索图标 | |||
setCompoundDrawablesWithIntrinsicBounds(drawableStart, null, null, null) | |||
} else { | |||
setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, null, drawableEnd, null) | |||
} | |||
} | |||
override fun onTextChanged(text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int) { | |||
super.onTextChanged(text, start, lengthBefore, lengthAfter) | |||
//时刻监听 | |||
checkEditFocus(true) | |||
} | |||
@SuppressLint("ClickableViewAccessibility") | |||
override fun onTouchEvent(event: MotionEvent): Boolean { | |||
if (event.action == MotionEvent.ACTION_DOWN && isFocused && clearRect.contains(event.x.toInt(),event.y.toInt()) && text.isNullOrEmpty()){ | |||
setText("") | |||
return true | |||
} | |||
return super.onTouchEvent(event) | |||
} | |||
override fun onEditorAction(actionCode: Int) { | |||
if (actionCode == EditorInfo.IME_ACTION_SEARCH){ | |||
KeyboardUtil.hideKeyboard(this) | |||
} | |||
super.onEditorAction(actionCode) | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:width="14dp" | |||
android:height="14dp" | |||
android:viewportWidth="14" | |||
android:viewportHeight="14"> | |||
<path | |||
android:pathData="M10.8631,3.3702C11.0324,3.5394 11.0445,3.8063 10.8994,3.9894L10.8631,4.0302L7.8933,7L10.8631,9.9698C11.0454,10.1521 11.0454,10.4476 10.8631,10.6298C10.6939,10.799 10.4271,10.8111 10.2439,10.6661L10.2032,10.6298L7.2333,7.66L4.2635,10.6298C4.0812,10.8121 3.7858,10.8121 3.6035,10.6298C3.4343,10.4606 3.4222,10.1937 3.5673,10.0106L3.6035,9.9698L6.5734,7L3.6035,4.0302C3.4213,3.8479 3.4213,3.5524 3.6035,3.3702C3.7727,3.201 4.0396,3.1889 4.2228,3.3339L4.2635,3.3702L7.2333,6.34L10.2032,3.3702C10.3854,3.1879 10.6809,3.1879 10.8631,3.3702Z" | |||
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="18dp" | |||
android:height="18dp" | |||
android:viewportWidth="18" | |||
android:viewportHeight="18"> | |||
<path | |||
android:pathData="M7.8,1.8C11.1137,1.8 13.8,4.4863 13.8,7.8C13.8,9.2455 13.2888,10.5716 12.4374,11.6075C12.451,11.6139 12.4631,11.6227 12.4749,11.6321L12.5272,11.6787L15.9213,15.0728C16.1556,15.3071 16.1556,15.687 15.9213,15.9213C15.7037,16.1389 15.3606,16.1544 15.1251,15.9679L15.0728,15.9213L11.6787,12.5272C11.6509,12.4995 11.6265,12.4697 11.6053,12.4384C10.5708,13.2892 9.245,13.8 7.8,13.8C4.4863,13.8 1.8,11.1137 1.8,7.8C1.8,4.4863 4.4863,1.8 7.8,1.8ZM7.8,3C5.149,3 3,5.149 3,7.8C3,10.451 5.149,12.6 7.8,12.6C10.451,12.6 12.6,10.451 12.6,7.8C12.6,5.149 10.451,3 7.8,3Z" | |||
android:strokeWidth="1" | |||
android:fillColor="#8A8A99" | |||
android:fillType="evenOdd" | |||
android:strokeColor="#00000000"/> | |||
</vector> |
@@ -0,0 +1,16 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:oneshot="false"> <!--是否只执行一次--> | |||
<item android:drawable="@drawable/loading00" android:duration="100"/> | |||
<item android:drawable="@drawable/loading01" android:duration="100"/> | |||
<item android:drawable="@drawable/loading02" android:duration="100"/> | |||
<item android:drawable="@drawable/loading03" android:duration="100"/> | |||
<item android:drawable="@drawable/loading04" android:duration="100"/> | |||
<item android:drawable="@drawable/loading05" android:duration="100"/> | |||
<item android:drawable="@drawable/loading06" android:duration="100"/> | |||
<item android:drawable="@drawable/loading07" android:duration="100"/> | |||
<item android:drawable="@drawable/loading08" android:duration="100"/> | |||
<item android:drawable="@drawable/loading09" android:duration="100"/> | |||
<item android:drawable="@drawable/loading10" android:duration="100"/> | |||
</animation-list> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:shape="oval"> | |||
<solid android:color="#FFFFFF"/> | |||
<size android:width="120dp" android:height="120dp"/> | |||
</shape> |
@@ -0,0 +1,35 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
xmlns:app="http://schemas.android.com/apk/res-auto" | |||
tools:context=".base.LoadingDialog"> | |||
<androidx.appcompat.widget.AppCompatImageView | |||
android:id="@+id/loadImage" | |||
android:layout_width="120dp" | |||
android:layout_height="120dp" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintBottom_toTopOf="@+id/loadText" | |||
android:src="@drawable/loading" | |||
android:scaleType="centerInside" | |||
android:foregroundGravity="center" | |||
android:background="@drawable/loading_bg"/> | |||
<TextView | |||
android:id="@+id/loadText" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
app:layout_constraintTop_toBottomOf="@+id/loadImage" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
android:layout_marginTop="24dp" | |||
android:textStyle="bold" | |||
android:textColor="#FFFFFF" | |||
android:text="加载中..."/> | |||
</androidx.constraintlayout.widget.ConstraintLayout> |
@@ -0,0 +1,12 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<resources> | |||
<!--自定义的带搜索和删除按钮的EditTextView--> | |||
<declare-styleable name="InputSearchEditText"> | |||
<!--搜索图标--> | |||
<attr name="ic_search_src" format="reference"/> | |||
<attr name="ic_search_color" format="color"/> | |||
<!--删除图标--> | |||
<attr name="ic_delete_src" format="reference"/> | |||
<attr name="ic_delete_color" format="color"/> | |||
</declare-styleable> | |||
</resources> |
@@ -0,0 +1,4 @@ | |||
<resources> | |||
<!-- TODO: Remove or change this placeholder text --> | |||
<string name="hello_blank_fragment">Hello blank fragment</string> | |||
</resources> |