Android 上可以处理不规则形状上的事件吗?

发布于 2024-10-31 15:12:37 字数 272 浏览 6 评论 0原文

据我目前所知(了解很少),Android 中的所有视图都是正方形或矩形。这几乎一直都很好,直到您想要(我真正想要的)创建可以处理事件的非方形形状。

我的目标是将一个圆分为 3 个部分,每个部分 120°。圆圈的每个部分都应该像一个按钮。问题是,如果我们查看圆的三分之二并将它们放在严格包含它们的方框中,它们会相互重叠:不切实际地知道用户要单击哪个...

我尝试使用自定义视图我在其中绘制了部分,但事件在视图的所有表面上触发。

任何建议或方向都非常受欢迎。

谢谢,保罗

As far as I know for now (which is little), all the Views in Android have square or rectangular shapes. This is fine almost all the time until you want - what I actually want - to create non-square shapes that could handle events.

My goal is to have a circle divided in 3 sections of 120° each. Each section of the circle should behave like a button. The problem is that if we look at those 3rds of a circle and place them in a square box that strictly contains them, they are overlapping each other: not practical to know which the user meant to click...

I tried with a custom view in which I drew my sections but the events are triggering on all the surface of the view.

Any suggestion or direction is very welcome.

Thx, Paul

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

久夏青 2024-11-07 15:12:37

我相信处理此问题的方法是重写每个控件上的 onTouch,对触摸坐标执行您自己的几何检查,如果它位于自定义区域之外返回 false 这将将事件传递到另一个视图。否则返回调用该方法的超级版本。我不是 100% 确定,所以如果我错了,请有人纠正我,但这可能值得一试。

I believe the way to handle this is to override onTouch on each control, perform your own geometric checks on the touch coordinates, and if it's outside the custom area return false which will pass over the event to another view. Otherwise return invoking the super version of the method. I'm not 100% sure so someone please correct me if I'm wrong, but it could be worth a try.

一绘本一梦想 2024-11-07 15:12:37

我不知道您是否可以显式创建这些形状,但肯定有一种方法可以在自定义视图中使用它。但这是相当困难的。

首先,您需要通过设置 OnTouchListener 来挂钩 OnTouch 事件,更多此处。最简单的方法是仅对 MotionEventACTION_UP 操作做出反应,可通过 MotionEvent.getAction() 访问,详细信息请参见 此处。然后,您可以通过 getX()getY() 获取此 MotionEvent 中事件发生的 X 和 Y 坐标。

现在开始数学...您现在必须计算点击发生在哪个部分。为此,您需要自定义视图的大小(以像素为单位)及其位置(左上角),恐怕我现在无法告诉您适当的方法......您必须挖掘它们......假设圆的中心始终位于视图的中心,并且圆完全延伸到视图边界,您现在可以计算扇区。

我们将事件的 X 和 Y 坐标分别称为 eventXeventY,并让 centerXcenterY 为圆心坐标以及radius 是圆的半径。

首先检查事件是否发生在圆内,然后计算角度:

int deltaX = eventX - centerX;
    int deltaY = eventY - centerY;

    if (Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) > radius) {
        // out of circle!
    }

    // offset for radiant, depending on quadrant
    double offset = 0;
    if (deltaX > 0 && deltaY < 0) {
        // top right -> nothing to do
    } else if (deltaX > 0 && deltaY > 0) {
        // bottom right -> add 90 degrees
        offset = Math.PI/2;
    } else if (deltaX < 0 && deltaY > 0) {
        // bottom left -> add 180 degrees
        offset = Math.PI;
    } else if (deltaX < 0 && deltaY < 0) {
        // top left -> add 270 degrees
        offset = Math.PI * 3/2;
    }

    //now calculate angle
    double angle = Math.asin(deltaY / deltaX);
    double total = angle + offset;

最后,total 应该是辐射点中点击的顺时针角度,您可以根据您的部分;)如果有问题请纠正我^^

I don't know whether you can create those shapes explicitly but there would definitely be a way to use this in a custom view. But this is quite difficult.

First you would need to hook on to the OnTouch event via setting an OnTouchListener, more here. The easiest way would be to only react on ACTION_UP actions of the MotionEvent, accessable via MotionEvent.getAction(), details over here. Of this MotionEvent you can then get the X and Y coordinates, where the event occured via getX() and getY().

Now begins the maths... you now have to calculate in which section the click occured. To do this, you need the size of your custom view in pixels and its location (upper left corner), I'm afraid I can't tell you the appropriate methods right now... you would have to dig them up... Assuming the center of your circle is always in the center of your view and that the circle extends fully to the views boundaries, you can now calculate the sector.

Let's call the events X and Y coordinates eventX and eventY respectively and let centerX and centerY be the circles center coordinates as well as radius being the circles radius.

First check, whether the event occured in the circle, then calculate angle:

int deltaX = eventX - centerX;
    int deltaY = eventY - centerY;

    if (Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) > radius) {
        // out of circle!
    }

    // offset for radiant, depending on quadrant
    double offset = 0;
    if (deltaX > 0 && deltaY < 0) {
        // top right -> nothing to do
    } else if (deltaX > 0 && deltaY > 0) {
        // bottom right -> add 90 degrees
        offset = Math.PI/2;
    } else if (deltaX < 0 && deltaY > 0) {
        // bottom left -> add 180 degrees
        offset = Math.PI;
    } else if (deltaX < 0 && deltaY < 0) {
        // top left -> add 270 degrees
        offset = Math.PI * 3/2;
    }

    //now calculate angle
    double angle = Math.asin(deltaY / deltaX);
    double total = angle + offset;

In the end, total should be the clockwise angle of the click in radiants which you can check against your sections ;) Correct me if there is something wrong^^

天暗了我发光 2024-11-07 15:12:37

首先非常感谢您的所有建议,这些建议帮助我实现了目标。

由于无法触发形状上的事件,因此在包含形状的视图上使用 onTouch() 是可行的方法。

接下来是运行它所需的一切。


首先,自定义视图 Zones.java:

package com.vector;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

public class Zones extends View {
    RectF rectf = new RectF(0, 0, 300, 300);
    Paint paint = new Paint();
    Canvas canvas = null;
    Integer zone = 0;

    // important: do not forget to add AttributeSet, it is necessary to have this
    // view called from an xml view file
    public Zones(Context context, AttributeSet attributeset) {
        super(context, attributeset);
        this.setBackgroundColor(0xFF207CA1);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        // set layout of the view
        layout(0, 0, 300, 300);

        // expose canvas at view level
        this.canvas = canvas;

        // check for zone 1 to 3
        if(zone >= 1 && zone <= 3)
        {
            drawTouchZones(zone);
        }
    }

    protected void drawTouchZones(Integer zone)
    {
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setColor(Color.WHITE);
        paint.setAlpha(75);

        Path path = new Path();

        if(zone == 1) {
            path.moveTo(150,150);
            path.lineTo(150,0);
            path.arcTo(rectf, 270, 120);
            path.close();
        } else if(zone == 2) {
            path.moveTo(150,150);
            path.arcTo(rectf, 30, 120);
            path.lineTo(150,150);
            path.close();
        } else if(zone == 3) {
            path.moveTo(150,0);
            path.lineTo(150,150);
            path.arcTo(rectf, 150, 120);
            path.close();
        }

        canvas.drawPath(path, paint);       
    }
}

其次,主要活动 Design.java:

package com.vector;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class Design extends Activity {
    /** Called when the activity is first created. */
    private Zones v;
    protected Integer zone = 0;

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

        try {
            // get our custom view
            setContentView(R.layout.main);
            v = (Zones) findViewById(R.id.zone);

            // add onClick Listener
            v.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    Log.i("zone clicked", "" + zone);

                    // tell to our view which zone has been clicked
                    v.zone = zone;

                    // invalidate to call onDraw method of the custom view and draw the zone
                    v.invalidate();
                }
            });

            v.setOnTouchListener(new View.OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    zone = getZone(event);
                    return false;
                }
            });
        }
        catch(Exception e)
        {
            Log.e("e", e.getMessage());
        }
    }

    // detect clicked zone through MotionEvent
    public int getZone(MotionEvent e)
    {
        Float x = e.getX();
        Float y = e.getY();

        // 0:00 to 4:00
        if((x > 150 && x < 300 && y < 150) ||
           (x > 150 && x < 300 && y > 150 && (x - 150) / (y - 150) > 1.5))
        {
            return 1;
        }
        // 4:00 to 8:00
        else if((x >= 150 && x < 300 & y > 150 && (x - 150) / (y - 150) < 1.5) ||
                (x > 0 && x < 150 && y > 150 && (150 - x) / (y - 150) < 1.5))
        {

            return 2;
        }
        // 8:00 to 0:00
        else
        {
            return 3;
        }       
    }
}

... 以及主 xml 视图(嵌入我们的自定义类视图) main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <com.vector.Zones android:id="@+id/zone" android:layout_height="wrap_content" android:layout_width="wrap_content">
    </com.vector.Zones>
</LinearLayout>

任何评论表示赞赏!
保罗:)

First thank you a lot for all your suggestions which helped me to reach the goal.

Since there is no way to trigger events on shapes, using onTouch() on the view which contains the shapes is the way to go.

Following, all you need to run it.


First, the custom view Zones.java:

package com.vector;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

public class Zones extends View {
    RectF rectf = new RectF(0, 0, 300, 300);
    Paint paint = new Paint();
    Canvas canvas = null;
    Integer zone = 0;

    // important: do not forget to add AttributeSet, it is necessary to have this
    // view called from an xml view file
    public Zones(Context context, AttributeSet attributeset) {
        super(context, attributeset);
        this.setBackgroundColor(0xFF207CA1);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        // set layout of the view
        layout(0, 0, 300, 300);

        // expose canvas at view level
        this.canvas = canvas;

        // check for zone 1 to 3
        if(zone >= 1 && zone <= 3)
        {
            drawTouchZones(zone);
        }
    }

    protected void drawTouchZones(Integer zone)
    {
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setColor(Color.WHITE);
        paint.setAlpha(75);

        Path path = new Path();

        if(zone == 1) {
            path.moveTo(150,150);
            path.lineTo(150,0);
            path.arcTo(rectf, 270, 120);
            path.close();
        } else if(zone == 2) {
            path.moveTo(150,150);
            path.arcTo(rectf, 30, 120);
            path.lineTo(150,150);
            path.close();
        } else if(zone == 3) {
            path.moveTo(150,0);
            path.lineTo(150,150);
            path.arcTo(rectf, 150, 120);
            path.close();
        }

        canvas.drawPath(path, paint);       
    }
}

Second, the main activity, Design.java:

package com.vector;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class Design extends Activity {
    /** Called when the activity is first created. */
    private Zones v;
    protected Integer zone = 0;

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

        try {
            // get our custom view
            setContentView(R.layout.main);
            v = (Zones) findViewById(R.id.zone);

            // add onClick Listener
            v.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    Log.i("zone clicked", "" + zone);

                    // tell to our view which zone has been clicked
                    v.zone = zone;

                    // invalidate to call onDraw method of the custom view and draw the zone
                    v.invalidate();
                }
            });

            v.setOnTouchListener(new View.OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    zone = getZone(event);
                    return false;
                }
            });
        }
        catch(Exception e)
        {
            Log.e("e", e.getMessage());
        }
    }

    // detect clicked zone through MotionEvent
    public int getZone(MotionEvent e)
    {
        Float x = e.getX();
        Float y = e.getY();

        // 0:00 to 4:00
        if((x > 150 && x < 300 && y < 150) ||
           (x > 150 && x < 300 && y > 150 && (x - 150) / (y - 150) > 1.5))
        {
            return 1;
        }
        // 4:00 to 8:00
        else if((x >= 150 && x < 300 & y > 150 && (x - 150) / (y - 150) < 1.5) ||
                (x > 0 && x < 150 && y > 150 && (150 - x) / (y - 150) < 1.5))
        {

            return 2;
        }
        // 8:00 to 0:00
        else
        {
            return 3;
        }       
    }
}

... and the main xml view (which embeds our custom class view) main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <com.vector.Zones android:id="@+id/zone" android:layout_height="wrap_content" android:layout_width="wrap_content">
    </com.vector.Zones>
</LinearLayout>

Any comments appreciated!
Paul :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文