当前位置: 首页 > 知识库问答 >
问题:

如果用户单击圆圈附近,将圆圈按钮移动到随机位置的应用程序

闻修筠
2023-03-14

我目前有一个Android应用程序,如果我单击屏幕上不是圆本身的某个位置,它会将绘制的圆移动到屏幕上的随机位置。

我的问题是,我如何实现它,使这个逻辑只在点击距离圆圈相对较近时执行?距离的定义并不严格,但是,如果一个圆位于屏幕的左下方,单击屏幕的右上方不会触发该圆移动到新位置。我在想象一个比圆形稍大的正方形,它可以作为点击框(但仍然排除圆形内的点击)

我假设必须在isInsideCircle函数中进行更改,但我缺乏必要的知识来理解应该添加/减去哪些变量来限制触发逻辑的区域。以下是我目前掌握的代码:

    public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new Circle(this));
    }
    public class Circle extends View {
        private float x = 300;
        private float y = 300;
        private int r = 150;
        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private Random random = new Random();

        // draws circle
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(Color.RED);
            canvas.drawCircle(x, y, r, mPaint);
        }

        // constructor
        public Circle(Context context) {
            super(context);
        }

        // gets random coordinates
        void generateRandom() {

            int w = getWidth() - r - 50;
            int h = getHeight()- r - 50;

            int border = r + 50;

            this.x = border + random.nextInt(w-border);
            this.y = border + random.nextInt(h-border);
        }

        // when screen is tapped, old circle removed, new circle drawn
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isInsideCircle(event.getX(), event.getY())) { // only register clicks outside of circle
                generateRandom();
                invalidate();
            }
            return super.onTouchEvent(event);
        }


        boolean isInsideCircle(float xPoint, float yPoint) {
            float dx = (x - xPoint);
            float dxPow = (float) Math.pow(dx, 2);
            float dy = (y - yPoint);
            float dyPow = (float) Math.pow(dy, 2);
            float radPow = (float) Math.pow(r, 2);
            return ((dxPow + dyPow) <= radPow));
        }
    }
}

任何帮助都将不胜感激,谢谢!

共有1个答案

嵇昱
2023-03-14

这个问题有几个部分,我想确保在讨论之前非常清楚。

  1. 我们希望将命中区域修改为正方形,而不是圆形

我实际上要先从第二点开始,然后继续到第一点。

这一点非常简单,因为您已经在测试单击是否在圆内,至少这是由类的isInsideCircle方法假设的。碰巧,如果你不是(!)的话,可以很安全地假设在圈内,你就在圈外。如果(!isInsideCircle(…)修改<代码>) 到<代码>如果(isInsideCircle(…)) (删除not[!])运营商)应该在这方面帮助我们,不是吗?嗯,这是相当痛苦的,希望测试是在一个正方形内是一样容易!

。。。不会像检查我们是否在袭击区域内部而不是外部那么容易
我的意思是,它确实需要在isInsideCircle执行的逻辑中进行整个转换。此外,我不确定更改isInsideCircle的主体是否是一个好主意,以测试它是否位于正方形内。这可能有点令人困惑,不是吗?

因此,创建一个新方法来测试给定的(x, y)坐标是否在正方形内可能是一个更好的主意。也许isInside Square?等一下!一个Square肯定不是一个Circle那么,我们需要什么信息来表示Square?现在我们需要一种方法来表示Square以及一种方法来判断坐标是否在正方形内。正方形是一个特殊的矩形,您只需要一个特殊的坐标位置(顶点或确切的中心)和边长来比较坐标与它。我们只是碰巧有一个坐标位置可以用作中心,所以,我想我们需要hitbox的边长。让我们在我们的圆形类中添加一个新字段来表示hitbox边长。下面的行将默认为比圆的直径多50个像素。

    private int hitboxLength = (2 * r) + 50;

现在我们有一个坐标点,有点遗憾,它位于正方形的中间,它是边长。那么,我们如何确定一个点是否在我们的方形命中框内?很高兴知道一个矩形也可以被描述为4个顶点。对于边长为2、以(0,0)为中心的正方形,顶点位于(1,1)、(1,-1)、(-1,-1)和(-1,1)。为了找出(x,y)是否在我们的矩形内,我们只需要不在这4个坐标内。X从1到-1,Y也从1到-1。如果x介于-1和1之间,则表示

    boolean isInsideSquare(float xPoint, float yPoint) {
        // The center of a square is HALF it's length from any vertex.
        // Always try to use equivalent number types when comparing
        float halfHitboxLength = this.hitboxLength / 2.0F;
        float xUpperBound = this.x + halfHitboxLength;
        float xLowerBound = this.x - halfHitboxLength;
        // smallest expected X <= xPoint <= largest expected X
        boolean xInSquare = xLowerBound <= xPoint && xPoint <= xUpperBound;

        float yUpperBound = this.y + halfHitboxLength;
        float yLowerBound = this.y - halfHitboxLength;
        boolean yInSquare = yLowerBound <= yPoint && yPoint <= yUpperBound;

        return xInSquare && yInSquare;
    }

毫无疑问,这不是最有效的处理方法,但它应该为我们提供在给定范围内的逻辑。

现在我们只需要在onTouchEvent方法中使用isInsideSquare而不是isinsidercle

    // This "if"
    if (!isInsideCircle(event.getX(), event.getY())) {
    // Would become
    if (!isInsideSquare(event.getX(), event.getY())) {

然而,我不禁认为用正方形表示一个圆圈感觉有点奇怪。至少,考虑到它们的形状有多么不同。更不用说isInside Square是否属于Circle类的问题了!如何用另一个稍微大一点的圆圈外观来表示我们圆圈的命中区域?

我们已经有了检查(x,y)坐标对是否在当前圆内的逻辑。我的意思是有一种方法叫做isInsideCircle。

        boolean isInsideCircle(float xPoint, float yPoint) {
            float dx = (x - xPoint);
            float dxPow = (float) Math.pow(dx, 2);
            float dy = (y - yPoint);
            float dyPow = (float) Math.pow(dy, 2);
            float radPow = (float) Math.pow(r, 2);
            return ((dxPow + dyPow) <= radPow));
        }

就我个人而言,我的注意力立刻被吸引到了几个地方

  1. 返回<代码>返回((dxPow dyPow)

返回,因为这是我们确定给定(xPoint,yPoint)对是否在我们的圆实例中的方式,但更重要的是,radPow变量,因为它似乎是引用的,我只能假设是圆的半径,要测试一个更大的圆,我们真正需要做的就是调整它的半径!

    int userWithinPixels = 50;
    float radPow = (float) Math.pow(r + userWithinPixels, 2);

嗯,这是相当无痛的!

再次值得指出的是,在圆上调用isInsideCircle可以测试它是否在所表示的圆内而不是稍大的圆内,但这仍然是个人偏好,您应该做对项目程序员有意义的事情。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new Circle(this));
    }
    public class Circle extends View {
        private float x = 300;
        private float y = 300;
        private int r = 150;
        // Add hitbox side length
        private int hitboxLength = (2 * r) + 50;
        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private Random random = new Random();

        // draws circle
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(Color.RED);
            canvas.drawCircle(x, y, r, mPaint);
        }

        // constructor
        public Circle(Context context) {
            super(context);
        }

        // gets random coordinates
        void generateRandom() {

            int w = getWidth() - r - 50;
            int h = getHeight()- r - 50;

            int border = r + 50;

            this.x = border + random.nextInt(w-border);
            this.y = border + random.nextInt(h-border);
        }

        // when screen is tapped, old circle removed, new circle drawn
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (isInsideSquare(event.getX(), event.getY())) { // only register clicks outside of circle
                generateRandom();
                invalidate();
            }
            return super.onTouchEvent(event);
        }

        boolean isInsideSquare(float xPoint, float yPoint) {
            // The center of a square is HALF it's length from any vertex.
            // Always try to use the same number types when comparing/computing
            float halfHitboxLength = this.hitboxLength / 2.0F;
            float xUpperBound = this.x + halfHitboxLength;
            float xLowerBound = this.x - halfHitboxLength;
            // smallest expected X <= xPoint <= largest expected X
            boolean xInSquare = xLowerBound <= xPoint && xPoint <= xUpperBound;

            float yUpperBound = this.y + halfHitboxLength;
            float yLowerBound = this.y - halfHitboxLength;
            boolean yInSquare = yLowerBound <= yPoint && yPoint <= yUpperBound;

            return xInSquare && yInSquare;
        }


        boolean isInsideCircle(float xPoint, float yPoint) {
            float dx = (x - xPoint);
            float dxPow = (float) Math.pow(dx, 2);
            float dy = (y - yPoint);
            float dyPow = (float) Math.pow(dy, 2);
            float radPow = (float) Math.pow(r, 2);
            return ((dxPow + dyPow) <= radPow));
        }
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new Circle(this));
    }
    public class Circle extends View {
        private float x = 300;
        private float y = 300;
        private int r = 150;
        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private Random random = new Random();

        // draws circle
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(Color.RED);
            canvas.drawCircle(x, y, r, mPaint);
        }

        // constructor
        public Circle(Context context) {
            super(context);
        }

        // gets random coordinates
        void generateRandom() {

            int w = getWidth() - r - 50;
            int h = getHeight()- r - 50;

            int border = r + 50;

            this.x = border + random.nextInt(w-border);
            this.y = border + random.nextInt(h-border);
        }

        // when screen is tapped, old circle removed, new circle drawn
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (isInsideCircle(event.getX(), event.getY())) { // only register clicks outside of circle
                generateRandom();
                invalidate();
            }
            return super.onTouchEvent(event);
        }


        boolean isInsideCircle(float xPoint, float yPoint) {
            float dx = (x - xPoint);
            float dxPow = (float) Math.pow(dx, 2);
            float dy = (y - yPoint);
            float dyPow = (float) Math.pow(dy, 2);
            // maximum amount of pixels user can click off the circle and trigger event
            float userWithinPixels = 25;
            float radPow = (float) Math.pow(r + userWithinPixels, 2);
            return ((dxPow + dyPow) <= radPow));
        }
    }
}

 类似资料:
  • 我想在我的一个项目中更改RadioButton圆圈的颜色,但我不知道要设置哪个属性。背景色为黑色,因此不可见。我想把圆圈的颜色设置为白色。

  • 问题内容: 如何将几个元素放置在另一个元素周围的圆中,并使这些元素也都可单击链接?我希望它看起来像下面的图片,但是我不知道如何实现这种效果。 这有可能吗? 问题答案: 是的,仅使用CSS很有可能而且非常简单。您只需要 清楚要与图像链接的角度即可 (我在末尾添加了一段代码,用于在 您悬停其中一个角度时显示它们)。 演示 您首先需要一个包装器。我将其直径设置为24em(width: 24em; hei

  • 问题内容: 我正在创建一个应用程序,该应用程序在gridPane的每个单元格内随机显示(不同颜色的)圆圈。 我想要做的是创建一个“随机播放”按钮,以随机更改gridPane中每个圆的位置。但是,我一直遇到很多问题。 这是我到目前为止所拥有的。我的两个类(尚未添加XML文件): 控制器类 主班 问题答案: 这是一个示例,演示了如何在。如果添加了一个,你可以删除从。然后,您可以随机播放。最后,您可以将

  • 我正在尝试制作重力的效果,但它看起来只是有越来越多的圆圈条纹,而不是单个圆圈向下移动。我不知道如何删除我已经绘制的圆圈。顺便说一句,代码中没有错误。

  • 问题内容: 我正在JPanel中绘制两个形状(圆形),我需要用一条 线将它们连接起来。我这样做是通过简单地获得圆的中点并 相互连接来实现的。 问题在于,现在我需要制作单向线,该单向线 的末尾带有“箭头”,以指出线的前进方向。所以现在我不能 使用圆的中点,因为我需要从 边界到边界相互连接,因此“箭头”可以正确显示。 在我上一次尝试的结果是,没有任何好处: PS:在屏幕截图中,我并不是为了看到 直线的

  • 问题内容: 我只想在鼠标单击后绘制圆圈。由于paintComponent方法调用了自身,因此首先绘制圆而无需单击。 问题答案: 您的代码存在一些问题: 你永远不会打电话 你只需要一个和 请注意,当您调整框架大小时,某些圆圈将消失,并且总体上以奇怪的方式表现。 我会将所有s 存储在用户单击的位置,然后在方法内部遍历该列表。这样您就可以通话,而圈子不会消失。 更改后的工作代码: