Skip to content

Instantly share code, notes, and snippets.

@koinzhang
Last active July 2, 2022 02:59
Show Gist options
  • Save koinzhang/6a0c92872af4f9ac892b749faa8152f0 to your computer and use it in GitHub Desktop.
Save koinzhang/6a0c92872af4f9ac892b749faa8152f0 to your computer and use it in GitHub Desktop.
[Utils] Android Utils Code
import android.content.Context
import android.graphics.*
import android.graphics.Bitmap.CompressFormat
import android.graphics.drawable.Drawable
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import android.text.TextUtils
import android.util.Log
import java.io.*
import java.util.*
object BitmapUtils {
private val randomColor: Int
private get() {
val random = Random()
return -0x1000000 or random.nextInt(0x00ffffff)
}
/**
* @方法描述 将RGB字节数组转换成Bitmap,
*/
fun rgb2SafeBitmap(data: ByteArray, width: Int, height: Int): Bitmap? {
val colors = convertByteToColor(data) ?: return null //取RGB值转换为int数组
var bmp = Bitmap.createBitmap(
colors, 0, width, width, height,
Bitmap.Config.RGB_565
)
val matrix = Matrix()
val wScale = 384.0f / width
val hScale = 512.0f / height
val realScale = if (wScale > hScale) hScale else wScale
matrix.setScale(realScale, realScale)
bmp = Bitmap.createBitmap(bmp!!, 0, 0, width, height, matrix, true)
return bmp
}
/**
* @方法描述 将RGB字节数组转换成Bitmap,
*/
fun rgb2Bitmap(data: ByteArray, width: Int, height: Int): Bitmap? {
val colors = convertByteToColor(data) ?: return null //取RGB值转换为int数组
return Bitmap.createBitmap(
colors, 0, width, width, height,
Bitmap.Config.ARGB_8888
)
}
// 将一个byte数转成int
// 实现这个函数的目的是为了将byte数当成无符号的变量去转化成int
private fun convertByteToInt(data: Byte): Int {
return data.toInt() and 0x0F
}
// 将纯RGB数据数组转化成int像素数组
fun convertByteToColor(data: ByteArray): IntArray? {
val size = data.size
if (size == 0) {
return null
}
var arg = 0
if (size % 3 != 0) {
arg = 1
}
// 一般RGB字节数组的长度应该是3的倍数,
// 不排除有特殊情况,多余的RGB数据用黑色0XFF000000填充
val color = IntArray(size / 3 + arg)
var red: Int
var green: Int
var blue: Int
val colorLen = color.size
if (arg == 0) {
for (i in 0 until colorLen) {
red = convertByteToInt(data[i * 3])
green = convertByteToInt(data[i * 3 + 1])
blue = convertByteToInt(data[i * 3 + 2])
// 获取RGB分量值通过按位或生成int的像素值
color[i] = red shl 16 or (green shl 8) or blue or -0x1000000
}
} else {
for (i in 0 until colorLen - 1) {
red = convertByteToInt(data[i * 3])
green = convertByteToInt(data[i * 3 + 1])
blue = convertByteToInt(data[i * 3 + 2])
color[i] = red shl 16 or (green shl 8) or blue or -0x1000000
}
color[colorLen - 1] = -0x1000000
}
return color
}
//将bitmap保存到fileName指定的文件中
fun saveBitmap2File(bitmap: Bitmap?, fileName: String, filePath: String) {
if (bitmap == null || TextUtils.isEmpty(fileName)) {
return
}
val path = "$filePath/$fileName.jpg"
val compressFormat = CompressFormat.JPEG
val quality = 100
var outputStream: OutputStream? = null
try {
outputStream = FileOutputStream(path)
bitmap.compress(compressFormat, quality, outputStream)
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
outputStream!!.flush()
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
//读取fileName指定的bitmap,并将fileName对应的文件删除
fun getBitmapFromFile(fileName: String, filePath: String): Bitmap? {
if (TextUtils.isEmpty(fileName)) {
return null
}
val path = "$filePath/$fileName.jpg"
var bitmap: Bitmap? = null
try {
val fileInputStream = FileInputStream(path)
bitmap = BitmapFactory.decodeStream(fileInputStream)
fileInputStream.close()
val file = File(path)
if (file.exists()) {
file.delete()
}
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
return bitmap
}
fun Bitmap2Bytes(bm: Bitmap): ByteArray {
val baos = ByteArrayOutputStream()
bm.compress(CompressFormat.PNG, 100, baos)
return baos.toByteArray()
}
/**
* 按比例缩放图片
*
* @param origin 原图
* @param ratio 比例
* @return 新的bitmap
*/
fun scaleBitmap(origin: Bitmap?, ratio: Float, x: Int, y: Int): Bitmap? {
if (origin == null) {
return null
}
val width = origin.width
val height = origin.height
val matrix = Matrix()
matrix.setScale(ratio, ratio, (width / 2).toFloat(), (height / 2).toFloat())
matrix.postTranslate(x * ratio, y * ratio)
val afterBitmap = Bitmap.createBitmap(width, height, origin.config)
// 设置画笔,消除锯齿
val paint = Paint()
paint.isAntiAlias = true
val canvas = Canvas(afterBitmap)
canvas.drawBitmap(origin, matrix, paint)
//origin.recycle();
return afterBitmap
}
fun calPictureCenter(
origin: Bitmap,
userDot: Bitmap?,
faceLTX: Int,
faceLTY: Int,
centerX: Float,
centerY: Float,
faceW: Int,
faceH: Int,
scale: Float,
circleRadius: Int
): CaptureBitmap {
var scaleFactor = scale * faceW / (circleRadius * 2)
val result = floatArrayOf(centerX, centerY)
val captureBitmap = CaptureBitmap()
captureBitmap.bitmap = origin
captureBitmap.pos = result
captureBitmap.userDot = userDot
captureBitmap.scaleSize = scale
//scaleFactor>=1:脸跟框一样大或者比框大
//faceW != faceH:优图返回的人脸长宽不等(目前优图返回的人脸长宽是等长的)
if (scaleFactor >= 1 || faceW != faceH) {
Log.e("captureFace", "UITrack,scaleFactor invalid(>=1) || faceW != faceH!")
return captureBitmap
}
var viewWidth = faceW / scaleFactor
var space = viewWidth - faceW
var halfSpace = space / 2
val bitmapWidth = origin.width
val bitmapHeight = origin.height
if (bitmapWidth < viewWidth || bitmapHeight < viewWidth) {
val tmpSize = Math.min(bitmapHeight, bitmapWidth).toFloat()
val destSize = Math.min(tmpSize, (2 * circleRadius).toFloat())
scaleFactor = faceW.toFloat() / destSize
if (scaleFactor >= 1) {
Log.e("captureFace", "UITrack,scaleFactor invalid(>=1)")
return captureBitmap
}
captureBitmap.scaleSize = circleRadius * 2 * scaleFactor / faceW
viewWidth = faceW / scaleFactor
space = viewWidth - faceW
halfSpace = space / 2
Log.e(
"captureFace",
"UITrack,viewWidth invalid(>bitmapWidth || > bitmapHeight)!fix scaleSize:" + scale + "->" + captureBitmap.scaleSize
)
}
var left = faceLTX - halfSpace
var right = faceLTX + faceW + halfSpace
var top = faceLTY - halfSpace
var bottom = faceLTY + faceH + halfSpace
val leftDistance = left - 0
val rightDistance = bitmapWidth - right
val topDistance = top - 0
val bottomDistance = bitmapHeight - bottom
if (leftDistance < 0) {
left = 0f
right = right + Math.abs(leftDistance)
result[0] += Math.abs(leftDistance)
} else if (rightDistance < 0) {
right = bitmapWidth.toFloat()
left = left - Math.abs(rightDistance)
result[0] -= Math.abs(rightDistance)
}
if (topDistance < 0) {
top = 0f
bottom = bottom + Math.abs(topDistance)
result[1] += Math.abs(topDistance)
} else if (bottomDistance < 0) {
bottom = bitmapHeight.toFloat()
top = top - Math.abs(bottomDistance)
result[1] -= Math.abs(bottomDistance)
}
Log.i(
"captureFace",
"UITrack,left=" + left + ",right=" + +right + ",top=" + top + ",bottom=" + bottom + ",viewWidth=" + Math.floor(
viewWidth.toDouble()
)
.toInt() + ",viewHeight=" +
Math.floor(viewWidth.toDouble())
.toInt() + " ,origin centerX=" + centerX + ",centerY=" + centerY + ",fixed centerX=" + result[0] + ",centerY=" + result[1] + ",origin scaleSize=" + scale + ",fix scaleSize=" + captureBitmap.scaleSize
)
var bitmap: Bitmap? = null
var userDotBitmap: Bitmap? = null
try {
bitmap = Bitmap.createBitmap(
origin,
left.toInt(),
top.toInt(),
Math.floor(viewWidth.toDouble()).toInt(),
Math.floor(viewWidth.toDouble())
.toInt()
)
userDotBitmap = Bitmap.createBitmap(
userDot!!,
left.toInt(),
top.toInt(),
Math.floor(viewWidth.toDouble()).toInt(),
Math.floor(viewWidth.toDouble())
.toInt()
)
} catch (e: Exception) {
Log.i("captureFace", "UITrack,createBitmap exception:" + Log.getStackTraceString(e))
captureBitmap.scaleSize = scale
return captureBitmap
}
captureBitmap.pos = result
captureBitmap.bitmap = bitmap
captureBitmap.userDot = userDotBitmap
return captureBitmap
}
/**
* 该方法会创建RenderScript,在Android 5系统上RenderScript中的线程不会自动释放,所以这里需要
* 在Android 5的机器上进行释放
*
* @param iContext
* @param iBitmap
* @param radius
* @return
*/
fun fastBlur(iContext: Context?, iBitmap: Bitmap?, radius: Int): Bitmap? {
if (iBitmap == null) {
return null
}
val bitmap = iBitmap.copy(iBitmap.config, true)
val rs = RenderScript.create(iContext) ?: return bitmap
val input = Allocation.createFromBitmap(
rs, iBitmap, Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT
)
val output = Allocation.createTyped(rs, input.type)
val script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
script.setRadius(radius /* e.g. 3.f */.toFloat())
script.setInput(input)
script.forEach(output)
output.copyTo(bitmap)
// 这里需要销毁RenderScript对象,否则在Android 5系统上会造成线程泄漏
rs.destroy()
return bitmap
}
fun getBitmap(drawable: Drawable): Bitmap {
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
if (drawable.opacity != PixelFormat.OPAQUE) Bitmap.Config.ARGB_8888 else Bitmap.Config.RGB_565
)
val canvas = Canvas(bitmap)
//canvas.setBitmap(bitmap);
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
drawable.draw(canvas)
return bitmap
}
class CaptureBitmap {
var pos: FloatArray? = null
var scaleSize = 0f
var bitmap: Bitmap? = null
var userDot: Bitmap? = null
}
}
import android.os.Handler
import android.os.Looper
import android.os.Message
import java.util.*
object CountDownUtils {
private val TOKEN = Any()
private val LISTENERS: MutableSet<Listener> = HashSet()
private val CALLBACK: Handler.Callback = Handler.Callback { msg ->
synchronized(CountDownUtils::class.java) {
val seconds = msg.what
if (seconds <= 0) {
for (listener in LISTENERS) {
listener.onFinish()
}
return@Callback true
} else {
for (listener in LISTENERS) {
listener.onTick(seconds)
}
HANDLER.sendMessageDelayed(
android.os.Message.obtain(
HANDLER,
seconds - 1,
TOKEN
), 1000
)
return@Callback false
}
}
}
private val HANDLER = Handler(Looper.getMainLooper(), CALLBACK)
@Synchronized
fun start(seconds: Int) {
HANDLER.removeCallbacksAndMessages(TOKEN)
HANDLER.sendMessageDelayed(
Message.obtain(HANDLER, seconds - 1, TOKEN), 1000
)
}
@Synchronized
fun cancel() {
HANDLER.removeCallbacksAndMessages(TOKEN)
}
fun addListener(listener: Listener) {
HANDLER.post { LISTENERS.add(listener) }
}
fun removeListener(listener: Listener) {
HANDLER.post { LISTENERS.remove(listener) }
}
interface Listener {
fun onTick(seconds: Int)
fun onFinish()
}
}
import android.os.Parcelable
import android.util.Log
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser
import java.lang.reflect.Type
/**
* @author koinzhang
* @date 2021/12/30
* @email koinzhang@gmail.com
*/
object GsonUtils {
private const val TAG = "GsonUtils"
private val gson = GsonBuilder().create()
fun <T> String.fromJson(gson: Gson, cls: Type?): T? {
var obj: T? = null
try {
obj = gson.fromJson(this, cls)
} catch (e: Exception) {
Log.e(TAG, "fromJson $e")
}
return obj
}
fun <T> String.fromJson(cls: Type?): T? {
var obj: T? = null
try {
obj = gson.fromJson(this, cls)
} catch (e: Exception) {
Log.e(TAG, "fromJson $e")
}
return obj
}
fun <T> String.fromJson(gson: Gson, clazz: Class<T>?): T? {
return try {
gson.fromJson(this, clazz)
} catch (e: Throwable) {
Log.e(TAG, "fromJson $e")
null
}
}
fun <T> String.fromJson(clazz: Class<T>?): T {
return this.fromJson(gson, clazz)!!
}
fun Parcelable.toJson(gson: Gson): String? {
var json: String? = null
try {
json = gson.toJson(this)
} catch (e: Exception) {
Log.e(TAG, "fromJson $e")
}
return json
}
fun Parcelable.toJson(): String? {
return this.toJson(gson)
}
fun Parcelable.toJson(gson: Gson, tTYpe: Type?): String? {
var json: String? = null
try {
json = gson.toJson(this, tTYpe)
} catch (e: Exception) {
Log.e(TAG, "toJson $e")
}
return json
}
fun Parcelable.toJson(tTYpe: Type?): String? {
return this.toJson(gson, tTYpe)
}
fun <T> String.listFromJson(gson: Gson, clazz: Class<T>?): List<T>? {
var list: ArrayList<T>? = null
try {
val jsonArray = JsonParser.parseString(this).asJsonArray
list = ArrayList()
var i = 0
val size = jsonArray.size()
while (i < size) {
list.add(gson.fromJson(jsonArray[i], clazz))
++i
}
} catch (e: Exception) {
Log.e(TAG, "fromJsonList $e")
}
return list
}
fun <T> String.listFromJson(clazz: Class<T>?): List<T>? {
return this.listFromJson(gson, clazz)
}
fun <T> List<T>.listToJson(gson: Gson): String? {
var json: String? = null
try {
json = gson.toJson(this)
} catch (e: Exception) {
Log.e(TAG, "listToJson $e")
}
return json
}
fun <T> List<T>.listToJson(): String? {
return this.listToJson(gson)
}
}
import java.text.SimpleDateFormat
import java.util.*
object TimeUtils {
const val UNIT_MILLI_SECONDS: Long = 1000
const val SECONDS_IN_ONE_HOUR = (60 * 60).toLong()
private const val defaultPattern = "yyyy-MM-dd HH:mm:ss"
private val sDateFormat = SimpleDateFormat(defaultPattern, Locale.CHINA)
/**
* unix时间[timestamp]戳转换为日期,单位s
* 默认形式 yyyy-MM-dd HH:mm:ss
*/
fun TimeStamp2Date(timestamp: Long, pattern: String = defaultPattern): String {
if (timestamp > 0) {
val date = Date(timestamp * 1000)
return SimpleDateFormat(pattern, Locale.CHINA)
.format(date)
}
return ""
}
/**
* 获取当前时间字符串 yyyy-MM-dd HH:mm:ss
*/
val currentTimeStr: String get() = sDateFormat.format(Date())
}
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.os.Build
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.view.WindowManager
import android.webkit.WebView
import android.widget.AbsListView
import android.widget.Button
import android.widget.ScrollView
import android.widget.TextView
object ViewUtils {
private const val TAG = "ViewUtils"
/**
* 获取屏幕的高度
*/
private var sWindowHeight = -1
/**
* 尝试获取view的文案
*/
fun View.getViewContent(): String {
var result = ""
try {
if (this is TextView) { //TextView是EditText的父类
val content = this.text
result = content?.toString() ?: ""
} else if (this is Button) { //Button是CompoundButton的父类
val content = this.text
result = content?.toString() ?: ""
}
} catch (e: Exception) {
Log.e(TAG, e.message!!)
}
return result
}
/**
* 获取View的resourceId
*/
fun View.getResourcesId(): String {
val resourcesId: String = try {
this.resources.getResourceEntryName(this.id) //view.getResources().getResourceName(view.getId())
} catch (e: Exception) {
""
}
return resourcesId
}
fun View.getActivityName(): String {
val activityName: String = try {
this.context.javaClass.simpleName
} catch (e: Exception) {
Log.e(TAG, e.message!!)
""
}
return activityName
}
fun View.getTag(view: View): String {
try {
val tag = this.tag
return if (tag is String) {
tag
} else {
""
}
} catch (e: Exception) {
Log.e(TAG, e.message!!)
}
return ""
}
/**
* view是否可见
*/
fun View.isViewVisible(): Boolean {
val rect = Rect()
return this.isShown && this.getGlobalVisibleRect(rect) && this.isVisibleToUser()
}
/**
* 是否在父组件的可视范围内
*/
private fun View.isVisibleToUser(): Boolean {
return try {
val xyView = IntArray(2)
val xyParent = IntArray(2)
val viewHeight = this.height.toFloat()
val parent = this.getScrollOrListParent()
this.getLocationOnScreen(xyView)
if (parent == null) {
xyParent[1] = 0
} else {
parent.getLocationOnScreen(xyParent)
}
if (xyView[1] + viewHeight / 2.0f > this.getScrollListWindowHeight()) return false else if (xyView[1] + viewHeight / 2.0f < xyParent[1]) return false
true
} catch (t: Throwable) {
false
}
}
/**
* 获取它的滚动父组件
*/
private fun View.getScrollOrListParent(): View? {
return if (this !is AbsListView && this !is ScrollView && this !is WebView) {
try {
this.getScrollOrListParent()
} catch (e: Throwable) {
null
}
} else {
this
}
}
/**
* 获取滚动父组件的高度
*/
private fun View.getScrollListWindowHeight(): Float {
val xyParent = IntArray(2)
val parent = this.getScrollOrListParent()
val windowHeight: Float = if (parent == null) {
this.context.getDisplayHeight().toFloat()
} else {
parent.getLocationOnScreen(xyParent)
(xyParent[1] + parent.height).toFloat()
}
return windowHeight
}
private fun Context.getDisplayHeight(): Int {
if (sWindowHeight == -1) {
val display = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this.display
} else {
val windowManager = this.getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay
}
val p = Point()
display?.getSize(p)
sWindowHeight = p.y
}
return sWindowHeight
}
/**
* 是否textview可见:可见且内容不为空
*/
fun TextView.isTextViewVisible(): Boolean {
return this.visibility == View.VISIBLE && !TextUtils.isEmpty(this.getViewContent())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment