| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701 |
- /*
- * ********************************************************************************************************************
- *
- * iFHS7.
- * ;BBMBMBMc rZMBMBR BMB
- * MBEr:;PBM, 7MBMMEOBB: BBB RBW
- * XK: BO SB. :SZ MBM. c;; ir BBM :FFr :SSF: ;xBMB:r iuGXv. i:. iF2;
- * DBBM0r. :D S7 ;XMBMB GMBMu. MBM: BMB MBMBBBMBMS WMBMBMBBK MBMBMBM BMBRBMBW .MBMBMBMBB
- * :JMRMMD .. , 1MMRM1; ;MBMBBR: MBM ;MB: BMB: MBM. RMBr sBMH BM0 UMB, BMB. KMBv
- * ;. XOW B1; :uM: 1RE, i .2BMBs rMB. MBO MBO JMB; MBB MBM BBS 7MBMBOBM: MBW :BMc
- * OBRJ.SEE MRDOWOR, 3DE:7OBM . ;BMB RMR7BM BMB MBB. BMB ,BMR .BBZ MMB rMB, BMM rMB7
- * :FBRO0D0 RKXSXPR. JOKOOMPi BMBSSWBMB; BMBB: MBMB0ZMBMS .BMBOXRBMB MBMDE RBM2;SMBM; MBB xBM2
- * iZGE O0SHSPO. uGZ7. sBMBMBDL :BMO OZu:BMBK, rRBMB0; ,EBMB xBMBr:ER. RDU :OO;
- * ,BZ, 1D0 RPSFHXR. xWZ .SMr . .BBB
- * :0BMRDG RESSSKR. 2WOMBW; BMBMR
- * i0BM: SWKHKGO MBDv
- * .UB OOGDM. MK, Copyright (c) 2015-2019. 斯伯坦机器人
- * , XMW ..
- * r All rights reserved.
- *
- * ********************************************************************************************************************
- */
- package com.sybotan.android.graphy
- import android.content.Context
- import android.graphics.*
- import android.util.AttributeSet
- import android.util.Log
- import android.view.*
- import com.sybotan.android.graphy.enums.SGraphyViewTouchState
- import com.sybotan.android.graphy.events.SGraphyViewMoveEvent
- import com.sybotan.android.graphy.events.SGraphyViewZoomEvent
- import com.sybotan.android.graphy.utils.MatrixUtil
- import com.sybotan.base.extensions.toJson
- import org.greenrobot.eventbus.EventBus
- import java.util.concurrent.ExecutorService
- import java.util.concurrent.Executors
- import kotlin.math.max
- import kotlin.math.min
- /**
- * SGraphy图形引擎视图类
- *
- * @author 庞利祥(sybotan@126.com)
- */
- open class SGraphyView(context: Context, attrs: AttributeSet? = null) : SurfaceView(context, attrs),
- SurfaceHolder.Callback, View.OnTouchListener, GestureDetector.OnGestureListener, Runnable {
- /**
- * 类对象
- */
- companion object {
- private val TAG = SGraphyView::class.java.name
- private var fitrate = 0.8f
- } // companion object
- /** 指向加载到视图中的场景 */
- var scene: SGraphyScene? = null
- set(value) {
- if (field == value) { // 如果场景没有变化
- return
- }
- field?.view = null
- value?.view = this
- // 保存场景
- field = value
- }
- /** 旋转 */
- var rotate = 0.0f
- /** 是否支持二指缩放操作 */
- var canZoom = true
- /** 缩放比例 */
- var scale = 1f
- get() = max(min(field, maxScale), minScale)
- set(value) {
- field = value
- moveRange()
- }
- /** 最小缩放比例 */
- var minScale = 0.0000001f
- set(value) {
- field = min(value, maxScale)
- }
- /** 最大缩放比例 */
- var maxScale = 1000000f
- set(value) {
- field = max(value, minScale)
- }
- /** 是否支持移动场景操作 */
- var canMove = true
- /** 场景原点位置在视图中的位置 */
- var pos = PointF(0f, 0f)
- get() {
- field.x = min(max(field.x, minPos.x), maxPos.x)
- field.y = min(max(field.y, minPos.y), maxPos.y)
- return field
- }
- /** 视图原点最小值 */
- var minPos = PointF(Float.MIN_VALUE, Float.MIN_VALUE)
- /** 视图原点最大值 */
- var maxPos = PointF(Float.MAX_VALUE, Float.MAX_VALUE)
- /** 手执状态 */
- private var touchState = SGraphyViewTouchState.NoneState
- /** 是否在绘制状态 */
- private var isDrawing = false
- /** 手势解析器 */
- private val gesture by lazy {
- GestureDetector(this)
- }
- /** 未缩放时二指间距离 */
- private var beforeLength = 0.0f
- /** 缩放后二指间距离 */
- private var afterLength = 0.0f
- /** 后台绘制线程 */
- private var paintThread: Thread? = null
- private var canvas: Canvas? = null
- /** 线程池*/
- private var executorService: ExecutorService? = null
- private var runable = Runnable {
- run {
- while (true) {
- if (Thread.interrupted()) {
- Log.e("sajkskjd", "线程终止了")
- break
- }
- if (isDrawing) {
- try {
- // Thread.sleep(100)
- canvas = holder.lockCanvas()
- if (null == canvas) { // 如果canvas为空
- continue
- }
- drawScene(canvas!!)
- /*synchronized(holder) {
- drawScene(canvas!!)
- }*/
- } catch (e: Exception) {
- e.printStackTrace()
- } finally {
- try {
- if (null != canvas && null != holder) {
- holder.unlockCanvasAndPost(canvas)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- }
- }
- }
- }
- fun runableTask() {
- executorService = Executors.newSingleThreadExecutor()
- executorService!!.execute(runable)
- }
- fun shutDown() {
- if (null != executorService) {
- executorService!!.shutdown()
- executorService!!.shutdownNow()
- }
- }
- /** 默认构造函数 */
- init {
- Log.d(TAG, "init")
- initView()
- } // init
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- Log.e("w", widthMeasureSpec.toString())
- Log.e("h", heightMeasureSpec.toString())
- }
- /**
- * 适配视图到视图
- */
- fun fitSceneToView() {
- // 未设置场景
- if (null == scene) {
- return
- }
- // val w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
- // val h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
- // this.measure(width, height)
- // val h: Int = this.getMeasuredHeight()
- // val w: Int = this.getMeasuredWidth()
- this.post {
- val w = width.toFloat()
- val h = height.toFloat()
- val rect = scene!!.worldRect()
- setCenterPoint(
- PointF(rect.centerX(), rect.centerY()),
- min(w / rect.width(), h / rect.height()) * fitrate
- )
- // Log.e("w",w.toString())
- // Log.e("h",h.toString())
- // Log.e("rect1",rect.toJson())
- // Log.e("min",(min(w / rect.width(), h / rect.height())).toJson())
- }
- } // Function FitView()
- /**
- * 设置中心点
- *
- * @param scale 场景的缩放比例
- */
- fun setCenterPoint(pos: PointF, scale: Float? = null) {
- if (scale != null) {
- this.scale = scale
- }
- this.pos = PointF(
- -pos.x * this.scale + this.width.toFloat() / 2f,
- -pos.y * this.scale + this.height.toFloat() / 2f
- )
- } // Function setCenterPoint()
- /**
- * 限定移动范围
- */
- fun moveRange() {
- if (scene == null) {
- return
- }
- val w = this.width.toFloat()
- val h = this.height.toFloat()
- val rect = scene!!.worldRect()
- this.minPos = PointF(
- -rect.centerX() * this.scale + w / 2f - rect.width() / 2f * this.scale,
- -rect.centerY() * this.scale + h / 2f - rect.height() / 2f * this.scale
- )
- /**
- * -(rect.left + rect.right) / 2f * graphyView.scale + width / 2 图形的中心点,
- * rect.width() / 2 * graphyView.scale / 0.8f 图形宽度的一半乘以缩放比例除以适配百分比
- *
- */
- this.maxPos = PointF(
- -rect.centerX() * this.scale + w / 2f + rect.width() / 2f * this.scale,
- -rect.centerY() * this.scale + h / 2f + rect.height() / 2f * this.scale
- )
- } // Function moveRange()
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Surface相关操作
- /**
- * Surface创建成功时回调函数。
- *
- * @param holder surface创建成功时的保持器。
- */
- override fun surfaceCreated(holder: SurfaceHolder?) {
- Log.d(TAG, "surfaceCreated")
- isDrawing = true
- // 启动后台绘制线程
- paintThread = Thread(this)
- paintThread!!.start()
- // runableTask()
- return
- } // Function surfaceCreated()
- /**
- * Surface变现时回调函数。
- *
- * @param holder Surface保持器
- * @param format 新的surface格式
- * @param width 宽度
- * @param height 高度
- */
- override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
- Log.d(TAG, "surfaceChanged:width=$width, height=$height")
- return
- } // Function surfaceChanged()
- /**
- * Surface被销毁时回调函数。
- *
- * @param holder surface销毁时的保持器。
- */
- override fun surfaceDestroyed(holder: SurfaceHolder?) {
- Log.d(TAG, "surfaceDestroyed")
- paintThread!!.interrupt()
- paintThread = null
- isDrawing = false
- // shutDown()
- return
- } // Function surfaceDestroyed()
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // 坐标换算相关操作
- /**
- * 将场景中的xy坐标转换成视图坐标。
- *
- * @param x 场景中的横坐标
- * @param y 场景中的纵坐标
- *
- * @return 在item中的坐标
- */
- fun mapFromScene(x: Float, y: Float): PointF {
- return PointF(x * scale + pos.x, y * scale + pos.y)
- } // Function mapFromScene()
- /**
- * 将场景中的xy坐标转换成视图坐标。
- *
- * @param point 场景中的坐标
- * @return 在item中的坐标
- */
- fun mapFromScene(point: PointF): PointF {
- return mapFromScene(point.x, point.y)
- } // Function mapFromScene()
- /**
- * 将item中的xy坐标转换成场景坐标。
- *
- * @param x item中的横坐标
- * @param y item中的纵坐标
- * @return 在场景中的坐标
- */
- fun mapToScene(x: Float, y: Float): PointF {
- return PointF((x - pos.x) / scale, (y - pos.y) / scale)
- } // Function mapToScene()
- /**
- * 将item中的xy坐标转换成场景坐标。
- *
- * @param point item中的坐标
- * @return 在场景中的坐标
- */
- fun mapToScene(point: PointF): PointF {
- return mapToScene(point.x, point.y)
- } // Function mapToScene()
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // 手势相关操作
- /**
- * 用户对视图进行触摸操作时触发
- *
- * @param v 触摸的视图对象
- * @param event 保存触摸事件参数
- * @return 如果处理了该事件返回true, 否则返回false
- */
- override fun onTouch(v: View, event: MotionEvent): Boolean {
- try {
- return when (event.action and MotionEvent.ACTION_MASK) {
- MotionEvent.ACTION_POINTER_DOWN -> { // 多点触控
- if (canZoom) { // 如果可以缩放
- touchState = SGraphyViewTouchState.ZoomState
- beforeLength = getDistance(event) // 获取两点的距离
- }
- true
- }
- MotionEvent.ACTION_POINTER_UP -> { // 多点释放
- touchState = SGraphyViewTouchState.NoneState
- true
- }
- MotionEvent.ACTION_MOVE -> {
- scene?.onActionMove(event)
- if (SGraphyViewTouchState.ZoomState == touchState && canZoom) { // 两点绽放操作
- viewZoom(event)
- true
- } else {
- gesture.onTouchEvent(event)
- }
- }
- MotionEvent.ACTION_UP -> {
- scene?.onActionUp(event)
- gesture.onTouchEvent(event)
- }
- else -> {
- gesture.onTouchEvent(event)
- }
- }
- } catch (e: Exception) {
- return true
- }
- } // Function onTouch()
- /**
- * 用户点击屏幕时触发。
- *
- * @param e 触摸事件
- */
- override fun onShowPress(e: MotionEvent) {
- scene?.onShowPress(toSceneMotionEvent(e))
- return
- } // Function onShowPress()
- /**
- * 用户按下屏幕就会触发.
- *
- * @param e 按下手势事件。
- * @return 如果事件被处理返回 true , 否则返回 false 。
- */
- override fun onDown(e: MotionEvent): Boolean {
- scene?.onDown(toSceneMotionEvent(e))
- return true
- } // Function onDown()
- /**
- * 用户释放屏幕触发.
- *
- * @param e 释放手势事件。
- * @return 如果事件被处理返回 true , 否则返回 false 。
- */
- override fun onSingleTapUp(e: MotionEvent): Boolean {
- Log.e("手势view", e.toString())
- scene?.onSingleTapUp(toSceneMotionEvent(e))
- return true
- } // Function onSingleTapUp()
- /**
- * 长按触摸屏超过一定时间。
- *
- * @param e 长按手势事件。
- */
- override fun onLongPress(e: MotionEvent) {
- scene?.onLongPress(toSceneMotionEvent(e))
- return
- } // Function onLoagPress()
- /**
- * 用户按下触摸屏,并拖动。
- *
- * @param e1 第1个 ACTION_DOWN 手势事件
- * @param e2 最后一个 ACTION_MOVE 手势事件
- * @param distanceX X轴上的移动距离,单位:像素
- * @param distanceY Y轴上的移动距离,单位:像素
- * @return 如果事件被处理返回 true , 否则返回 false 。
- */
- override fun onScroll(
- e1: MotionEvent,
- e2: MotionEvent,
- distanceX: Float,
- distanceY: Float
- ): Boolean {
- if (scene == null) {
- return true
- }
- // 如果场景处理了该事件,则返回
- if (scene!!.onScroll(
- toSceneMotionEvent(e1),
- toSceneMotionEvent(e2),
- distanceX / scale,
- distanceY / scale
- )
- ) {
- return true
- }
- if (canMove) { // 如果可以移动场景
- // 移动场景
- pos.x = pos.x - distanceX
- pos.y = pos.y - distanceY
- Log.e("x", pos.x.toString())
- Log.e("y", pos.y.toString())
- EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
- }
- return true
- } // Function onScroll()
- /**
- * 用户按下触摸屏、快速移动后松开。即滑动操作。
- *
- * @param e1 第1个 ACTION_DOWN 手势事件
- * @param e2 最后一个 ACTION_MOVE 手势事件
- * @param velocityX X轴上的移动速度,像素/秒
- * @param velocityY Y轴上的移动速度,像素/秒
- * @return 如果事件被处理返回 true , 否则返回 false 。
- */
- override fun onFling(
- e1: MotionEvent,
- e2: MotionEvent,
- velocityX: Float,
- velocityY: Float
- ): Boolean {
- scene?.onFling(
- toSceneMotionEvent(e1),
- toSceneMotionEvent(e2),
- velocityX / scale,
- velocityY / scale
- )
- return true
- } // Function onFling()
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // 绘制相关操作
- /**
- * 重载线程run函数。
- */
- override fun run() {
- while (null != paintThread && !paintThread!!.isInterrupted) { // 循环控制绘制
- var canvas: Canvas? = null
- try {
- Thread.sleep(100)
- canvas = holder.lockCanvas()
- if (null == canvas) { // 如果canvas为空
- continue
- }
- synchronized(holder) {
- drawScene(canvas)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- } finally {
- if (null != canvas) {
- holder.unlockCanvasAndPost(canvas)
- }
- }
- }
- return
- } // Function run()
- /**
- * 后台场景绘制线程
- *
- * @param canvas 画布
- */
- open fun drawScene(canvas: Canvas) {
- // 绘制背景
- canvas.save()
- scene?.drawBackground(canvas, RectF())
- canvas.restore()
- // 绘制场景
- canvas.save()
- canvas.translate(pos.x, pos.y)
- canvas.scale(scale, scale)
- scene?.drawScene(canvas, RectF())
- canvas.restore()
- // 绘制前景
- canvas.save()
- scene?.drawForeground(canvas, RectF())
- canvas.restore()
- return
- } // Function drawScene()
- /**
- * 缩放视图时计算视图的位置与缩放比例
- *
- * @param zoom 缩放比例
- * @param midPoint 缩放计算的中心点
- */
- fun scaleByPoint(zoom: Float, midPoint: PointF) {
- var z = zoom
- /**
- * 缩放比例在最小比例和最大比例范围内
- */
- when {
- scale * zoom >= maxScale -> {
- z = maxScale / scale
- scale = maxScale
- }
- scale * zoom <= minScale -> {
- z = minScale / scale
- scale = minScale
- }
- else -> {
- scale *= zoom
- }
- }
- pos.x = midPoint.x - (midPoint.x - pos.x) * z
- pos.y = midPoint.y - (midPoint.y - pos.y) * z
- EventBus.getDefault().post(SGraphyViewZoomEvent(this, scale))
- EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
- return
- } // Function scaleByPoint()
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // 初始化相关操作
- /**
- * 初始化视图控件
- */
- private fun initView() {
- holder.addCallback(this)
- // 设置支持透明色
- holder.setFormat(PixelFormat.TRANSPARENT)
- isFocusable = true
- isFocusableInTouchMode = true
- setOnTouchListener(this)
- return
- } // Function initView()
- /**
- * 缩放视图
- *
- * @param event 触摸事件
- */
- private fun viewZoom(event: MotionEvent): Boolean {
- if (touchState == SGraphyViewTouchState.ZoomState) {
- afterLength = getDistance(event)// 获取两点的距离
- val gapLenght = afterLength - beforeLength// 变化的长度
- if (Math.abs(gapLenght) > 5f && beforeLength != 0.0f) {
- val tempScale = afterLength / beforeLength// 求的缩放的比例
- val p = getMiddlePoint(event)
- scaleByPoint(tempScale, p) //重设置
- beforeLength = afterLength
- }
- }
- return true
- } // Function onTouchMove()
- /**
- * 获取两手指之间的距离
- *
- * @param event 触摸事件
- * @return 两手指之间的距离
- */
- private fun getDistance(event: MotionEvent): Float {
- val x = (event.getX(0) - event.getX(1)).toDouble()
- val y = (event.getY(0) - event.getY(1)).toDouble()
- return Math.sqrt(x * x + y * y).toFloat()
- } // Function getDistance()
- /**
- * 获得两个手指触摸点的中点坐标
- *
- * @param event
- * @return 得到视图的x坐标中点
- */
- private fun getMiddlePoint(event: MotionEvent): PointF {
- return PointF(
- (event.getX(1) + event.getX(0)) / 2f,
- (event.getY(1) + event.getY(0)) / 2f
- )
- } // Function getMiddlePoint()
- /**
- * MotionEvent转场景对象MotionEvent
- *
- * @param e MotionEvent
- * @return 子对象MotionEvent
- */
- private fun toSceneMotionEvent(e: MotionEvent): SMotionEvent {
- val src1 = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)
- val src2 = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)
- val se = SMotionEvent(e)
- // se.matrix.postTranslate(pos.x,pos.y)
- // se.matrix.postScale(this.scale, this.scale);
- // se.matrix.postRotate(this.rotate)
- se.matrix.preTranslate(pos.x, pos.y)
- se.matrix.preScale(this.scale, this.scale);
- se.matrix.preRotate(this.rotate)
- val matrixMat = Matrix()
- se.matrix.invert(matrixMat)
- se.matrix.getValues(src1)
- Log.e("src1= ", src1.toJson())
- matrixMat.getValues(src2)
- Log.e("src2= ", src2.toJson())
- val mp = MatrixUtil.matrixTransform(matrixMat, e.x, e.y)
- se.x = mp.x
- se.y = mp.y
- // Log.e("ex = ",e.x.toString())
- // Log.e("ey = ",e.y.toString())
- // Log.e("sex = ",se.x .toString())
- // Log.e("sey = ",se.y.toString())
- // se.setLocation((e.x - pos.x) / scale, (e.y - pos.y) / scale)
- return se
- } // Function toSceneMotionEvent()
- } // Class SGraphyView
|