美文网首页
安卓黑板功能实现

安卓黑板功能实现

作者: 叫我壮士 | 来源:发表于2022-02-18 17:07 被阅读0次

    前两年的疫情爆发让线上教育特别火,很多公司选择线上办公,很多学校线上教学,不过后来受政策影响很大。这些机构学校依赖的线上教育软件功能强大又繁杂,功能不仅有基础的书写板书,还能添加图片,打字、播放视频、3D模型…作为安卓程序员要实现那些功能可能需要很多多的时间和精力。
    废话不多说,那么本人这次分享一下这黑板的基本实现。包含的功能也不多,包含了手绘,输入文字,选择图层,删除,图片缩放,添加小黑板,缩放、拖拽、删除小黑板,混动整个黑板(大概一个屏幕的50倍高度),每个功能对应下面按钮介绍。如图


    黑板功能图

    实现思路实是这样的,每个元素是用图层来实现的,每一个元素生成一张图片,这样才能拖动图片到另一张图片上,因为使用的是图片,所以这其中会有稍许频繁的图片操作,并不是很友好,元素内容用hashmap来管理,图片拖动是用ViewDragHelper来管理。
    既然是黑板。那么自定义view流程还是要稍微熟悉的,这些不概述了。

    • 创建图层
    /**
    
    * 创建图层并添加到BoardLayout
    
    */
    
    private void createImageView() {
    
    creatBitmap();
    
        try {
    
    Bitmap newbm =null;
    
            if(board.action==BoardAction.ACTION_DRAW){
    
    cacheCanvas.drawPath(path,paint);
    
                cacheCanvas.save();
    
                cacheCanvas.restore(); // 存储
    
                //裁剪出需要保存的bitmap坐标和高宽需要加上画笔的宽度。不然会裁切到线条.*2表示左右两边都需要加线条宽度
    
                newbm = Bitmap.createBitmap(cacheBitmap, Math.max((int) ((int)minX-paint.getStrokeWidth()),0),Math.max((int) ((int)minY-paint.getStrokeWidth()),0),
    
                        (int) ((int)(maxX-Math.max(minX,0))+paint.getStrokeWidth()*2),(int) ((int)(maxY-Math.max(minY,0))+paint.getStrokeWidth()*2));
    
            }else if(board.action==BoardAction.ACTION_DRAW_TEXT){
    
    cacheCanvas.drawText(drawText,preX,preY,paint);
    
                cacheCanvas.save();
    
                cacheCanvas.restore(); // 存储
    
                //裁剪出需要保存的bitmap坐标和高宽需要加上画笔的宽度。不然会裁切到线条.*2表示左右两边都需要加线条宽度
    
                newbm = Bitmap.createBitmap(cacheBitmap, (int)minX,(int)minY,
    
                        (int) ((int) (maxX-minX)+paint.getStrokeWidth()*2),(int)(maxY-minY));
    
            }
    
    //向当前view中添加图片
    
            if(minX==0&&minY==0&&maxX==0&&maxY==0){
    
    }else{
    
    DragScaleView imageView=new DragScaleView(getContext());
    
                imageView.setImageBitmap(newbm);
    
                int l=Math.max((int) ((int)minX-paint.getStrokeWidth()),0);
    
                int t=Math.max((int) ((int)minY-paint.getStrokeWidth()),0);
    
                ViewInfo info=board.setViewInfo(l,t,l+newbm.getWidth(),t+newbm.getHeight(),newbm.getWidth(),newbm.getHeight());
    
                if(board.action==BoardAction.ACTION_DRAW_TEXT){//输入的是文字需要记录文字内容和按下的xy坐标
    
                    info.setContent(drawText);
    
                    info.setPreX(preX);
    
                    info.setPreY(preY);
    
                    info.setType(ViewType.VIEW_TEXT);
    
                    imageView.setOnClickListener(this);
    
                }else if(board.action==BoardAction.ACTION_DRAW){
    
    info.setType(ViewType.VIEW_DRAW);
    
                }
    
    board.addDragChildView(imageView,info);
    
                addView(imageView,smallBoardNum<=0?-1:getChildCount()-smallBoardNum);//如果没有小黑板就添加到小黑板的下面
    
                if(board.action==BoardAction.ACTION_DRAW_TEXT){//设置文字为选中状态
    
                    board.chooseView(imageView);
    
                }
    
    }
    
    setMmPoint(0,0,0,0);
    
            cacheBitmap=null;
    
        }catch (Exception e){
    
    Log.e("黑板创建图层错误",e.toString());
    
        }
    
    }
    /**
    
    * 创建bitmap以便裁切
    
    */
    
    private void creatBitmap() {
    
    int view_height=board.boardHight+marginTop;//加上偏移量
    
        // 创建一个与该View相同大小的缓存区
    
        cacheBitmap = Bitmap.createBitmap(board.boardWidth, view_height,Bitmap.Config.ALPHA_8);
    
        cacheCanvas =new Canvas(cacheBitmap);
    
    }
    
    • 按钮1
      选择功能主要是为了框选黑板上的元素内容,以便拖拽删除放大如图
      框选图
      ViewDragHelper捕获到某个元素后设置选中框,拖动时再将选中的元素一起移动,大致代码如下
    dragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
    
    /**
    
        * 是否捕获childView:
    
        * 如果viewList包含child,那么捕获childView
    
        * 如果不包含child,就不捕获childView
    
    */
    
        @Override
    
        public boolean tryCaptureView(View child, int pointerId) {
    
    if(board.action== BoardAction.ACTION_DRAW||board.action==BoardAction.ACTION_DRAW_TEXT||board.action==BoardAction.ACTION_MOVE){//绘制模式和文字模式
    
                return false;
    
            }else{
    
    return board.containsView(child);
    
            }
    
    }
    
    @Override
    
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
    
    super.onViewPositionChanged(changedView, left, top, dx, dy);
    
            for (Map.Entry entry :board.viewsMap.entrySet()) {
    
    ViewInfo info=entry.getValue();
    
                if(info.isChoose()){
    
    View view = entry.getKey();
    
                    info.setLeft(info.getLeft()+dx);
    
                    info.setTop(info.getTop()+dy);
    
                    info.setRight(info.getRight()+dx);
    
                    info.setBottom(info.getBottom()+dy);
    
                    if(info.getType()== ViewType.VIEW_TEXT){//如果View是文字内容需要重置PreX和PreY
    
                        info.setPreX(info.getPreX()+dx);
    
                        info.setPreY(info.getPreY()+dy);
    
                    }
    
    board.viewsMap.put(view,info);
    
                    if(view!=changedView){//非当前按下的view也需要移动
    
                        view.layout(info.getLeft(),info.getTop(), info.getRight(), info.getBottom());
    
                    }
    
    }
    
    }
    
    }
    
    /**
    
        * 当捕获到child后的处理:
    
        * 获取child的监听
    
        */
    
        @Override
    
        public void onViewCaptured(View capturedChild, int activePointerId) {
    
    super.onViewCaptured(capturedChild, activePointerId);
    
            jumpDraw=true;
    
            //遍历map,如果按下的view在选中状态的时候。不需要重新设置选中的view。该操作是在拖动view了
    
            for (Map.Entry entry :board.viewsMap.entrySet()) {
    
    ViewInfo info=entry.getValue();
    
                if(!(entry.getKey()instanceof DragBoardView)){
    
    DragScaleView view= (DragScaleView) entry.getKey();
    
                    view.setCanTouch(false);//遍历禁止View可以操作
    
                    if(!info.isChoose()&&capturedChild==entry.getKey()){
    
    //得到一个被选中的View,该View可以实现缩放
    
                        board.setScaView(info.getType()==ViewType.VIEW_IMAGE?(DragScaleView) capturedChild:null);
    
                        board.setScaViewInfo(info);
    
                        board.chooseView(capturedChild);
    
                    }
    
    }
    
    }
    
    onDragDrop(true);
    
        }
    
    /**
    
        * 当释放child后的处理:
    
        * 取消监听,不再处理
    
        */
    
        @Override
    
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
    
    super.onViewReleased(releasedChild, xvel, yvel);
    
            jumpDraw=false;
    
            onDragDrop(false);
    
        }
    
    /**
    
        * 当前view的left
    
    */
    
        @Override
    
        public int clampViewPositionHorizontal(View child, int left, int dx) {
    
    return left;
    
        }
    
    /**
    
        * 到上边界的距离
    
        */
    
        @Override
    
        public int clampViewPositionVertical(View child, int top, int dy) {
    
    return top;
    
        }
    
    });
    
    选中状态
    /**
    
    * 拖动选择框选中View并修改选中状态
    
    * @param minX
    
    * @param minY
    
    * @param maxX
    
    * @param maxY
    
    */
    
    public void chooseView(float minX, float minY, float maxX, float maxY) {
    
    int chooseViewNum=0;
    
        for (Map.Entry entry :viewsMap.entrySet()) {
    
    if( !(entry.getKey()instanceof DragBoardView)){
    
    ViewInfo info=entry.getValue();
    
                float zx =  Math.abs(minX+maxX-info.getLeft()-info.getRight()); //两个矩形重心在x轴上的距离的两倍
    
                float x = (Math.abs(minX-maxX)+Math.abs(info.getLeft()-info.getRight())); //两矩形在x方向的边长的和
    
                float zy =  Math.abs(minY+maxY-info.getTop()-info.getBottom()); //重心在y轴上距离的两倍
    
                float y =  (Math.abs(minY-maxY)+Math.abs(info.getTop()-info.getBottom())); //y方向边长的和
    
                DragScaleView view = (DragScaleView) entry.getKey();
    
                view.setCanTouch(false);
    
                if(action==BoardAction.ACTION_CHOOSE){
    
    view.setClickable(false);//禁止点击事件
    
                }else if(action==BoardAction.ACTION_DRAW_TEXT&&info.getType()==ViewType.VIEW_TEXT){
    
    view.setClickable(true);
    
                }
    
    if(zx <= x && zy <= y){//view在选择框中
    
                    view.setBackgroundResource(view_BG);
    
                    info.setChoose(true);
    
                    chooseViewNum++;
    
                    if (chooseViewNum==1){//选中的view只能是一个的时候才能放大缩小,否则就清空
    
                        scaView=view;
    
                    }
    
    }else{
    
    view.setBackgroundResource(0);
    
                    info.setChoose(false);
    
                }
    
    viewsMap.put(view,info);
    
            }
    
    }
    
    if(chooseViewNum==1){
    
    setScaViewInfo(viewsMap.get(scaView));
    
        }else{
    
    scaView=null;
    
        }
    
    }
    
    • 按钮2
      书写功能类似老师在黑板上板书。
      板书功能
    1. 绘制的基本功能肯定是在onTouchEvent事件中处理逻辑
    @Override
    
        public boolean onTouchEvent(MotionEvent event) {
    
    dragHelper.processTouchEvent(event);
    
    //        // 获取触摸事件的发生位置
    
            float x = event.getX();
    
            float y = event.getY();
    
            if(jumpDraw){
    
    return true;
    
            }else{
    
    switch (event.getAction()) {
    
    case MotionEvent.ACTION_DOWN:
    
    //得到第一个点的xy坐标
    
                        setMmPoint(x,y,x,y);
    
                        preX = x;
    
                        preY = y;
    
                        if(board.action==BoardAction.ACTION_CHOOSE){
    
    board.chooseRect.setRectVal((int)preX,(int)preY,(int)preX,(int)preY);
    
                        }else if(board.action==BoardAction.ACTION_MOVE){
    
    }else{
    
    if(drawType== BoardDrawType.DRAW_PATH){
    
    path.moveTo(x, y); // 将绘图的起始点移到(x,y)坐标点的位置
    
                            }else if(drawType== BoardDrawType.DRAW_TEXT){//输入文字模式的时候点击黑板时回调展示输入的dialog
    
                                textChooseView=null;
    
                                onShowInputTextDialog("");
    
                            }else{
    
    }
    
    }
    
    break;
    
                    case MotionEvent.ACTION_MOVE:
    
    if(board.action==BoardAction.ACTION_CHOOSE){
    
    board.chooseRect.setRectVal((int) x, (int) y);
    
                        }else if(board.action==BoardAction.ACTION_MOVE) {
    
    }else{
    
    if(drawType== BoardDrawType.DRAW_PATH){
    
    float dx = Math.abs(x -preX);
    
                                float dy = Math.abs(y -preY);
    
                                if (dx >=3 || dy >=3) {// 两点之间的距离大于等于3时,生成贝塞尔绘制曲线
    
                                    path.quadTo(preX, preY, (x +preX) /2, (y +preY) /2);
    
                                    preX = x;
    
                                    preY = y;
    
                                }
    
    }else{
    
    }
    
    }
    
    if(x
    
    minX=x;
    
                        }
    
    if(x>maxX){
    
    maxX=x;
    
                        }
    
    if(y
    
    minY=y;
    
                        }
    
    if(y>maxY){
    
    maxY=y;
    
                        }
    
    break;
    
                    case MotionEvent.ACTION_UP://拖动图片的时候抬起手势也会执行该方法。(jumpDraw先设置了false)
    
                        if(board.action==BoardAction.ACTION_CHOOSE){
    
    if(minX==0&&minY==0&&maxX==0&&maxY==0){
    
    }else{
    
    board.chooseView(minX,minY,maxX,maxY);
    
                                board.chooseRect.rect=new Rect();
    
                                setMmPoint(0,0,0,0);
    
                            }
    
    }else if(board.action==BoardAction.ACTION_MOVE) {
    
    }else{
    
    if(board.action==BoardAction.ACTION_DRAW){
    
    createImageView();
    
                                path.reset();//清空所有path至原始状态。
    
                            }
    
    }
    
    break;
    
                }
    
    }
    
    invalidate();//view刷新
    
            return true;// 返回true表明处理方法已经处理该事件
    
        }
    
    1. onDraw绘制
    @SuppressLint("DrawAllocation")
    
    @Override
    
    public void onDraw(Canvas canvas) {
    
    if(board.action==BoardAction.ACTION_CHOOSE){
    
    board.drawRec(canvas);
    
        }else if(board.action==BoardAction.ACTION_MOVE) {
    
    board.drawRec(canvas);
    
        }else{
    
    if(drawType== BoardDrawType.DRAW_PATH){
    
    board.drawPath(canvas);
    
            }else if(drawType== BoardDrawType.DRAW_TEXT){
    
    if(drawText.isEmpty()){
    
    return;
    
                }
    
    canvas.drawText(drawText,preX,preY,paint);
    
                Rect rect =new Rect();
    
                paint.getTextBounds(drawText, 0, drawText.length(), rect);
    
                int w = rect.width();
    
                int h = rect.height();
    
                Paint.FontMetrics fontMetrics=paint.getFontMetrics();
    
                minX=preX;
    
                maxX=minX+w;
    
                maxY=preY+fontMetrics.descent;
    
                minY=maxY+fontMetrics.top;
    
                createImageView();
    
                drawText="";
    
            }
    
    }
    
    }
    
    • 按钮3
      文字功能是直接打字在黑板上,获取到点击事件的坐标点后。弹出输入框输
      输入文字
    1. 输入框代码
    public class InputTextMsgDialogextends AppCompatDialog {
    
    private ContextmContext;
    
        private InputMethodManagerimm;
    
        public EditTextmessageTextView;
    
        private TextViewconfirmBtn;
    
        private RelativeLayoutrlDlg;
    
        private int mLastDiff =0;
    
        private TextViewtvNumber;
    
        private TextViewtvSend;
    
        private int maxNumber =200;
    
        public interface OnTextSendListener {
    
    void onTextChange(String msg);
    
            void onTextSend(String msg);
    
        }
    
    private OnTextSendListenermOnTextSendListener;
    
        public InputTextMsgDialog(@NonNull Context context, int theme) {
    
    super(context, theme);
    
            this.mContext = context;
    
            this.getWindow().setWindowAnimations(R.style.class_main_menu_animstyle);
    
            init();
    
            setLayout();
    
        }
    
    /**
    
        * 最大输入字数  默认200
    
    */
    
        @SuppressLint("SetTextI18n")
    
    public void setMaxNumber(int maxNumber) {
    
    this.maxNumber = maxNumber;
    
            tvNumber.setText("0/" + maxNumber);
    
        }
    
    /**
    
        * 设置输入提示文字
    
        */
    
        public void setHint(String text) {
    
    messageTextView.setHint(text);
    
        }
    
    /**
    
        * 设置按钮的文字  默认为:发送
    
        */
    
        public void setBtnText(String text) {
    
    confirmBtn.setText(text);
    
        }
    
    private void init() {
    
    setContentView(R.layout.class_edit_pop);
    
            messageTextView = (EditText) findViewById(R.id.edit_content);
    
            tvSend = (TextView) findViewById(R.id.chat_send_btn);
    
            final LinearLayout rldlgview = (LinearLayout) findViewById(R.id.rl_inputdlg_view);
    
            TextView other=(TextView) findViewById(R.id.other_view);
    
            imm = (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
    
            this.setOnDismissListener(new OnDismissListener() {
    
    @Override
    
                public void onDismiss(DialogInterface dialog) {
    
    hideKeyboard(messageTextView.getWindowToken(),mContext);
    
                }
    
    });
    
            other.setOnClickListener(new View.OnClickListener() {
    
    @Override
    
                public void onClick(View v) {
    
    dismiss();
    
                }
    
    });
    
            messageTextView.addTextChangedListener(new TextWatcher() {
    
    @Override
    
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
    }
    
    @Override
    
                public void onTextChanged(CharSequence s, int start, int before, int count) {
    
    }
    
    @Override
    
                public void afterTextChanged(Editable s) {
    
    String msg =messageTextView.getText().toString().trim();
    
                    if (msg.length() >maxNumber) {
    
    Toast.makeText(mContext, "超过最大字数限制", Toast.LENGTH_LONG).show();
    
    return;
    
                    }
    
    if(mOnTextSendListener!=null){
    
    mOnTextSendListener.onTextChange(msg);
    
                    }
    
    }
    
    });
    
            tvSend.setOnClickListener(new View.OnClickListener() {
    
    @Override
    
                public void onClick(View view) {
    
    String msg =messageTextView.getText().toString().trim();
    
                    if (msg.length() >maxNumber) {
    
    Toast.makeText(mContext, "超过最大字数限制", Toast.LENGTH_LONG).show();
    
    return;
    
                    }
    
    if (!TextUtils.isEmpty(msg)) {
    
    if(mOnTextSendListener!=null){
    
    mOnTextSendListener.onTextSend(msg);
    
                        }
    
    //                    imm.showSoftInput(messageTextView, InputMethodManager.SHOW_FORCED);
    
    //                    imm.hideSoftInputFromWindow(messageTextView.getWindowToken(), 0);
    
                        messageTextView.setText("");
    
                        dismiss();
    
                    }else {
    
    //                    Toast.makeText(mContext, "请输入文字", Toast.LENGTH_LONG).show();
    
    //                    return;
    
                        dismiss();
    
                    }
    
    messageTextView.setText(null);
    
                }
    
    });
    
            messageTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    
    @Override
    
                public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    
    switch (actionId) {
    
    case KeyEvent.KEYCODE_ENDCALL:
    
    case KeyEvent.KEYCODE_ENTER:
    
    if (messageTextView.getText().length() >maxNumber) {
    
    Toast.makeText(mContext, "超过最大字数限制", Toast.LENGTH_LONG).show();
    
    return true;
    
                            }
    
    if (messageTextView.getText().length() >0) {
    
    //                            imm.hideSoftInputFromWindow(messageTextView.getWindowToken(), 0);
    
    //                            dismiss();
    
                                if(mOnTextSendListener!=null){
    
    mOnTextSendListener.onTextSend(messageTextView.getText().toString());
    
                                }
    
    }else {
    
    //                            Toast.makeText(mContext, "请输入文字", Toast.LENGTH_LONG).show();
    
                            }
    
    dismiss();
    
                            messageTextView.setText(null);
    
    return true;
    
                        case KeyEvent.KEYCODE_BACK:
    
    dismiss();
    
    return false;
    
                        default:
    
    return false;
    
                    }
    
    }
    
    });
    
            messageTextView.setOnKeyListener(new View.OnKeyListener() {
    
    @Override
    
                public boolean onKey(View view, int i, KeyEvent keyEvent) {
    
    Log.d("My test", "onKey " + keyEvent.getCharacters());
    
    return false;
    
                }
    
    });
    
            rlDlg = findViewById(R.id.rl_outside_view);
    
            rlDlg.setOnClickListener(new View.OnClickListener() {
    
    @Override
    
                public void onClick(View v) {
    
    if (v.getId() != R.id.rl_inputdlg_view)
    
    dismiss();
    
                }
    
    });
    
            rldlgview.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    
    @Override
    
                public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
    
    Rect r =new Rect();
    
                    //获取当前界面可视部分
    
                    getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
    
                    //获取屏幕的高度
    
                    int screenHeight = getWindow().getDecorView().getRootView().getHeight();
    
                    //此处就是用来获取键盘的高度的, 在键盘没有弹出的时候 此高度为0 键盘弹出的时候为一个正数
    
                    int heightDifference = screenHeight - r.bottom;
    
                    if (heightDifference <=0 &&mLastDiff >0) {
    
    dismiss();
    
                    }
    
    mLastDiff = heightDifference;
    
                }
    
    });
    
            rldlgview.setOnClickListener(new View.OnClickListener() {
    
    @Override
    
                public void onClick(View v) {
    
    //                imm.hideSoftInputFromWindow(messageTextView.getWindowToken(), 0);
    
                    dismiss();
    
                }
    
    });
    
            setOnKeyListener(new OnKeyListener() {
    
    @Override
    
                public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent keyEvent) {
    
    if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() ==0)
    
    dismiss();
    
    return false;
    
                }
    
    });
    
        }
    
    private void setLayout() {
    
    getWindow().setGravity(Gravity.BOTTOM);
    
            WindowManager m = getWindow().getWindowManager();
    
            Display d = m.getDefaultDisplay();
    
            WindowManager.LayoutParams p = getWindow().getAttributes();
    
            p.width = WindowManager.LayoutParams.MATCH_PARENT;
    
            p.height = WindowManager.LayoutParams.MATCH_PARENT;
    
            getWindow().setAttributes(p);
    
            setCancelable(true);
    
            getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    
        }
    
    public void setmOnTextSendListener(OnTextSendListener onTextSendListener) {
    
    this.mOnTextSendListener = onTextSendListener;
    
        }
    
    @Override
    
        public void dismiss() {
    
    imm.hideSoftInputFromWindow(messageTextView.getWindowToken(), 0);
    
            super.dismiss();
    
            //dismiss之前重置mLastDiff值避免下次无法打开
    
            mLastDiff =0;
    
        }
    
    @Override
    
        public void show() {
    
    super.show();
    
            openKeyboard(messageTextView,mContext);
    
            //延时加载dialog避免输入法无法弹出
    
            new Handler().postDelayed(new Runnable() {
    
    @Override
    
                public void run() {
    
    openKeyboard(messageTextView,mContext);
    
                }
    
    },200);
    
        }
    
    public void setContentText(String s){
    
    messageTextView.setText(s);
    
            messageTextView.setSelection(s.length());//将光标移至文字末尾
    
        }
    
    /**
    
        * 获取InputMethodManager,隐藏软键盘
    
        *
    
        * @param token
    
        */
    
        private void hideKeyboard(IBinder token, Context context) {
    
    if (token !=null) {
    
    InputMethodManager im = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    
                im.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS);
    
            }
    
    }
    
    /**
    
        * 获取InputMethodManager,打开软键盘
    
        *
    
    */
    
        private void openKeyboard(EditText et, Context context) {
    
    et.setFocusable(true);
    
            et.setFocusableInTouchMode(true);
    
            et.requestFocus();
    
            InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    
            imm.showSoftInput(et, InputMethodManager.RESULT_UNCHANGED_SHOWN);
    
        }
    
    }
    
    1. 布局文件class_edit_pop
    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    
        android:orientation="vertical" android:layout_width="match_parent"
    
        android:gravity="bottom"
    
        android:id="@+id/rl_outside_view"
    
        android:layout_height="match_parent">
    
            android:id="@+id/other_view"
    
            android:layout_width="match_parent"
    
            android:layout_height="match_parent"
    
            android:layout_above="@+id/rl_inputdlg_view"/>
    
            android:id="@+id/rl_inputdlg_view"
    
            android:layout_width="match_parent"
    
            android:layout_height="wrap_content"
    
            android:background="@color/white"
    
            android:gravity="center_vertical"
    
            android:orientation="horizontal">
    
                android:id="@+id/edit_content"
    
                android:layout_width="wrap_content"
    
                android:layout_height="wrap_content"
    
                android:layout_weight="1"
    
                android:layout_marginLeft="15dp"
    
                android:background="@drawable/common_shape_line_100"
    
                android:layout_marginTop="8dp"
    
                android:layout_marginBottom="8dp"
    
                android:textColor="@color/black"
    
                android:maxLines="3"
    
                android:maxLength="200"
    
                android:layout_marginRight="15dp"
    
                android:hint="聊天输入"
    
                android:padding="10dp" />
    
                android:id="@+id/chat_send_btn"
    
                android:layout_width="wrap_content"
    
                android:layout_height="wrap_content"
    
                android:background="@color/teal_200"
    
                android:paddingTop="7dp"
    
                android:paddingBottom="7dp"
    
                android:paddingLeft="20dp"
    
                android:paddingRight="20dp"
    
                android:layout_marginRight="16dp"
    
                android:text="发送"/>
    
    </RelativeLayout>
    
    • 按钮4
      删除黑板上的元素
      删除
    /**
    
    * 删除黑板中选中的view
    
    */
    
    public void removeChooseView(ViewGroup group) {
    
    for (Iterator> it =viewsMap.entrySet().iterator(); it.hasNext();){
    
    Map.Entry entry = it.next();
    
            //删除key值为Two的数据
    
            if (entry.getValue().isChoose()) {
    
    group.removeView(entry.getKey());
    
                it.remove();
    
            }
    
    }
    
    }
    
    • 按钮5
      插入图片功能每选择一张图片都放到黑板左上角,并且叠加到最上层,支持缩放
      添加图片

    添加的图片使用的知乎Matisseglide

    • 按钮6
      拖动黑板需要自己代码设置黑板大小,才能滚动黑板到底部。demo中设置了50x屏幕宽度
      滚动黑板
    boardParams =boardView.layoutParams as RelativeLayout.LayoutParams//取控件黑板的布局参数
    
    boardParams!!.width = Util.getScreenInfo(this).widthPixels
    
    boardParams!!.height=Util.getScreenInfo(this).heightPixels*50//50倍黑板
    
    boardView.layoutParams =boardParams!!
    
    • 按钮7
      添加小黑板功能属于黑板中的另一个黑板,支持书写,放大缩小和拖拽
      小黑板功能
    1. 小黑板布局contr_view
    <?xml version="1.0" encoding="utf-8"?>
    
    <merge xmlns:android="http://schemas.android.com/apk/res/android"
    
        android:layout_width="match_parent"
    
        android:layout_height="match_parent">
    
            android:id="@+id/title_R"
    
            android:layout_width="wrap_content"
    
            android:layout_height="wrap_content"
    
            android:orientation="horizontal"
    
            android:background="@color/cardview_dark_background"
    
            android:padding="10dp">
    
                android:id="@+id/contr_title"
    
                android:layout_width="wrap_content"
    
                android:layout_height="wrap_content"
    
                android:text="标题"
    
                android:layout_centerInParent="true"/>
    
                android:layout_width="wrap_content"
    
                android:layout_height="wrap_content"
    
                android:layout_alignParentRight="true">
    
                    android:id="@+id/contr_small"
    
                    android:layout_width="wrap_content"
    
                    android:layout_height="wrap_content"
    
                    android:textStyle="bold"
    
                    android:textSize="20sp"
    
                    android:padding="10dp"
    
                    android:text="-"/>
    
                    android:id="@+id/contr_close"
    
                    android:layout_width="wrap_content"
    
                    android:layout_height="wrap_content"
    
                    android:textStyle="bold"
    
                    android:textSize="20sp"
    
                    android:layout_marginLeft="10dp"
    
                    android:padding="10dp"
    
                    android:text="X"/>
    
            android:id="@+id/contr_scrollView"
    
            android:layout_width="wrap_content"
    
            android:layout_height="match_parent"
    
            android:fillViewport="true">
    
                android:layout_width="match_parent"
    
                android:layout_height="match_parent"
    
                android:layout_centerInParent="true">
    
                <!--黑板-->
    
    
    
                    android:id="@+id/contr_view"
    
                    android:background="@color/design_default_color_secondary_variant"
    
                    android:layout_width="match_parent"
    
                    android:layout_height="match_parent">
    
    </merge>
    
    1. 其中DragBoardView处理拖拽放大缩小
    public class DragBoardViewextends LinearLayout/*implements View.OnTouchListener */{
    
    private  final int touchDistance =40; //触摸边界的有效距离
    
        private  final int TOP =0x15;
    
        private  final int LEFT =0x16;
    
        private  final int BOTTOM =0x17;
    
        private  final int RIGHT =0x18;
    
        private  final int LEFT_TOP =0x11;
    
        private  final int RIGHT_TOP =0x12;
    
        private  final int LEFT_BOTTOM =0x13;
    
        private  final int RIGHT_BOTTOM =0x14;
    
        private  final int TITLE =0x19;
    
        protected int lastdistX;
    
        protected int lastlastY;
    
        private int dragDirection;
    
        protected int lastX;
    
        protected int lastY;
    
        private int oriLeft;
    
        private int oriRight;
    
        private int oriTop;
    
        private int oriBottom;
    
        private int offset =0; //可超出其父控件的偏移量
    
        private int ACTION_DOWN_VAL=0;//按下的时候的值。记录下来。
    
        private TextViewtitle;
    
        private TextViewsmall;
    
        private TextViewclose;
    
        private RelativeLayouttitle_R;
    
        private MScrollViewscrollView;
    
        private NomalBoardLayoutboard;
    
        private SmallBoardStatusListenerstatusListener;
    
        private boolean animting;//是否正在动画中
    
        public void setAnimting(boolean animting) {
    
    this.animting = animting;
    
        }
    
    public void setStatusListener(SmallBoardStatusListener statusListener) {
    
    this.statusListener = statusListener;
    
        }
    
    private int mHeight=0;
    
        private int mWidth=0;
    
        public DragBoardView(Context context) {
    
    this(context,null);
    
        }
    
    public DragBoardView(Context context, @Nullable AttributeSet attrs) {
    
    this(context, attrs,0);
    
        }
    
    public DragBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    super(context, attrs, defStyleAttr);
    
            init();
    
        }
    
    private void init() {
    
    View parentView = LayoutInflater.from(getContext()).inflate(R.layout.contr_view, this, true);
    
            this.setOrientation(LinearLayout.VERTICAL);
    
            title=parentView.findViewById(R.id.contr_title);
    
            small=parentView.findViewById(R.id.contr_small);
    
            close=parentView.findViewById(R.id.contr_close);
    
            scrollView=parentView.findViewById(R.id.contr_scrollView);
    
            board=parentView.findViewById(R.id.contr_view);
    
            title_R=parentView.findViewById(R.id.title_R);
    
            scrollView.setScroll(false);
    
            board.setAction(BoardAction.ACTION_DRAW);
    
            close.setOnClickListener(new OnClickListener() {
    
    @Override
    
                public void onClick(View v) {
    
    statusListener.boardClose(DragBoardView.this);
    
                }
    
    });
    
        }
    
    @Override
    
        public boolean onInterceptTouchEvent(MotionEvent ev) {
    
    dragDirection = getDirection(this, (int) ev.getX(),
    
                    (int) ev.getY());
    
            if(ev.getAction()==MotionEvent.ACTION_DOWN){
    
    ACTION_DOWN_VAL=dragDirection;//记录第一次按下的时候的值是标题栏TITLE的话,在ACTION_MOVE的时候才能拖动
    
            }
    
    if(dragDirection!=-1&&dragDirection!=TITLE&&ACTION_DOWN_VAL!=TITLE){//避免拦截了关闭等按钮
    
                return true;
    
            }
    
    return super.onInterceptTouchEvent(ev);
    
        }
    
    @Override
    
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
    
    super.onLayout(changed, l, t, r, b);
    
        }
    
    @Override
    
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
        }
    
    @Override
    
        public boolean onTouchEvent(MotionEvent event) {
    
    if(animting){
    
    return true;
    
            }
    
    if(dragDirection==TITLE&&ACTION_DOWN_VAL==TITLE){
    
    statusListener.boardMove(this,event);
    
            }else if(ACTION_DOWN_VAL!=-1/*&&ACTION_DOWN_VAL==TITLE*/){//第一次按下的时候不能在黑板中才可以执行
    
                int action = event.getAction()& MotionEvent.ACTION_MASK;
    
                if (action == MotionEvent.ACTION_DOWN) {
    
    oriLeft = getLeft();
    
                    oriRight = getRight();
    
                    oriTop = getTop();
    
                    oriBottom = getBottom();
    
                    lastY = (int) event.getRawY();
    
                    lastX = (int) event.getRawX();
    
                }
    
    // 处理拖动事件
    
                delDrag(this, event, action);
    
            }
    
    return true;
    
        }
    
    /**
    
        * 处理拖动事件
    
        *
    
        * @param v
    
        * @param event
    
        * @param action
    
        */
    
        protected void delDrag(View v, MotionEvent event, int action) {
    
    switch (action) {
    
    case MotionEvent.ACTION_MOVE:
    
    int dx = (int) event.getRawX() -lastX;
    
                    int dy = (int) event.getRawY() -lastY;
    
                    switch (dragDirection) {
    
    case TITLE://标题栏
    
                            return;
    
                        case LEFT:// 左边缘
    
                            left(v, dx);
    
    break;
    
                        case RIGHT:// 右边缘
    
                            right(v, dx);
    
    break;
    
                        case BOTTOM:// 下边缘
    
                            bottom(v, dy);
    
    break;
    
                        case TOP:// 上边缘
    
                            top(v, dy);
    
    break;
    
                        case LEFT_BOTTOM:// 左下
    
                            left(v, dx);
    
                            bottom(v, dy);
    
    break;
    
                        case LEFT_TOP:// 左上
    
                            left(v, dx);
    
                            top(v, dy);
    
    break;
    
                        case RIGHT_BOTTOM:// 右下
    
                            right(v, dx);
    
                            bottom(v, dy);
    
    break;
    
                        case RIGHT_TOP:// 右上
    
                            right(v, dx);
    
                            top(v, dy);
    
    break;
    
                    }
    
    if (dragDirection != -1) {
    
    statusListener.boardSizeChange(this,oriLeft, oriTop, oriRight, oriBottom);
    
                    }
    
    lastX = (int) event.getRawX();
    
                    lastY = (int) event.getRawY();
    
    break;
    
                case MotionEvent.ACTION_UP:
    
    ACTION_DOWN_VAL=0;
    
                    dragDirection =0;
    
                    lastdistX=0;
    
                    lastlastY=0;
    
    break;
    
            }
    
    }
    
    /**
    
        * 触摸点为左边缘
    
        *
    
        * @param v
    
        * @param dx
    
        */
    
        private void left(View v, int dx) {
    
    oriLeft += dx;
    
        }
    
    /**
    
        * 触摸点为右边缘
    
        *
    
        * @param v
    
        * @param dx
    
        */
    
        private void right(View v, int dx) {
    
    oriRight += dx;
    
        }
    
    /**
    
        * 触摸点为下边缘
    
        *
    
        * @param v
    
        * @param dy
    
        */
    
        private void bottom(View v, int dy) {
    
    oriBottom += dy;
    
        }
    
    /**
    
        * 触摸点为上边缘
    
        *
    
        * @param v
    
        * @param dy
    
        */
    
        private void top(View v, int dy) {
    
    oriTop += dy;
    
        }
    
    /**
    
        * 获取触摸点flag
    
    *
    
        * @param v
    
        * @param x
    
        * @param y
    
        * @return
    
        */
    
        protected int getDirection(View v, int x, int y) {
    
    int left = v.getLeft();
    
            int right = v.getRight();
    
            int bottom = v.getBottom();
    
            int top = v.getTop();
    
            if (x
    
    return LEFT_TOP;
    
            }
    
    if(x>touchDistance&&xtouchDistance&&y
    
    return TITLE;
    
            }
    
    if (y
    
    return RIGHT_TOP;
    
            }
    
    if (x
    
    return LEFT_BOTTOM;
    
            }
    
    if (right - left - x
    
    return RIGHT_BOTTOM;
    
            }
    
    if (x
    
    return LEFT;
    
            }
    
    if (y
    
    return TOP;
    
            }
    
    if (right - left - x
    
    return RIGHT;
    
            }
    
    if (bottom - top - y
    
    return BOTTOM;
    
            }
    
    return -1;
    
        }
    
    /**
    
        * 黑板移动监听
    
        */
    
        public interface SmallBoardStatusListener{
    
    void boardMove(View v, MotionEvent event);
    
            void boardClose(View v);
    
            void boardSizeChange(View v,int l, int t, int r, int b);
    
        }
    }
    
    1. boardMove回调中添加移动动画
    @Override
    
    public void boardMove(View v, MotionEvent event) {
    
    setAction(BoardAction.ACTION_MOVE);
    
        float x =event.getX();
    
        float y = event.getY();
    
        switch (event.getAction()) {
    
    case MotionEvent.ACTION_DOWN:
    
    board.chooseRect.setRectVal(board.viewsMap.get(v).getLeft(), board.viewsMap.get(v).getTop(),board.viewsMap.get(v).getRight(),board.viewsMap.get(v).getBottom());
    
                left=board.viewsMap.get(v).getLeft();
    
                top=board.viewsMap.get(v).getTop();
    
                v.bringToFront();
    
    break;
    
            case MotionEvent.ACTION_MOVE:
    
    int dx= (int) (x-board.chooseRect.preX);
    
                int dy= (int) (y-board.chooseRect.preY);
    
                board.chooseRect.setRectVal(board.chooseRect.rect.left+dx,board.chooseRect.rect.top+dy,
    
                        board.chooseRect.rect.right+dx,board.chooseRect.rect.bottom+dy);
    
                invalidate();//view刷新
    
                break;
    
            case MotionEvent.ACTION_UP:
    
    ViewInfo info=board.viewsMap.get(v);
    
                info.setLeft(board.chooseRect.rect.left);
    
                info.setTop(board.chooseRect.rect.top);
    
                info.setRight(board.chooseRect.rect.right);
    
                info.setBottom(board.chooseRect.rect.bottom);
    
                TranslateAnimation animation =new TranslateAnimation(0, info.getLeft()-left, 0, info.getTop()-top);//传入移动的距离而不是绝对值的坐标点
    
                animation.setDuration(300);
    
                v.setAnimation(animation);
    
                animation.setAnimationListener(new Animation.AnimationListener() {
    
    @Override
    
                    public void onAnimationStart(Animation animation) {
    
    DragBoardView boardView=(DragBoardView)v;
    
                        boardView.setAnimting(true);
    
                    }
    
    @Override
    
                    public void onAnimationEnd(Animation animation) {
    
    DragBoardView boardView=(DragBoardView)v;
    
                        boardView.setAnimting(false);
    
                        animation.cancel();
    
                        v.clearAnimation();
    
                        board.viewsMap.put(v,info);
    
                        v.layout(info.getLeft(),info.getTop(),info.getRight(),info.getBottom());
    
                    }
    
    @Override
    
                    public void onAnimationRepeat(Animation animation) {
    
    }
    
    });
    
    break;
    
        }
    
    board.chooseRect.preX=x;
    
        board.chooseRect.preY=y;
    
    }
    

    以上为部分代码

    demo现目前只有这些功能,并且很多功能是有一些bug的,只是提供一种功能的思路。如果对你有帮助麻烦点个赞Star

    demoBoard

    相关文章

      网友评论

          本文标题:安卓黑板功能实现

          本文链接:https://www.haomeiwen.com/subject/fdkylrtx.html