基于HarmonyOS分布式小游戏之你画我猜

一、游戏逻辑

游戏分为单双人模式

  • 单人模式: 自画自猜,只需要一个设备即可
  • 双人模式: 需要两台设备,主设备根据关键字进行绘图,从设备根据主设备的绘图描述猜关键字。从设备猜对则从设备获胜,否则主设备获胜。

游戏流程图如下

在这里插入图片描述

二、数据管理

参考官方教程Data Ability基本概念简单创建一个Data用于管理关键字,用户可以增加、删除关键字。在进行游戏前可以选择数据库中的关键字进行游戏。

1.增加

    /**
     * 插入一条数据
     *
     * @param value
     */
    private int insert(String value) {

        ValuesBucket valuesBucket = new ValuesBucket();
        valuesBucket.putString(Constants.DB_COLUMN_KEY, value);
        try {
           return databaseHelper.insert(Uri.parse(Constants.BASE_URI + Constants.DATA_PATH), valuesBucket);
        } catch (DataAbilityRemoteException | IllegalStateException exception) {
            LogUtils.error("insert: dataRemote exception|illegalStateException"+exception.getMessage());
        }
        return -1;
    }

2.删除

/**
     * 删除一条数据
     * @param value
     * @return
     */
    private int delete(String value){

        DataAbilityPredicates predicates = new DataAbilityPredicates();
        predicates.equalTo(Constants.DB_COLUMN_KEY,value);

        try {
           return databaseHelper.delete(Uri.parse(Constants.BASE_URI + Constants.DATA_PATH), predicates);
        } catch (DataAbilityRemoteException | IllegalStateException exception) {
            LogUtils.error("delete: dataRemote exception|illegalStateException"+exception.getMessage());
        }
        return 0;
    }

3.查询

/**
     * 模糊查询
     *
     * @param key
     * @return
     */
    private List<String> fuzzyQuery(String key) {

        String[] columns = new String[]{Constants.DB_COLUMN_KEY};
        List<String> result = new ArrayList<>();

        DataAbilityPredicates predicates = new DataAbilityPredicates();
        predicates.contains(Constants.DB_COLUMN_KEY,key);
        try {
            ResultSet resultSet = databaseHelper.query(Uri.parse(Constants.BASE_URI + Constants.DATA_PATH), columns,
                    predicates);
            if (!resultSet.goToFirstRow())
                return null;
            int index=resultSet.getColumnIndexForName(Constants.DB_COLUMN_KEY);

            do {
                String tmp = resultSet.getString(index);
                result.add(tmp);
            } while (resultSet.goToNextRow());
            resultSet.close();

        } catch (DataAbilityRemoteException | IllegalStateException exception) {
            LogUtils.error( "fuzzyQuery: dataRemote exception|illegalStateException"+exception.getMessage());
        }

        return result;
    }

三、绘图模块

在这里插入图片描述

参考官方自定义组件文档说明自定义组件自定义view-(你画我猜)画板

1. 自定义画笔

用户可以选择画笔的粗细。
部分代码如下

public void onDraw(Component component, Canvas canvas) {
        mPaint.setColor(new Color(mPaintColor));
        HiLog.info(HI_LOG_LABEL, getClass().getSimpleName()+" --- onDraw");
        for (int i = 0; i < rectList.size(); i++){
            Rect rect = rectList.get(i);
            float radius = widthArray[i];
            switch (mCurrentState){
                case PEN:
                    mChoosePaint.setColor(Color.WHITE);
                    if (mCurrentIndex == i){
                        canvas.drawCircle(rect.getCenterX(), rect.getCenterY(), radius + 10, mChoosePaint);
                    }
                    canvas.drawCircle(rect.getCenterX(), rect.getCenterY(), radius, mPaint);
                    break;
                case ERASER:
                    mChoosePaint.setColor(Color.BLACK);
                    if (mCurrentIndex == i){
                        canvas.drawCircle(rect.getCenterX(), rect.getCenterY(), radius + 10, mChoosePaint);
                    }
                    canvas.drawCircle(rect.getCenterX(), rect.getCenterY(), radius, mWhitePaint);
                    break;
            }

        }
    }

2.自定义颜色选择器

用户通过点击对应的颜色来选择画笔的颜色,通过滑动来查看更多颜色


public void onDraw(Component component, Canvas canvas) {
        Rect rect;
        Rect chooseRect;
        switch (mCurrentState){
            //橡皮擦模式不突出选中颜色
            case ERASER:
                for (int i = 0; i < rectList.size(); i++){
                    mPaint.setColor(new Color(colors[i]));
                    rect = rectList.get(i);
                    rect = new Rect(rect.left-getScrollValue(AXIS_X), rect.top , rect.right-getScrollValue(AXIS_X), rect.bottom );
                    canvas.drawRect(rect, mPaint);
                }
                break;
            case PEN:
                for (int i = 0; i < rectList.size(); i++){
                    mPaint.setColor(new Color(colors[i]));
                    rect = rectList.get(i);
                    //把选中的颜色突出显示
                    if (currentIndex == i){
                        chooseRect = new Rect(rect.left-getScrollValue(AXIS_X), rect.bottom - 10, rect.right-getScrollValue(AXIS_X), rect.bottom);
                        rect = new Rect(rect.left-getScrollValue(AXIS_X), rect.top - 30, rect.right-getScrollValue(AXIS_X), rect.bottom - 30);
                        canvas.drawRect(chooseRect, mPaint);
                        canvas.drawRect(rect, mPaint);
                    }else{
                        rect = new Rect(rect.left-getScrollValue(AXIS_X), rect.top , rect.right-getScrollValue(AXIS_X), rect.bottom );
                        canvas.drawRect(rect, mPaint);
                    }

                }
                break;
        }
    }
    
/**
     * 根据点击区域判断选中的颜色
     * @param component
     * @param touchEvent
     * @return
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        MmiPoint mmiPoint =touchEvent.getPointerPosition(touchEvent.getIndex());;
        switch (touchEvent.getAction()){
            case TouchEvent.PRIMARY_POINT_DOWN:
                HiLog.info(HI_LOG_LABEL,"TouchEvent.PRIMARY_POINT_DOWN");

                startX = mmiPoint.getX();
                startY = mmiPoint.getY();
                break;
            case TouchEvent.POINT_MOVE:
                HiLog.info(HI_LOG_LABEL,"TouchEvent.POINT_MOVE");

                dx = mmiPoint.getX() - startX;
                dy = mmiPoint.getY() - startY;
                HiLog.info(HI_LOG_LABEL," --- dx "+dx+", dy "+dy);
                if (Math.abs(dx) -  Math.abs(dy) >0){
                    if (getScrollValue(AXIS_X) + (-dx) < 0 || getScrollValue(AXIS_X) + (-dx) > getWidth()){
                        return true;
                    }
                    this.scrollBy((int) -dx, 0);
                    HiLog.info(HI_LOG_LABEL,"TouchEvent.Scroll"+getScrollValue(AXIS_X));
                    startX = mmiPoint.getX();
                    startY = mmiPoint.getY();
                }
                invalidate();
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                HiLog.info(HI_LOG_LABEL,getClass().getSimpleName()+"TouchEvent.PRIMARY_POINT_UP");

                if (Math.abs(dx) <= 10 && Math.abs(dy) <= 10){
                    for (int i = 0; i < rectList.size(); i++){
                        if (rectList.get(i).contains((int) startX+getScrollValue(AXIS_X)  , (int) startY+getScrollValue(AXIS_Y) ,(int)startX+getScrollValue(AXIS_X),(int)startY+getScrollValue(AXIS_Y))){
                            HiLog.info(HI_LOG_LABEL,getClass().getSimpleName()+" --- check rect "+i);
                            Rect rect;
                            rectList.clear();
                            for (int j = 0; j < colors.length; j++){
                                int centerX = 20 * j + radius * 2 * j + radius;
                                int centerY = height/2;
                                rect = new Rect(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
                                rectList.add(rect);
                            }

                            currentIndex = i;
                            onStateChangedListener.onPen();
                            strokeWidthChooseView.setmPaintColor(colors[i]);

                            invalidate();

                        }
                    }
                }

                startX = 0;
                startY = 0;
                dx = 0;
                dy = 0;
                break;
        }
        return true;
    }

3.自定义画板

用户根据画笔的粗细和颜色在画板上面滑动来进行绘图
部分代码

 public void onDraw(Component component, Canvas canvas) {
        HiLog.info(HI_LOG_LABEL, getClass().getSimpleName()+" --- onDraw");

        if (cacheBitmap!=null){
            PixelMapHolder pixelMapHolder=new PixelMapHolder(cacheBitmap);
            canvas.drawPixelMapHolder(pixelMapHolder,0,0,backPaint);
        }
    }

    /**
     * 按下开始画线,抬起结束画线,把画线结果保存
     * @param component
     * @param touchEvent
     * @return
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if(!canDraw){
            return true;
        }
        MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
        float x = point.getX();
        float y = point.getY();
        //HiLog.info(HI_LOG_LABEL,getClass().getSimpleName()+" --- point ("+x+","+y+")");
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
               // HiLog.info(HI_LOG_LABEL,getClass().getSimpleName()+" --- TouchEvent.PRIMARY_POINT_DOWN ");
                TouchDown(x, y);
                break;
            case TouchEvent.POINT_MOVE:
                //HiLog.info(HI_LOG_LABEL,getClass().getSimpleName()+" --- TouchEvent.POINT_MOVE ");
                TouchMove(x, y);
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                //HiLog.info(HI_LOG_LABEL,getClass().getSimpleName()+" --- TouchEvent.PRIMARY_POINT_UP ");
                TouchUp();
                break;
        }
        //HiLog.info(HI_LOG_LABEL, getClass().getSimpleName()+" --- path size " +paths.size());
        return true;
    }



    private void TouchUp() {
        Path path = new Path(mPath);
        paths.push(path);
        mPath.reset();
        if (mCurrentState == State.PEN) {
            states.push(0);
            Color color = drawPaint.getColor();
            colors.push(color);
            widths.push(drawPaint.getStrokeWidth());
        } else {
            states.push(1);
            Color color = eraserPaint.getColor();
            colors.push(color);
            widths.push(eraserPaint.getStrokeWidth());
        }

        if (canDraw && callback!=null)
            callback.sendDrawData(points,colors.lastElement().getValue(),mCurrentState,widths.lastElement());
    }

    private void TouchMove(float x, float y) {
        float dx = Math.abs(x - lastX);
        float dy = Math.abs(y - lastY);
        mPath.quadTo(lastX, lastY, (x + lastX) / 2, (y + lastY) / 2);
        lastX = x;
        lastY = y;

        points.add(new Point(x, y));
        drawPath();
        invalidate();

    }

    private void TouchDown(float x, float y) {
        if (mPath==null)
            mPath=new Path();
        points.clear();
        points.add(new Point(x,y));
        mPath.moveTo(x, y);
        lastX = x;
        lastY = y;
        drawPath();
    }

三、 跨端通信

分布式游戏需要在主设备每完成一次绘图动作后把数据传输给从设备进行显示。分布式操作可以参考前面鸿蒙HarmonyOS学习笔记之Service Ability实现跨端通信
由于传输的数据只能是简单的数据类型,故不能把整个画布传输过去,只能把每一条线的点坐标集合传输过去,从设备再根据点坐标进行绘制显示。
部分代码如下

 /**
     * 发送绘制线的点坐标信息和画笔信息
     * @param points
     * @param colors
     * @param states
     * @param widths
     */
    public void sendDrawData(List<Point> points, int colors, DrawView.State states, float widths){
        LogUtils.info(getClass().getSimpleName()+" points "+points);
        LogUtils.info(getClass().getSimpleName()+" colors "+colors);
        LogUtils.info(getClass().getSimpleName()+" states "+states);
        LogUtils.info(getClass().getSimpleName()+" widths "+widths);
        //把消息封装到MessageParcel
        MessageParcel data= MessageParcel.obtain();

        float pointX[]=new float[points.size()];
        float pointY[]=new float[points.size()];

        for (int i = 0; i < points.size(); i++) {
            pointX[i]=points.get(i).getPointX();
            pointY[i]=points.get(i).getPointY();
        }
        data.writeFloatArray(pointX);
        data.writeFloatArray(pointY);
        data.writeInt(colors);
        data.writeInt(states== DrawView.State.PEN?0:1);
        data.writeFloat(widths);

        MessageParcel reply =MessageParcel.obtain();
        MessageOption option =new MessageOption(MessageOption.TF_SYNC);
        try {
            //通过RemoteObject实例发送消息
            remote.sendRequest(Constants.SEND_DRAW_DATA,data,reply,option);
            //获取消息传递结果
            int ec=reply.readInt();

            if(ec!= Constants.ERR_OK){
                throw new RemoteException();
            }
        } catch (RemoteException e) {
            HiLog.error(LABEL_LOG,"RemoteException: %{public}s",e.getMessage());
        }

    }

四、游戏界面逻辑

在这里插入图片描述

主、从设备通过消息代码进行简单通信,根据不同的消息代码做出对应的回应。


    /**
     * 接受并处理收到的跨端信息
     *
     * @param message 消息
     */
    private void handleMessage(int message) {
        LogUtils.info(getClass().getSimpleName() + " --- handleMessage " + message);
        //切换到主线程
        getUITaskDispatcher().asyncDispatch(() -> {
            switch (message){
                case Constants.BACK_COM:
                    playerQuit();
                    break;
                case Constants.FINISH_COM:
                    finish();
                    break;
                case Constants.REVOKE_COM:
                     drawView.revoke();
                     break;
                case Constants.CLEAR_COM:
                    drawView.clear();
                    break;
                case Constants.GUESS_RIGHT:
                    gameOver(true);
                    break;
                case Constants.GUESS_ERROR:
                    gameOver(false);
                    break;
                case Constants.TIME_OUT:
                    timeOut();
                    break;
                default:
                    break;
            }
        });

    }

主设备的画板实现了个传输绘图数据的接口,每次进行绘制操作都把数据发送给从设备

    /**
     * 把新画的path数据传送给远程端
     *
     */
    private void sendDrawData(List<Point> points,int color,DrawView.State state,float width) {
        LogUtils.info(getClass().getSimpleName() + " sendPixelMap");
        if (mRemoteProxy == null) {
            ToastUtils.show(getContext(), "无跨端连接代理");
        } else {
            mRemoteProxy.sendDrawData(points, color, state, width);
        }
    }

从设备根据接受到的数据进行还原达到展示主设备上面的绘图信息效果

/**
     * 远程端处理主机端发送来的path数据
     *
     * @param points path经过point集合
     * @param colors 画笔颜色
     * @param states paint or eraser
     * @param widths 画笔宽度
     */
    private void handleDrawData(List<Point> points, int colors, DrawView.State states, float widths) {
        //切换到主线程
        getUITaskDispatcher().asyncDispatch(() -> {
            LogUtils.info(getClass().getSimpleName()+" points "+points);
            drawView.setDrawPaintColor(new Color(colors));
            drawView.setCurrentState(states);
            if(states== DrawView.State.PEN){
                drawView.setDrawPaintStrokeWidth(widths);
            }
            else {
                drawView.setEraserPaintStrokeWidth(widths);
            }
            drawView.drawPoints(points);
        });
    }

五、简单的Dialog

游戏中需要使用到确认对话框、选择对话框、输入对话框、提示对话框。通过继承CommonDialog来实现自定义的Dialog

1.AlertDialog

在这里插入图片描述

package com.tang.draw.dialog;
import com.tang.draw.ResourceTable;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.text.RichText;
import ohos.agp.text.richstyles.ImageRichStyle;
import ohos.agp.text.richstyles.RangedRichStyle;
import ohos.agp.text.richstyles.UnderlineRichStyle;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.media.image.ImageSource;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;

import java.io.IOException;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;

/********
 *文件名: AlertDialog
 *创建者: 醉意丶千层梦
 *创建时间:2022/2/16 16:16
 *描述: AlertDialog
 ********/
public class AlertDialog extends CommonDialog {
    private Context mContext;
    private Text titleText, contentText;
    private Button okBtn;
    private DialogClickListener dialogClickListener;

    public AlertDialog(Context context) {
        super(context);
        this.mContext = context;
        initComponents();
        initClickedListener();

    }

    private void initClickedListener() {
        okBtn.setClickedListener(component -> {
                hide();
                if (dialogClickListener != null) {
                    dialogClickListener.onOKClick();
                }
            }
        );

    }

    private void initComponents() {
        Component component = LayoutScatter.getInstance(mContext)
                .parse(ResourceTable.Layout_dialog_alert, null, true);

        contentText = component.findComponentById(ResourceTable.Id_text_content);
        titleText = component.findComponentById(ResourceTable.Id_text_title);
        okBtn = component.findComponentById(ResourceTable.Id_btn_ok);

        //设置对话框的布局
        setContentCustomComponent(component);
        //居中
        setAlignment(LayoutAlignment.CENTER);

        setCornerRadius(50);

        //设置高度为自适应,宽度为屏幕的0.8
        setSize(mContext.getResourceManager().getDeviceCapability().width
                * mContext.getResourceManager().getDeviceCapability().screenDensity
                / 160*4/5, MATCH_CONTENT);

    }

    public void setDialogClickListener(DialogClickListener dialogClickListener){
        this.dialogClickListener=dialogClickListener;
    }


    public void setmContentText(String text,boolean isWinner){
        if (ohos.system.version.SystemVersion.getApiVersion()==7){
            RichText richText=new RichText(text);
            UnderlineRichStyle underlineRichStyle = new UnderlineRichStyle(Color.BLUE);
            richText.setRichStyle(underlineRichStyle,0,text.length(), RangedRichStyle.Flag.EXCLUDE);
            ImageRichStyle imageRichStyle=getImageRichStyle(isWinner);
            if (imageRichStyle!=null){
                richText.setRichStyle(imageRichStyle,text.length()-1,text.length(),RangedRichStyle.Flag.EXCLUDE);
            }
            contentText.setRichText(richText);
        }

        else {
            contentText.setText(text);
        }
    }
    public void setmContentText(String text){
        contentText.setText(text);
    }

    private ImageRichStyle getImageRichStyle(boolean isWinner){
        ImageRichStyle imageRichStyle = null;
        ImageSource imageSource;
        try {
            if (isWinner){
                imageSource = ImageSource.create(mContext.getResourceManager().getResource(ResourceTable.Media_icon_victory),null);
            }
            else {
                imageSource = ImageSource.create(mContext.getResourceManager().getResource(ResourceTable.Media_icon_defeat),null);
            }

            ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();

            decodingOpts.desiredSize = new Size(contentText.getHeight()-10, contentText.getHeight()-10);

            decodingOpts.desiredRegion = new Rect(0, 0, 0, 0);

            decodingOpts.desiredPixelFormat = PixelFormat.ARGB_8888;

            PixelMapElement pixelMapElement =new PixelMapElement(imageSource.createPixelmap(decodingOpts));

            imageRichStyle=new ImageRichStyle(pixelMapElement);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotExistException e) {
            e.printStackTrace();
        }
        return imageRichStyle;
    }

    public interface DialogClickListener {
        void onOKClick();
    }
}

2.ConfirmDialog

在这里插入图片描述

package com.tang.draw.dialog;

import com.tang.draw.ResourceTable;
import com.tang.draw.utils.LogUtils;
import com.tang.draw.view.StrokeWidthChooseView;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;

/********
 *文件名: ConfirmDialog
 *创建者: 醉意丶千层梦
 *创建时间:2022/2/7 17:24
 *描述: ConfirmDialog
 ********/
public class ConfirmDialog extends CommonDialog implements Component.ClickedListener {


    private Context context;
    private Text  detailText;
    private Button okBtn,cancelBtn;
    private OnDialogClickListener dialogClickListener;
    public ConfirmDialog(Context context) {
        super(context);
        this.context=context;

        //居中
        setAlignment(LayoutAlignment.CENTER);

        //设置高度为自适应,宽度为屏幕的0.8
        setSize(context.getResourceManager().getDeviceCapability().width
                * context.getResourceManager().getDeviceCapability().screenDensity
                / 160*4/5, MATCH_CONTENT);
        //设置是否启用触摸对话框外区域关闭对话框的功能
        setAutoClosable(false);
        initComponents();

        LogUtils.info(getClass().getSimpleName()+" --- create dialog");
    }

    @Override
    public void onClick(Component component) {
        if (component==okBtn){
            LogUtils.info(getClass().getSimpleName()+" --- ok click");
            //关闭dialog
            hide();
            if(dialogClickListener != null){
                dialogClickListener.onOKClick();
            }
        }
        else if(component==cancelBtn){
            LogUtils.info(getClass().getSimpleName()+" --- cancel click");
            //关闭dialog
            hide();
            if(dialogClickListener != null){
                dialogClickListener.onCancelClick();
            }
        }
    }

    /**
     *按钮接口
     */
    public interface OnDialogClickListener{
        void onOKClick();
        void onCancelClick();
    }

    /**
     * 初始化组件以及设置对应按钮监听事件
     */
    private void initComponents(){
        //设置布局xml
        Component component= LayoutScatter.getInstance(context)
                .parse(ResourceTable.Layout_dialog_confirm,null,true);

        detailText=component.findComponentById(ResourceTable.Id_text_detail);
        okBtn=component.findComponentById(ResourceTable.Id_btn_ok);
        cancelBtn=component.findComponentById(ResourceTable.Id_btn_cancel);

        //设置监听
        okBtn.setClickedListener(this::onClick);
        cancelBtn.setClickedListener(this::onClick);


        super.setContentCustomComponent(component);
    }

    public void setOnDialogClickListener(OnDialogClickListener clickListener){
        dialogClickListener = clickListener;
    }


    /**
     * 设置提示内容
     *
     * @param text
     */
    public void setDetailText(String text){
        if (detailText!=null){
            detailText.setText(text);
        }
    }
}

3.PromptDialog

在这里插入图片描述

package com.tang.draw.dialog;

import com.tang.draw.ResourceTable;
import com.tang.draw.utils.LogUtils;
import ohos.agp.components.*;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.text.RichText;
import ohos.agp.text.richstyles.ImageRichStyle;
import ohos.agp.text.richstyles.RangedRichStyle;
import ohos.agp.text.richstyles.UnderlineRichStyle;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.media.image.ImageSource;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;

import java.io.IOException;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;

/********
 *文件名: PromptDialog
 *创建者: 醉意丶千层梦
 *创建时间:2022/2/9 22:36
 *描述: PromptDialog
 ********/
public class PromptDialog extends CommonDialog {
    private Context mContext;
    private Text titleText,errorText;
    private TextField input;
    private Button okBtn,cancelBtn;
    private DialogClickListener dialogClickListener;

    public PromptDialog(Context context) {
        super(context);
        this.mContext =context;

        initComponents();
        initClickedListener();
    }

    private void initClickedListener() {
        okBtn.setClickedListener(this::onClick);
        cancelBtn.setClickedListener(this::onClick);

    }
    public void onClick(Component component) {
        if (component==okBtn){
            LogUtils.info(getClass().getSimpleName()+" --- ok click");
            //关闭dialog

            if(input.getText().length()>8){
                setError(true,"最大长度8");
            }
            else{
                if(dialogClickListener != null){
                    dialogClickListener.onOKClick(input.getText());
                    input.setText("");
                }
            }

        }
        else if(component==cancelBtn){
            LogUtils.info(getClass().getSimpleName()+" --- cancel click");
            //关闭dialog
            hide();
            if(dialogClickListener != null){
                dialogClickListener.onCancelClick();
            }
        }
    }

    private void initComponents(){
        Component component= LayoutScatter.getInstance(mContext)
                .parse(ResourceTable.Layout_dialog_prompt,null,true);
        okBtn=component.findComponentById(ResourceTable.Id_btn_ok);
        cancelBtn=component.findComponentById(ResourceTable.Id_btn_cancel);
        input=component.findComponentById(ResourceTable.Id_field_input);
        titleText=component.findComponentById(ResourceTable.Id_text_title);
        errorText=component.findComponentById(ResourceTable.Id_error_tip_text);
        okBtn.setEnabled(false);
        input.addTextObserver(new Text.TextObserver() {
            @Override
            public void onTextUpdated(String s, int i, int i1, int i2) {
                if(s==null || s.isEmpty()){
                    okBtn.setEnabled(false);
                }
                else {
                    okBtn.setEnabled(true);
                }
            }
        });

        input.setFocusChangedListener(new Component.FocusChangedListener() {
            @Override
            public void onFocusChange(Component component, boolean hasFocus) {
                if(hasFocus){
                  setError(false,"");

                }
            }
        });

        super.setContentCustomComponent(component);

        //居中
        setAlignment(LayoutAlignment.CENTER);

        setCornerRadius(50);

        //设置高度为自适应,宽度为屏幕的0.8
        setSize(mContext.getResourceManager().getDeviceCapability().width
                * mContext.getResourceManager().getDeviceCapability().screenDensity
                / 160*4/5, MATCH_CONTENT);
    }



    /**
     *按钮接口
     */
    public interface DialogClickListener{
        void onOKClick(String inputData);
        void onCancelClick();
    }

    public void setOnDialogClickListener(DialogClickListener clickListener){
        dialogClickListener = clickListener;
    }

    public void setError(boolean isError,String message){
        if(isError){
            errorText.setVisibility(Component.VISIBLE);
            errorText.setText(message);
            ShapeElement errorElement = new ShapeElement(this.mContext, ResourceTable.Graphic_background_text_field_error);
            input.setBackground(errorElement);
            input.clearFocus();
        }
        else{
            errorText.setVisibility(Component.INVISIBLE);
            ShapeElement errorElement = new ShapeElement(this.mContext, ResourceTable.Graphic_background_field_input);
            input.setBackground(errorElement);
        }
    }
}

4.SelectDialog

在这里插入图片描述

package com.tang.draw.dialog;

import com.tang.draw.ResourceTable;
import com.tang.draw.provide.DeviceItemProvider;
import com.tang.draw.provide.SelectItemProvider;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.distributedschedule.interwork.DeviceInfo;

import java.util.ArrayList;
import java.util.List;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;
import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_PARENT;

/********
 *文件名: SelectDialog
 *创建者: 醉意丶千层梦
 *创建时间:2022/2/15 14:17
 *描述: SelectDialog
 ********/
public class SelectDialog extends CommonDialog {
    //圆角矩形的圆角半径
    private static final int RADIO_SIZE =10;

    //支持分布式协同的设备列表
    private  String[] mDataList ;

    //设备选择回调监听
    private final ItemSelectListener mItemSelectListener;

    //当前上下文对象
    private final Context mContext;

    public SelectDialog(Context context, String[] dataList,ItemSelectListener itemSelectListener) {
        super(context);
        this.mItemSelectListener = itemSelectListener;
        this.mContext = context;
        this.mDataList =dataList;
    }


    @Override
    public void onCreate(){
        super.onCreate();

        //初始化界面布局
        Component rootView = LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_dialog_select,null,false);

        //初始化picker
        Picker picker=rootView.findComponentById(ResourceTable.Id_picker);

        //初始化按钮
        Button cancelBtn= rootView.findComponentById(ResourceTable.Id_btn_cancel);
        Button okBtn=rootView.findComponentById(ResourceTable.Id_btn_ok);


        if (mDataList==null || mDataList.length==0){
            String item[]={"没有数据"};
            picker.setDisplayedData(item);
            picker.setValue(0);
            picker.setEnabled(false);
            okBtn.setClickedListener(component -> {
                destroy();
            });
        }
        else{
            ShapeElement shape = new ShapeElement();
            shape.setShape(ShapeElement.RECTANGLE);
            shape.setRgbColor(RgbColor.fromArgbInt(0xCFCFCF));
            picker.setDisplayedLinesElements(shape, shape);
            picker.setDisplayedData(mDataList);
            okBtn.setClickedListener(component -> {
                mItemSelectListener.onItemSelected(mDataList[picker.getValue()]);
                destroy();
            });
        }
        //设置点击监听
        cancelBtn.setClickedListener(component -> {
            destroy();
        });



        //设置对话框尺寸
        setSize(MATCH_PARENT,MATCH_CONTENT);

        //设置对话框位置
        setAlignment(LayoutAlignment.CENTER);

        //设置对话框的圆角背景
        setCornerRadius(RADIO_SIZE);

        //设置对话框背景为透明
        setTransparent(true);

        //设置对话框的布局
        setContentCustomComponent(rootView);

    }

    public interface ItemSelectListener{
        void onItemSelected(String item);
    }
}

六、效果

数据管理
长按数据可以选择删除
在这里插入图片描述

游戏
选择关键字和游戏时间发起跨端连接
在这里插入图片描述
选择画笔颜色、粗细,撤销操作
在这里插入图片描述
橡皮擦功能和作答
在这里插入图片描述
退出游戏
在这里插入图片描述

七、源码

github:https://github.com/TangtangSix/DrawAndGuess
gitee:https://gitee.com/tangtangsix/DrawAndGuess

八、参考

自定义view-(你画我猜)画板

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值