create/change/delete/undo 按钮 canvas、onClick() 上不同形状的数量

create/change/delete/undo number of different shapes on canvas, onClick() of button

我正在为 形状创建 android 应用程序 canvas onClick of button.

我想要的:

我有三个按钮 'Square'、'Circle'、'Triangle'。 每次我点击按钮时,该形状的对象将被创建并显示在一个 canvas 上的随机位置。(完成)

点击每个形状会使它变成另一个形状。 点击一个正方形会使它变成一个圆 点击一个圆圈会使它变成一个三角形 点击三角形会变成正方形(完成)

长按形状可以删除形状。(完成)

如何实现撤销功能!

尝试和错误:为了实现此功能,我到目前为止尝试了以下代码,在该代码中我可以使用 java 和图像中的位图单击按钮创建一个形状在 xml 中查看。

Update_29/10: 我使用相同的代码并使用动态相对布局添加视图在 canvas 上创建多个形状。

我的问题:

通过单击按钮在 canvas 上创建形状的这种方式(位图和图像视图)是否正确?

我现在可以在 canvas 上创建多个形状,但每次我都在创建新的 canvas 实例!还有其他方法可以实现吗?

Update_29_10:

如何单击形状以便我也可以删除形状和撤消功能。

XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".MainActivity">

<RelativeLayout
    android:id="@+id/relative4"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:layout_above="@+id/btnCircle"
    android:background="@color/colorAccent">

</RelativeLayout>
<Button
    android:id="@+id/btnSquare"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:text="@string/square" />

<Button
    android:id="@+id/btnCircle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_toRightOf="@+id/btnSquare"
    android:text="@string/circle" />


<Button
android:id="@+id/btnTriangle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
    android:layout_toRightOf="@+id/btnCircle"
    android:text="@string/triangle" />

<Button
    android:id="@+id/btnUndo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@+id/btnTriangle"
    android:layout_alignParentBottom="true"
    android:text="@string/undo" />

MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Context mContext;
    private Resources mResources;
    private RelativeLayout mRelativeLayout;
    private Button btnSquare, btnCircle, btnTriangle,btnUndo,btnState;
    private int mSuareCount=0,mCircleCount=0,mTriangelCount=0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private void initViews() {
        mContext = getApplicationContext();
        mResources = getResources();
        mRelativeLayout = (RelativeLayout) findViewById(R.id.rl);
        btnSquare = (Button) findViewById(R.id.btnSquare);
        btnCircle = (Button)findViewById(R.id.btnCircle);
        btnTriangle = (Button)findViewById(R.id.btnTriangle);
        btnUndo=(Button)findViewById(R.id.btnUndo);
        setOnClickListeners();
    }

    private void setOnClickListeners() {
        btnSquare.setOnClickListener(this);
        btnCircle.setOnClickListener(this);
        btnTriangle.setOnClickListener(this);
        btnUndo.setOnClickListener(this);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btnSquare:
                drawSquare(null);
                mSuareCount++;
                break;

            case R.id.btnCircle:
                drawCircle(null);
                mCircleCount++;
                break;

            case R.id.btnTriangle:
                drawTriangle(null);
                mTriangelCount++;
                break;

            case R.id.btnUndo:

                break;

        }
    }

    private void drawSquare(ImageView imageView) {
        Bitmap bitmap = Bitmap.createBitmap(
                50, // Width
                50, // Height
                Bitmap.Config.ARGB_8888 // Config
        );

        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(Color.LTGRAY);
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.YELLOW);
        paint.setAntiAlias(true);

        int padding = 50;
        Rect rectangle = new Rect(
                padding, // Left
                padding, // Top
                canvas.getWidth() - padding, // Right
                canvas.getHeight() - padding // Bottom
        );
        canvas.drawRect(rectangle, paint);
        addViews(bitmap,imageView,1);

        // Display the newly created bitmap on app interface
        if (imageView == null) {
            imageView = new ImageView(this);
        }
        imageView.setImageBitmap(bitmap);

        final ImageView finalImageView = imageView;
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                drawCircle(finalImageView);
                mSuareCount--;
                mCircleCount++;
            }
        });
    }

    private void drawCircle(ImageView imageView) {
        Bitmap bitmap = Bitmap.createBitmap(
                50, // Width
                50, // Height
                Bitmap.Config.ARGB_8888 // Config
        );

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.RED);
        paint.setAntiAlias(true);

        int radius = Math.min(canvas.getWidth(), canvas.getHeight() / 2);
        int padding = 5;
        canvas.drawCircle(
                canvas.getWidth() / 2, // cx
                canvas.getHeight() / 2, // cy
                radius - padding, // Radius
                paint // Paint
        );

        addViews(bitmap,imageView,2);

        // Display the newly created bitmap on app interface
        if (imageView == null) {
            imageView = new ImageView(this);
        }
        imageView.setImageBitmap(bitmap);

        final ImageView finalImageView = imageView;
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                drawTriangle(finalImageView);
                mCircleCount--;
                mTriangelCount++;
            }
        });

    }

    private void drawTriangle(ImageView imageView) {
        Bitmap bitmap = Bitmap.createBitmap(
                500, // Width
                500, // Height
                Bitmap.Config.ARGB_8888 // Config
        );

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(),
                bitmap.getHeight());


        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.YELLOW);
        paint.setAntiAlias(true);
        Point point1_draw = new Point(90, 0);
        Point point2_draw = new Point(0, 180);
        Point point3_draw = new Point(180, 180);

        Path path = new Path();
        path.moveTo(point1_draw.x, point1_draw.y);
        path.lineTo(point2_draw.x, point2_draw.y);
        path.lineTo(point3_draw.x, point3_draw.y);
        path.lineTo(point1_draw.x, point1_draw.y);
        path.close();
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(Color.parseColor("#3F51B5"));
        canvas.drawPath(path, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
        //addViews(bitmap,imageView);

        addViews(bitmap,imageView,3);

        if (imageView == null) {
            imageView = new ImageView(this);
        }
        imageView.setImageBitmap(bitmap);
        final ImageView finalImageView = imageView;
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                drawSquare(finalImageView);
                mSuareCount++;
                mTriangelCount--;
            }
        });

    }

    private void addViews(Bitmap bitmap, ImageView imageView, final int value) {
        final int min = 20;
        final int max = 80;


        Drawable d = getResources().getDrawable(R.mipmap.ic_launcher_round);
        final int w = d.getIntrinsicWidth();
        final int random = new Random().nextInt((max - min) + 1) + min;

        RelativeLayout relative4 = (RelativeLayout) findViewById(R.id.relative4);
        int width = relative4.getMeasuredWidth();
        int height = relative4.getMeasuredHeight();
        if (imageView == null) {
            imageView = new ImageView(this);
        }
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
        params.setMargins(new Random().nextInt((width - 0) + 1), new Random().nextInt((height - 0) + 1), 10, 10);
        imageView.setLayoutParams(params);
        imageView.setImageBitmap(bitmap);


        if (imageView != null) {
            ViewGroup parent = (ViewGroup) imageView.getParent();
            if (parent != null) {
                parent.removeView(imageView);
            }
        }

        relative4.addView(imageView);

        final ImageView finalImageView = imageView;


        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                switch (value) {
                    case 1:
                        drawCircle(finalImageView);
                        mSuareCount--;
                        mCircleCount++;
                        break;

                    case 2:
                        drawTriangle(finalImageView);
                        mCircleCount--;
                        mTriangelCount++;
                        break;

                    case 3:
                        drawSquare(finalImageView);
                        mTriangelCount--;
                        mSuareCount++;
                        break;

                }

            }
        });

        imageView.setOnLongClickListener(new View.OnLongClickListener(){


            @Override
            public boolean onLongClick(View v) {
                  switch (value) {
                case 1:
                    relative4.removeView(finalImageView);
                    mSquareCount--;
                    break;

                case 2:
                    relative4.removeView(finalImageView);
                    mCircleCount--;
                    break;

                case 3:
                    relative4.removeView(finalImageView);
                    mTriangleCount--;
                    break;
            }
            return true;
        });
    }

}

您可以使用以下方法(伪代码)实现功能:

第 1 步:

interface ShapeInfo {
void drawShape(Canvas canvas);
}

第 2 步: 使用此接口实现 3 种不同的 类(矩形、三角形、圆形)

第 3 步:将背景设置为 RelativeLayout

第 4 步:在 RelativeLayout 中添加 CustomView 使这个自定义视图透明。

    public class CustomView extends ImageView{

    ArrayList<ShapeInfo> alShapesInfo = new ArrayList();
            public CustomView(Context context) {
                super(context);
                // TODO Auto-generated constructor stub
            }

            @Override
            protected void onDraw(Canvas canvas) {
                super.onDraw(canvas);
                for ( ShapeInfo s:alShapesInfo)
                  s.drawShape(canvas);

            }
public void addShapeInfo(ShapeInfo s){alShapeinfo.add(s); invalidate();}

public void undo(){alShapeInfo.delete(alShapeInfo.size()-1); invalidate();}
        }

第 5 步: 在主 activity 按钮上单击添加三角形、圆形、矩形调用 customview.addshapeinfo

第 6 步:撤消调用

customview.undo

虽然 aanshu 的解决方案可能是更有效的方法,但他的解决方案很难识别对单个形状的点击。

与其制作一个 ImageView 的大子class 并覆盖 onDraw 并绘制每个子形状,您可以为每个 class 制作一个 ImageView 的子class正方形、圆形和三角形。像这样:

public class SquareView extends ImageView {
    @Override
    protected void onDraw(Canvas canvas) {
        //draw your Square on the canvas given
    }
}

圆形和三角形相同。然后像之前生成图像视图一样生成这些视图之一,但不要设置位图。您可以将它们添加到布局中,它会调用 onDraw 函数并给它一个 canvas。现在您可以像以前一样为每个视图注册您的 onClickListener。单击视图时,您将其替换为不同 class 的实例,例如,您将 SquareView 替换为 CircleView。

对于撤销操作,你可以这样做。这是非常像伪代码,我也可能会混合一些编程语言,但这个想法到处都是一样的。如有不明之处请追问。

Stack<Runnable> undoStack;
//and now whenever you do something that should be undoable you just add a runnable that will undo it:
//for example if a user clicked a SquareView:
removeView(squareView);
CircleView circleView = new CircleView();
//take the information from the squareView that is needed
circleview.position = squareView.position;
addView(circleView);
undoStack.push(() -> removeView(circleView); addView(squareView););
//When you undo you just call
undoStack.pop().run();

关于您的代码的一些背景知识,因为我认为它会帮助您理解。 This is the source of android ImageView. When you call setImageBitmap(bitmap), it passes the bitmap to an instance of BitmapDrawable which it calls mDrawable. Then in the ImageViews onDraw(Canvas canvas) method it calls mDrawable.draw(canvas) which in case of BitmapDrawable (source here) 在很多其他东西调用 canvas.drawBitmap(bitmap) 之后。所以基本上你的代码做了什么:它从 canvas 创建一个位图,然后通过 ImageView 和 BitmapDrawable 将位图绘制回 canvas。我和 aanshu 的解决方案直接借鉴了这个最终 canvas。这就是为什么它们比您当前的解决方案更好。

编辑: 在搜索其他内容时,我偶然发现了 drawable shape resources。与任何可绘制资源一样,您可以将它们传递给 ImageViews。这样您可能不必重写 onDraw 函数。但是我从来没有和他们一起工作过,所以我就把这个留在这里。