Android 模拟一个电子画板同屏的效果

吴松
2023-12-01

年初的时候刚把去年底的一个电子书包的项目完结了,这不刚过了几天又收到了boss新的需求:添加一个电子白板,要求老师端绘画的时候,学生端的屏幕也能看到老师端的操作。

难点:

        1.画板的写法

         2.如何推送数据到学生端,该推送什么格式的数据

         3.学生端收到数据该如何绘制。


思路:

         1.画板的写法ok,网上一大堆

         2.老师端绘制的时候,绘制的每一个操作,又都横纵坐标记录,我只要推送这些记录给学生端,让学生端再根据             坐标绘制

         3.依然采取1.0的方案,客户端用socket(tcp)服务器端用(walkman)方式推送数据(已解决)


方案:

         1.画板采用github上星星最多的AndroidDrawingView,前人种树好乘凉,我的建议是最好下载源码自己先看一遍他的思路,当然这里我早就发现这个demo完全契合我的项目。这个demo竟然已经为你扩展了如何实现同屏。请看

DrawingView.java里的这个接口:

 /**
     * 绘制代理,通知状态变更和获取数据
     */
    public interface DrawingStepDelegate {
        /**
         * 当前绘制step创建时回调,通常用于远程同步
         * step处于变化状态
         *
         * @param drawingView 当前view
         * @param step        当前绘制step,任意修改此step可能导致错误
         */
        void onDrawingStepBegin(DrawingView drawingView, DrawingStep step);

        /**
         * 当前绘制step变更时回调,每次touch绘制都会执行,text图层修改内容也会执行,此回调执行频繁,通常用于远程同步
         * step处于变化状态
         *
         * @param drawingView 当前view
         * @param step        当前绘制step,任意修改此step可能导致错误
         */
        void onDrawingStepChange(DrawingView drawingView, DrawingStep step);

        /**
         * 当前绘制状态已改变,绘制一笔或进行撤销/重做/清空等变更记录数据的操作都会触发此回调
         * step已经完成
         *
         * @param drawingView 当前view
         * @param step        当前绘制step,任意修改此step可能导致错误
         */
        void onDrawingStepEnd(DrawingView drawingView, DrawingStep step);

        /**
         * 当前step撤销
         *
         * @param drawingView 当前view
         * @param step        当前绘制step,任意修改此step可能导致错误
         */
        void onDrawingStepCancel(DrawingView drawingView, DrawingStep step);
    }

很明显,当我在DrawingView上绘图时会触发这个接口里的四个方法,我想这里就应该是我想要的坐标数据,我只要推送这些数据就行了,那么问题来了,现在我推送只能推送json数据,接收的时候再转成我想要的类,那么DrawingStep如何才能变json呢,点到DrawingStep类里你就回恍然大悟,才会发现此人早就替你想好这一点。原来这个类本身就是个json解析类。他的思路应该是这样的,绘画的时候每一步记录坐标(JSon数据)——解析成DrawingStep——然后drawingView根据DrawingStep绘制,DrawingData存放DrawingStep的集合。既然它是个解析Json的类,那么自然有类转JSon的方法


    /**
     * 复制step
     * @return 复制的step
     */
    public DrawingStep copy() {
        return new Json<>(this.getClass()).modelFromJson(this.toJson());
    }

那么现在就差与服务器约定 命令的请求头,将这坐标数据发送出去

 dv.setDrawingStepDelegate(new DrawingView.DrawingStepDelegate() {
            @Override
            public void onDrawingStepBegin(DrawingView drawingView, DrawingStep step) {

                //drawingView.getDrawingData().addDrawingStep(step);
                Logger.i("画板操作onDrawingStepBegin::"+step.toJson().toString());
                SendOrderBean sendOrderBean=new SendOrderBean("userSendPositionRequest",JSON.toJSONString(new UserSendPositionRequest("4450187",step.toJson().toString())),System.currentTimeMillis()+"");
                singleSocket.sendMessage(JSON.toJSONString(sendOrderBean));



            }

            @Override
            public void onDrawingStepChange(DrawingView drawingView, DrawingStep step) {
                Logger.i("画板操作onDrawingStepChange::"+step.toJson().toString());
                SendOrderBean sendOrderBean=new SendOrderBean("userSendPositionRequest",JSON.toJSONString(new UserSendPositionRequest("4450187",step.toJson().toString())),System.currentTimeMillis()+"");
                singleSocket.sendMessage(JSON.toJSONString(sendOrderBean));
            }

            @Override
            public void onDrawingStepEnd(DrawingView drawingView, DrawingStep step) {
                Logger.i("画板操作onDrawingStepEnd::"+step.toJson().toString());

                SendOrderBean sendOrderBean=new SendOrderBean("userSendPositionRequest",JSON.toJSONString(new UserSendPositionRequest("4450187",step.toJson().toString())),System.currentTimeMillis()+"");
                singleSocket.sendMessage(JSON.toJSONString(sendOrderBean));
            }

            @Override
            public void onDrawingStepCancel(DrawingView drawingView, DrawingStep step) {
                Logger.i("画板操作onDrawingStepCancel::"+step.toJson().toString());

                SendOrderBean sendOrderBean=new SendOrderBean("userSendPositionRequest",JSON.toJSONString(new UserSendPositionRequest("4450187",step.toJson().toString())),System.currentTimeMillis()+"");

                singleSocket.sendMessage(JSON.toJSONString(sendOrderBean));
            }
        });

数据发送出去了,学生端该接受数据:那么问题又来了,画板该如何跟据坐标绘制呢,让我们看drawingView里的方法:

/**
     * 在当前绘制基础上增加绘制传入的step,如果传入的step与当前未完成的step是同一step(编号相同,远程同步step可能有未完成和已完成两种状态),更新当前step
     *
     * @param step 将要绘制的step
     */
    public void drawNextStep(@NonNull DrawingStep step) {
        step.setRemote(true);
        if (step.getStep() != getCurrentDrawingStep().getStep()) {
            endUnfinishedStep();
            getDrawingData().addDrawingStep(step);
        } else {
            getDrawingData().replaceDrawingStep(step);
        }

        if (step.isCanceled()) {
            internalCancelCurrentStep();
            getDrawingData().cancelDrawingStep();
        } else {
            internalUpdateCurrentStep(false);
        }
    }

    /**
     * 在当前绘制基础上增加绘制传入的step,此step必须是stepOver状态
     * 在远程同步绘制时,采用低频同步,仅在每一步绘制完成后同步时调用此方法
     * 不可与{@link #drawNextStep(DrawingStep)}同时使用
     *
     * @param step 将要绘制的step
     */
    public void drawNextOverStep(@NonNull DrawingStep step) {
        if (!step.isStepOver()) {
            return;
        }

        getDrawingData().addDrawingStep(step);

        if (step.isCanceled()) {
            return;
        }

        internalUpdateCurrentStep(true);
    }
那么接下来就是soeasy了:

 private void initBroadCastReceiver() {

        IntentFilter filter = new IntentFilter();
        filter.addAction("SOCKET_MESSAGE");
        if (receiver==null){
            receiver=new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {

                    if (intent.getAction().equals("SOCKET_MESSAGE")) {


                        Bundle data = intent.getBundleExtra("data");
                        String msg = data.getString("message", "none");
                        Log.i("服务器返回信息::", msg);

                        try {
                            ReceiveOrderBean receiveOrderBean = JSON.parseObject(msg, ReceiveOrderBean.class);

                            switch (receiveOrderBean.getType()) {
                                case "userSendPositionReceive":
                                    Toast.makeText(TestActivity.this,"获取到坐标数据",Toast.LENGTH_SHORT).show();
                                   // Logger.i("获取到坐标数据"+receiveOrderBean.getData());
                                    DrawingStep drawingStep = new Json<>(DrawingStep.class).modelFromJsonString(receiveOrderBean.getData());

                                    Logger.i("获取到坐标数据"+drawingStep.toJson().toString());

                                    dv.drawNextStep(drawingStep);
                                    dv.drawNextOverStep(drawingStep);
//                                    dv.getDrawingData().addDrawingStep(drawingStep);
//                                    dv.refreshWithDrawingData(dv.getDrawingData());
                                    break;
                            }

                        } catch (com.alibaba.fastjson.JSONException ex) {
                            Log.i("命令解析出错", msg + "");

                        }

                    }

                }
            };
            App.localBroadcastManager.registerReceiver(receiver, filter);
        }
    }

附页:学生端应该是看老师端操作,自己不能操作,所以要设置:

   dv.setDisableTouchDraw(true);//true是设置不能画,false反之

问题:已丢包的形式传递一些命令数据好像是可以,比如开关机之类的,但如果要实现类似于直播,屏幕的完全监控好像有点力不从心,可能与流媒体有关,这也是我需要不断学习的地方。










  



 类似资料: