This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
@@ -0,0 +1,13 @@
<?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="io.dcloud.uni_modules.xMlkitScannigU">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<application>
<meta-data android:name="UTS_MLKIT_SCANNIG" android:value="xMlkitScannigU" />
<activity android:name="uts.sdk.modules.xMlkitScannigU.DemoActivity"></activity>
</application>
</manifest>
@@ -0,0 +1,821 @@
import ListView from 'android.widget.ListView';
import FrameLayout from 'android.widget.FrameLayout';
import CameraManager from 'android.hardware.camera2.CameraManager'
import CameraDevice from 'android.hardware.camera2.CameraDevice'
import StateCallback from 'android.hardware.camera2.CameraDevice.StateCallback'
import CameraCaptureSession from 'android.hardware.camera2.CameraCaptureSession'
import CaptureRequest from 'android.hardware.camera2.CaptureRequest'
import TotalCaptureResult from 'android.hardware.camera2.TotalCaptureResult'
import StreamConfigurationMap from 'android.hardware.camera2.params.StreamConfigurationMap'
import MeteringRectangle from 'android.hardware.camera2.params.MeteringRectangle'
import OutputConfiguration from 'android.hardware.camera2.params.OutputConfiguration'
import SessionConfiguration from 'android.hardware.camera2.params.SessionConfiguration'
import CameraCharacteristics from 'android.hardware.camera2.CameraCharacteristics'
import CameraMetadata from 'android.hardware.camera2.CameraMetadata'
import Range from 'android.util.Range';
import Executor from 'java.util.concurrent.Executor'
import Looper from 'android.os.Looper'
import ImageFormat from 'android.graphics.ImageFormat'
import Size from 'android.util.Size'
import SurfaceTexture from 'android.graphics.SurfaceTexture'
import Handler from 'android.os.Handler'
import Surface from 'android.view.Surface'
import Image from 'android.media.Image'
import LinearLayout from 'android.widget.LinearLayout';
import Intent from 'android.content.Intent';
import Integer from 'java.lang.Integer';
import ByteOrder from 'java.nio.ByteOrder';
import SurfaceHolder from 'android.view.SurfaceHolder'
import SurfaceView from 'android.view.SurfaceView'
import TextureView from 'android.view.TextureView'
import View from 'android.view.View'
import Context from 'android.content.Context'
import Manifest from "android.Manifest";
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import System from 'java.lang.System'
import Activity from 'android.app.Activity';
import Bundle from 'android.os.Bundle';
import Intent from 'android.content.Intent';
import IntentFilter from 'android.content.IntentFilter';
import Rect from 'android.graphics.Rect';
import Matrix from 'android.graphics.Matrix'
import ImageReader from 'android.media.ImageReader'
import OnImageAvailableListener from 'android.media.ImageReader.OnImageAvailableListener'
import ByteBuffer from 'java.nio.ByteBuffer'
import MediaStore from 'android.provider.MediaStore'
import Plane from 'android.media.Image.Plane'
import Bitmap from "android.graphics.Bitmap"
import BitmapFactory from "android.graphics.BitmapFactory"
import Vibrator from "android.os.Vibrator"
import Animation from "android.view.animation.Animation"
import ScaleAnimation from "android.view.animation.ScaleAnimation"
import AnimationUtils from "android.view.animation.AnimationUtils"
import Color from "android.graphics.Color"
import Window from "android.view.Window"
import WindowManager from "android.view.WindowManager"
import TextView from "android.widget.TextView"
import RelativeLayout from "android.widget.RelativeLayout"
import ImageView from "android.widget.ImageView"
import HandlerThread from 'android.os.HandlerThread'
import YuvImage from 'android.graphics.YuvImage'
import ByteArrayOutputStream from 'java.io.ByteArrayOutputStream'
import GradientDrawable from 'android.graphics.drawable.GradientDrawable'
import MotionEvent from 'android.view.MotionEvent';
import ViewGroup from 'android.view.ViewGroup';
import IOException from 'java.io.IOException'
import InputStream from 'java.io.InputStream'
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
import InputImage from "com.google.mlkit.vision.common.InputImage"
import xView from "./libview/view.uts"
import Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
type buffoptsPlanesType = {
data : ByteBuffer,
getRowStride : number,
getPixelStride : number,
}
type buffoptsType = {
width : number,
height : number,
format : number,
rect : Rect,
planes : buffoptsPlanesType[],
}
import { SCANNING_OPTIONS, CAMERA_PHOTO_SIZE, CallFunType, SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND } from "../interface.uts"
import { decoderUriPathToQr, decoderBuffToQr } from "./index.uts"
type cameraListNameType = { id : string, device : CameraCharacteristics }
function px2dp(n : number) : number {
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
return mets.density * n
}
export class xCamera {
parentView : FrameLayout;
// 相机区域视图
cameraView : SurfaceView | null = null;
// 识别点的区域
resultView : RelativeLayout | null = null;
viewBox_width = 0
viewBox_height = 0
imgPointsBycanmarea = [] as RelativeLayout[]
options = {
width: 800,
height: 800,
cameraDevice: 'BACK',
scanSpeedduration: 450,
scanningQrCode: true,
isSavePhoto: false,
qrCodeType: null,
vibatar: true,
zoom: 3,
continuousScan: false
} as SCANNING_OPTIONS
/** 手动震动服务 */
vibrator = null as null | Vibrator
context : Context;
// 相机旋转的角度。
cameraRate = new Map<Number, Number>([
[Surface.ROTATION_0, 0],
[Surface.ROTATION_90, 0],
[Surface.ROTATION_180, 0],
[Surface.ROTATION_270, 0]
])
// 相机服务
cameraService : CameraManager
// 相机列表,name,id
cameraListName = new Map<string, cameraListNameType>();
//
cameraPreviewSize = { width: 800, height: 800 } as CAMERA_PHOTO_SIZE
// 当前预览的什么是设备
cameraDevice : CameraDevice | null = null
cameraDeviceCaptureSession : CameraCaptureSession | null = null
sufaceHolderView : Surface | null = null
// 当前是否在解析码制中。
isDecoderQring = false
// 当前相机是否正在预览中。
isOpeningCameraing = false;
callFunEvent : CallFunType = (str : string[], type : 'auto' | 'click') => { }
// 临时存放的识别成功的结果,防止重复扫码。
tempresultList = [] as string[];
isAppPause = false
constructor(GroupView : FrameLayout, options : SCANNING_OPTIONS | null = null) {
this.context = UTSAndroid.getAppContext()! as Context;
this.parentView = GroupView;
if (options != null) {
this.options = {
width: options.width,
height: options.height,
cameraDevice: options.cameraDevice,
scanSpeedduration: options.scanSpeedduration == null ? this.options.scanSpeedduration : options.scanSpeedduration,
scanningQrCode: options.scanningQrCode == null ? this.options.scanningQrCode : options.scanningQrCode,
isSavePhoto: options.isSavePhoto == null ? this.options.isSavePhoto : options.isSavePhoto,
qrCodeType: options.qrCodeType == null ? this.options.qrCodeType : options.qrCodeType,
vibatar: options.vibatar == null ? this.options.vibatar : options.vibatar,
zoom: options.zoom == null ? this.options.zoom : options.zoom,
continuousScan: options.continuousScan == null ? this.options.continuousScan : options.continuousScan
} as SCANNING_OPTIONS;
}
this.cameraService = this.context!.getSystemService(Context.CAMERA_SERVICE) as CameraManager
this.vibrator = this.context!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
this._createCameraView()
this._creatCameraResultView()
}
//震动手机
setVibatar() {
if (this.vibrator == null) return
// 检查设备是否支持震动
if (this.vibrator!.hasVibrator()) {
// 触发短暂的震动,持续时间为200毫秒
this.vibrator!.vibrate(80);
}
}
/** 创建相机预览区域 */
private _createCameraView() {
let sv = new SurfaceView(this.context)
// 让相机的高和宽与组件对齐。
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
sv.setLayoutParams(layaout)
this.cameraView = sv;
this.parentView.addView(this.cameraView!)
}
// 创建一个动画层容器
private _creatCameraResultView() {
let t = this;
let resultView = new RelativeLayout(this.context)
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
resultView.setLayoutParams(layaout)
// resultView.setBackgroundColor(Color.argb(30,255,0,0))
this.resultView = resultView;
this.parentView.addView(this.resultView!)
//监听尺寸变化。
class previewChangeRectadd extends View.OnLayoutChangeListener {
override onLayoutChange(v : View, left : Int, top : Int, right : Int, bottom : Int,
oldLeft : Int, oldTop : Int, oldRight : Int, oldBottom : Int
) {
t.viewBox_width = right - left;
t.viewBox_height = bottom - top;
// t.parentView!.removeOnLayoutChangeListener(this);
}
}
this.parentView!.addOnLayoutChangeListener(new previewChangeRectadd())
}
openCamera() : Promise<boolean> {
let _this = this;
if (this.isOpeningCameraing) {
this.closeCamera()
}
return new Promise((res, rej) => {
_this.checkPermissions()
.then((res : boolean) => {
if (!res) {
uni.showToast({ title: "未授权相机使用权", icon: "none" })
return;
}
try {
_this.getCamreaListName()
_this.getCamreaDeviceSizeList()
_this.setCamreaSize()
_this._openCamera();
_this.setCamreaFocalLength(1)
_this.addListenApp()
} catch (e) {
//TODO handle the exception
}
res(true)
})
.catch(() => {
uni.showToast({ title: "未授权相机使用权", icon: "none" })
rej(false)
})
})
}
addListenApp() {
let t = this;
let listener : (() => void) | null = null;
listener = () => {
t.isAppPause = true;
// console.log(UTSAndroid.getDispatcher())
if (t.isOpeningCameraing) {
t.pauseCamera()
}
if (listener != null) {
UTSAndroid.offAppActivityPause(listener!);
}
}
UTSAndroid.onAppActivityPause(listener);
let listener2 : (() => void) | null = null;
listener2 = () => {
t.isAppPause = false
if (t.isOpeningCameraing) {
t.openCamera()
}
if (listener2 != null) {
UTSAndroid.offAppActivityResume(listener2!);
}
}
UTSAndroid.onAppActivityResume(listener2);
}
pauseCamera() {
this.sufaceHolderView?.release()
this.cameraDeviceCaptureSession?.close()
this.cameraDevice?.close()
this.cameraDevice = null;
this.cameraDeviceCaptureSession = null;
this.sufaceHolderView = null;
}
closeCamera() {
this.sufaceHolderView?.release()
this.cameraDeviceCaptureSession?.close()
this.cameraDevice?.close()
this.cameraDevice = null;
this.cameraDeviceCaptureSession = null;
this.isOpeningCameraing = false;
this.sufaceHolderView = null;
for (let i = 0; i < this.imgPointsBycanmarea.length; i++) {
this.resultView?.removeView(this.imgPointsBycanmarea[i])
}
}
private _openCamera() {
let _this = this;
if (this.cameraListName.size == 0) return;
let camreaNow = this.cameraListName.get(this.options.cameraDevice);
if (camreaNow == null) return;
class CamerCallTstasBack implements StateCallback {
constructor() {
}
override onOpened(camera : CameraDevice) : void {
console.log('打开成功')
_this.cameraDevice = camera
_this.createCameraPreviewSession()
}
override onDisconnected(camera : CameraDevice) : void {
console.log('连接失败')
_this.cameraDevice?.close()
_this.cameraDevice = null
}
override onError(camera : CameraDevice, error : Int) : void {
console.log('出错')
_this.cameraDevice?.close()
_this.cameraDevice = null
}
override onClosed(camera : CameraDevice) : void {
console.log('关闭')
_this.cameraDevice?.close()
_this.cameraDevice = null
}
}
this.cameraService.openCamera(camreaNow.id! as string, new CamerCallTstasBack(), null);
}
/**
* 检查相机权限
*/
checkPermissions() : Promise<boolean> {
let permissionCheck = ["android.permission.CAMERA"]
let _this = this;
// 请求拍照权限
return new Promise(res => {
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, permissionCheck)) {
// _this.cameraPermissioPass = true
res(true)
} else {
console.log("当前不具备指定权限")
// 请求拍照权限
UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionCheck, function (allRight : boolean, _ : string[]) {
// _this.cameraPermissioPass = allRight
res(true)
}, function (_ : boolean, _ : string[]) {
//用户拒绝了部分权限
// _this.cameraPermissioPass = false
res(false)
})
}
})
}
/**
* 获取相机设备列表
*/
getCamreaListName(deviceName : 'FRONT' | 'BACK' | null = null) {
// 前置还是后置。
let deviceLens = CameraCharacteristics.LENS_FACING_BACK;
let dname = this.options.cameraDevice
if (deviceName != null) {
dname = deviceName!
}
if (dname == 'FRONT') {
deviceLens = CameraCharacteristics.LENS_FACING_FRONT;
}
if (dname == 'BACK') {
deviceLens = CameraCharacteristics.LENS_FACING_BACK;
}
this.cameraListName.clear()
for (caIds in this.cameraService.getCameraIdList()) {
let ps = this.cameraService.getCameraCharacteristics(caIds as string);
let facing = ps.get(CameraCharacteristics.LENS_FACING);
if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
this.cameraListName.set('FRONT', { id: caIds as string, device: ps } as cameraListNameType)
}
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
this.cameraListName.set('BACK', { id: caIds as string, device: ps } as cameraListNameType)
}
}
this.setCamreaSize();
}
/**
* 获取相机设备尺寸列表
* 调用此函数的前提是要先获取设备列表,要先调用getCamreaListName函数
*/
getCamreaDeviceSizeList() : CAMERA_PHOTO_SIZE[] {
let sizelist = [] as CAMERA_PHOTO_SIZE[]
if (this.cameraListName.size == 0) {
console.error("请先调用getCamreaListName获取设备列表")
return sizelist;
}
let camrea = this.cameraListName.get(this.options.cameraDevice) as cameraListNameType;
if (camrea != null) {
let maps = camrea!.device!.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) as StreamConfigurationMap
if (maps != null) {
let testSize = maps!.getOutputSizes(ImageFormat.JPEG)
for (sz in testSize) {
let w = sz.getWidth()
let h = sz.getHeight()
sizelist.push({ width: w, height: h } as CAMERA_PHOTO_SIZE)
}
}
}
return sizelist;
}
/**
* 设置相机焦距
* 0-1之间。
* 暂未实现。
*/
setCamreaFocalLength(focal : number) {
if (this.cameraListName.size == 0) {
console.error("请先调用getCamreaListName获取设备列表")
return
}
let camrea = this.cameraListName.get(this.options.cameraDevice) as cameraListNameType;
if (camrea != null) {
let zoomMax = camrea!.device!.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
let nowZoom = camrea!.device!.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
}
}
/**
* 设置相机前置还是后置,设置后需要重新打开相机。
* BACK 后置
* FRONT 前置
*/
setCamreaDevice(name : 'FRONT' | 'BACK') {
this.options.cameraDevice = name;
}
/**
* 设置相机摄像尺寸,通过options中的width,height来匹配设置
* 返回的不一定是真实的options中的宽高,只是与其相近的值。
* 以设置后要重新打开。相机生效。
* 返回相机生效的尺寸
*/
setCamreaSize(width : number | null = null, height : number | null = null) : CAMERA_PHOTO_SIZE {
// 宽高列表
const dimensions = this.getCamreaDeviceSizeList()
// 目标宽高
const targetWidth = width != null ? width! : this.options.width;
const targetHeight = height != null ? height! : this.options.height;
const targetRatio = targetWidth / targetHeight;
// 找到最接近目标比例的宽高值
const closestDimension = dimensions.reduce((prev : CAMERA_PHOTO_SIZE, curr : CAMERA_PHOTO_SIZE) : CAMERA_PHOTO_SIZE => {
const currRatio = curr.width / curr.height;
const prevRatio = prev.width / prev.height;
const currDiffRatio = Math.abs(currRatio - targetRatio);
const prevDiffRatio = Math.abs(prevRatio - targetRatio);
// 计算宽高值与目标值的差的平方和
const currDiffValue = Math.pow(curr.width - targetWidth, 2) + Math.pow(curr.height - targetHeight, 2);
const prevDiffValue = Math.pow(prev.width - targetWidth, 2) + Math.pow(prev.height - targetHeight, 2);
// 给比例差和值差赋予不同的权重,这里假设两者权重相同
const currTotalDiff = currDiffRatio + currDiffValue;
const prevTotalDiff = prevDiffRatio + prevDiffValue;
return currTotalDiff < prevTotalDiff ? curr : prev;
}, dimensions[0]);
this.cameraPreviewSize = closestDimension
return closestDimension;
}
// 获取当前相机的旋转角度。
getCamraRotation() : Number {
let sensorOrientation = 0
let tempra = 0
if (this.cameraListName.size == 0) return sensorOrientation;
let camreaNow = this.cameraListName.get(this.options.cameraDevice);
if (camreaNow == null) return sensorOrientation;
let windowManager = UTSAndroid.getUniActivity()!.getWindowManager() as WindowManager
let deviceRotation = windowManager!.getDefaultDisplay().getRotation()
let rotationCompensation = this.cameraRate.get(deviceRotation) as number
tempra = camreaNow.device.get(CameraCharacteristics.SENSOR_ORIENTATION)!! as number
if (this.options.cameraDevice == 'FRONT') {
tempra = ((tempra + rotationCompensation) % 360)
} else {
tempra = ((tempra - rotationCompensation + 360) % 360)
}
return tempra
}
// 设置识别成功的回调函数
setCallEvent(call : CallFunType) {
this.callFunEvent = call;
}
// 创建视频预览流。
private createCameraPreviewSession() {
if (this.cameraView == null) return;
// 设置预览区域
let previewAspectRatio = this.cameraPreviewSize.width / this.cameraPreviewSize.height;
let layoutWidth = this.viewBox_width
let layoutHeight = this.viewBox_height
let layoutAspectRatio = layoutWidth / layoutHeight;
let holder = this.cameraView!.getHolder()
let suface = holder!.getSurface();
this.sufaceHolderView = suface
if (this.cameraDevice == null) return;
let t = this;
let previewRequestBuilder = this.cameraDevice!.createCaptureRequest(CameraDevice.PREVIEW)
previewRequestBuilder?.addTarget(suface)
let totalWidth = this.cameraPreviewSize.width
let totalHeight = this.cameraPreviewSize.height
// 再添加一个image来预览
let minh = Math.min(this.options.width, this.options.height)
let reader = ImageReader.newInstance(minh.toInt(), minh.toInt(), ImageFormat.YUV_420_888, 1)
previewRequestBuilder?.addTarget(reader.getSurface())
let outsufaces = [suface, reader.getSurface()] as Surface[]
class sessionCallback extends CameraCaptureSession.StateCallback {
constructor() { }
override onConfigured(session : CameraCaptureSession) {
t.cameraDeviceCaptureSession = session
t.isOpeningCameraing = true;
try {
t.cameraDeviceCaptureSession?.setRepeatingRequest(previewRequestBuilder.build(), null, null)
} catch (e) {
console.log("Failed to start camera preview session.", e)
}
}
override onConfigureFailed(session : CameraCaptureSession) {
}
override onClosed(session : CameraCaptureSession) {
t.closeCamera()
}
override onSurfacePrepared(session : CameraCaptureSession, testSuface : Surface) {
}
}
reader.setOnImageAvailableListener(this.createCameraImageRenderLisen() as OnImageAvailableListener, null)
this.cameraDevice!.createCaptureSession(outsufaces, new sessionCallback(), null)
}
private createCameraImageRenderLisen() : any {
let t = this;
class imageReaderCallback extends ImageReader.OnImageAvailableListener {
private currentImage = null as Image | null;
override onImageAvailable(irader : ImageReader) {
if (this.currentImage != null) {
this.currentImage!.close(); // 先关闭上一帧的Image
}
let curimage = irader.acquireNextImage();
@Suppress("DEPRECATION")
class IntentRunable extends Runnable {
override run() {
if (t.isDecoderQring || !t.isOpeningCameraing || t.isAppPause) return;
try {
t.qrDecoder(curimage!)
} catch (e) {
t.closeCamera()
console.error('应用异常:', e)
}
}
}
this.currentImage = curimage
if (this.currentImage != null) {
new Thread(new IntentRunable()).start()
}
}
}
return new imageReaderCallback()
}
// 设置图片上的小圆的识别位置。
setImageViewRusletBox(width : number, height : number, realWidth : number, realHeight : number, jieguo : SCANNING_PHOTO_RESULT) {
let t = this;
let wRatio = t.viewBox_width / realWidth;
let hRatio = t.viewBox_height / realHeight;
let btnwidth = 120;
let zoom = 10 - this.options.zoom!
for (let i = 0; i < jieguo.bounds.length; i++) {
let item = jieguo.bounds[i];
let layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
let x = wRatio * item.centerX
let y = hRatio * item.centerY
layoutParams.topMargin = (y - 60).toInt() // 设置顶部边距
layoutParams.leftMargin = (x - 60).toInt() // 设置左边距
let relativeLayout = new RelativeLayout(t.context)
relativeLayout.layoutParams = layoutParams
const rtxt = jieguo.text[i]
let v = new xView(t.context)
v.setSize(120, 120)
.setRadius(100)
.setBackgroundColor('#21d429')
.setBorder(12, "#ffffff", false)
.setClick((event : MotionEvent) => {
try {
t.callFunEvent([rtxt], 'click')
} catch (e) {
//TODO handle the exception
}
})
let ov = v.getView();
// 创建缩放动画
let scaleAnimation = new ScaleAnimation(1.0.toFloat(), 0.7.toFloat(), 1.0.toFloat(), 0.7.toFloat(), Animation.RELATIVE_TO_SELF, 0.7.toFloat(), Animation.RELATIVE_TO_SELF, 0.7.toFloat());
scaleAnimation.setDuration(600); // 设置动画持续时间
scaleAnimation.setRepeatMode(Animation.REVERSE); // 设置重复模式为反向
scaleAnimation.setRepeatCount(Animation.INFINITE); // 设置重复次数为无限
// 启动动画
ov.startAnimation(scaleAnimation);
relativeLayout.addView(ov)
this.imgPointsBycanmarea.push(relativeLayout)
this.resultView!.addView(relativeLayout)
}
}
private YUV_420_888toNV21(opts : buffoptsType) : ByteArray {
let crop = opts.rect;
let format = opts.format;
let width = crop.width();
let height = crop.height();
let planes = opts.planes!;
let data = new ByteArray((width * height * ImageFormat.getBitsPerPixel(format.toInt()) / 8).toInt());
let rowData = new ByteArray((planes[0].getRowStride).toInt());
let channelOffset = 0;
let outputStride = 1;
for (let i = 0; i < planes.length; i++) {
switch (i) {
case 0:
channelOffset = 0;
outputStride = 1;
break;
case 1:
channelOffset = width * height;
outputStride = 1;
break;
case 2:
channelOffset = width * height * 1.25;
outputStride = 1;
break;
}
let buffer = planes[i.toInt()].data!;
let rowStride = planes[i.toInt()].getRowStride;
let pixelStride = planes[i.toInt()].getPixelStride;
let shift = (i == 0) ? 0 : 1;
let w = width >> shift;
let h = height >> shift;
buffer.position((rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift)).toInt());
for (let row = 0; row < h; row++) {
let length = 0;
if (pixelStride == 1 && outputStride == 1) {
length = w;
buffer.get(data, channelOffset.toInt(), length.toInt());
channelOffset += length;
} else {
length = (w - 1) * pixelStride + 1;
buffer.get(rowData, 0, length.toInt());
for (let col = 0; col < w; col++) {
data[channelOffset.toInt()] = rowData[(col * pixelStride).toInt()];
channelOffset += outputStride;
}
}
if (row < h - 1) {
buffer.position((buffer.position() + rowStride - length).toInt());
}
}
}
return data;
}
private qrDecoder(image : Image) {
let t = this;
if (!this.isDecoderQring && !this.isAppPause) {
this.isDecoderQring = true;
let width = image.getWidth();
let height = image.getHeight();
let frameSize = width * height;
let qFrameSize = frameSize / 4;
// 获取各平面的数据与信息
let planes = image.getPlanes();
let yBufferCopy = [] as ByteArray[]
let yrurvrNumber = [] as number[]
let yr = planes[0]
let ur = planes[1]
let vr = planes[2]
let oraingMax = yr.getBuffer().limit()
for (let i = 0; i < 3; i++) {
let buffer = planes[i.toInt()].getBuffer();
yBufferCopy.push(new ByteArray(buffer.remaining().toInt()))
buffer.get(yBufferCopy[i.toInt()]);
}
let y = ByteBuffer.wrap(yBufferCopy[0]!)
let u = ByteBuffer.wrap(yBufferCopy[1]!)
let v = ByteBuffer.wrap(yBufferCopy[2]!)
let opts = {
width,
height,
format: image.getFormat(),
rect: image.getCropRect(),
planes: [
{
data: y,
getRowStride: yr.getRowStride(),
getPixelStride: yr.getPixelStride(),
},
{
data: u,
getRowStride: ur.getRowStride(),
getPixelStride: ur.getPixelStride(),
},
{
data: v,
getRowStride: vr.getRowStride(),
getPixelStride: vr.getPixelStride(),
}
] as buffoptsPlanesType[]
} as buffoptsType
decoderBuffToQr(this.YUV_420_888toNV21(opts), width, height, this.getCamraRotation())
.then((res) => {
if (res != null && t.isOpeningCameraing && !t.isAppPause) {
let texts = res.text as string[]
if (texts.length > 0) {
// texts.some((el:string):boolean=>t.tempresultList.includes(el))
if (t.options.vibatar == true) {
t.setVibatar()
}
if (t.options.continuousScan == false) {
t.closeCamera()
}
t.callFunEvent(texts, 'auto')
t.setImageViewRusletBox(t.options.width, t.options.height, width, height, res as SCANNING_PHOTO_RESULT)
}
t.isDecoderQring = false
} else {
t.isDecoderQring = false
}
// image.close();
})
.catch(() => {
// this.imageUrl = null
})
// setTimeout(function () {
// t.isDecoderQring = false
// }, t.options!.scanSpeedduration!);
// image.close();
}
image.close();
}
}
@@ -0,0 +1,648 @@
import ListView from 'android.widget.ListView';
import FrameLayout from 'android.widget.FrameLayout';
import CameraManager from 'android.hardware.camera2.CameraManager'
import CameraDevice from 'android.hardware.camera2.CameraDevice'
import StateCallback from 'android.hardware.camera2.CameraDevice.StateCallback'
import CameraCaptureSession from 'android.hardware.camera2.CameraCaptureSession'
import CaptureRequest from 'android.hardware.camera2.CaptureRequest'
import TotalCaptureResult from 'android.hardware.camera2.TotalCaptureResult'
import StreamConfigurationMap from 'android.hardware.camera2.params.StreamConfigurationMap'
import MeteringRectangle from 'android.hardware.camera2.params.MeteringRectangle'
import OutputConfiguration from 'android.hardware.camera2.params.OutputConfiguration'
import SessionConfiguration from 'android.hardware.camera2.params.SessionConfiguration'
import CameraCharacteristics from 'android.hardware.camera2.CameraCharacteristics'
import CameraMetadata from 'android.hardware.camera2.CameraMetadata'
import Range from 'android.util.Range';
import Executor from 'java.util.concurrent.Executor'
import Looper from 'android.os.Looper'
import ImageFormat from 'android.graphics.ImageFormat'
import Size from 'android.util.Size'
import SurfaceTexture from 'android.graphics.SurfaceTexture'
import Handler from 'android.os.Handler'
import Surface from 'android.view.Surface'
import Image from 'android.media.Image'
import LinearLayout from 'android.widget.LinearLayout';
import Intent from 'android.content.Intent';
import Integer from 'java.lang.Integer';
import ByteOrder from 'java.nio.ByteOrder';
import SurfaceHolder from 'android.view.SurfaceHolder'
import SurfaceView from 'android.view.SurfaceView'
import TextureView from 'android.view.TextureView'
import View from 'android.view.View'
import Context from 'android.content.Context'
import Manifest from "android.Manifest";
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import System from 'java.lang.System'
import Activity from 'android.app.Activity';
import Bundle from 'android.os.Bundle';
import Intent from 'android.content.Intent';
import IntentFilter from 'android.content.IntentFilter';
import Rect from 'android.graphics.Rect';
import Matrix from 'android.graphics.Matrix'
import ImageReader from 'android.media.ImageReader'
import OnImageAvailableListener from 'android.media.ImageReader.OnImageAvailableListener'
import ByteBuffer from 'java.nio.ByteBuffer'
import MediaStore from 'android.provider.MediaStore'
import Plane from 'android.media.Image.Plane'
import Bitmap from "android.graphics.Bitmap"
import BitmapFactory from "android.graphics.BitmapFactory"
import Vibrator from "android.os.Vibrator"
import Animation from "android.view.animation.Animation"
import ScaleAnimation from "android.view.animation.ScaleAnimation"
import AnimationUtils from "android.view.animation.AnimationUtils"
import Color from "android.graphics.Color"
import Window from "android.view.Window"
import WindowManager from "android.view.WindowManager"
import TextView from "android.widget.TextView"
import RelativeLayout from "android.widget.RelativeLayout"
import ImageView from "android.widget.ImageView"
import HandlerThread from 'android.os.HandlerThread'
import YuvImage from 'android.graphics.YuvImage'
import ByteArrayOutputStream from 'java.io.ByteArrayOutputStream'
import GradientDrawable from 'android.graphics.drawable.GradientDrawable'
import MotionEvent from 'android.view.MotionEvent';
import ViewGroup from 'android.view.ViewGroup';
import IOException from 'java.io.IOException'
import InputStream from 'java.io.InputStream'
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
import InputImage from "com.google.mlkit.vision.common.InputImage"
import xView from "./libview/view.uts"
import Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
// x
import ProcessCameraProvider from "androidx.camera.lifecycle.ProcessCameraProvider"
import PreviewView from "androidx.camera.view.PreviewView"
import CameraController from "androidx.camera.view.CameraController"
import CameraSelector from "androidx.camera.core.CameraSelector"
import Preview from 'androidx.camera.core.Preview'
import Camera from 'androidx.camera.core.Camera'
import LifecycleObserver from "androidx.lifecycle.LifecycleObserver"
import LifecycleOwner from "androidx.lifecycle.LifecycleOwner"
import OnLifecycleEvent from "androidx.lifecycle.OnLifecycleEvent"
import ImageAnalysis from 'androidx.camera.core.ImageAnalysis'
import ImageProxy from 'androidx.camera.core.ImageProxy'
import NonNull from 'androidx.annotation.NonNull'
import ExecutorService from 'java.util.concurrent.ExecutorService'
import Executors from 'java.util.concurrent.Executors'
// import TargetResolution from 'androidx.camera.core.ImageAnalysis.TargetResolution'
type buffoptsPlanesType = {
data : ByteBuffer,
getRowStride : number,
getPixelStride : number,
}
type buffoptsType = {
width : number,
height : number,
format : number,
rect : Rect,
planes : buffoptsPlanesType[],
}
import { SCANNING_OPTIONS, CAMERA_PHOTO_SIZE, CallFunType, SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND } from "../interface.uts"
import { decoderUriPathToQr, decoderBuffToQr } from "./index.uts"
import ContextCompat from 'androidx.core.content.ContextCompat';
type cameraListNameType = { id : string, device : CameraCharacteristics }
function px2dp(n : number) : number {
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
return mets.density * n
}
export class xCamera {
parentView : FrameLayout;
// 识别点的区域
resultView : RelativeLayout | null = null;
viewBox_width = 0
viewBox_height = 0
imgPointsBycanmarea = [] as RelativeLayout[]
options = {
width: 800,
height: 800,
cameraDevice: 'BACK',
scanSpeedduration: 450,
scanningQrCode: true,
isSavePhoto: false,
qrCodeType: null,
vibatar: true,
zoom: 3,
continuousScan: false
} as SCANNING_OPTIONS
/** 手动震动服务 */
vibrator = null as null | Vibrator
context : Context;
// 相机旋转的角度。
cameraRate = new Map<Number, Number>([
[Surface.ROTATION_0, 0],
[Surface.ROTATION_90, 0],
[Surface.ROTATION_180, 0],
[Surface.ROTATION_270, 0]
])
// 相机服务
cameraService : CameraManager
// 相机列表,name,id
cameraListName = new Map<string, cameraListNameType>();
//
cameraPreviewSize = { width: 800, height: 800 } as CAMERA_PHOTO_SIZE
// 当前预览的什么是设备
cameraDevice : CameraDevice | null = null
cameraDeviceCaptureSession : CameraCaptureSession | null = null
sufaceHolderView : Surface | null = null
// 相机区域视图
cameraView : PreviewView | null = null;
cameraProvider : ProcessCameraProvider | null = null
camera : Camera | null = null;
cameraPreview : Preview | null = null;
// 当前是否在解析码制中。
isDecoderQring = false
// 当前相机是否正在预览中。
isOpeningCameraing = false;
callFunEvent : CallFunType = (str : string[], type : 'auto' | 'click') => { }
// 临时存放的识别成功的结果,防止重复扫码。
tempresultList = [] as string[];
isAppPause = false
flashMode = false;
constructor(GroupView : FrameLayout, options : SCANNING_OPTIONS | null = null) {
this.context = UTSAndroid.getAppContext()! as Context;
this.parentView = GroupView;
if (options != null) {
this.options = {
width: options.width,
height: options.height,
cameraDevice: options.cameraDevice,
scanSpeedduration: options.scanSpeedduration == null ? this.options.scanSpeedduration : options.scanSpeedduration,
scanningQrCode: options.scanningQrCode == null ? this.options.scanningQrCode : options.scanningQrCode,
isSavePhoto: options.isSavePhoto == null ? this.options.isSavePhoto : options.isSavePhoto,
qrCodeType: options.qrCodeType == null ? this.options.qrCodeType : options.qrCodeType,
vibatar: options.vibatar == null ? this.options.vibatar : options.vibatar,
zoom: options.zoom == null ? this.options.zoom : options.zoom,
continuousScan: options.continuousScan == null ? this.options.continuousScan : options.continuousScan
} as SCANNING_OPTIONS;
this.flashMode = options.flashMode
}
this.cameraService = this.context!.getSystemService(Context.CAMERA_SERVICE) as CameraManager
this.vibrator = this.context!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
this._createCameraView()
this._creatCameraResultView()
}
//震动手机
setVibatar() {
if (this.vibrator == null) return
// 检查设备是否支持震动
if (this.vibrator!.hasVibrator()) {
// 触发短暂的震动,持续时间为200毫秒
this.vibrator!.vibrate(80);
}
}
/** 创建相机预览区域 */
private _createCameraView() {
let sv = new PreviewView(UTSAndroid.getAppContext()!)
sv.scaleType = PreviewView.ScaleType.FILL_CENTER
// 让相机的高和宽与组件对齐。
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
sv.setLayoutParams(layaout)
this.cameraView = sv;
this.parentView.addView(this.cameraView!)
}
// 创建一个动画层容器
private _creatCameraResultView() {
let t = this;
let resultView = new RelativeLayout(this.context)
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
resultView.setLayoutParams(layaout)
// resultView.setBackgroundColor(Color.argb(30,255,0,0))
this.resultView = resultView;
this.parentView.addView(this.resultView!)
//监听尺寸变化。
class previewChangeRectadd extends View.OnLayoutChangeListener {
override onLayoutChange(v : View, left : Int, top : Int, right : Int, bottom : Int,
oldLeft : Int, oldTop : Int, oldRight : Int, oldBottom : Int
) {
t.viewBox_width = right - left;
t.viewBox_height = bottom - top;
}
}
this.parentView!.addOnLayoutChangeListener(new previewChangeRectadd())
}
openCamera() : Promise<boolean> {
let _this = this;
if (this.isOpeningCameraing) {
this.closeCamera()
}
return new Promise((res, rej) => {
_this.checkPermissions()
.then((res : boolean) => {
if (!res) {
uni.showToast({ title: "未授权相机使用权", icon: "none" })
return;
}
try {
_this._openCamera();
} catch (e) {
console.log(e)
//TODO handle the exception
}
res(true)
})
.catch(() => {
uni.showToast({ title: "未授权相机使用权", icon: "none" })
rej(false)
})
})
}
// 设置识别成功的回调函数
setCallEvent(call : CallFunType) {
this.callFunEvent = call;
}
getCamraRotation() : Number {
return 0
}
setFlashModel(flash:boolean){
if(this.camera!=null&&this.isOpeningCameraing){
let cameraControl = this.camera!.cameraControl!
cameraControl.enableTorch(flash)
}
}
closeCamera() {
this.setFlashModel(false);
this.cameraProvider?.unbindAll()
this.cameraPreview?.setSurfaceProvider(null)
this.isOpeningCameraing = false;
for (let i = 0; i < this.imgPointsBycanmarea.length; i++) {
this.resultView?.removeView(this.imgPointsBycanmarea[i])
}
}
private _openCamera() {
let _this = this;
let cameraProviderFuture = ProcessCameraProvider.getInstance(this.context)
class IntentRunable extends Runnable {
override run() {
let cameraProvider : ProcessCameraProvider = cameraProviderFuture.get()
let previewView = _this.cameraView! as PreviewView
let cameraSelector : CameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
let preview : Preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider())
let imageAnalysis = new ImageAnalysis.Builder()
// .setTargetResolution(new Size(_this.viewBox_width.toInt(), _this.viewBox_height.toInt()))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
class MyImageAnalyzer implements ImageAnalysis.Analyzer {
constructor() {
super()
}
override analyze(image : ImageProxy) {
_this.qrDecoder(image)
}
override getDefaultTargetResolution() : Size {
return new Size(_this.viewBox_width.toInt(), _this.viewBox_height.toInt());
}
}
let cameraExecutor = Executors.newSingleThreadExecutor();
imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer());
let camera : Camera = cameraProvider.bindToLifecycle(
UTSAndroid.getUniActivity()! as LifecycleOwner, cameraSelector, preview, imageAnalysis);
_this.camera = camera
_this.cameraPreview = preview
_this.cameraProvider = cameraProvider
_this.isDecoderQring = false
_this.isOpeningCameraing = true
_this.setFlashModel(_this.flashMode)
}
}
cameraProviderFuture.addListener(new IntentRunable(), ContextCompat.getMainExecutor(this.context))
}
/**
* 检查相机权限
*/
checkPermissions() : Promise<boolean> {
let permissionCheck = ["android.permission.CAMERA"]
let _this = this;
// 请求拍照权限
return new Promise(res => {
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, permissionCheck)) {
// _this.cameraPermissioPass = true
res(true)
} else {
console.log("当前不具备指定权限")
// 请求拍照权限
UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionCheck, function (allRight : boolean, _ : string[]) {
// _this.cameraPermissioPass = allRight
res(true)
}, function (_ : boolean, _ : string[]) {
//用户拒绝了部分权限
// _this.cameraPermissioPass = false
res(false)
})
}
})
}
// 设置图片上的小圆的识别位置。
setImageViewRusletBox(width : number, height : number, realWidth : number, realHeight : number, jieguo : SCANNING_PHOTO_RESULT) {
let t = this;
let btnwidth = 100;
let btnwidth2 = 50;
for (let i = 0; i < jieguo.bounds.length; i++) {
let item = jieguo.bounds[i];
let layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
let x = item.centerX
let y = item.centerY
layoutParams.topMargin = (y - btnwidth2).toInt() // 设置顶部边距
layoutParams.leftMargin = (x - btnwidth2).toInt() // 设置左边距
let relativeLayout = new RelativeLayout(t.context)
relativeLayout.layoutParams = layoutParams
const rtxt = jieguo.text[i]
let v = new xView(t.context)
v.setSize(btnwidth, btnwidth)
.setRadius(100)
.setBackgroundColor('#21d429')
.setBorder(12, "#ffffff", false)
.setClick((event : MotionEvent) => {
try {
t.callFunEvent([rtxt], 'click')
} catch (e) {
//TODO handle the exception
}
})
let ov = v.getView();
// 创建缩放动画
let scaleAnimation = new ScaleAnimation(1.0.toFloat(), 0.7.toFloat(), 1.0.toFloat(), 0.7.toFloat(), Animation.RELATIVE_TO_SELF, 0.7.toFloat(), Animation.RELATIVE_TO_SELF, 0.7.toFloat());
scaleAnimation.setDuration(600); // 设置动画持续时间
scaleAnimation.setRepeatMode(Animation.REVERSE); // 设置重复模式为反向
scaleAnimation.setRepeatCount(Animation.INFINITE); // 设置重复次数为无限
// 启动动画
ov.startAnimation(scaleAnimation);
relativeLayout.addView(ov)
this.imgPointsBycanmarea.push(relativeLayout)
this.resultView!.addView(relativeLayout)
}
}
private YUV_420_888toNV21(opts : buffoptsType) : ByteArray {
let crop = opts.rect;
let format = opts.format;
let width = crop.width();
let height = crop.height();
let planes = opts.planes!;
let data = new ByteArray((width * height * ImageFormat.getBitsPerPixel(format.toInt()) / 8).toInt());
let rowData = new ByteArray((planes[0].getRowStride).toInt());
let channelOffset = 0;
let outputStride = 1;
for (let i = 0; i < planes.length; i++) {
switch (i) {
case 0:
channelOffset = 0;
outputStride = 1;
break;
case 1:
channelOffset = width * height;
outputStride = 1;
break;
case 2:
channelOffset = width * height * 1.25;
outputStride = 1;
break;
}
let buffer = planes[i.toInt()].data!;
let rowStride = planes[i.toInt()].getRowStride;
let pixelStride = planes[i.toInt()].getPixelStride;
let shift = (i == 0) ? 0 : 1;
let w = width >> shift;
let h = height >> shift;
buffer.position((rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift)).toInt());
for (let row = 0; row < h; row++) {
let length = 0;
if (pixelStride == 1 && outputStride == 1) {
length = w;
buffer.get(data, channelOffset.toInt(), length.toInt());
channelOffset += length;
} else {
length = (w - 1) * pixelStride + 1;
buffer.get(rowData, 0, length.toInt());
for (let col = 0; col < w; col++) {
data[channelOffset.toInt()] = rowData[(col * pixelStride).toInt()];
channelOffset += outputStride;
}
}
if (row < h - 1) {
buffer.position((buffer.position() + rowStride - length).toInt());
}
}
}
return data;
}
getCorrectionMatrix(imageProxy : ImageProxy, previewView : PreviewView) : Matrix {
let cropRect = imageProxy.cropRect
let rotationDegrees = imageProxy.imageInfo.rotationDegrees
let matrix = new Matrix()
// A float array of the source vertices (crop rect) in clockwise order.
let source = floatArrayOf(cropRect.left.toFloat(),
cropRect.top.toFloat(),
cropRect.right.toFloat(),
cropRect.top.toFloat(),
cropRect.right.toFloat(),
cropRect.bottom.toFloat(),
cropRect.left.toFloat(),
cropRect.bottom.toFloat())
let d0 = (0).toFloat()
// A float array of the destination vertices in clockwise order.
let destination = floatArrayOf(d0,
d0,
previewView.width.toFloat(),
d0,
previewView.width.toFloat(),
previewView.height.toFloat(),
d0,
previewView.height.toFloat())
// The destination vertexes need to be shifted based on rotation degrees. The
// rotation degree represents the clockwise rotation needed to correct the image.
// Each vertex is represented by 2 float numbers in the vertices array.
let vertexSize = 2
// The destination needs to be shifted 1 vertex for every 90° rotation.
let shiftOffset = rotationDegrees / 90 * vertexSize;
let tempArray = destination.clone()
for (toIndex in source.indices) {
let fromIndex = (toIndex + shiftOffset) % source.size
destination[toIndex] = tempArray[fromIndex.toInt()]
}
let success = matrix.setPolyToPoly(source, 0, destination, 0, 4)
return matrix
}
private qrDecoder(img : ImageProxy) {
let t = this;
let image = img.getImage();
if (!this.isDecoderQring && image != null) {
this.isDecoderQring = true;
let width = image.getWidth();
let height = image.getHeight();
let frameSize = width * height;
let qFrameSize = frameSize / 4;
// 获取各平面的数据与信息
let planes = image.getPlanes();
let yBufferCopy = [] as ByteArray[]
let yrurvrNumber = [] as number[]
let yr = planes[0]
let ur = planes[1]
let vr = planes[2]
let oraingMax = yr.getBuffer().limit()
for (let i = 0; i < 3; i++) {
let buffer = planes[i.toInt()].getBuffer();
yBufferCopy.push(new ByteArray(buffer.remaining().toInt()))
buffer.get(yBufferCopy[i.toInt()]);
}
let y = ByteBuffer.wrap(yBufferCopy[0]!)
let u = ByteBuffer.wrap(yBufferCopy[1]!)
let v = ByteBuffer.wrap(yBufferCopy[2]!)
let opts = {
width,
height,
format: image.getFormat(),
rect: image.getCropRect(),
planes: [
{
data: y,
getRowStride: yr.getRowStride(),
getPixelStride: yr.getPixelStride(),
},
{
data: u,
getRowStride: ur.getRowStride(),
getPixelStride: ur.getPixelStride(),
},
{
data: v,
getRowStride: vr.getRowStride(),
getPixelStride: vr.getPixelStride(),
}
] as buffoptsPlanesType[]
} as buffoptsType
decoderBuffToQr(this.YUV_420_888toNV21(opts), width, height,0)
.then((res) => {
if (res != null && t.isOpeningCameraing) {
let matrix = t.getCorrectionMatrix(img, this.cameraView!)
let texts = res.text as string[]
if (texts.length > 0) {
// texts.some((el:string):boolean=>t.tempresultList.includes(el))
if (t.options.vibatar == true) {
t.setVibatar()
}
if (t.options.continuousScan == false) {
t.closeCamera()
}
let ruslt = res as SCANNING_PHOTO_RESULT;
for (let i = 0; i < ruslt.bounds.length; i++) {
let item = ruslt.bounds[i]
let ponit = floatArrayOf(item.centerX.toFloat(), item.centerY.toFloat())
matrix.mapPoints(ponit)
item.centerX = ponit[0]
item.centerY = ponit[1]
}
t.callFunEvent(texts, 'auto')
t.setImageViewRusletBox(t.options.width, t.options.height, width, height, ruslt)
}
t.isDecoderQring = false
} else {
t.isDecoderQring = false
}
// image.close();
})
.catch(() => {
// this.imageUrl = null
})
// setTimeout(function () {
// t.isDecoderQring = false
// }, t.options!.scanSpeedduration!);
// image.close();
}
img.close();
}
}
@@ -0,0 +1,12 @@
{
"minSdkVersion": "21",
"dependencies":[
"com.google.mlkit:barcode-scanning:17.2.0",
"androidx.camera:camera-core:1.3.4",
"androidx.camera:camera-camera2:1.3.4",
"androidx.camera:camera-lifecycle:1.3.4",
"androidx.camera:camera-video:1.3.4",
"androidx.camera:camera-view:1.3.4",
"androidx.camera:camera-extensions:1.3.4"
]
}
@@ -0,0 +1,262 @@
/**
* 引用 Android 系统库,示例如下:
* import { Context } from "android.content.Context";
* [可选实现,按需引入]
*/
/* 引入 interface.uts 文件中定义的变量 */
import { SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND, SCANNING_PHOTO_RESULT_POS } from '../interface.uts';
/* 引入 unierror.uts 文件中定义的变量 */
import Context from 'android.content.Context'
import IOException from 'java.io.IOException'
import InputStream from 'java.io.InputStream'
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import Bitmap from "android.graphics.Bitmap"
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
import InputImage from "com.google.mlkit.vision.common.InputImage"
import Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
import ByteBuffer from 'java.nio.ByteBuffer'
import ImageFormat from 'android.graphics.ImageFormat'
/**
* 从相册中选取图片进行解码
* @public chooseImageDecodeQr
*/
export function chooseImage() : Promise<SCANNING_PHOTO_RESULT | null> {
return new Promise((res) => {
uni.chooseImage({
count: 1,
success(evt : ChooseImageSuccess) {
console.log('**')
if (evt.tempFilePaths.length > 0) {
console.log(evt, "----")
const imgs = evt.tempFilePaths[0] as string;
try {
const decodedPath = Uri.decode(imgs).substring(7)
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
res({
url: imgs,
position: listpos,
bounds: bounedlist,
text: rulstText
} as SCANNING_PHOTO_RESULT)
})
} catch (e : IOException) {
res(null)
}
}
},
fail() {
res(null)
}
})
})
}
/**
* 给定图片路径Uri解析图片。
* @public decoderUriPathToQr
*/
export function decoderUriPathToQr(filePath : Uri) : Promise<SCANNING_PHOTO_RESULT | null> {
return new Promise((res) => {
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, filePath);
let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
res({
url: "",
position: listpos,
bounds: bounedlist,
text: rulstText
} as SCANNING_PHOTO_RESULT)
})
})
}
/**
* 给定ImageBuffer
* @public decoderBuffToQr
*/
export function decoderBuffToQr(bitmap : ByteArray, width : Number, height : Number, ration : Number) : Promise<SCANNING_PHOTO_RESULT | null> {
return new Promise((res) => {
let options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_13,
Barcode.FORMAT_CODE_128,
Barcode.FORMAT_CODABAR
)
.build()
let image = InputImage.fromByteArray(bitmap, width.toInt(), height.toInt(), ration.toInt(), InputImage.IMAGE_FORMAT_NV21)
// 如果只扫qr开启这行,速度快一点。
let scanner = BarcodeScanning.getClient(options);
// let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
res({
url: "",
position: listpos,
bounds: bounedlist,
text: rulstText
} as SCANNING_PHOTO_RESULT)
})
.addOnFailureListener((e) => {
res(null)
})
})
}
/**
* 提供一个网络图片地址进行解码
* @public urlPathDecoderQr
*/
export function urlPathDecoderQr(url : String) : Promise<SCANNING_PHOTO_RESULT | null> {
return new Promise((res) => {
uni.downloadFile({
url,
success(res) {
const contentResolver = UTSAndroid.getAppContext()!.contentResolver
let decodedPath = res.tempFilePath
if (res.tempFilePath.indexOf('file://') > -1) {
decodedPath = Uri.decode(res.tempFilePath).substring(7)
}
try {
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
res({
url: url,
position: listpos,
bounds: bounedlist,
text: rulstText
} as SCANNING_PHOTO_RESULT)
})
} catch (e : IOException) {
res(null)
}
},
fail() {
res(null)
}
})
})
}
@@ -0,0 +1,280 @@
<template>
<view class="boxView">
</view>
</template>
<script lang="uts">
import Context from 'android.content.Context'
import LinearLayout from 'android.widget.LinearLayout';
import FrameLayout from 'android.widget.FrameLayout';
import ViewGroup from 'android.view.ViewGroup';
import View from 'android.view.View';
import ImageView from "android.widget.ImageView"
import InputStream from 'java.io.InputStream'
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import BufferedImage from 'java.awt.image.BufferedImage'
import IOException from 'java.io.IOException'
import NotFoundException from 'com.google.zxing.NotFoundException'
import FormatException from 'com.google.zxing.FormatException'
import ChecksumException from 'com.google.zxing.ChecksumException'
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
import InputImage from "com.google.mlkit.vision.common.InputImage"
import Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
import ByteBuffer from 'java.nio.ByteBuffer'
import ImageFormat from 'android.graphics.ImageFormat'
import Bitmap from "android.graphics.Bitmap"
import BitmapFactory from "android.graphics.BitmapFactory"
import Color from "android.graphics.Color"
import BarcodeFormat from "com.google.zxing.BarcodeFormat"
import EncodeHintType from "com.google.zxing.EncodeHintType"
import MultiFormatWriter from "com.google.zxing.MultiFormatWriter"
import MultiFormatReader from "com.google.zxing.MultiFormatReader"
import RGBLuminanceSource from "com.google.zxing.RGBLuminanceSource"
import BinaryBitmap from "com.google.zxing.BinaryBitmap"
import HybridBinarizer from "com.google.zxing.common.HybridBinarizer"
import WriterException from "com.google.zxing.WriterException"
import BitMatrix from "com.google.zxing.common.BitMatrix"
import BufferedImageLuminanceSource from "com.google.zxing.client.j2se.BufferedImageLuminanceSource"
import ByteArrayOutputStream from 'java.io.ByteArrayOutputStream'
import Base64 from 'java.util.Base64'
import Context from 'android.content.Context'
import SurfaceView from 'android.view.SurfaceView'
import { chooseImage } from "./index.uts"
import { xCamera } from "./camera.uts"
import { SCANNING_OPTIONS,CAMERA_PHOTO_SIZE,BARCODEFORMAT,CallFunType,SCANNING_PHOTO_RESULT_BOUND ,SCANNING_PHOTO_RESULT} from "../interface.uts"
export default {
name: "x-mlkit-scannig-u",
data() {
return {
CBOX: null as null | FrameLayout,
xcamera:null as null|xCamera,
defaultscaneventFun:null as CallFunType|null
}
},
emits:[
/**
* 多个码的时候点击触发,返回码内容
* @param {string} str - 点的码
*/
'clickQr',
/**
* 识别成功触发
* @param {string[]} str[] - 识别的码结果集
*/
'scan'
],
props:{
/**
* 相机预览像素宽,请最好为正方形
*/
cameraWidth:{
type:Number,
default:800
},
/**
* 相机预览像高,请最好为正方形
*/
cameraeiHght:{
type:Number,
default:800
},
/**
* 是否自动打开相机
*/
autoOpenCamera:{
type:Boolean,
default:true
},
/**
* 是否开启闪光灯
*/
flash:{
type:Boolean,
default:false
}
},
NVLoad() : FrameLayout {
let grop = new FrameLayout(UTSAndroid!.getAppContext()!)
this.CBOX = grop;
return grop
},
NVLayouted() {
if(this.autoOpenCamera){
this.createXcamrera();
}
},
NVBeforeUnload(){
this.xcamera?.closeCamera()
},
methods: {
/**
* 检查相机权限
*/
checkPermissions() : Promise<boolean> {
let permissionCheck = ["android.permission.CAMERA"]
let _this = this;
// 请求拍照权限
return new Promise(res => {
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, permissionCheck)) {
// _this.cameraPermissioPass = true
res(true)
} else {
console.log("当前不具备指定权限")
// 请求拍照权限
UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionCheck, function (allRight : boolean, _ : string[]) {
// _this.cameraPermissioPass = allRight
res(true)
}, function (_ : boolean, _ : string[]) {
//用户拒绝了部分权限
// _this.cameraPermissioPass = false
res(false)
})
}
})
},
/** 私有函数,外部不要调用。 */
createXcamrera(){
let _this = this;
this.xcamera = new xCamera(this.CBOX!,{
width:this.cameraWidth,height:this.cameraeiHght,cameraDevice:'BACK',
zoom:5,
flashMode:this.flash
} as SCANNING_OPTIONS)
this.xcamera!.setCallEvent((str:string[],type:'auto'|'click')=>{
if(type=='auto'){
_this.$emit('scan',str)
}else if(type=='click'){
_this.$emit('clickQr',str[0]!)
}
})
this.xcamera!.openCamera()
},
/** 手动开启和关闭闪光灯 */
setFlashModel(flash:boolean){
if(this.xcamera != null){
this.xcamera!.setFlashModel(flash)
}
},
/** 手动打开相机预览 */
openCamera(){
let _this = this;
let isPrivacyAgreeCamrea = uni.getStorageSync('isPrivacyAgreeCamreaByTmui')
if(isPrivacyAgreeCamrea!='on'){
uni.showModal({
title:"提醒",
content:"将授权你的设备相机使用权限进行拍摄识别功能,是否允许?",
success(isPermisseions){
if(isPermisseions.confirm){
_this.checkPermissions()
.then((res:boolean)=>{
_this.createXcamrera()
uni.setStorageSync('isPrivacyAgreeCamreaByTmui','on')
})
.catch(()=>{
uni.setStorageSync('isPrivacyAgreeCamreaByTmui','off')
})
}
},
fail(){
uni.showToast({title:"未授权相机使用权",icon:"none"})
}
})
}else{
_this.createXcamrera()
}
},
/** 关闭相机 */
closeCamera(){
this.xcamera?.closeCamera()
},
chooseImage(){
let _this = this;
uni.chooseImage({
count: 1,
albumMode:"system",
sourceType:['album'],
success(evt : ChooseImageSuccess) {
if (evt.tempFilePaths.length > 0) {
const imgs = evt.tempFilePaths[0] as string;
try {
const decodedPath = Uri.decode(imgs).substring(7)
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
// res({
// url: imgs,
// position: listpos,
// bounds: bounedlist,
// text: rulstText
// } as SCANNING_PHOTO_RESULT)
// let resultstr = uni.getStorageSync("xMlKITScaning")
_this.$emit('scan',rulstText.length==0?[]:rulstText)
})
} catch (e : IOException) {
_this.$emit('scan',[] as string[])
}
}
},
fail() {
_this.$emit('scan',[] as string[])
}
})
}
}
}
</script>
@@ -0,0 +1,468 @@
import Context from 'android.content.Context'
import LinearLayout from 'android.widget.LinearLayout';
import ViewGroup from 'android.view.ViewGroup';
import View from 'android.view.View';
import TextView from 'android.widget.TextView';
import ImageView from 'android.widget.ImageView';
import Gravity from 'android.view.Gravity';
import Color from 'android.graphics.Color';
import Activity from 'android.app.Activity';
// import CardView from 'androidx.cardview.widget.CardView';
import { ref, } from "vue"
import TextUtils from 'android.text.TextUtils';
import GradientDrawable from 'android.graphics.drawable.GradientDrawable'
import MotionEvent from 'android.view.MotionEvent';
import ColorStateList from 'android.content.res.ColorStateList';
import attr from 'android.R.attr'
import {hexToRgb,getDefaultColor} from "./xCoreColorUtil.uts"
/**
* 随机一个uid
* @param rdix 随机因子
* @param length 取的长度
* @param isAddStr 是否限制随机结果中的长度,不允许输出长度
* @returns String
*/
function getUid(rdix = 1, length = 12) : string {
let ix = "";
// #ifndef APP
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length);
// #endif
// #ifdef APP
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length as Int);
// #endif
return ix;
}
class MyClickListener implements View.OnClickListener {
callbackFun:()=>void;
constructor(cal?:()=>void){
this.callbackFun = cal as ()=>void;
}
override onClick(v?:View):void {
if(typeof this.callbackFun !='underfinde'){
this.callbackFun();
}
}
}
class MyTouchListener implements View.OnTouchListener {
//down,up,move,cancel,longClick
callbackFun:(types:string,event : MotionEvent,detail:UTSJSONObject)=>void;
dubleTime = 0;
tid = 56;
_x = 0;
_y = 0;
// 判断方向的差值
swipe_mindiff = 40
// 滑动时,一定时间内只能触发一次事件。不能连续触发。
tid_siper = 100;
swipeDirection = ""
constructor(call:(types:string,event : MotionEvent,detail?:UTSJSONObject)=>void) {
this.callbackFun = call;
}
override onTouch(view : View, event : MotionEvent) : Boolean {
if(event.action == MotionEvent.ACTION_DOWN ){
this._x = event.getX();
this._y = event.getY();
this.swipeDirection = ""
this.callbackFun('down',event,{});
let difftime = new Date().getTime() - this.dubleTime;
if(difftime >0 && difftime<=300){
this.callbackFun('doubleClick',event ,{});
}
this.dubleTime = new Date().getTime();
clearTimeout(this.tid)
tid = setTimeout(function() {
this.callbackFun('longPress',event,{});
}, 800);
}
if(event.action == MotionEvent.ACTION_UP ){
clearTimeout(this.tid)
this.callbackFun('up',event,{});
if(new Date().getTime() - this.dubleTime > 50){
this.callbackFun('click',event,{});
}
}
if(event.action == MotionEvent.ACTION_CANCEL || event.action== MotionEvent.ACTION_OUTSIDE ){
this.callbackFun('cancel',event,{});
clearTimeout(this.tid)
}
if(event.action == MotionEvent.ACTION_MOVE ){
let x = event.getX();
let y = event.getY();
let deltaX = Math.abs(x - this._x);
let deltaY = Math.abs(y - this._y);
if(deltaX > deltaY && deltaX > this.swipe_mindiff){
this.swipeDirection = (this._x > x) ? "left" : "right";
}else if(deltaY > deltaX && deltaY > this.swipe_mindiff){
this.swipeDirection = (this._y < y) ? "down" : "up";
}
if(this.swipeDirection !=""){
this.callbackFun('swiper',event,{x,y,diffX:deltaX,diffY:deltaY,direction:this.swipeDirection});
}
// this._x = x;
// this._y = y;
this.callbackFun('move',event,{});
clearTimeout(this.tid)
}
return true;
}
}
class xView {
public view : any;
public tagId:string = 'x_view_id_'+getUid();
public bgView:GradientDrawable
private _onclickFun= (_event : MotionEvent):void=>{};
private _onTouchStart= (_event : MotionEvent):void=>{};
private _onTouchEnd= (_event : MotionEvent):void=>{};
private _onTouchCancel= (_event : MotionEvent):void=>{};
private _onTouchMove= (_event : MotionEvent):void=>{};
private _onTouchLongPress= (_event : MotionEvent):void=>{};
private _onTouchDubleClick= (_event : MotionEvent):void=>{};
private _onTouchSwiper= (_event : MotionEvent,detail?:UTSJSONObject):void=>{};
// 设置禁用,不会触发上面的事件。
private _disabled = false;
constructor(context : Context) {
let view = new View(context);
view.setTag(this.tagId)
let layoutParams_crd = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
view.setLayoutParams(layoutParams_crd);
this.bgView = new GradientDrawable();
// let statuesInts= [[attr.state_enabled].toKotlinList().toIntArray(),[attr.state_pressed].toKotlinList().toIntArray(),[-attr.state_enabled].toKotlinList().toIntArray()]
// let statuesColors= [Color.WHITE,Color.RED,Color.RED]
// let states = new ColorStateList(statuesInts.toKotlinList().toTypedArray(),statuesColors.toKotlinList().toIntArray())
this.bgView.setShape(GradientDrawable.RECTANGLE)
this.bgView.setColor(Color.WHITE)
this.bgView.mutate()
view.setBackground(this.bgView)
this.view = view;
// view.setOnClickListener(new MyClickListener(():void=>{
// this._onclickFun()
// }));
view.setOnTouchListener(new MyTouchListener((types:string,event : MotionEvent,detail?:UTSJSONObject):void=>{
if(this._disabled == false){
if(types=='down'){
this._onTouchStart(event)
}
if(types=='up'){
this._onTouchEnd(event);
}
if(types=='click'){
this._onclickFun(event);
}
if(types=='move'){
this._onTouchMove(event)
}
if(types=='cancel'){
this._onTouchCancel(event)
}
if(types=='longPress'){
this._onTouchLongPress(event)
}
if(types=='doubleClick'){
this._onTouchDubleClick(event)
}
if(types=='swiper'){
this._onTouchSwiper(event,detail)
}
}
}));
}
getView():View{
return this.view as View;
}
setDisabled(dis:boolean) : xView{
this._disabled = dis;
return this;
}
/**
* 触发距离是大于50时触发并判断方向
* x,y当前坐标,diffX滑动时距离开始时按下的横向x距离,diffY表示竖向。Direction为方向:left,right,up,dowon
*detail:
*{x,y,diffX,diffY,direction}
*/
setSwiper(fun?:(event: MotionEvent,detail?:UTSJSONObject)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchSwiper = fun;
}
return this;
}
setDoubleClick(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchDubleClick = fun;
}
return this;
}
setClick(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onclickFun = fun;
}
return this;
}
setTouchStart(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchStart = fun;
}
return this;
}
setTouchMove(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchMove = fun;
}
return this;
}
setTouchEnd(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchEnd = fun;
}
return this;
}
setTouchCancel(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchCancel = fun;
}
return this;
}
setTouchLongPress(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchLongPress = fun;
}
return this;
}
setBackgroundColor(colorStr : string) : xView {
this.bgView.mutate()
if(colorStr == 'transparent'){
this.bgView.setColor(Color.TRANSPARENT)
}else{
this.bgView.setColor(Color.parseColor(getDefaultColor(colorStr)))
}
return this;
}
setBorder(w?:number,colorStr?:string,dashed?:boolean) : xView {
this.bgView.mutate()
let tw:number = 1;
let tcolor = 'transparent';
let tdashed = false;
if(typeof tw !== 'undefined'){
tw = w as number;
}
if(typeof tw !== 'undefined'){
tcolor = colorStr as string;
}
if(typeof dashed !== 'undefined'){
tdashed = dashed as boolean;
}
let colorNum = Color.TRANSPARENT;
if(tcolor != 'transparent'){
colorNum = Color.parseColor(getDefaultColor(tcolor));
}
if(dashed == true){
let dashedWidth = 8
let dashedGap = 5
this.bgView.setStroke(tw.toInt(),colorNum,dashedWidth.toFloat(),dashedGap.toFloat())
}else{
this.bgView.setStroke(tw.toInt(),colorNum)
}
return this;
}
setMargin(n:any):xView{
let view = this.view as View;
let selfPrams = view.getLayoutParams()
let params = new LinearLayout.LayoutParams(
selfPrams.width,
selfPrams.height
)
if(Array.isArray(n)){
let rd = n as number[];
if(rd.length==0){
let rds = 0;
let rdf = (rds).toInt();
params.setMargins(rdf,rdf,rdf,rdf)
}else if(rd.length==1){
let rdf = (rd[0]).toInt();
params.setMargins(rdf,rdf,rdf,rdf)
}else if(rd.length==2){
let rds = 0;
params.setMargins((rd[0]).toInt(),(rd[1]).toInt(),(rd[0]).toInt(),(rd[1]).toInt())
}else if(rd.length==3){
let rds = 0;
let rdf = rds.toInt();
params.setMargins((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),rdf)
}else if(rd.length==4){
params.setMargins((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),(rd[3]).toInt())
}
}else{
let rd = n as number;
let rdf = (rd).toInt();
params.setMargins(rdf,rdf,rdf,rdf)
}
view.setLayoutParams(params);
return this;
}
setPadding(n:any):xView{
let view = this.view as View;
if(Array.isArray(n)){
let rd = n as number[];
if(rd.length==0){
let rds = 0;
let rdf = (rds).toInt();
view.setPadding(rdf,rdf,rdf,rdf)
}else if(rd.length==1){
let rdf = (rd[0]).toInt();
view.setPadding(rdf,rdf,rdf,rdf)
}else if(rd.length==2){
view.setPadding((rd[0]).toInt(),(rd[1]).toInt(),(rd[0]).toInt(),(rd[1]).toInt())
}else if(rd.length==3){
let rds = 0;
let rdf = rds.toInt();
view.setPadding((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),rdf)
}else if(rd.length==4){
view.setPadding((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),(rd[3]).toInt())
}
}else{
let rd = n as number;
let rdf = (rd).toInt();
view.setPadding(rdf,rdf,rdf,rdf)
}
return this;
}
setRadius(radius:any) : xView{
let x =0
let y =0
this.bgView.mutate()
if(Array.isArray(radius)){
let rd = radius as number[];
if(rd.length==0){
let rdfs = 0;
this.bgView.setCornerRadius((rdfs).toFloat());
// view.setBackground(this.bgView)
}else if(rd.length==1){
let rdfs = rd[0];
this.bgView.setCornerRadius((rdfs).toFloat());
// view.setBackground(this.bgView)
}else if(rd.length==2){
let tl = [
(rd[0]).toFloat(),(rd[0]).toFloat(),
(rd[1]).toFloat(),(rd[1]).toFloat(),
x.toFloat(),x.toFloat(),
x.toFloat(),x.toFloat()
]
let tlint = tl.toKotlinList().toFloatArray()
this.bgView.setCornerRadii(tlint);
}else if(rd.length==3){
let tl = [
(rd[0]).toFloat(),(rd[0]).toFloat(),
(rd[1]).toFloat(),(rd[1]).toFloat(),
(rd[2]).toFloat(),(rd[2]).toFloat(),
x.toFloat(),x.toFloat()
]
let tlint = tl.toKotlinList().toFloatArray()
this.bgView.setCornerRadii(tlint);
}else if(rd.length==4){
let tl = [
(rd[0]).toFloat(),(rd[0]).toFloat(),
(rd[1]).toFloat(),(rd[1]).toFloat(),
(rd[2]).toFloat(),(rd[2]).toFloat(),
(rd[3]).toFloat(),(rd[3]).toFloat(),
]
let tlint = tl.toKotlinList().toFloatArray()
this.bgView.setCornerRadii(tlint);
}
}else{
let rd = radius as number;
this.bgView.setCornerRadius((rd).toFloat());
}
return this;
}
/** 设置当前宽和高为父级的相对宽和高
*w,h有效值为:auto|100%
*/
setSizeBy(w:string="100%",h:string = "auto"){
let view = this.view as View;
let ww = ViewGroup.LayoutParams.MATCH_PARENT;
let hh = ViewGroup.LayoutParams.MATCH_PARENT;
if(w=="auto"){
ww = ViewGroup.LayoutParams.WRAP_CONTENT
}
if(h=="auto"){
hh = ViewGroup.LayoutParams.WRAP_CONTENT
}
let layoutParams_crd = new LinearLayout.LayoutParams(ww,hh)
view.setLayoutParams(layoutParams_crd);
}
setSize(width : number, height : number) : xView {
let view = this.view as View;
let layoutParams_crd = new LinearLayout.LayoutParams(
(width).toInt(),
(height).toInt()
)
view.setLayoutParams(layoutParams_crd);
return this;
}
setWidth(width : number) : xView {
let view = this.view as View;
let lpar = view.getLayoutParams();
let layoutParams_crd = new LinearLayout.LayoutParams(
(width).toInt(),
(lpar.height).toInt(),
)
view.setLayoutParams(layoutParams_crd);
return this;
}
setHeight(height : number) : xView {
let view = this.view as View;
let lpar = view.getLayoutParams();
let layoutParams_crd = new LinearLayout.LayoutParams(
(lpar.width).toInt(),
(height).toInt(),
)
view.setLayoutParams(layoutParams_crd);
return this;
}
}
export default xView;
@@ -0,0 +1,669 @@
/**
* 数字转16进制
*/
function toHex(numbers : number) : string {
let i = 0 as number
if (numbers === i) {
return '00';
}
let hex = '';
const hexChars = '0123456789abcdef';
while (numbers > 0) {
const remainder = numbers % 16;
hex = hexChars[remainder] + hex;
numbers = Math.floor(numbers / 16);
}
return hex;
}
/**
* 颜色值列表
*/
export const colors = new Map<string, string>([
['primary', '#0579FF'],
['success', '#09CC54'],
['danger', '#FE6D1B'],
['warn', '#F0A22C'],
['error', '#E2001B'],
['info', '#F2F2F2'],
['kleinblue', '#002FA7'], // 克莱因蓝
['chinesered', '#FF0000'], // 中国红
['internationalorange', '#FF4F00'], // 国际橙
['egyptianvlue', '#1034A6'], // 埃及艳蓝
['parisviolet', '#6C3082'], // 巴黎紫
['moroccanblue', '#1256A7'], // 摩洛哥蓝
['brazilgreen', '#009B3A'], // 巴西绿
['britishracinggreen', '#004225'], // 英国赛车绿
['indianyellow', '#E3A857'], // 印度黄
['australiangold', '#FFDF00'], // 澳大利亚金
['venetianred', '#C80815'], // 威尼斯红
['majorelleblue', '#6050DC'], // 马若雷蓝
['tuscanred', '#7C3030'], // 托斯卡红
['naplesyellow', '#FADA5E'], // 那不勒斯黄
['capumortuum', '#592720'], // 死者之首
['mayablue', '#73C2FB'], // 玛雅蓝
['persianrose', '#FE28A2'], // 波斯玫瑰
['tyrianpurple', '#66023C'], // 泰尔紫
['saharasand', '#F1E788'], // 撒哈拉沙
['burmeseruby', '#B00A0A'], // 缅甸红宝石
['transparent', 'rgba(0,0,0,0)'],
['aliceblue', '#F0F8FF'],
['antiquewhite', '#FAEBD7'],
['aqua', '#00FFFF'],
['aquamarine', '#7FFFD4'],
['azure', '#F0FFFF'],
['beige', '#F5F5DC'],
['bisque', '#FFE4C4'],
['black', '#000000'],
['blanchedalmond', '#FFEBCD'],
['blue', '#0000FF'],
['blueviolet', '#8A2BE2'],
['brown', '#A52A2A'],
['burlywood', '#DEB887'],
['cadetblue', '#5F9EA0'],
['chartreuse', '#7FFF00'],
['chocolate', '#D2691E'],
['coral', '#FF7F50'],
['cornflowerblue', '#6495ED'],
['cornsilk', '#FFF8DC'],
['crimson', '#DC143C'],
['cyan', '#00FFFF'],
['darkblue', '#00008B'],
['darkcyan', '#008B8B'],
['darkgoldenrod', '#B8860B'],
['darkgray', '#A9A9A9'],
['darkgreen', '#006400'],
['darkkhaki', '#BDB76B'],
['darkmagenta', '#8B008B'],
['darkolivegreen', '#556B2F'],
['darkorange', '#FF8C00'],
['darkorchid', '#9932CC'],
['darkred', '#8B0000'],
['darksalmon', '#E9967A'],
['darkseagreen', '#8FBC8F'],
['darkslateblue', '#483D8B'],
['darkslategray', '#2F4F4F'],
['darkturquoise', '#00CED1'],
['darkviolet', '#9400D3'],
['deeppink', '#FF1493'],
['deepskyblue', '#00BFFF'],
['dimgray', '#696969'],
['dodgerblue', '#1E90FF'],
['firebrick', '#B22222'],
['floralwhite', '#FFFAF0'],
['forestgreen', '#228B22'],
['fuchsia', '#FF00FF'],
['gainsboro', '#DCDCDC'],
['ghostwhite', '#F8F8FF'],
['gold', '#FFD700'],
['goldenrod', '#DAA520'],
['gray', '#808080'],
['green', '#008000'],
['greenyellow', '#ADFF2F'],
['honeydew', '#F0FFF0'],
['hotpink', '#FF69B4'],
['indianred', '#CD5C5C'],
['indigo', '#4B0082'],
['ivory', '#FFFFF0'],
['khaki', '#F0E68C'],
['lavender', '#E6E6FA'],
['lavenderblush', '#FFF0F5'],
['lawngreen', '#7CFC00'],
['lemonchiffon', '#FFFACD'],
['lightblue', '#ADD8E6'],
['lightcoral', '#F08080'],
['lightcyan', '#E0FFFF'],
['lightgoldenrodyellow', '#FAFAD2'],
['lightgray', '#D3D3D3'],
['lightgreen', '#90EE90'],
['lightpink', '#FFB6C1'],
['lightsalmon', '#FFA07A'],
['lightseagreen', '#20B2AA'],
['lightskyblue', '#87CEFA'],
['lightslategray', '#778899'],
['lightsteelblue', '#B0C4DE'],
['lightyellow', '#FFFFE0'],
['lime', '#00FF00'],
['limegreen', '#32CD32'],
['linen', '#FAF0E6'],
['magenta', '#FF00FF'],
['maroon', '#800000'],
['mediumaquamarine', '#66CDAA'],
['mediumblue', '#0000CD'],
['mediumorchid', '#BA55D3'],
['mediumpurple', '#9370DB'],
['mediumseagreen', '#3CB371'],
['mediumslateblue', '#7B68EE'],
['mediumspringgreen', '#00FA9A'],
['mediumturquoise', '#48D1CC'],
['mediumvioletred', '#C71585'],
['midnightblue', '#191970'],
['mintcream', '#F5FFFA'],
['mistyrose', '#FFE4E1'],
['moccasin', '#FFE4B5'],
['navajowhite', '#FFDEAD'],
['navy', '#000080'],
['oldlace', '#FDF5E6'],
['olive', '#808000'],
['olivedrab', '#6B8E23'],
['orange', '#FFA500'],
['orangered', '#FF4500'],
['orchid', '#DA70D6'],
['palegoldenrod', '#EEE8AA'],
['palegreen', '#98FB98'],
['paleturquoise', '#AFEEEE'],
['palevioletred', '#DB7093'],
['papayawhip', '#FFEFD5'],
['peachpuff', '#FFDAB9'],
['peru', '#CD853F'],
['pink', '#FFC0CB'],
['plum', '#DDA0DD'],
['powderblue', '#B0E0E6'],
['purple', '#800080'],
['rebeccapurple', '#663399'],
['red', '#FF0000'],
['rosybrown', '#BC8F8F'],
['royalblue', '#4169E1'],
['saddlebrown', '#8B4513'],
['salmon', '#FA8072'],
['sandybrown', '#F4A460'],
['seagreen', '#2E8B57'],
['seashell', '#FFF5EE'],
['sienna', '#A0522D'],
['silver', '#C0C0C0'],
['skyblue', '#87CEEB'],
['slateblue', '#6A5ACD'],
['slategray', '#708090'],
['snow', '#FFFAFA'],
['springgreen', '#00FF7F'],
['steelblue', '#4682B4'],
['tan', '#D2B48C'],
['teal', '#008080'],
['thistle', '#D8BFD8'],
['tomato', '#FF6347'],
['turquoise', '#40E0D0'],
['violet', '#EE82EE'],
['wheat', '#F5DEB3'],
['white', '#FFFFFF'],
['whitesmoke', '#F5F5F5'],
['yellow', '#FFFF00'],
['yellowgreen', '#9ACD32']
])
export function isValidColor(color : string) : boolean {
// return chroma.valid(color);
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/;
const rgbRegex = /^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/;
const rgbaRegex = /^rgba\((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*((1(\.0{1,2})?)|(0(\.\d{1,2})?))\)$/;
const hslRegex = /^hsl\((\d{1,3}), (\d{1,3})%, (\d{1,3})%\)$/;
const hslaRegex = /^hsla\(\s*((\d{1,2}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)\s*,\s*((\d{1,2}|\d{0,1}\d{1}\d{1}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)%\s*,\s*((\d{1,2}|\d{0,1}\d{1}\d{1}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)%\s*,\s*((1|0(\.\d{1,2})?|(\.\d{1,2})))\)$/i;
if (color == '') {
return false; // 颜色值为空
} else if (color === 'inherit' || color === 'transparent') {
return false; // 特殊颜色值
} else if (color === 'currentColor') {
return false; // currentColor 不是有效的颜色值
} else if (hexRegex.test(color) || rgbRegex.test(color) || rgbaRegex.test(color) || hslRegex.test(color) || hslaRegex.test(color)) {
return true; // 符合颜色值的格式
}
return false;
}
/**
* 获取默认的颜色值
* @param [string] 颜色名称或者16进制颜色值
*/
export function getDefaultColor(sColor : string) : string {
if(sColor=="") return ""
let sc = sColor.toLocaleLowerCase().trim().replace(" ","");
if (isValidColor(sc)) {
// 如果符合所有颜色值,进行转换为16进制。css方便统一解析。
return sc;
}
// 检测是否是颜色名称。
let sco = colors.get(sc);
if (typeof sco == 'string') return sco as string;
return colors.get("primary")!;
}
/**
* 16进制转rgb
*/
export function hexToRgb(sColors : string) : UTSJSONObject {
if (sColors == "") {
return { r: 0, g: 0, b: 0, a: 0 }
}
let reg = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/;
let sColor : string = sColors.toLowerCase();
if (sColor != '' && reg.test(sColor)) {
if (sColor.length == 4) {
let sColorNew = "#";
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
}
sColor = sColorNew;
}
//处理六位的颜色值
let sColorChange : number[] = [];
sColorChange.push(parseInt(sColor.substring(1, 3), 16));
sColorChange.push(parseInt(sColor.substring(3, 5), 16));
sColorChange.push(parseInt(sColor.substring(5, 7), 16));
if(sColor.length==9){
sColorChange.push(parseInt(sColor.substring(7, 9), 16)/255);
}
return {
r: sColorChange[0],
g: sColorChange[1],
b: sColorChange[2],
a: sColorChange.length==4?sColorChange[3]:1
}
} else if (/^(rgb|RGB|rgba|RGBA)/.test(sColor)) {
let arr : string[] = sColor.replace(/(?:\(|\)|rgba|rgb|RGB|RGBA)*/g, "").split(",")
let p : number[] = arr.map((val : string) : number => parseInt(val));
if (p.length < 3) {
return {
r: 0,
g: 0,
b: 0,
a: 1
}
}
if (p.length == 3) {
p.push(1)
}
return {
r: p[0],
g: p[1],
b: p[2],
a: p[3]
}
} else {
return {
r: 0,
g: 0,
b: 0,
a: 1
}
}
}
/**
* rgb转hsl
*/
export function rgbToHsl(rgb : UTSJSONObject) : UTSJSONObject {
let r = rgb.getNumber("r")
r = r as number / 255
let g = rgb.getNumber("g")
g = g as number / 255
let b = rgb.getNumber("b")
b = b as number / 255
let a = rgb.getNumber("a")
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
let h=60 * (4 + (r - g) / (max - min));
let s=(max - min) / (2 - max - min);
let l= (max + min) / 2;
if (max === r) {
h = (60 * (g - b)) / (max - min);
} else if (max === g) {
h = 60 * (2 + (b - r) / (max - min));
}
if (h < 0) {
h += 360;
}
if (max === min) {
s = 0;
} else if (l < 0.5) {
s = (max - min) / (max + min);
}
return { h: h, s: s * 100, l: l * 100, a: a };
}
/**
* hsl转rgb
*/
export function hslToRgb(hsl : UTSJSONObject) : UTSJSONObject {
let h = hsl.getNumber("h")!
let s = hsl.getNumber("s")!
let l = hsl.getNumber("l")!
let a = hsl.getNumber("a")!
h = h / 360;
s = s / 100;
l = l / 100;
let r = l;
let g = l;
let b = l;
function hue2rgb(pxx : number, q : number, txs : number) : number {
let t = txs;
let p = pxx;
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
if (s > 0) {
let q = l * (1 + s);
if (l >= 0.5) {
q = l + s - l * s;
}
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
r = Math.round(r * 255);
g = Math.round(g * 255);
b = Math.round(b * 255);
return { r, g, b, a };
}
/**
* rgb转16进制
*/
export function rgbToHex(rgb : UTSJSONObject) : string {
let r = rgb.getNumber("r")!
let g = rgb.getNumber("g")!
let b = rgb.getNumber("b")!
let a = rgb.getNumber("a")!
return "#" + toHex(r) + toHex(g) + toHex(b) + toHex(a*255)
}
export function hslaToCss(hsl : UTSJSONObject) : string {
let rgb = hslToRgb(hsl)
return rgbToHex(rgb)
}
export function hslaToRgbCss(hsl : UTSJSONObject) : string {
let rgb = hslToRgb(hsl)
let r = rgb.getNumber("r")!
let g = rgb.getNumber("g")!
let b = rgb.getNumber("b")!
let a = rgb.getNumber("a")!
return `rgba(${r},${g},${b},${a})`
}
// 根据 HSLA 颜色计算亮度
export function getLuminance(color : UTSJSONObject, type = "rgba") : number {
let colordefault : UTSJSONObject = type == 'rgba' ? color : hslToRgb(color);
const r= colordefault.getNumber("r")!
const g= colordefault.getNumber("g")!
const b= colordefault.getNumber("b")!
const rs = r / 255
const gs = g / 255
const bs = b / 255
const rl = Math.pow(rs <= 0.03928 ? rs / 12.92 : ((rs + 0.055) / 1.055), 2.4);
const gl = Math.pow(gs <= 0.03928 ? gs / 12.92 : ((gs + 0.055) / 1.055), 2.4);
const bl = Math.pow(bs <= 0.03928 ? bs / 12.92 : ((bs + 0.055) / 1.055), 2.4);
return 0.2126 * rl + 0.7152 * gl + 0.0722 * bl;
}
/**
* 对颜色加深
*/
export function colorAddDeepen(sColor : string) : string {
let rgb = hexToRgb(getDefaultColor(sColor));
let hsl = rgbToHsl(rgb);
let l = hsl.getNumber("l")! - 5;
l = Math.max(0, Math.min(l, 100))
return hslaToRgbCss({ h: hsl.getNumber("h"), s: hsl.getNumber("s"), l, a: hsl.getNumber("a")! })
}
/**
* 对颜色提亮
*/
export function colorAddBrighten(sColor : string) : string {
let rgb = hexToRgb(getDefaultColor(sColor));
let hsl = rgbToHsl(rgb);
let l = hsl.getNumber("l")! + 10;
l = Math.max(0, Math.min(l, 100))
return rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s:hsl.getNumber("s"), l, a: hsl.getNumber("a")! }))
}
/**
* 获取浅色系主题,根据颜色值计算。返回正常值和hover值。
*/
export function colorGetThint(sColor : string) {
let rgb = hexToRgb(getDefaultColor(sColor));
let hsl = rgbToHsl(rgb);
let s = hsl.getNumber("s")!;
// let l = hsl.getNumber("l");
if (Math.ceil(s) > 0) {
s = 45
}
return {
default: rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s, l: 93, a: hsl.getNumber("a")! })),
hover: rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s, l: 85, a: hsl.getNumber("a")! }))
}
}
/**
* 获取正常主题,根据颜色值计算。返回正常值和hover值。
*/
export function colorGetHover(sColor : string) {
return {
default: sColor,
hover: colorAddDeepen(sColor)
}
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getDefaultColorObj(color : string, hoverColor : string) : UTSJSONObject {
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
let shadow = hslaToRgbCss({ h: hsla.getNumber("h")!, s: (hsla.getNumber("s")!)== 0 ? 0 : 60, l: 40, a: 0.4 });
let shadowHOver = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: (hoverHsla.getNumber("s")!)== 0 ? 0 : 60, l: 40, a: 0.4 });
if((hsla.getNumber("h")!)==0&&(hsla.getNumber("s")!)==0){
shadow = "transparent"
}
if((hoverHsla.getNumber("h")!)==0&&(hoverHsla.getNumber("s")!)==0){
shadowHOver = "transparent"
}
let o = {
default: {
background: hslaToRgbCss(hsla),
borderColor: hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: Math.min(Math.max(0, hsla.getNumber("l")! - 5), hsla.getNumber("l")!) }),
fontColor: getLuminance(hsla, 'hsla') < 0.46 && hsla.getNumber("a")! > 0.1 ? '#ffffff' : '#000000',
shadow: shadow
},
active: {
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: Math.min(Math.max(0, hoverHsla.getNumber("l")! - 10), hoverHsla.getNumber("l")!) }),
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: Math.max(Math.max(0, hoverHsla.getNumber("l")! - 10), hoverHsla.getNumber("l")!) }),
fontColor: getLuminance(hoverHsla, 'hsla') < 0.46 && hoverHsla.getNumber("a")! > 0.1 ? '#ffffff' : '#000000',
shadow: shadowHOver
}
} as UTSJSONObject
return o;
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getTextColorObj(color : string, hoverColor : string) : UTSJSONObject {
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
let o = {
default: {
background: "transparent",
borderColor: "transparent",
fontColor: getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
},
active: {
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 95 }),
borderColor:"transparent",
fontColor: getLuminance(hoverHsla, 'hsla') < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
}
} as UTSJSONObject
return o;
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getThinColorObj(color : string, hoverColor : string,isCoverDark?:boolean) : UTSJSONObject {
let dark = isCoverDark==null?false:isCoverDark
// #ifdef WEB
dark = isCoverDark==undefined?false:dark
// #endif
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
let fontcolor = getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l:30 })
let bordercolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?21:92 })
let bgcolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?20:95 })
if(dark){
let p = hsla;
p.set('l',90)
fontcolor = hslaToRgbCss(p)
p.set('l',20)
p.set('s',p.getNumber('s')!==0?0:20)
bgcolor = hslaToRgbCss(p)
p.set('l',22)
bordercolor = hslaToRgbCss(p)
}
let o = {
default: {
background: bgcolor,
borderColor:bordercolor,
fontColor: fontcolor,
shadow: "transparent",
},
active: {
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?18:88 }),
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a:0, l: dark==true?18:80 }),
fontColor: getLuminance(hoverHsla, 'hsla') < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
}
} as UTSJSONObject
return o;
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getOutlineColorObj(color : string, hoverColor : string) : UTSJSONObject {
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
let o = {
default: {
background: 'transparent',
borderColor:hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: 1, l: 75 }),
fontColor: getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
},
active: {
background: 'transparent',
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a:1, l: 70 }),
fontColor: getLuminance(hoverHsla, 'hsla') < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
}
} as UTSJSONObject
return o;
}
/**
* 设置颜色的亮度值。29
* isCoverDark:是根据亮度值,是否要设置n的值,如果n超过了
* 指定的30就不操作。
*/
export function setTextColorLightByDark(color:string):string{
let realColor = getDefaultColor(color);
let hsla = rgbToHsl(hexToRgb(realColor))
let l = hsla.getNumber('l')!
let s = hsla.getNumber('s')!
if(l<30){
if(s>0){
hsla.set('l',50)
}else{
hsla.set('l',100)
}
}
return hslaToRgbCss(hsla)
}
/**
* 变动的颜色是否需要变深。如果不需要返回原始颜色
* 如果果需要自动加深。
*/
export function setBgColorLightByDark(color:string):string{
let realColor = getDefaultColor(color);
let hsla = rgbToHsl(hexToRgb(realColor))
let l = hsla.getNumber('l')!
let s = hsla.getNumber('s')!
if(s==0&&l>50){
hsla.set('l',100-l)
}
if(s>0){
if(l>50){
hsla.set('l',100-l)
}
}
return hslaToRgbCss(hsla)
}
export function isBlackAndWhite(color:string):boolean{
let realColor = getDefaultColor(color);
let hsla = rgbToHsl(hexToRgb(realColor))
let s = hsla.getNumber('s')!
return s==0
}