【Android 需求方案实现】 在屏幕上画一条线的时候,如何把这条线经过的所有坐标点(高效&有序&完整)地发送出去?

本文介绍了如何在电子签名场景中实现用户签名轨迹的实时传输,包括定义接口获取down和move事件的坐标、处理触摸事件、使用补偿算法填充虚线为实线的过程,以及如何在多线程环境下确保坐标点的准确发送。
摘要由CSDN通过智能技术生成

需求背景

买东西付款的时候,或者银行一些业务办理的时候,我们常常需要签名。目前大多数场景都是电子签名。
我们需要拿到当前用户签名的轨迹,然后把签名的轨迹实时的发送到另外一台机器上。从而让工作人员实时看到对方签名的实况。
那么这个业务怎么实现呢?

过来看看实现它的完整思路!

定义接口,获取down和move事件获取到的坐标信息

/**
 * 监听签名坐标
 */
public interface ICoordinatesListener {
    /** 
     * click down的坐标
     */
    void getDownCoordinates(int x, int y);

    /**
     * touch move的坐标
     */
    void getCoordinates(int x,int y);
}

获取触摸事件MotionEvent

获取触摸事件,这是很自然的做法,把触摸事件的拿到的坐标点传递进到我们定义的接口中来。
对应的系统方法是,下面是伪代码,可以根据你项目实际需求进行更精细的控制:

@Override
    public boolean onTouchEvent(MotionEvent event) {
    	if(down){
    		 getDownCoordinates(int x, int y)
    	}
    	if(move){
			 getCoordinates(int x,int y);
		}
    }

需要注意的是,由于不同屏幕有不同的像素密度和尺寸,实际上获取到的x,y轴的坐标点可能会和你画板绘制时候拿到的view的宽高对不上,需要进行一定的转换。

对拿到的x、y坐标进行处理

我们实现接口的匿名内部类,作为监听器传递到画板View中去。当onTouchEvent来数据的时候,我们并可以拿到坐标信息。

可以脑海里想象一下,当我们画一条线,是不是一定有先点击,就是down的动作,然后我们手指移动,就是move的动作。

我们在代码中,先记录down的坐标点,

  /**
     * 获取当前点击down的坐标
     */
    public void setTouchDownCoordinate(int touchDownX, int touchDownY) {
        preX = touchDownX;
        preY = touchDownY;
    }

接着发送move的坐标点:

  public void sendSignatureCoordinates(int xCoordinate, int yCoordinate) {
       //待实现
    }

接着先把监听器函数实现了:

 binding.sign.setCoordinatesListener(new ICoordinatesListener() {
            @Override
            public void getDownCoordinates(int x, int y) {
                System.out.println("down coordinate :x = " + x + " y = " + y);
                getSignature().setTouchDownCoordinate(x, y);
            }

            @Override
            public void getCoordinates(int x, int y) {
                System.out.println("move coordinate :x = " + x + " y = " + y);
                getSignature().sendSignatureCoordinates(x, y);
            }
        });

核心点在于sendSignatureCoordinates的实现。

发送实时坐标点

我们知道发送坐标数据,这是一个相对耗时的操作。更何况,当我们快速画线,并且大量快速画线的时候,想要接受对方高性能的获取准确的坐标点的话,是需要考虑一下方案的。

我们在主线程做这事情肯定不行,这然造成卡顿,甚至ARN。那么新开一个子线程呢?
效率也不高,众所周知,多线程并发是提高性能的好办法。

那么我们就使用线程池来实现:

   public void sendSignatureCoordinates(int xCoordinate, int yCoordinate) {
        ThreadPoolManager.getInstance().runInBackground(new Runnable() {
            @Override
            public void run() {
               	发送坐标点操作
            }
        });
    }

需要注意是发送坐标点的时候,为了避免多线程造成线程抢占的问题。我们需要在合适的地方加个锁:

   public void sendSignatureCoordinates(int xCoordinate, int yCoordinate) {
        ThreadPoolManager.getInstance().runInBackground(new Runnable() {
            @Override
            public void run() {
            同步锁(){
				发送坐标点操作
			}
             	
            }
        });
    }

跑一下程序,通过日志观察,我们得知坐标点按照绘制的先后顺序有序的发送到目标终端了,速度也还不错。

到这里我们会以为大功告成了。
结果目标终端实时绘制了我们发送过去的坐标点,发现画到线条变成了虚线。

我们获取到的坐标点是从系统触摸事件中拿到的,我们看到自己终端画线并发虚线,而是实线。为什么发送过去就是虚线了呢?

看下画板的实现代码就可以知道,画板画线的实现是通过贝塞尔曲线来实现的。3个坐标点即可连成一条实线。

所以目标终端需要实时显示实线有两个方案:

  • 方案1:也通过贝塞尔曲线实现
  • 方案2:我们虚线的坐标集合自动填充为实线,需要添加确实的坐标点。

考虑到目标终端需要的是完全实线的坐标点集合,我这边直接采取方案2。

算法实现:把虚线通过补偿算法填充为实线

当场手撸了一个算法,如下:


   private int preX;
    private int preY;
/**
     * 通过算法补全当快速滑动时候touchEvent没法捕抓的,漏掉的点,防止发送的线段缺失一个个的point
     * 思路:
     * 1.记录前一个坐标,记录为:preX坐标,preY坐标,后一个坐标记录为:x,y
     * 2.记录前后两个坐标的差值:dvalueX = x - preX,dvalueY = y - preY
     *
     * @param x                 x坐标
     * @param y                 y坐标
     * @param compensationValue 补偿值,
     */
    private synchronized void fillWithEmptyPoints(int x, int y,
            int compensationValue) {
        //发送第一个当前获取到的坐标
        /*System.out.printf("--------begin--------- (%d :%d)  (%d :%d)\n", preX, preY, x, y);*/

        //算法补偿
        while (true) {
            int dValueX = Math.abs(x - preX);
            int dValueY = Math.abs(y - preY);
            /*System.out.println("dValueX = " + dValueX + " dValueY = " + dValueY);*/

            //假如用户画点,退出循环,假如当前没有漏点,退出循环
            if (dValueX > compensationValue || dValueY > compensationValue
                    || (dValueX <= 1 && dValueY <= 1)) {
                preX = x;
                preY = y;
                /*System.out.printf("send(%d,%d)\n", preX, preY);*/
                sendRealtimeCoordinate(preX, preY);
                break;
            }

            if (dValueX > dValueY) {
                if (x > preX) {
                    ++preX;
                } else {
                    --preX;
                }
            } else if (dValueX < dValueY) {
                if (y > preY) {
                    ++preY;
                } else {
                    --preY;
                }
            } else {
                if (x > preX) {
                    ++preX;
                } else {
                    --preX;
                }

                if (y > preY) {
                    ++preY;
                } else {
                    --preY;
                }
            }
            /*System.out.printf("send(%d,%d)\n", preX, preY);*/
            sendRealtimeCoordinate(preX, preY);
        }
        /*System.out.println("--------end----------");*/
    }

说说这个算法的使用方法,入参传入了当前手指触摸到并相应的x,y坐标点,compensationValue作为填充系数,我们根据不同的屏幕像素密度和尺寸等信息,填入不同的系数。需要同时兼顾到目标终端拿到的实线坐标集合的效果和性能表现。根据测试效果调整即可。

思路很简单,我们实时绘制的时候,首先会有一个落脚点,拿到它,我们移动的时候,可能会直接拿到坐标点(0,0)和坐标点(10,10),这样的话我们看着就是很明显的虚线了,我们计算两个点距离的绝对值,假如绝对值大于入参compensationValue的话,那么就需要补偿发送缺失的坐标点。
从而达到补偿的效果。

需要注意的是,我们由于使用了多线程,需要做好线程同步。

完成

测试,从目标终端可以看到很不错的实时签名效果。

以上便是实现方案的完整内容了。如有什么疑问,可以评论区交流讨论。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林树杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值