本篇文章是我研究的项目整理的一些知识点,方便自查,后续会不断整理旧的内容和添加新的内容,本篇文章也加了一些常用库的地址,方便查找
Android Matirx详解
android matrix 最全方法详解与进阶(完整篇) - 掘金 (juejin.cn)
Android—Bitmap解析与应用 - 掘金 (juejin.cn)
概述:
Matrix提供了preXXX()和postXXX()方法来组合多个矩阵操作,多个矩阵操作之间通过乘法运算。
由于矩阵的乘法是不满足交换律的,因此进行矩阵运算时需要注意乘法的顺序。
在使用preXXX()和postXXX()时,我们可以将矩阵变换的所有计算看成一个乘法列表,调用preXXX()方法时就是向列表头部添加操作,调用postXXX()时就是向列表尾部添加操作。
例如以下代码中矩阵乘法的执行顺序就是2->1->3->4。需要注意的是,setXXX()方法会重置Matrix的变换,如果对下方执行完4个运算的Matrix调用setTranslate()方法,那么该Matrix就只有平移效果了。
Matrix matrix = new Matrix();
matrix.preScale(...); // 1
matrix.preTranslate(...); // 2
matrix.postTranslate(...); // 3
matrix.postRotate(...); // 4
Android判断点是否在三角形内部
向量叉乘快速判断点在三角形内外
/**
* 判断点是否在三角形内部
* 这个方法接受一个点和三角形的三个顶点作为参数,并返回一个布尔值,表示该点是否在三角形内部
*/
fun isPointInTriangle(p: PointF, a: PointF, b: PointF, c: PointF): Boolean {
val abCrossProduct = crossProductLength(a, b, p)
val bcCrossProduct = crossProductLength(b, c, p)
val caCrossProduct = crossProductLength(c, a, p)
return sameSign(abCrossProduct, bcCrossProduct) && sameSign(bcCrossProduct, caCrossProduct)
}
private fun crossProductLength(
a: PointF,
b: PointF,
p: PointF
): Float {
return (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x)
}
private fun sameSign(a: Float, b: Float): Boolean {
return a * b >= 0
}
Android保存资源图片到相册
Android 保存资源图片到相册最新写法适用于Android10.0及以上 - 掘金 (juejin.cn)
二阶贝塞尔曲线的应用
以下是关键代码,贝塞尔曲线的应用之一是使画线时转角平滑过渡
private static final float TOUCH_TOLERANCE = 4;
private float mX, mY;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath = new Path();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
mCanvas.drawPath(mPath, mPaint);
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
invalidatePath();
mCanvas.drawPath(mPath, mPaint);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
invalidate();
return true;
}
下划线("_")作为数字分隔符
为了方便阅读,数字位数较多的时候,最好使用下划线("_")作为数字分隔符
1000000可以用1_000_000表示,1_000_000能够更好的阅读
注意:这只是提高可读性,但这不是必须的,也不影响代码的功能
RecyclerView使用ListAdapter
RecyclerView在频繁的增删改查操作时建议使用ListAdapter
ListAdapter是RecyclerView.Adapter的子类
相比较RecyclerView.Adapter来说,ListAdapter提供了以下优点:
- 自动计算列表项的差异,从而更高效地更新列表
- 内置数据集合,可以更方便地对列表数据进行操作
- 支持异步数据加载和自动更新列表
总之,如果需要对列表数据进行频繁的增删改查操作,使用ListAdapter会更加方便和高效。
LinearLayout动画
android:animateLayoutChanges="true"xml中加个这个属性可以打开LinearLayout自带的动画
ScaleGestureDetector的缩放抖动问题
onScale(detector: ScaleGestureDetector)方法中,return false
detector会在之前的缩放上继续进行计算,就可以避免抖动
class ScaleListener: ScaleGestureDetector.SimpleOnScaleGestureListener() {
private var scaleFactor = 1.0f // 初始缩放比例为 1.0
/**
* 缩放进行中,返回值表示是否下次缩放需要重置,
* 如果返回ture,那么detector就会重置缩放事件,如果返回false,detector会在之前的缩放上继续进行计算
*/
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = max(0.5f, min(scaleFactor, 2f)) // 设置最大和最小缩放比例
this@BaseLayout.scaleY = scaleFactor
this@BaseLayout.scaleX = scaleFactor
//return true的话,缩放会出现抖动
return false
}
/**
* 缩放开始,返回值表示是否受理后续的缩放事件
*/
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
return true
}
override fun onScaleEnd(detector: ScaleGestureDetector) {
}
}
Android两根和单根手指旋转
两根手指旋转
单根手指旋转
private var degree = 0f
private var oriX = 0f
private var oriY = 0f
//注意这里取的都是event.x不是event.rawX
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
oriX = event.x
oriY = event.y
degree = rotation
}
MotionEvent.ACTION_MOVE -> {
val tempRawX = event.x
val tempRawY = event.y
val first = Point(oriX.toInt(), oriY.toInt())
val second = Point(tempRawX.toInt(), tempRawY.toInt())
val cen = Point( width/2, height/2)
//旋转
degree += angle(cen,first,second)
rotation = degree
}
MotionEvent.ACTION_UP -> {}
else -> {}
}
return true
}
//获取角度
private fun angle(cen: Point, first: Point, second: Point): Float {
val dx1: Float = (first.x - cen.x).toFloat()
val dy1: Float = (first.y - cen.y).toFloat()
val dx2: Float = (second.x - cen.x).toFloat()
val dy2: Float = (second.y - cen.y).toFloat()
// 计算三边的平方
val ab2 =
((second.x - first.x) * (second.x - first.x) + (second.y - first.y) * (second.y - first.y)).toFloat()
val oa2 = dx1 * dx1 + dy1 * dy1
val ob2 = dx2 * dx2 + dy2 * dy2
// 根据两向量的叉乘来判断顺逆时针
val isClockwise =
(first.x - cen.x) * (second.y - cen.y) - (first.y - cen.y) * (second.x - cen.x) > 0
// 根据余弦定理计算旋转角的余弦值
var cosDegree =
(oa2 + ob2 - ab2) / (2 * Math.sqrt(oa2.toDouble()) * Math.sqrt(ob2.toDouble()))
// 异常处理,因为算出来会有误差绝对值可能会超过一,所以需要处理一下
if (cosDegree > 1) {
cosDegree = 1.0
} else if (cosDegree < -1) {
cosDegree = -1.0
}
// 计算弧度
val radian = acos(cosDegree)
// 计算旋转过的角度,顺时针为正,逆时针为负
return (if (isClockwise) Math.toDegrees(radian) else -Math.toDegrees(radian)).toFloat()
}
XML中标签的使用,导致布局错乱
在Android Studio中,标签的使用,导致布局预览的时候,界面显示错乱,这时我们可以用tools:parentTag="自己定义的布局",可以避免因使用<merge>标签导致的布局排列错乱问题,同时可以在预览中查看自定义布局效果
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
tools:parentTag="com.xx.xx.xx.RulerLayout"
android:layout_height="wrap_content">
</merge>
Android文件存储
getExternalFilesDir() 是Context类的方法,而Environment是一个系统类,它们都提供了获取存储路径的方法。
两者的主要区别在于:
getExternalFilesDir() 返回与应用程序相关的外部存储目录,这个目录只能被应用程序本身访问,其他应用程序不能访问。这个目录随着应用程序的卸载而被删除。
Environment 类提供了一些获取系统存储路径的方法,如 getExternalStorageDirectory() 和 getExternalStoragePublicDirectory()。这些目录可被其他应用程序和用户访问,但需要特定的权限。这些目录不会随着应用程序的卸载而删除,因此你需要自己清除数据。
如果你需要一个只能被应用程序访问的目录,并且希望在应用程序卸载时自动清除数据,则可以使用 getExternalFilesDir() 方法。如果你需要一个可被其他应用程序和用户访问的目录,则可以使用 Environment 类提供的方法。
注意:
- 在低于 android10 的版本中,外部存储目录是对所有应用可见的
- 高于android10的版本无法使用Environment.getExternalStorageDirectory api操作文件,因为 api 已经被删除,低于android10可以继续使用该 api
- android10以上版本如何获取sd卡中普通目录的图片:可以通过SAF在android11以上版本获取普通目录图片(即非专属目录,非共享目录的图片)
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/jpeg"
startActivityForResult(intent, 100)//跳转系统浏览器
getCacheDir():它位于应用程序的私有目录下,通常是 /data/data/<application_package>/cache/。这个目录是每个应用程序私有的,其他应用程序无法访问
getExternalCacheDir():这个方法返回应用程序外部的缓存目录。它通常位于设备的外部存储空间,通常是 /sdcard/Android/data/<application_package>/cache/。这个目录是公共的,其他应用程序可以访问,访问外部存储的权限可能会受到限制
【android】关于android10-11存储的一些知识 - 掘金 (juejin.cn)
Android 存储进化:分区存储 - 掘金 (juejin.cn)
adb获取顶层Activity
Android-adb获取当前手机顶层activity (sunofbeach.net)
adb shell dumpsys activity top | grep ACTIVITY
pause
pause
adb查看应用的安装路径
查看手机中已安装的所有apk文件
adb shell pm list package
查看APP安装路径
//com.DeviceTest 是我要导出的APP包名
adb shell pm path com.DeviceTest
多线程AtomicInteger
在Kotlin中,AtomicInteger是一种特殊的数据类型,它可以保证多个线程对它进行操作时的原子性。这意味着AtomicInteger的值可以在多个线程之间安全地共享和修改,而不必担心竞态条件或数据不一致的问题
多线程CountDownLatch
在Java中,CountDownLatch是一种同步工具类,它可以让一个或多个线程等待其他线程完成操作后再执行。CountDownLatch通过一个计数器来实现这个功能,计数器的初始值可以通过构造函数来指定,当计数器的值为0时,等待线程将会被唤醒继续执行。CountDownLatch通常用于协调多个线程之间的操作,以确保它们按照预期的顺序执行
如果你想快速实现多任务的并发分合策略,CountDownLatch会是一个不错的选择,而且它是官方帮你封装好的
Kotlin操作符以及集合
- let:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行操作,返回Lambda表达式的执行结果。
- apply:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行操作,返回该对象本身。
- run:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行操作,返回Lambda表达式的执行结果。
- also:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行操作,返回该对象本身。
- with:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行操作,返回Lambda表达式的执行结果。
- takeIf:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行判断,如果判断为true,则返回该对象本身,否则返回null。
- takeUnless:将一个对象作为参数传递给Lambda表达式,并在Lambda表达式中对该对象进行判断,如果判断为false,则返回该对象本身,否则返回null。
- repeat:重复执行指定次数的操作。
- listOf:创建一个不可变的List集合。
- mutableListOf:创建一个可变的List集合。
- setOf:创建一个不可变的Set集合。
- mutableSetOf:创建一个可变的Set集合。
- mapOf:创建一个不可变的Map集合。
- mutableMapOf:创建一个可变的Map集合。
Kotlin中自定义View
在构造函数中需要加@JvmOverloads constructor
例如:
class GuidingPrinciple@JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
View(context, attrs, defStyleAttr) {
}
Kotlin中List用+操作符
一开始看别人代码,有点懵,后面查了资料才发现,kotlin中可以重写了集合的+操作符
val stringList = ArrayList<String>()
val name = "John"
stringList + name
贴一下源码:
Kotlin中集合操作符
groupBy
根据分组函数对集合进行分组,返回分组结果 Map,Map 中的 key 类型由分组函数决定, value 的类型是 List
groupBy 两个参数的函数会对集合元素的值进行转换,最终添加到 Map 中
例如我们需要对字符串按长度进行分组,那么 Map 中 key 就是 Int 类型
val company = listOf("Google", "Microsoft", "IBM", "Apple", "Yahoo", "Alibaba", "Baidu")
// {6=[Google], 9=[Microsoft], 3=[IBM], 5=[Apple, Yahoo, Baidu], 7=[Alibaba]}
println(company.groupBy { it.length })Baidu], 7=[Alibaba]}
// 两个参数,给元素添加个_下标
// {6=[Google_], 9=[Microsoft_], 3=[IBM_], 5=[Apple_, Yahoo_, Baidu_], 7=[Alibaba_]}
println(company.groupBy({ it.length },{ it + "_" }))
flatMap
将集合中的元素根据转换函数得到新的值,并且将所有值铺平到新的集合中。与 map 不同的是,flatMap 的转换函数需要返回一个新的集合,并且会将转换后的值都铺平到集合中,所以如果有嵌套的集合转换成单层的集合时请使用 flatMap
val intList = listOf(1, 2, 3, 4, 5)
val nestList = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6, 7))
println(intList.flatMap { listOf(it + it, it * it, it * it * it) }) // [2, 1, 1, 4, 4, 8, 6, 9, 27, 8, 16, 64, 10, 25, 125]
println(nestList.flatMap { item -> item.map { it * it } }) // [1, 4, 9, 16, 25, 36, 49]
map
根据转换函数进行转换元素,得到一个包含所有转换之后的元素的 List
val intList = listOf(1, 2, 3, 4, 5)
val nestList = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6, 7))
println(intList.map { it * it }) // [1, 4, 9, 16, 25]
println(nestList.map { item -> item.map { it * it } }) // [[1, 4], [9, 16], [25, 36, 49]]
可以很明显看到 map 和 flatMap 的区别,flatMap 会铺平整个集合,而 map 只是将转换元素累计
indexOfFirst
indexOfFirst()是Kotlin标准库中的一个函数,它可以用于查找满足指定条件的第一个元素的索引。它接收一个Lambda表达式作为参数,该表达式接收一个元素作为输入,并返回一个布尔值,表示该元素是否满足条件。如果存在满足条件的元素,则返回该元素的索引;否则返回-1。
开源库
AndroidUtilCode
// if u use AndroidX, use the following
implementation 'com.blankj:utilcodex:1.31.1'
// Not in maintenance
implementation 'com.blankj:utilcode:1.30.7'
地址:
Glide
implementation 'com.github.bumptech.glide:glide:4.16.0'
地址:
单RecyclerView实现抖音二级评论
地址:
blackfrogxxoo/CommentDemo: 单RecyclerView简单实现的二级评论功能。 (github.com)
富文本编辑器
地址:
chinalwb/Android-Rich-text-Editor
Android 资源库
地址:
ColorfulCat/AndroidLibs:正在成为史上最全分类 Android 开源大全)
FrameWork教程
地址:
yuandaimaahao/AndroidFrameworkTutorial: 写给应用开发的 Android Framework 教程
AOSP生成系统签名
地址:
Google机器学习套件
地址:
机器学习套件 | ML Kit | Google for Developers
RxTool
dependencies {
//基础工具库
implementation 'com.github.tamsiree.RxTool:RxKit:2.6.3'
//UI库
implementation 'com.github.tamsiree.RxTool:RxUI:2.6.3'
//相机库
implementation 'com.github.tamsiree.RxTool:RxCamera:2.6.3'
//功能库(Zxing扫描与生成二维码条形码)
implementation 'com.github.tamsiree.RxTool:RxFeature:2.6.3'
//ArcGis For Android工具库(API:100.1以上版本)
implementation 'com.github.tamsiree.RxTool:RxArcGisKit:2.6.3'
//支付模块(支付宝 微信)[暂为待优化模块,谨慎]
implementation 'com.github.tamsiree.RxTool:RxPay:2.6.3'
}
作用:
工具类集合 | 支付宝支付 | 微信支付(统一下单) | 微信分享 | Zip4j压缩(支持分卷压缩与加密) | 一键集成UCrop选择圆形头像 | 一键集成二维码和条形码的扫描与生成 | 常用Dialog | WebView的封装可播放视频 | 仿斗鱼滑动验证码 | Toast封装 | 震动 | GPS | Location定位 | 图片缩放 | Exif 图片添加地理位置信息(经纬度) | 蛛网等级 | 颜色选择器 | ArcGis | VTPK
地址:
GSYVideoPlayer
地址:
图片视频选择框架 PictureSelector
地址: