自定义View学习(二)拖动+重绘

简单可拖动自定义View+重绘

可移动、可改变大小的矩形

需求:
1、矩形样式
2、可拖动
3、拖动时颜色改变
4、设置拖动边界
5、拖动四个角、可改变矩形大小

在这里插入图片描述

实现代码:

/**
 * Android 自定义View可拖动移动位置及边缘拉伸放大缩小
 * 矩形
 */

class MyRectangleView : View, View.OnTouchListener {

    private var listener: RudderListener? = null // 事件回调接口
    var mPaintK: Paint? = null//默认边框
    var mPaintAngel: Paint? = null//四个角

    //是否开启抗锯齿
    private val antiAlias = true
    private val mArcWidth = 1f

    //view中心点
    var centerIndexX = 0
    var centerIndexY = 0

    //默认宽高
    var viewHeight = 200
    var viewWidth = 250

    //四个角的宽高
    var angelWidth = 20
    var angelinterval = 1//边角距离外层的距离

    //双层中间隔
    var interval = 5

    var downX = 0
    var downY = 0
    var downRawX = 0
    var downRawY = 0
    var isMove = true


    var aviewWidth = 300
    var aviewHeight = 300
    var originalViewWidth = aviewWidth
    var originalViewHeight = aviewHeight
    var mFatherViewWidth = 720
    var mFatherViewHeight = 1080

    var mContext: Context? = null
    private var direction = "Center"

    //view的原始尺寸
    var originalLeft = 0
    var originalRight = 0
    var originalTop = 0
    var originalBottom = 0

    var index=0



    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        this.mContext = context
        this.keepScreenOn = true
        initPaint2(Color.WHITE)
        isFocusable = true
        isFocusableInTouchMode = true
        setOnTouchListener(this)
    }

    constructor(context: Context?, mFatherViewWidth: Int, mFatherViewHeight: Int,num:Int) : super(context) {
        this.mFatherViewWidth = mFatherViewWidth
        this.mFatherViewHeight = mFatherViewHeight
        this.mContext = context
        this.keepScreenOn = true
        this.index=num
        initPaint2(Color.WHITE)
        //根据选中状态设置默认颜色
        /*if(ParamsUtils.listLocation[index].check==1){
            initPaint2(resources.getColor(R.color.tv_color_00))
        }else{
            initPaint2(Color.WHITE)
        }*/
        isFocusable = true
        isFocusableInTouchMode = true
        setOnTouchListener(this)
    }

    fun initPaint2(color:Int) {
        /**
         * 默认边框
         */
        mPaintK = Paint()
        mPaintK!!.isAntiAlias = antiAlias
        // 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
        mPaintK!!.style = Paint.Style.STROKE
        // 设置画笔粗细
        mPaintK!!.strokeWidth = 2 * mArcWidth
        mPaintK!!.color = color
        mPaintK!!.textAlign = Paint.Align.CENTER
        /**
         * 四个角
         */
        mPaintAngel = Paint()
        mPaintAngel!!.isAntiAlias = antiAlias
        // 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
        mPaintAngel!!.style = Paint.Style.STROKE
        // 设置画笔粗细
        mPaintAngel!!.strokeWidth = 4 * mArcWidth
        mPaintAngel!!.color = color
        mPaintAngel!!.textAlign = Paint.Align.CENTER
    }


    /**
     * 设置对应图形信息
     */
    fun setGraphContext(width: Int, height: Int) {
        viewWidth = (width - 20).toInt()
        viewHeight = (height - 20).toInt()
        // 根据屏幕大小绘制
        centerIndexX = aviewWidth / 2
        centerIndexY = aviewHeight / 2
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        setGraphContext(width, height)
        drawRectangular(canvas)
    }


    /**
     * 绘制矩形
     */
    fun drawRectangular(canvas: Canvas?) {
        //绘制左边矩形
        val pathB = Path()
        pathB.moveTo(
            (centerIndexX + viewWidth / 2).toFloat(),
            (centerIndexY - viewHeight / 2).toFloat()
        )//起点  暂定右上角为起点
        pathB.lineTo(
            (centerIndexX - viewWidth / 2).toFloat(),
            (centerIndexY - viewHeight / 2).toFloat()
        )//左上角
        pathB.lineTo(
            (centerIndexX - viewWidth / 2).toFloat(),
            (centerIndexY + viewHeight / 2).toFloat()
        )//左下角
        pathB.lineTo(
            (centerIndexX + viewWidth / 2).toFloat(),
            (centerIndexY + viewHeight / 2).toFloat()
        )//右下角
        pathB.close() // 使这些点构成封闭的多边形
        canvas!!.drawPath(pathB, mPaintK!!)
        //绘制左边矩形
        val pathS = Path()
        pathS.moveTo(
            (centerIndexX + viewWidth / 2 - interval).toFloat(),
            (centerIndexY - viewHeight / 2 + interval).toFloat()
        )//起点  暂定右上角为起点
        pathS.lineTo(
            (centerIndexX - viewWidth / 2 + interval).toFloat(),
            (centerIndexY - viewHeight / 2 + interval).toFloat()
        )//左上角
        pathS.lineTo(
            (centerIndexX - viewWidth / 2 + interval).toFloat(),
            (centerIndexY + viewHeight / 2 - interval).toFloat()
        )//左下角
        pathS.lineTo(
            (centerIndexX + viewWidth / 2 - interval).toFloat(),
            (centerIndexY + viewHeight / 2 - interval).toFloat()
        )//右下角
        pathS.close() // 使这些点构成封闭的多边形
        canvas!!.drawPath(pathS, mPaintK!!)
        /**
         * 四个角边框
         */
        //右上角边框
        canvas!!.drawLine(
            (centerIndexX + viewWidth / 2 + angelinterval).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval).toFloat(),
            (centerIndexX + viewWidth / 2 + angelinterval - angelWidth).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval).toFloat(),
            mPaintAngel!!
        )
        canvas!!.drawLine(
            (centerIndexX + viewWidth / 2 + angelinterval).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval).toFloat(),
            (centerIndexX + viewWidth / 2 + angelinterval).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval + angelWidth).toFloat(),
            mPaintAngel!!
        )
        //左上角边框
        canvas!!.drawLine(
            (centerIndexX - viewWidth / 2 - angelinterval).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval).toFloat(),
            (centerIndexX - viewWidth / 2 - angelinterval + angelWidth).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval).toFloat(),
            mPaintAngel!!
        )
        canvas!!.drawLine(
            (centerIndexX - viewWidth / 2 - angelinterval).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval).toFloat(),
            (centerIndexX - viewWidth / 2 - angelinterval).toFloat(),
            (centerIndexY - viewHeight / 2 - angelinterval + angelWidth).toFloat(),
            mPaintAngel!!
        )
        //左下角边框
        canvas!!.drawLine(
            (centerIndexX - viewWidth / 2 - angelinterval).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval).toFloat(),
            (centerIndexX - viewWidth / 2 - angelinterval).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval - angelWidth).toFloat(),
            mPaintAngel!!
        )
        canvas!!.drawLine(
            (centerIndexX - viewWidth / 2 - angelinterval).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval).toFloat(),
            (centerIndexX - viewWidth / 2 - angelinterval + angelWidth).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval).toFloat(),
            mPaintAngel!!
        )
        //右下角
        canvas!!.drawLine(
            (centerIndexX + viewWidth / 2 + angelinterval).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval).toFloat(),
            (centerIndexX + viewWidth / 2 + angelinterval).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval - angelWidth).toFloat(),
            mPaintAngel!!
        )
        canvas!!.drawLine(
            (centerIndexX + viewWidth / 2 + angelinterval).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval).toFloat(),
            (centerIndexX + viewWidth / 2 + angelinterval - angelWidth).toFloat(),
            (centerIndexY + viewHeight / 2 + angelinterval).toFloat(),
            mPaintAngel!!
        )
    }

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        val action = event!!.action
        if (action == MotionEvent.ACTION_DOWN) {
            downX = event.x.toInt()
            downY = event.y.toInt()
            downRawX = event.rawX.toInt()
            downRawY = event.rawY.toInt()
            originalLeft = v!!.left
            originalBottom = v!!.bottom
            originalTop = v!!.top
            originalRight = v!!.right
            direction = getDirection(v, downX, downY)
            Log.i(
                "Move3==",
                "downRawX==" + downRawX + "downRawY==" + downRawY + "originalBottom==" + originalBottom + "originalRight==" + originalRight
            )
            Choose()//选中变色
        }
        //处理拖动事件
        contactDrag(v!!, event, action)
        invalidate()
        return true
    }

    override fun isFocused(): Boolean {
        Log.i("isFocused==",super.isFocused().toString())
        return super.isFocused()
    }
    /**
     * 获取触摸位置
     */
    fun getDirection(v: View, x: Int, y: Int): String {
        var reDrawRatio = 0.3
        var reDrawX = (aviewWidth * reDrawRatio).toInt()
        var reDrawY = (aviewHeight * reDrawRatio).toInt()
        if ((x in 0..reDrawX) && (y in 0..reDrawY)) {
            return "Left_Top"
        }
        if ((x in 0..reDrawX) && (y in (aviewHeight - reDrawY)..aviewHeight)) {
            return "Left_Bottom"
        }
        if ((x in (aviewWidth - reDrawX)..aviewWidth) && (y in 0..reDrawY)) {
            return "Right_Top"
        }
        if ((x in (aviewWidth - reDrawX)..aviewWidth) && (y in (aviewHeight - reDrawY)..aviewHeight)) {
            return "Right_Bottom"
        }
        return "Center"
    }

    /**
     * 处理拖动事件
     */
    fun contactDrag(v: View, event: MotionEvent, action: Int) {
        if (action == MotionEvent.ACTION_MOVE) {
            val dx: Int = event.rawX.toInt() - downRawX
            val dy: Int = event.rawY.toInt() - downRawY
            Log.i(
                "Move3==",
                "rawX==" + event.rawX.toInt() + "rawY==" + event.rawY.toInt() + "dx==" + dx + "dy==" + dy
            )
            when (direction) {
                "Center" -> {
                    center(event.x.toInt(), event.y.toInt(), v)
                }
                "Left_Top" -> {//左上角
                    left_Top(dx, dy, v)
                }
                "Left_Bottom" -> {//左下角
                    left_Bottom(dx, dy, v)
                }
                "Right_Top" -> {//右上角
                    right_Top(dx, dy, v)
                }
                "Right_Bottom" -> {//右下角
                    right_Bottom(dx, dy, v)
                }
            }
            downRawX = event.rawX.toInt()
            downRawY = event.rawY.toInt()
            Log.i("Move3==", "(contactDrag)downRawX==" + downRawX + "downRawY==" + downRawY)
            if (direction != "Center" ) {
                v.layout(originalLeft, originalTop, originalRight, originalBottom)
                //保存位置数据1
               /* ParamsUtils.listLocation[index].top=originalTop
                ParamsUtils.listLocation[index].left=originalLeft*/
                //保存位置数据2
                val params: FrameLayout.LayoutParams = layoutParams as FrameLayout.LayoutParams
                params.leftMargin = originalLeft
                params.topMargin = originalTop
                layoutParams = params
            }


        }


    }

    /**
     * 拖动
     */
    fun center(x: Int, y: Int, v: View) {
        //计算移动的距离
        var moveX: Int = x - downX
        var moveY: Int = y - downY
        if (isMove) {
            var l = v.left + moveX
            var r = l + aviewWidth
            var t = v.top + moveY
            var b = t + aviewHeight
            //划出左边边界
            if (l < 0) {
                l = 0
                r = l + aviewWidth
            }
            //划出右边边界
            if (r > mFatherViewWidth) {
                r = mFatherViewWidth
                l = r - aviewWidth
            }
            //划出上边边界
            if (t < 0) {
                t = 0
                b = t + aviewHeight
            }
            //划出下边边界
            if (b > mFatherViewHeight) {
                b = mFatherViewHeight
                t = b - aviewHeight
            }
            v.layout(l, t, r, b)
            Log.i(
                "Move==",
                "neft==" + l + "ntop==" + t + "nright==" + r + "nbottom==" + b
            )
           /* //保存位置数据1
            ParamsUtils.listLocation[index].top=originalTop
            ParamsUtils.listLocation[index].left=originalLeft*/
            //保存位置数据2
            val params: FrameLayout.LayoutParams = layoutParams as FrameLayout.LayoutParams
            params.leftMargin = left
            params.topMargin = top
            layoutParams = params
        }
    }

    fun left_Top(moveX: Int, moveY: Int, v: View) {
        //最小区域限制
        if (originalBottom - (originalTop + moveY) > 100) {
            originalTop += moveY
        }
        if (originalRight - (originalLeft + moveX) > 100) {
            originalLeft += moveX
        }
    }

    fun left_Bottom(moveX: Int, moveY: Int, v: View) {
        if ((originalBottom+moveY) - originalTop  > 100) {
            originalBottom += moveY
        }

        if (originalRight - (originalLeft + moveX) > 100) {
            originalLeft += moveX
        }
        limitMastArea()
    }

    fun right_Top(moveX: Int, moveY: Int, v: View) {
        if (originalBottom - (originalTop + moveY) > 100) {
            originalTop += moveY
        }
        if ((originalRight + moveX) - originalLeft > 100) {
            originalRight += moveX
        }
        limitMastArea()
    }

    fun right_Bottom(moveX: Int, moveY: Int, v: View) {
        if ((originalBottom+moveY) - originalTop  > 100) {
            originalBottom += moveY
        }
        if ((originalRight + moveX) - originalLeft > 100) {
            originalRight += moveX
        }
        limitMastArea()
        Log.i("Move3==", "originalBottom==" + originalBottom + "originalRight==" + originalRight)
    }

    /**
     *  限制最大区域
     */
    fun limitMastArea() {
        if (originalTop < 0) {
            originalTop = 0
        }
        if (originalLeft < 0) {
            originalLeft = 0
        }
        if (originalBottom > mFatherViewHeight) {
            originalBottom = mFatherViewHeight
        }
        if (originalRight > mFatherViewWidth) {
            originalRight = mFatherViewWidth
        }
    }


    /**
     * 选中
     */
    fun Choose() {
        //重置选中paint
        mPaintK!!.reset()
        //把画笔颜色设置为透明,重绘
        mPaintK!!.isAntiAlias = antiAlias
        // 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
        mPaintK!!.style = Paint.Style.STROKE
        // 设置画笔粗细
        mPaintK!!.strokeWidth = 2 * mArcWidth
        mPaintK!!.color = resources.getColor(R.color.tv_color_00)
        mPaintK!!.textAlign = Paint.Align.CENTER
        //重置选中paint
        mPaintAngel!!.reset()
        mPaintAngel!!.isAntiAlias = antiAlias
        // 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
        mPaintAngel!!.style = Paint.Style.STROKE
        // 设置画笔粗细
        mPaintAngel!!.strokeWidth = 4 * mArcWidth
        mPaintAngel!!.color = resources.getColor(R.color.tv_color_00)
        mPaintAngel!!.textAlign = Paint.Align.CENTER
        invalidate()
        //设置选中标记
       /* ParamsUtils.listLocation.forEach {
            it.check=0
        }
        ParamsUtils.listLocation[index].check=1
        EventBus.getDefault().post(PayEven("改变选中状态"))*/
    }
    // 设置回调接口
    fun setRudderListener(rockerListener: RudderListener?) {
        listener = rockerListener
    }

    // 回调接口
    interface RudderListener {
        fun onSteeringWheelChanged(
            cross: Float,
            longitudinal: Float
        )

        fun isChoose(isChoose: Boolean)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(
            aviewWidth,
            aviewHeight
        )
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        aviewWidth = w
        aviewHeight = h
    }



}

类代码:

var recnum =0
    /**
     * 添加矩形
     */
    fun addRectangle() {
        if (recnum < 3) {
            var RectangleView =
                MyRectangleView(this, waiWidth, waiHeight, recnum)
            RectangleView.id = recnum
            li_wai.addView(RectangleView)
            recnum++
        }
    }

遇到问题
(一)在实现重绘的时候,走了点弯路,我一开始朝着

/**
 *在view 中调用requestLayout();view会重新计算大小,
 *也就是会重新执行onMeasure 方法。在onMeasure中调用
 *setMeasuredDimension(tempWidth, tempHeight); 就会重新设定view的大小
**/
requestLayout();

的方向走,发现通过该方法的确能修改View背景画布的大小,但是放在我的代码里面,重绘的时候并没有改变画布上绘制矩形的尺寸,我又在requestLayout后重新调用了onDraw()里面的方法重新绘制矩形,运行出来发现异常卡顿,之后又换了思路使用v.layout(l, t, r, b)+ invalidate()的方法,发现异常顺利。 invalidate()调用后,会重新走onDraw()的方法。
(二)这个功能是同事项目里面的一个小功能,我试图自己来试着实现,这个功能里面还有一个我没实现的就是,这个矩形可以点击按钮动态添加,点击矩形选中变色,点击页面空白处矩形取消选中,页面多个矩形时候,只能选中一个,因为自定义VIew正在学习中,这个功能我暂时还没想到简单的实现方法,我暂时想到的方法就是:添加tag的方法,将矩形位置数据保存起来,每次点击后发送通知,父布局收到通知后removeAllViews(),然后再根据保存的位置数据重新addView()并根据保存的 tag数据设置是否变色,不知有思路的大佬可否指点一二(笑脸)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值