【问题标题】:How do you draw a custom view(e.g. Circle) on a View?您如何在视图上绘制自定义视图(例如圆形)?
【发布时间】:2024-04-30 00:10:02
【问题描述】:

对于我的 Pong 克隆,我正在尝试将自定义视图从 Ball 类绘制到 GamePanel 类中的视图上。 然而,在运行时它在屏幕上无处可见。

public class Ball extends View {

private float posX;
private float posY;
private float radius;

private Paint paint;

public Ball(Context context, float posX, float posY, float radius){
    super(context);
    this.posX = posX;
    this.posY = posY;
    this.radius = radius;
    setWillNotDraw(false);
    init();

}

private void init(){

    paint = new Paint();
    paint.setColor(Color.WHITE);
}

@Override
protected void onDraw(Canvas canvas) {

    canvas.drawCircle(posX, posY, radius, paint);

}

现在这是我的班级 GamePanel。

public class GamePanel extends View implements GestureDetector.OnGestureListener{

private Context context;

//Screen height and width in pixels
private static int width;
private static int height;

private MainThread thread;

private Rect paddle1;
private Rect paddle2;

private Ball ball;

private Paint paint;

//Starting position paddle1
private int xPos1;
private int yPos1;

// Starting position paddle2
private int xPos2;
private int yPos2;

//Width and height of paddles
private int pWidth;
private int pHeight;

private GestureDetectorCompat gestureDetector;

public GamePanel(Context context) {

    super(context);

    this.context = context;

    //Information we will need for positioning our objects inside the panel
    DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();

    width = displayMetrics.widthPixels;
    height = displayMetrics.heightPixels;

    //Make the GamePanel focusable
    setFocusable(true);

    // Thread needs information about the game panel
    thread = new MainThread(this);

    pWidth = 100;
    pHeight = height/2;

    xPos1 = width/15;
    yPos1 = 0;

    xPos2 = width - xPos1 - pWidth;
    yPos1 = 0;

    paddle1 = new Rect(xPos1, yPos1, xPos1 + pWidth, yPos1 + pHeight);
    paddle2 = new Rect(xPos2, yPos2, xPos2 + pWidth, yPos2 + pHeight);

    paint = new Paint();
    paint.setColor(Color.WHITE);

    ball = new Ball(context, 300, 300, 300);

    setBackgroundColor(Color.BLACK);

    this.gestureDetector = new GestureDetectorCompat(context, this);

    thread.setRunning(true);
    thread.start();
}


@Override
protected void onDraw(Canvas canvas) {

    canvas.drawRect(paddle1, paint);
    canvas.drawRect(paddle2, paint);

}

我尝试在我的 MainThread 线程、GamePanel 的构造函数和 Ball 的构造函数中调用 invalidate() 和 postInvalidate()。球永远不会被拉到屏幕上。

如您所见,我已经在 Pong 中遇到了这个问题,因此我在 GamePanel 中创建了两个非常难看的矩形。

如何封装矩形和圆形(球),使其仍然可以在 Canvas 上绘制?

【问题讨论】:

    标签: android android-studio canvas draw android-custom-view


    【解决方案1】:

    您正在 GamePanel 视图中创建 Ball(circle) 视图。仅仅因为您正在创建一个视图并不意味着它将在屏幕上绘制。您需要将视图附加到布局。 简单的解决方案是将您的 GamePanel 扩展到 Ball

    public class GamePanel extends Ball {
    
    }
    

    然后在GamePanelonDraw()函数中添加super.onDraw();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(); //Will draw the circle before drawing rectangle
        canvas.drawRect(paddle1, paint);
        canvas.drawRect(paddle2, paint);    
    }
    

    如果您将super.onDraw() 放在drawRect 下方,则绘制矩形后将绘制圆。

    您还需要了解添加视图的基本概念 布局。

    【讨论】:

    • 非常感谢您的回答。我理解你的做法。一个问题:如果我想添加多个自定义视图并在 GamePanel 中绘制它们怎么办?所以我的意思是,如果我也将 Rectangles 封装到它自己的类中,比如 Ball 呢?我会让 GamePanel 从 ViewGroup 或布局类型扩展并将自定义视图添加到那里吗?
    • 让我列出这些案例。如果您不打算移动这些形状,那么最好将 GamePanel 扩展到 ViewGroup 并将形状创建为单独的视图,就像您创建“Ball”一样,并将这些视图添加到 GmePanel。但是,如果您要将矩形和圆形等形状动态移动或平移到 GamePanel 内的任何位置,那么最好将它们的功能移动到另一个类(不是单独的视图)中并在 GamePanel 内初始化。请参阅此项目库,例如 github.com/81813780/AVLoadingIndicatorView
    • 好的,我明白了。如您所知,我已经将 Ball 放入一个单独的类中。我不能将它们放入 ViewGroup,因为它们需要是动态的。它现在需要扩展哪个类,我在 GamePanel 的哪里初始化它/我在哪里告诉它在 GamePanel 中绘制 Ball?抱歉问了这么多基本的东西。我是 Android 开发的新手,几天来我一直在寻找如何封装矩形和球。这似乎很简单,但我无法以某种方式弄清楚。谢谢你帮助我
    • 没有问题 :) 您需要将 GamePanel 扩展为 View,其他形状类不应扩展任何内容。在 GamePanel 的构造函数中初始化所有类(Ball)。然后计算绘制所有形状的位置。如果您使用触摸作为输入,则将触摸事件传递给所有形状类。他们应该计算他们的绘图位置。同样,当调用 GamePanel 的 onDraw 时,将画布对象传递给所有其他形状类,让它们使用自己计算的位置进行绘制。
    • 非常感谢。现在我看到它是如此明显。