/* * ******************************************************************************************************************** * * 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.graphics.Canvas import android.graphics.Matrix import android.graphics.PointF import android.graphics.RectF import android.util.Log import android.view.MotionEvent import com.sybotan.android.graphy.enums.SGraphyItemFlag import com.sybotan.android.graphy.listeners.SGraphyItemPosListener import com.sybotan.android.graphy.utils.MatrixUtil import com.sybotan.base.extensions.toJson import org.jetbrains.anko.doAsync import java.util.* /** * SGraphy图形引擎Item类 * * @author 庞利祥(sybotan@126.com) */ open class SGraphyItem(parent: SGraphyItem? = null) { /** * 类对象 */ companion object { private val TAG = SGraphyItem::class.java.name /** 当前移动Item */ private var currentMoveItem: SGraphyItem? = null /** 当前焦点Item */ private var currentFocusItem: SGraphyItem? = null /** * MotionEvent转子对象MotionEvent * * @param child 子item对象 * @param e 事件参数 * @return 子对象MotionEvent */ fun toChildMotionEvent(child: SGraphyItem, e: SMotionEvent): SMotionEvent { val ce = SMotionEvent(e) // ce.matrix.postTranslate(child.pos.x, child.pos.y); // ce.matrix.postScale(child.scale.x, child.scale.y); // ce.matrix.postRotate(child.rotate,0f, 0f); ce.matrix.preTranslate(child.pos.x, child.pos.y); ce.matrix.preScale(child.scale.x, child.scale.y); ce.matrix.preRotate(child.rotate, 0f, 0f); // 不跟随缩放 if (!child.isTransform) { val src = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f) // ce.matrix.postScale(child._inverseScaleX, child._inverseScaleY); ce.matrix.getValues(src) val matrix = Matrix() matrix.preTranslate(src[2], src[5]) matrix.preScale(child.scale.x, child.scale.y) matrix.preRotate(child.rotate) ce.matrix = matrix } // se.setLocation((e.x - child.pos.x) / child.scale.x, (e.y - child.pos.y) / child.scale.y) val matrixMat = Matrix() ce.matrix.invert(matrixMat) val matrixTransform = MatrixUtil.matrixTransform(matrixMat, e.viewX, e.viewY) ce.x = matrixTransform.x ce.y = matrixTransform.y return ce } // Function toSceneMotionEvent() } // companion object /** 父节点 */ var parent: SGraphyItem? = parent set(value) { if (field == value) { // 如果值没变化 return } if (null != field) { // 如果原parent不为空 // 将节点从原parent节点中摘除 field!!.children.remove(this) } field = value if (null != field) { // 如果新parent不为空 // 将节点加入到新parent节点中 field!!.children.add(this) field!!.children.sortByDescending { it.zOrder } } invalidate() } /** 子节点列表 */ val children = ArrayList() /** item所属场景 */ var scene: SGraphyScene? = null get() { return if (null != parent) { parent!!.scene } else { field } } /** item位置 */ var pos: PointF = PointF(0f, 0f) set(value) { if (field == value) { // 如果值没变化 return } field = value invalidate() posListener?.onMoveTo(field.x, field.y) } /** item位置变更监听器 */ var posListener: SGraphyItemPosListener? = null /** z轴坐标,数值越大,越靠上 */ var zOrder = 0f set(value) { if (field == value) { // 如果值没变化 return } field = value if (null != parent) { // 如果你节点不空 parent!!.children.sortByDescending { it.zOrder } } invalidate() } /** 缩放比例 */ var scale = PointF(1f, 1f) /** 旋转 */ var rotate = 0.0f /** 是否进行变形 */ var isTransform = true; /** 放缩反比例 */ var _inverseScaleX = 1f; var _inverseScaleY = 1f; /** item标志 */ var flags: EnumSet = EnumSet.noneOf(SGraphyItemFlag::class.java) /** item是否可见 */ var isVisible = true set(value) { if (field == value) { // 如果值没变化 return } field = value invalidate() } /** item是否获得焦点 */ var focus = false set(value) { if (!flags.contains(SGraphyItemFlag.ItemIsFocusable)) { // 如果item不能获得焦点 return } if (field == value) { // 如果值没变化 return } field = value if (value) { // 如果获得焦点 currentFocusItem?.focus = false onGetFocus() currentFocusItem = this } else { onLostFocus() if (currentFocusItem === this) { // 如果当前焦点对象失去焦点 currentFocusItem = null } } } /** * 当前获得焦点的item对象 */ val focusItem: SGraphyItem? get() = currentFocusItem /** item是否被选中 */ var isSelected = false set(value) { if (field == value) { // 如果值没变化 return } field = value invalidate() } /** * Item对象边界区域 * * @return 边界区域 */ open fun boundingRect(): RectF { return RectF() } // Function boundingRect() /** * 更新Item * * @param rect 更新区域 */ fun invalidate(rect: RectF = RectF()) { scene?.update() return } // Function update() /** * 延迟刷新 * * @param delayed 延时的时间 */ fun postInvalidateDelayed(delayed: Long) { doAsync { Thread.sleep(delayed) invalidate() } return } // Function postInvalidateDelayed() /** * 隐藏item */ fun hide() { isVisible = false return } // Function hide() /** * 显示item */ fun show() { isVisible = true return } // Function show() /** * 移动item到指定位置 * * @param pos 新的位置 */ fun moveTo(pos: PointF) { this.pos = pos return } // Function move() /** * 移动item到指定位置 * * @param x 新位置的x坐标 * @param y 新位置的y坐标 */ fun moveTo(x: Float, y: Float) { pos = PointF(x, y) return } // Function move() //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 坐标换算相关操作 /** * 将场景中的xy坐标转换成item坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @param x 场景中的横坐标 * @param y 场景中的纵坐标 * * @return 在item中的坐标 */ fun mapFromScene(x: Float, y: Float): PointF { val m = this.scene2itemMattrix() val matrix = Matrix() m.invert(matrix) return MatrixUtil.matrixTransform(matrix, x, y) } // Function mapFromScene() /** * 将场景中的xy坐标转换成item坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @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 { if (null == parent) { return PointF(x, y) } val m = this.scene2itemMattrix() return return MatrixUtil.matrixTransform(m, x, y) // return parent!!.mapToScene(x * scale.x + pos.x, y * scale.y + pos.y) } // Function mapToScene() /** * 将item中的xy坐标转换成场景坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @param point item中的坐标 * * @return 在场景中的坐标 */ fun mapToScene(point: PointF): PointF { return mapToScene(point.x, point.y) } // Function mapToScene() /** * 获得item的路径节点列表。(该节点被加载到场景中,如果未被加载到场景中,计算会出错) * * @return 从根节点到当前节点的路径节点列表。 */ fun itemPath(): MutableList { if (null != parent) { val list = parent!!.itemPath() list.add(this) return list } return mutableListOf(this) } // Function itemPath() /** * 判断item是否包含点x,y * * @param x 横坐标(当前item) * @param y 纵坐标(当前item) * * @return true包含;false不包含。 */ open fun contains(x: Float, y: Float): Boolean { // Log.e("boundingRect", boundingRect().contains(x, y).toString()) return boundingRect().contains(x, y) } // Function () /** * 判断item是否包含点x,y * * @param point 点坐标(当前item) * * @return true包含;false不包含。 */ fun contains(point: PointF): Boolean { return contains(point.x, point.y) } // Function () //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 事件回调 /** * Item绘制操作 * * @param canvas 画布对象 * @param rect 更新区域 */ open fun onPaint(canvas: Canvas, rect: RectF) { canvas.save() // val mat1 = SJsonUtil.fromJson(canvas.matrix.toJson(), Matrix::class.java) this.onDraw(canvas) // canvas.matrix = mat1 canvas.restore() val src = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f) for (i in 1..children.size) { // 倒序依次取item列中的所有item。将所有item的边界做并交处理。 val item = children[children.size - i] if (!item.isVisible) { // 如果对象不可见 continue } try { // 保存画布状态 canvas.save() // val mat = SJsonUtil.fromJson(canvas.matrix.toJson(), Matrix::class.java) // item位移到指定位置绘制 canvas.translate(item.pos.x, item.pos.y) canvas.scale(item.scale.x, item.scale.y) canvas.rotate(item.rotate) if (!item.isTransform) { canvas.matrix.getValues(src) val matrix = Matrix() matrix.preTranslate(src[2], src[5]) matrix.preScale(item.scale.x, item.scale.y) matrix.preRotate(item.rotate) canvas.matrix = matrix // matrix.getValues(src) // item._inverseScaleX = 1.0f/src[0] // item._inverseScaleY = 1.0f/src[4] // canvas.scale(item._inverseScaleX,item._inverseScaleY) // matrix.preTranslate(src[2], src[5]) // matrix.preTranslate(item.scale.x,item.scale.y) // matrix.preRotate(item.rotate) // canvas.matrix = matrix } // 设置绘制区域 // canvas.clipRect(item.boundingRect()) // 绘制item item.onPaint(canvas, rect) // 恢复画布状态 canvas.restore() // canvas.matrix = mat; } catch (e: Exception) { e.printStackTrace() } } return } // Function paint() /** * Item 绘制操作 * * @param painter 绘制对象 */ open fun onDraw(canvas: Canvas) { } /** * 获得焦点变更回调 */ open fun onGetFocus() { // DO NOTHING return } // Function onGetFocus() /** * 失去焦点变更回调 */ open fun onLostFocus() { // DO NOTHING return } // Function onLostFocus() /** * 用户按下触发. * * @param e 按下手势事件。 * @return 如果事件被处理返回 true , 否则返回 false 。 */ open fun onDown(e: SMotionEvent): Boolean { for (item in children) { if (!item.isVisible) { // 如果对象不可见 continue } val ce = toChildMotionEvent(item, e) if (item.contains(ce.x, ce.y) // 如果点在子项目上 && item.onDown(ce)) { // 且子项目处理了事件 return true } } if (flags.contains(SGraphyItemFlag.ItemIsFocusable)) { // 如果是可获得焦点Item focus = true return true } return false } // Function onDown() /** * 用户释放触发. * * @param e 释放手势事件。 * @return 如果事件被处理返回 true , 否则返回 false 。 */ open fun onSingleTapUp(e: SMotionEvent): Boolean { Log.d(TAG, "onSingleTapUp: releaseItem") releaseItem() for (item in children) { if (!item.isVisible) { // 如果对象不可见 Log.e("手势3scene", e.toJson()) continue } val ce = toChildMotionEvent(item, e) // Log.e("手势view", "${scene?.view?.scale}") // Log.e("手势e", "${e.x}, ${e.y}") // Log.e("手势ce", "${ce.x}, ${ce.y}") // Log.e("手势1scene", (item.contains(ce.x, ce.y)).toString()) if (item.contains(ce.x, ce.y) // 如果点在子项目上 && item.onSingleTapUp(ce)) { // 且子项目处理了事件 // Log.e("手势4scene", e.toJson()) return true } } // Log.e("手势2scene", e.toJson()) return false } // Function onSingleTapUp() open fun onActionUp() { releaseItem() } open fun onActionMove(event: MotionEvent) { } /** * 用户点击触发 * * @param e 手势事件。 * @return 如果事件被处理返回 true , 否则返回 false 。 */ open fun onShowPress(e: SMotionEvent): Boolean { Log.d(TAG, "onShowPress: releaseItem") for (item in children) { if (!item.isVisible) { // 如果对象不可见 continue } val ce = toChildMotionEvent(item, e) if (item.contains(ce.x, ce.y) // 如果点在子项目上 && item.onShowPress(ce)) { // 且子项目处理了事件 return true } } return false } // Function onShowPress() /** * 长按触摸屏超过一定时间。 * * @param e 长按手势事件。 * @return 如果事件被处理返回 true , 否则返回 false 。 */ open fun onLongPress(e: SMotionEvent): Boolean { Log.d(TAG, "onLongPress: releaseItem") releaseItem() for (item in children) { if (!item.isVisible) { // 如果对象不可见 continue } val ce = toChildMotionEvent(item, e) if (item.contains(ce.x, ce.y) // 如果点在子项目上 && item.onLongPress(ce)) { // 且子项目处理了事件 return true } } return false } // Function onLoagPress() /** * 用户按下触摸屏,并拖动。 * * @param e1 第1个 ACTION_DOWN 手势事件 * @param e2 最后一个 ACTION_MOVE 手势事件 * @param distanceX X轴上的移动距离,单位:像素 * @param distanceY Y轴上的移动距离,单位:像素 * @return 如果事件被处理返回 true , 否则返回 false 。 */ open fun onScroll(e1: SMotionEvent, e2: SMotionEvent, distanceX: Float, distanceY: Float): Boolean { if (flags.contains(SGraphyItemFlag.ItemIsMovable) && currentMoveItem === this) { moveTo(pos.x - distanceX * scale.x, pos.y - distanceY * scale.y) return true } for (item in children) { if (!item.isVisible) { // 如果对象不可见 continue } val ce1 = toChildMotionEvent(item, e1) val ce2 = toChildMotionEvent(item, e2) // 如果点在子项目上且子项目目处理了事件 if (item.contains(ce2.x, ce2.y) // 如果点在子项目上 && item.onScroll(ce1, ce2, distanceX / item.scale.x, distanceY / item.scale.y)) { // 且子项目处理了事件 return true } } if (flags.contains(SGraphyItemFlag.ItemIsMovable) && null == currentMoveItem) { // 如果是可移动Item grabItem(this) currentMoveItem = this return true } return false } // Function onScroll() /** * 用户按下触摸屏、快速移动后松开。即滑动操作。 * * @param e1 第1个 ACTION_DOWN 手势事件 * @param e2 最后一个 ACTION_MOVE 手势事件 * @param velocityX X轴上的移动速度,像素/秒 * @param velocityY Y轴上的移动速度,像素/秒 * @return 如果事件被处理返回 true , 否则返回 false 。 */ open fun onFling(e1: SMotionEvent, e2: SMotionEvent, velocityX: Float, velocityY: Float): Boolean { Log.d(TAG, "onFling: releaseItem") releaseItem() for (item in children) { if (!item.isVisible) { // 如果对象不可见 continue } val ce1 = toChildMotionEvent(item, e1) val ce2 = toChildMotionEvent(item, e2) // 如果点在子项目上且子项目目处理了事件 if (item.contains(ce2.x, ce2.y) // 如果点在子项目上 && item.onFling(ce1, ce2, velocityX / item.scale.x, velocityY / item.scale.y)) { // 且子项目处理了事件 return true } } return false } // Function onFling() //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 私有方法 /** * 锁定item * * @param item 被锁定的item */ private fun grabItem(item: SGraphyItem) { Log.d(TAG, "grabItem") isSelected = true scene!!.grabItem = item return } // Function grabItem /** * 释放被锁定的item */ private fun releaseItem() { isSelected = false currentMoveItem = null scene!!.grabItem = null return } // Function grabItem /** * 场景对象到 item 对象的转换矩阵 * * @return 转换矩阵 */ fun scene2itemMattrix(): Matrix { var m = Matrix() val list = this.itemPath() for (item in list) { // m.postTranslate(item.pos.x, item.pos.y); // m.postScale(item.scale.x, item.scale.y); // m.postRotate(item.rotate) m.preTranslate(item.pos.x, item.pos.y); m.preScale(item.scale.x, item.scale.y); m.preRotate(item.rotate) // 如果不进行变形处理,则取消 painter 的变型操作 if (!item.isTransform) { // m.postScale(item._inverseScaleX, item._inverseScaleY); /****************************************** 待确认 ************************************************/ val src = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f) m.getValues(src) val matrix = Matrix() matrix.preTranslate(src[2], src[5]) matrix.preScale(item.scale.x, item.scale.y) matrix.preRotate(item.rotate) m = matrix } } return m } } // Class SGraphyItem