尾声
在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:
展示学习笔记
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
//Google Material控件,以及迁移到AndroidX下一些控件的依赖
implementation ‘com.google.android.material:material:1.2.0’
//RecyclerView最好的适配器,让你的适配器一目了然,告别代码冗余
implementation ‘com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30’
//权限请求框架
implementation ‘com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar’
implementation ‘io.reactivex.rxjava2:rxandroid:2.0.2’
implementation “io.reactivex.rxjava2:rxjava:2.0.0”
添加位置如下图所示:
现在工程和app模块的build.gradle就修改完成了,直接Sync同步一下,同步之后你添加的依赖库才能使用。
然后配置AndroidManifest.xml文件
添加位置如下图所示:
然后改动colors.xml中系统默认的颜色
这两个颜色会影响到你的状态栏。
然后是styles.xml文件
上面不涉及到代码,所以Java和Kotlin中的资源文件配置是差不多的。
图片资源
当然里面的一些其他的图标请到我的源码里面去拿,我就不一一贴出来了
在drawable下创建一个名为progressbar.xml的样式文件,代码如下:
<?xml version="1.0" encoding="utf-8"?><rotate
android:drawable=“@drawable/icon_loading”
android:fromDegrees=“0.0”
android:pivotX=“50.0%”
android:pivotY=“50.0%”
android:toDegrees=“360.0” />
修改activity_main.xml布局文件,代码如下:
<?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”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:fitsSystemWindows=“true”
android:orientation=“vertical”
tools:context=“.MainActivity”>
<androidx.appcompat.widget.Toolbar
android:elevation=“3dp”
android:id=“@+id/toolbar”
android:layout_width=“match_parent”
android:layout_height=“?attr/actionBarSize”
android:background=“@color/colorPrimary”
app:layout_constraintEnd_toEndOf=“parent”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintTop_toTopOf=“parent”>
<TextView
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_gravity=“center”
android:text=“我的蓝牙”
android:textColor=“#000”
android:textSize=“18sp” />
</androidx.appcompat.widget.Toolbar>
<View
android:layout_width=“match_parent”
android:layout_height=“1dp”
android:background=“#EEEEEE” />
<LinearLayout
android:id=“@+id/loading_lay”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:gravity=“center”
android:visibility=“gone”>
<ProgressBar
android:layout_width=“@dimen/dp_40”
android:layout_height=“@dimen/dp_40”
android:indeterminate=“true”
android:indeterminateDrawable=“@drawable/progressbar” />
<TextView
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“扫描中…” />
<androidx.recyclerview.widget.RecyclerView
android:id=“@+id/rv”
android:background=“#FFF”
android:layout_width=“match_parent”
android:layout_height=“0dp”
android:layout_weight=“1” />
<View
android:layout_width=“match_parent”
android:layout_height=“1dp”
android:background=“#EEEEEE” />
<TextView
android:id=“@+id/scan_devices”
android:layout_width=“match_parent”
android:layout_height=“50dp”
android:background=“?android:attr/selectableItemBackground”
android:gravity=“center”
android:text=“扫描蓝牙” />
在layout下创建列表展示的item的布局文件,名为item_device_list.xml
代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:orientation=“vertical”
android:id=“@+id/item_device”
android:background=“?android:attr/selectableItemBackground”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”>
<LinearLayout
android:gravity=“center_vertical”
android:padding=“12dp”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”>
<ImageView
android:id=“@+id/iv_device_type”
android:src=“@mipmap/icon_bluetooth”
android:layout_width=“30dp”
android:layout_height=“30dp”/>
<TextView
android:id=“@+id/tv_name”
android:paddingLeft=“12dp”
android:textSize=“16sp”
android:text=“设备名称”
android:textColor=“#000”
android:layout_width=“0dp”
android:layout_weight=“1”
android:layout_height=“wrap_content”/>
<TextView
android:gravity=“right”
android:id=“@+id/tv_bond_state”
android:text=“绑定状态”
android:layout_width=“0dp”
android:layout_weight=“1”
android:layout_height=“wrap_content”/>
<View
android:background=“#EBEBEB”
android:layout_marginLeft=“54dp”
android:layout_width=“match_parent”
android:layout_height=“1dp”/>
一切准备工作都已经就绪了,下面就进入编码环节,前面的内容其实和Kotlin的关系都不大,下面上正菜,Kotlin相比于Java来说的优势就是简洁,这一点会在下面的编码过程中体现。
1. 通知栏样式修改
首先修改状态栏的文字颜色,如果你现在运行这个项目在手机上时,你会发现状态栏是白色的背景以及白色的文字。如下图所示:
这样的用户体验是很不好的,而在Android6.0以后支持设置高亮状态栏样式。在之前我写Java版的时候特别弄了一个工具类,里面有针对性状态栏的一些样式和颜色改动,但实际上我只用了其中的一个方法,为了一个方法而去写一个工具类显然多此一举了。所以在Kotlin中我想到了更简单的办法,直接在MainActivity中修改状态栏样式。
代码如下:
//设置亮色状态栏模式 systemUiVisibility在Android11中弃用了,可以尝试一下。
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
放在onCreate方法中,然后运行。
是不是立竿见影,这个效果一行代码解决问题还不用写工具类,完全调用系统的方法,请注意我是Android10.0版本的手机,也是我自己用的手机。
下面写列表的适配器,因为你扫描蓝牙肯定会是一个列表,既然是一个列表那么肯定要有适配器。
2. 蓝牙设备列表适配器编写
创建一个adapter包,包下创建一个DeviceAdapter.kt文件,如下所示
DeviceAdapter的代码如下:
package com.llw.bluetooth.adapter
import android.bluetooth.BluetoothClass
import android.bluetooth.BluetoothClass.Device.*
import android.bluetooth.BluetoothClass.Device.Major.*
import android.bluetooth.BluetoothDevice
import android.widget.ImageView
import android.widget.TextView
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.BaseViewHolder
import com.llw.bluetooth.R
/**
- 蓝牙设备适配器
*/
class DeviceAdapter(layoutResId: Int, data: MutableList?) :
BaseQuickAdapter<BluetoothDevice, BaseViewHolder>(layoutResId, data) {
override fun convert(helper: BaseViewHolder?, item: BluetoothDevice?) {
val tvName = helper!!.getView(R.id.tv_name)
val icon = helper.getView(R.id.iv_device_type)
//根据设备类型设置图标
getDeviceType(item!!.bluetoothClass.majorDeviceClass, icon)
tvName.text = if (item.name == null) “无名” else item.name
//蓝牙设备绑定状态判断
val tvState = helper!!.getView(R.id.tv_bond_state)
tvState.text = when (item.bondState) {
10 -> “未配对”
11 -> “正在配对…”
12 -> “已配对”
else -> “未配对”
}
//添加item点击事件
helper.addOnClickListener(R.id.item_device)
}
/**
-
根据类型设置图标
-
@param type 类型码
-
@param icon 图标
*/
private fun getDeviceType(type: Int, icon: ImageView) {
when (type) {
AUDIO_VIDEO_HEADPHONES,//耳机
AUDIO_VIDEO_WEARABLE_HEADSET,//穿戴式耳机
AUDIO_VIDEO_HANDSFREE,//蓝牙耳机
AUDIO_VIDEO //音频设备
-> icon.setImageResource(R.mipmap.icon_headset)
COMPUTER //电脑
-> icon.setImageResource(R.mipmap.icon_computer)
PHONE //手机
-> icon.setImageResource(R.mipmap.icon_phone)
HEALTH //健康类设备
-> icon.setImageResource(R.mipmap.icon_health)
AUDIO_VIDEO_CAMCORDER, //照相机录像机
AUDIO_VIDEO_VCR //录像机
-> icon.setImageResource(R.mipmap.icon_vcr)
AUDIO_VIDEO_CAR_AUDIO //车载设备
-> icon.setImageResource(R.mipmap.icon_car)
AUDIO_VIDEO_LOUDSPEAKER //扬声器
-> icon.setImageResource(R.mipmap.icon_loudspeaker)
AUDIO_VIDEO_MICROPHONE //麦克风
-> icon.setImageResource(R.mipmap.icon_microphone)
AUDIO_VIDEO_PORTABLE_AUDIO //打印机
-> icon.setImageResource(R.mipmap.icon_printer)
AUDIO_VIDEO_SET_TOP_BOX //音频视频机顶盒
-> icon.setImageResource(R.mipmap.icon_top_box)
AUDIO_VIDEO_VIDEO_CONFERENCING //音频视频视频会议
-> icon.setImageResource(R.mipmap.icon_meeting)
AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER //显示器和扬声器
-> icon.setImageResource(R.mipmap.icon_tv)
AUDIO_VIDEO_VIDEO_GAMING_TOY //游戏
-> icon.setImageResource(R.mipmap.icon_game)
AUDIO_VIDEO_VIDEO_MONITOR //可穿戴设备
-> icon.setImageResource(R.mipmap.icon_wearable_devices)
else -> icon.setImageResource(R.mipmap.icon_bluetooth)
}
}
/**
- 刷新适配器
*/
fun changeBondDevice() {
notifyDataSetChanged()
}
}
代码讲解:
class DeviceAdapter(layoutResId: Int, data: MutableList?) :
BaseQuickAdapter<BluetoothDevice, BaseViewHolder>(layoutResId, data)
首先看这个类,在Kotlin继承和实现都是通过 :(英文下的冒号)来操作的,而Java中继承是extends,实现是implements。在上面的代码中DeviceAdapter继承了BaseQuickAdapter,这一点和Java的相似,如下图所示
而Kotlin的语法可以让你把构造方法的参数作为类参数使用,这样解释不知道是不是对的,这里传了一个布局id和数据源。
然后重写里面convert方法
override fun convert(helper: BaseViewHolder?, item: BluetoothDevice?) {
val tvName = helper!!.getView(R.id.tv_name)
val icon = helper.getView(R.id.iv_device_type)
getDeviceType(item!!.bluetoothClass.majorDeviceClass, icon)
tvName.text = if (item.name == null) “无名” else item.name
//蓝牙设备绑定状态判断
val tvState = helper!!.getView(R.id.tv_bond_state)
tvState.text = when (item.bondState) {
10 -> “未配对”
11 -> “正在配对…”
12 -> “已配对”
else -> “未配对”
}
//添加item点击事件
helper.addOnClickListener(R.id.item_device)
}
在代码中你会看到 !! 和 ? 这个你就不明所以了,因为Java中是没有的,这里解释一下,首先是Kotlin对于空安全做了处理, !! 表示当前对象不会空的情况下执行,而 ? 表示当前对象可以为空。
val tvName = helper!!.getView(R.id.tv_name)
val icon = helper.getView(R.id.iv_device_type)
而这两行代码,可以看到,第一行我给了!!,第二行没有给,这是因为在Kotlin中只要一开始做了处理之后后面就可以不用再次处理,当然你加上!!也没有问题。val 表示不可变量,而通过Kotlin的类型推导机制,tvName此时代表的就是一个通过R.id.tv_name实例化之后的TextView。
//根据设备类型设置图标
getDeviceType(item!!.bluetoothClass.majorDeviceClass, icon)
这行代码调用getDeviceType方法,传入两个参数,这两个参数都已经做了非空的处理,所以在getDeviceType的里面就不用在做空处理了。下面看这个方法的代码:
/**
-
根据类型设置图标
-
@param type 类型码
-
@param icon 图标
*/
private fun getDeviceType(type: Int, icon: ImageView) {
when (type) {
AUDIO_VIDEO_HEADPHONES,//耳机
AUDIO_VIDEO_WEARABLE_HEADSET,//穿戴式耳机
AUDIO_VIDEO_HANDSFREE,//蓝牙耳机
AUDIO_VIDEO //音频设备
-> icon.setImageResource(R.mipmap.icon_headset)
COMPUTER //电脑
-> icon.setImageResource(R.mipmap.icon_computer)
PHONE //手机
-> icon.setImageResource(R.mipmap.icon_phone)
HEALTH //健康类设备
-> icon.setImageResource(R.mipmap.icon_health)
AUDIO_VIDEO_CAMCORDER, //照相机录像机
AUDIO_VIDEO_VCR //录像机
-> icon.setImageResource(R.mipmap.icon_vcr)
AUDIO_VIDEO_CAR_AUDIO //车载设备
-> icon.setImageResource(R.mipmap.icon_car)
AUDIO_VIDEO_LOUDSPEAKER //扬声器
-> icon.setImageResource(R.mipmap.icon_loudspeaker)
AUDIO_VIDEO_MICROPHONE //麦克风
-> icon.setImageResource(R.mipmap.icon_microphone)
AUDIO_VIDEO_PORTABLE_AUDIO //打印机
-> icon.setImageResource(R.mipmap.icon_printer)
AUDIO_VIDEO_SET_TOP_BOX //音频视频机顶盒
-> icon.setImageResource(R.mipmap.icon_top_box)
AUDIO_VIDEO_VIDEO_CONFERENCING //音频视频视频会议
-> icon.setImageResource(R.mipmap.icon_meeting)
AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER //显示器和扬声器
-> icon.setImageResource(R.mipmap.icon_tv)
AUDIO_VIDEO_VIDEO_GAMING_TOY //游戏
-> icon.setImageResource(R.mipmap.icon_game)
AUDIO_VIDEO_VIDEO_MONITOR //可穿戴设备
-> icon.setImageResource(R.mipmap.icon_wearable_devices)
else -> icon.setImageResource(R.mipmap.icon_bluetooth)
}
}
其实就是条件分支,根据不同的条件做不同的事情,在Java中使用switch/case,而在Kotlin中使用when。when的语法结构更加的简洁明了,通过 -> 代替了 : ,冒号前面是条件,冒号后面是执行业务。而当多个条件对应一个执行业务时,条件之间用英文逗号隔开,一行代码完成一个条件分支,很简洁,但是不要忘了加上else,这是标准写法,你不加也没事,就如同你写switch/case不加default一样。相信这么一解释你已经理解了when的基本用法了,当然还有很多其他的用法由于业务的原因无法展示,自行百度吧。
tvName.text = if (item.name == null) “无名” else item.name
这行代码等同于
if (item.getName() == null) {
helper.setText(R.id.tv_name, “无名”);
} else {
helper.setText(R.id.tv_name, item.getName());
}
这么一看是不是觉得Kotlin的语法很简单,它允许你的返回值一致的判断进行直接赋值,比如这里判断设备名称为空则显示无名二字,不为空则显示设备名,这两个返回都是String类型,而tvName.text设置的就是String类型,所以就有了上面的简洁代码,有点像三目运算符。
//蓝牙设备绑定状态判断
val tvState = helper!!.getView(R.id.tv_bond_state)
tvState.text = when (item.bondState) {
10 -> “未配对”
11 -> “正在配对…”
12 -> “已配对”
else -> “未配对”
}
//添加item点击事件
helper.addOnClickListener(R.id.item_device)
这几行代码也没有什么好讲解的了,都讲过了,这也是when的另一种使用方法,可以直接赋值使用。
/**
- 刷新适配器
*/
fun changeBondDevice() {
notifyDataSetChanged()
}
最后一个方法就是刷新适配器,当页面的数据有变动是及时刷新。好了这个适配器就讲完了,应该够详细了吧。
3. 权限请求
不管你是用的什么语言来开发Android,你都得遵守Android制定的规则,因此也是要做Android版本大于6.0时动态请求权限。
于是就有了如下这个方法
/**
- 检查Android版本
*/
private fun checkVersion() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//6.0或6.0以上
permissionsRequest() //动态权限申请
} else {
//6.0以下
initBlueTooth() //初始化蓝牙配置
}
}
记得在OnCreate中调用喔!
然后来看看这个权限请求的方法
/**
- 动态权限申请
*/
private fun permissionsRequest() {
val rxPermissions = RxPermissions(this)
rxPermissions.request(Manifest.permission.ACCESS_FINE_LOCATION)
.subscribe {
if (it) {
initBlueTooth()
} else {
showMsg(“权限未开启”)
}
}
}
showMsg方法
/**
- 显示提示消息
*/
Android高级架构师
由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。
- 330页PDF Android学习核心笔记(内含上面8大板块)
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
- Android BAT部分大厂面试题(有解析)
好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
DM2MjE0,size_16,color_FFFFFF,t_70#pic_center)
然后来看看这个权限请求的方法
/**
- 动态权限申请
*/
private fun permissionsRequest() {
val rxPermissions = RxPermissions(this)
rxPermissions.request(Manifest.permission.ACCESS_FINE_LOCATION)
.subscribe {
if (it) {
initBlueTooth()
} else {
showMsg(“权限未开启”)
}
}
}
showMsg方法
/**
- 显示提示消息
*/
Android高级架构师
由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。
- 330页PDF Android学习核心笔记(内含上面8大板块)
[外链图片转存中…(img-yW4m6mwG-1715183474226)]
[外链图片转存中…(img-2xNoMB1E-1715183474227)]
-
Android学习的系统对应视频
-
Android进阶的系统对应学习资料
[外链图片转存中…(img-bEdmg6lJ-1715183474227)]
- Android BAT部分大厂面试题(有解析)
[外链图片转存中…(img-nwtVi0gG-1715183474227)]
好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!