Может ли обрабатывать события неправильной формы на Android? - PullRequest
3 голосов
/ 10 апреля 2011

Насколько я знаю сейчас (что мало), все виды в Android имеют квадратную или прямоугольную форму.Это нормально почти все время, пока вы не захотите - то, что я на самом деле хочу - создавать неквадратные фигуры, которые могли бы обрабатывать события.

Моя цель - разделить круг на 3 секции по 120 ° каждая.Каждый раздел круга должен вести себя как кнопка.Проблема в том, что, если мы посмотрим на эти 3-и окружности и поместим их в квадратную рамку, в которой они строго содержатся, они перекрывают друг друга: не практично знать, на что пользователь хотел щелкнуть ...

Я попытался с пользовательским представлением, в котором я нарисовал свои секции, но события запускаются на всей поверхности представления.

Любое предложение или направление очень приветствуются.

Спасибо, Пол

Ответы [ 3 ]

2 голосов
/ 10 апреля 2011

Я не знаю, можете ли вы создать эти фигуры явно, но определенно был бы способ использовать это в пользовательском представлении. Но это довольно сложно.

Сначала вам нужно подключиться к событию OnTouch, установив OnTouchListener, больше здесь . Самым простым способом было бы реагировать только на ACTION_UP действия MotionEvent, доступ к которому осуществляется через MotionEvent.getAction(), подробности по здесь . Из этого MotionEvent вы можете затем получить координаты X и Y, где событие произошло через getX() и getY().

Теперь начинается математика ... теперь вам нужно вычислить, в каком разделе произошел щелчок. Для этого вам нужен размер вашего пользовательского представления в пикселях и его местоположение (верхний левый угол), боюсь, я не могу сейчас рассказать вам о подходящих методах ... вам придется их откопать .. Предполагая, что центр вашего круга всегда находится в центре вашего обзора и что круг полностью распространяется до границ видов, теперь вы можете вычислить сектор.

Давайте назовем события X и Y координатами eventX и eventY соответственно, и пусть centerX и centerY будут координатами центра окружностей, а 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 должен быть по часовой стрелке угол щелчка в радианах, который вы можете проверить по своим секциям;) Исправьте меня, если что-то не так ^^

2 голосов
/ 13 апреля 2011

Во-первых, большое спасибо за все ваши предложения, которые помогли мне достичь цели.

Поскольку невозможно вызвать события на фигурах, использование 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>

Любые комментарии приветствуются!Пол :) 1017 *

2 голосов
/ 10 апреля 2011

Я считаю, что способ справиться с этим - переопределить onTouch для каждого элемента управления, выполнить свои собственные геометрические проверки координат касания и, если они находятся за пределами пользовательской области return false, передать событие в другое представление.В противном случае вернитесь, вызвав супер версию метода.Я не уверен на 100%, поэтому, пожалуйста, поправьте меня, если я ошибаюсь, но это может стоить попробовать.

...