在上一次https://www.cnblogs.com/webor2006/p/12622874.html已经初步对主界面进行了一个框架布局,目前的样子如下:
![](https://i-blog.csdnimg.cn/blog_migrate/edcf90cb9d0de399d23e40c55be0debb.png)
其主界面的布局文件为:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.roughike.bottombar.BottomBar
android:id="@+id/bottomBar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
app:bb_tabXmlResource="@xml/bottombar_tabs" />
</LinearLayout>
下面继续进行功能的实现。
toolbar处理:
修改背景色:
这里先来处理一下toolbar,先回忆一下最终的效果:
![](https://i-blog.csdnimg.cn/blog_migrate/e63c011a0a053525ceff9287a9a28e7c.png)
首先背景色不对,目前toolbar的背景用的是主题色:
![](https://i-blog.csdnimg.cn/blog_migrate/305f00bceecf4764c0a95c021c7b89a5.png)
所有这里将修改一下标题栏的主题色既可:
![](https://i-blog.csdnimg.cn/blog_migrate/8f8e6f86c17ccdaabecd2de0550e8d68.png)
运行:
![](https://i-blog.csdnimg.cn/blog_migrate/7e4dfc71f12ed159bdc9a09cf6e31b37.png)
标题封装:
对于标题是每个界面都会要进行处理的,所以这里有必要对它进行一个封装处理了,这里就学一下在Kotlin中是如何来达到封装的目的的,跟Java的写法还是不太一样的。
这里新建一个封装管理类:
![](https://i-blog.csdnimg.cn/blog_migrate/b3b602836b2d6f9b42653b070689bf0e.png)
package com.kotlin.musicplayer.utils
/**
* 标题栏管理类
*/
class ToolBarManager {
/**
* 初始化主界面的toolbar
*/
fun initMainToolBar() {
//TODO
}
}
然后这里就跟Java不同的地方出来了,看好了,这里面可以定义一个Toolbar的引用:
![](https://i-blog.csdnimg.cn/blog_migrate/4e8c45bba5f40e8b386dd93655738739.png)
但是,很明显这个Toolbar的初始化应该交由子类来提供,只有具体的界面才知道是什么样的Toolbar,所以为了不让它报错,可以将它声明成一个接口,此时val的变量就不需要强制初始化而转由子类来进行:
![](https://i-blog.csdnimg.cn/blog_migrate/3fc7e5e857c6e571fed879bb8176117d.png)
有了Toolbar的引用,接下来咱们就可以在初始化方法中设置一下主界面的标题:
![](https://i-blog.csdnimg.cn/blog_migrate/9a3df881a1dde2b027818c5466a32590.png)
设置标题:
接下来则可以在MainActivity来实现这个接口并对Toolbar进行初始化,具体如下:
![](https://i-blog.csdnimg.cn/blog_migrate/ecacf4b84044c7a64b0772b39dab891d.png)
此时就需要来初始化val的变量了:
![](https://i-blog.csdnimg.cn/blog_migrate/6a1112cb42877d24fba6c3d46a337904.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ae3a8db5eb122442928a8ef7f2c82495.png)
属性重写可以参考:https://www.cnblogs.com/webor2006/p/11203903.html,这里打算用延迟初始化来提高程序的性能,怎么做?
![](https://i-blog.csdnimg.cn/blog_migrate/2bace47830c980e1e719910131049c57.png)
此时看一下lazy函数:
![](https://i-blog.csdnimg.cn/blog_migrate/7afb5bb925955ccb8aa73bd0eed9660b.png)
所以为啥咱们调用是这样的:
![](https://i-blog.csdnimg.cn/blog_migrate/4b9da5bd4d582e2e68ac2746e57903e5.png)
另外,貌似之前咱们在学习Kotlin语言时还有一个延迟初始化的方式:
![](https://i-blog.csdnimg.cn/blog_migrate/57beb65452e14ffb3217129bc06af30f.png)
那这俩延迟初始化有啥区别呢?可以参考博主:https://www.jianshu.com/p/e2cb4c65d4ff,简单来说是lazyinit只能用到var的变量,而by lazy只能用于val的变量,而关于var和val有啥区别这里就不多说了,最最基础的了,接下来咱们则可以调用初始化方法了,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/390085ce817fb70469dbcd23d35897df.png)
运行:
![](https://i-blog.csdnimg.cn/blog_migrate/26ba5549d5c24bef42a1e6db86c0a169.png)
另外咱们来打印一下日志,看一下lazy()函数的调用确实是在我们使用了Toolbar之后才执行的么?
![](https://i-blog.csdnimg.cn/blog_migrate/b95fdd8f0b749575167818ef0be1eaa9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9de56083809ec96801a21b44336fdbc1.png)
运行看一下日志输出:
![](https://i-blog.csdnimg.cn/blog_migrate/5abc598416c6dab290ca07bdccdb13ea.png)
嗯,确实是!!
增加设置菜单:
接下来还需要给右侧增加一个设置菜单,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/f855d8a42f3fe4db4f790bf8b58db30f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c01ac954ad482261eb4397a07f65ccac.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4391e89b890bf8bb58918c50118e24f5.png)
其中用到一个图片:
![](https://i-blog.csdnimg.cn/blog_migrate/dc3fe1e6d778e5e3196e909e5e5d1554.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f0f3374972e14cf1de7e7f14e26c2057.png)
它其实长这样:
![](https://i-blog.csdnimg.cn/blog_migrate/85da4cddfe2081c64fc90d5866b70748.png)
此时可以在IDE上预览一下效果:
![](https://i-blog.csdnimg.cn/blog_migrate/91a21a92b0313a14be6e8d9576f9a0b8.png)
那此时需要再设置一个属性才行:
![](https://i-blog.csdnimg.cn/blog_migrate/e69d4c28367271a915d98345d8dd5554.png)
此时咱们再运行看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/70e4227b79858e52934487d855ee347f.png)
设置菜单点击事件处理:
接下来给菜单增加一个点击事件:
![](https://i-blog.csdnimg.cn/blog_migrate/09d9acb55abecfc497c386deec90eeab.png)
其中这里又得复习一下相关语法了-----对象表达式(Object expression),关于这块可以参考:https://www.cnblogs.com/webor2006/p/11352421.html,关于它的作用可以大致回顾一下:
![](https://i-blog.csdnimg.cn/blog_migrate/a8e2ed35c37e5a99015463a7d4f90fc5.png)
也就是在Koltin对于匿名内部类是采用对象表达式的形式来代替的,接下来咱们先来处理点击逻辑,这里先打个toast看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/a55fc451324068e89d6e775f10f3d553.png)
在Kotlin中木有switch关键字,这里是用的when关键字,而关于它的用法可以参考:https://www.cnblogs.com/webor2006/p/11186089.html,其中它还简化的写法:
![](https://i-blog.csdnimg.cn/blog_migrate/dd7c54530100671c1d3a77aa940eba41.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2d028f058ab4cda549a00527a6bb34ea.png)
运行看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/d8efd3801161e3946597d170c0028d98.png)
另外对于这个事件监听代码在IDE上已经有一个标黄的提示了,很明显是有更加优化的写法:
![](https://i-blog.csdnimg.cn/blog_migrate/1280c67b99bd9251b7f7a0aa26f85896.png)
用IDE的向导提示来修改一下,此时代码就精简为:
![](https://i-blog.csdnimg.cn/blog_migrate/bc300426a14152b7b8869f2ab7e6d990.png)
看一下这个回调接口的定义:
![](https://i-blog.csdnimg.cn/blog_migrate/fbbb39c6b4c9cd2d916f1f1d930f629f.png)
其实还可以进一步精简,这里就涉及到Kotlin调用Java的小细节了,“如果Java接口中只有一个未实现的方法,可以省略接口对象,直接用{}表示未实现的方法”,也就是:
![](https://i-blog.csdnimg.cn/blog_migrate/695ccab7382e780c333ed115bd096c66.png)
其实这个规则就是Kotlin关于参数是Lambda表达式时直接可以用花括号来进行调用的差不多,回忆一下:
![](https://i-blog.csdnimg.cn/blog_migrate/8698b191b6108aa09991f621a1ef0437.png)
好,逻辑了这么多关于语法的知识,目的就是为了巩固Kotlin的语法,只有这样才会对Kotlin掌握得更加得熟练,接下来则需要点击跳转到一个设置界面,所以下面来准备一下:
![](https://i-blog.csdnimg.cn/blog_migrate/2e8abb3060078704870bc14b3802c880.png)
package com.kotlin.musicplayer.ui.activity
import com.kotlin.musicplayer.R
import com.kotlin.musicplayer.base.BaseActivity
/**
* 设置界面
*/
class SettingsActivity : BaseActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_settings
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="settings" />
</LinearLayout>
![](https://i-blog.csdnimg.cn/blog_migrate/133732b60304e89431ab44c93d18eda9.png)
然后点击时跳转一下,这里又有一个Koltin的语法出来了:
![](https://i-blog.csdnimg.cn/blog_migrate/2356cf5db76f9d8b8949afe6ed41cbb3.png)
其中这里获取Class时后面跟了一个.java,原因是我们的每个Activity继承的是一个Java的Activity,而非Kotlin的,如下:
![](https://i-blog.csdnimg.cn/blog_migrate/988a3ce6f537d8a541200e45465434b6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0531ee1019c0ab44cf54c3f1d1f01b93.png)
而要获取对应Java的class类就需要用这种语法,关于这块可以参考:https://www.cnblogs.com/webor2006/p/11571472.html,
![](https://i-blog.csdnimg.cn/blog_migrate/a08a4482742719bb74b1a78e3f83db91.png)
好,接下来运行看一下设置界面:
![](https://i-blog.csdnimg.cn/blog_migrate/73efb0b946f2ed2d1daeb5c61d9a2a7b.png)
设置界面处理:
接下来则来对设置界面进行调整,先展示一下预期的效果图:
![](https://i-blog.csdnimg.cn/blog_migrate/d5ffa9a08283129bc253e04c0d3fdb62.png)
标题处理:
先来修改toolbar,照之前主界面的toolbar的写法来:
![](https://i-blog.csdnimg.cn/blog_migrate/4e9a6a0271b4932cbd92c82b65cad1fd.png)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="settings" />
</LinearLayout>
package com.kotlin.musicplayer.ui.activity
import androidx.appcompat.widget.Toolbar
import com.kotlin.musicplayer.R
import com.kotlin.musicplayer.base.BaseActivity
import com.kotlin.musicplayer.utils.ToolBarManager
import org.jetbrains.anko.find
/**
* 设置界面
*/
class SettingsActivity : BaseActivity(), ToolBarManager {
override fun getLayoutId(): Int {
return R.layout.activity_settings
}
override val toolbar by lazy { find<Toolbar>(R.id.toolbar) }
override fun initData() {
super.initData()
initSettingsToolBar()
}
}
运行看下效果:
![](https://i-blog.csdnimg.cn/blog_migrate/3f45395866465efd5e7a1348e03fe360.png)
设置项处理:
这里采用PreferenceFragment来构造咱们的设置项,所以新建一个Fragment:
![](https://i-blog.csdnimg.cn/blog_migrate/bad3f447ace0298e1fb23a80b3e92c4d.png)
package com.kotlin.musicplayer.ui.fragment
import android.os.Bundle
import android.preference.PreferenceFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.kotlin.musicplayer.R
/**
* 设置项
*/
class SettingsFragment: PreferenceFragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
addPreferencesFromResource(R.xml.setting)
return super.onCreateView(inflater, container, savedInstanceState)
}
}
其中配置项是在xml中:
![](https://i-blog.csdnimg.cn/blog_migrate/54c2650b1539eb0138b626f795a2fd27.png)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:key="clear_cache"
android:title="清除缓存" />
<SwitchPreference
android:key="push"
android:title="推送通知" />
<SwitchPreference
android:key="no_wifi"
android:title="非wifi下加载图片" />
<Preference
android:key="about"
android:title="关于" />
</PreferenceScreen>
然后将其声明到Activity布局当中:
![](https://i-blog.csdnimg.cn/blog_migrate/b6c519190a8a49f0720808944a9ee3a0.png)
运行看一下效果:
![](https://i-blog.csdnimg.cn/blog_migrate/d50faf3e421dcf9faf01ff6f51b2028c.gif)
使用PreferenceFragment的好处就是对于配置的开关就不需要咱们自己来写了,它里面已经为我们处理好了,下面咱们来读取一下开关状态,看是否能正常的读取出来:
package com.kotlin.musicplayer.ui.activity
import android.preference.PreferenceManager
import androidx.appcompat.widget.Toolbar
import com.kotlin.musicplayer.R
import com.kotlin.musicplayer.base.BaseActivity
import com.kotlin.musicplayer.utils.ToolBarManager
import org.jetbrains.anko.find
/**
* 设置界面
*/
class SettingsActivity : BaseActivity(), ToolBarManager {
override fun getLayoutId(): Int {
return R.layout.activity_settings
}
override val toolbar by lazy { find<Toolbar>(R.id.toolbar) }
override fun initData() {
super.initData()
initSettingsToolBar()
println("push checked:" + PreferenceManager.getDefaultSharedPreferences(this).getBoolean("push", false))
}
}
其中SP的key则是在xml中配置的:
![](https://i-blog.csdnimg.cn/blog_migrate/10ddca59db9badcff4d4525bdddab776.png)
运行一下:
![](https://i-blog.csdnimg.cn/blog_migrate/4ffe44236abc194ae044996d421ad12f.gif)
接下来需要处理“关于”的点击事件,简单处理下:
/**
* 设置项
*/
class SettingsFragment : PreferenceFragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
addPreferencesFromResource(R.xml.setting)
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onPreferenceTreeClick(preferenceScreen: PreferenceScreen?, preference: Preference?): Boolean {
val key = preference?.key
if ("about".equals(key)) {
//点击了关于
activity.startActivity(Intent(activity, AboutActivity::class.java))
}
return super.onPreferenceTreeClick(preferenceScreen, preference)
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/e67e17f7691168ee109b625e0731103f.png)
package com.kotlin.musicplayer.ui.activity
import com.kotlin.musicplayer.R
import com.kotlin.musicplayer.base.BaseActivity
/**
* 关于页面
*/
class AboutActivity : BaseActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_about
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="这是一款免费的Kotlin视频播放器" />
</LinearLayout>
![](https://i-blog.csdnimg.cn/blog_migrate/c260aaedc7fece3e4b757e36c05765a9.png)
运行:
![](https://i-blog.csdnimg.cn/blog_migrate/7fd50d8d7bda0ced0db075f9528339dc.png)