Android中的椭圆形视图

12

我需要创建一系列同心椭圆(环),并需要将用户图标放置在这些椭圆的周长上。请参见下面的图片。

Rings View

到目前为止,我已经在画布上绘制了3个椭圆形同心圆,并放置了用户图标。 我需要使用户图标可以跨越这些环进行拖动。

请建议实现此功能的方法。


请发布您现在拥有的代码。 - Rod_Algonquin
3个回答

5
既然您已经将图标放在环的周长上,我假设您知道如何进行数学计算[但请参见编辑]以确定沿周长的点,并且正在询问有关拖放的内容。
您可能希望使用拖放方法实现图标移动。假设您将环保留为单个图像,则只会有一个拖放目标。然后,您需要通过数学分析[请参见编辑](通过确定其像素颜色)来确定图标被放入哪个环中。如果为每个环创建单独的视图,则每个环可以是其自己的投放点。(您可能最终需要解决如何在每个环内重新分配图标的问题,但这是另一个问题。)
以下是一些代码,显示了一种处理拖放的最小方式,其中在单个视图组(在其中显示您的环图像)上使用了单个图像图标。

MainActivity.java

package com.example.dragexample;

import android.app.Activity;
import android.content.ClipData;
import android.os.Bundle;
import android.util.Log;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

public class MainActivity extends Activity {

static final String TAG = "DragActivity";

ImageView icon = null;


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

    findViewById(R.id.rings).setOnDragListener(new OnDragListener() {
        @Override
        public boolean onDrag(View vw, DragEvent event) {
            if (event.getAction() == DragEvent.ACTION_DROP) {
                // Drop the icon and redisplay it:
                icon.setX(event.getX());
                icon.setY(event.getY());
                icon.setVisibility(View.VISIBLE);

                // Analyze the drop point mathematically (or perhaps get its pixel color)
                //  to determine which ring the icon has been dragged into and then take
                //  appropriate action.
                int destRing = determineDestinationRing(event.getX(), event.getY());
            }

            return true;
        }
    });

    icon = (ImageView) findViewById(R.id.icon);
    icon.setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(View vw, MotionEvent event) {
            Log.v(TAG, "Touch event " + event.getAction());
            if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
                Log.v(TAG, "Starting drag");

               // Set up clip data (empty) and drag shadow objects and start dragging:
                ClipData cd = ClipData.newPlainText("", "");
                DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(vw);
                vw.startDrag(cd, shadowBuilder, vw, 0);
                vw.setVisibility(View.INVISIBLE);
            }

            return true;
        }
    });
}

public void resetImage(View vw) {
    Log.v(TAG, "Resetting image position");

    icon.setX(0f);
    icon.setY(0f);
    icon.setVisibility(View.VISIBLE);
}

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"

    android:id="@+id/rings"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:onClick="resetImage" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

</FrameLayout>

编辑1:

为了数学上确定图标掉落到哪个环中,可以使用以下方法,通过循环使用标准椭圆方程的实现,并使用硬编码大小来确定轴。请注意,如果图标掉落在最内部的“我”环内,则会返回零。此外,在实践中,由于布局呈现时屏幕上的环大小可能会调整,因此这种方法会更具挑战性。在这种情况下,轴的最终大小需要在运行时确定。

// Axis values must be ascending order; ring 0 is 'me';
float a[] = {50, 100, 150, 200};
float b[] = {100, 200, 300, 400};

public int determineDestinationRing(float x, float y) {

    // Check for inclusion within each ring:
    for (int i = 0; i < a.length; i++) {
        if (((x * x) / (a[i] * a[i]) + (y * y) / (b[i] * b[i])) <= 1)
            return i;
    }

    return -1;
}

2
为了解决这个问题,您需要使用椭圆的方程式:
(x/a)2 + (y/b)2 = 1,
其中: x、y 是椭圆周上任意一点的坐标 a、b 分别是 x 和 y 轴上的半径。
当用户拖放图标时,如果图标的中心坐标与椭圆周相交,则应满足上述公式。在这种情况下,您可以将图标放置在椭圆上。由于您有三个椭圆,因此您需要进行几次检查,每次分别对其他两个椭圆进行检查。
public class CustomView extends View {

        // Other methods 

        public boolean onTouchEvent(MotionEvent e) {
            int index = e.getActionIndex();
            float x = e.getX(index);
            float y = e.getY(index);

            int a = this.getWidth()/2;
            int b = this.getHeight()/2;

            // x-a instead of x and y-b instead of y, to get the offset from centre of ellipse.
            double result = Math.pow(((x-a)/a), 2) + Math.pow(((y-b)/b), 2);

            Log.v(TAG, "(" + (x-a) + "/" + a + ")2 + (" + (y-b) + "/" + b + ")2 = " + result);

            return true;
        }
}

2
我会这样做。首先,我们需要检测在TOUCH_DOWN事件中触摸了哪个图标。这可以通过比较触摸点和图标的坐标来简单完成。一旦找到最近的图标,我们还应该知道这个图标属于哪个椭圆,也就是说,我们知道这个椭圆的水平和垂直半径。我们还知道椭圆的中心坐标。
然后,这里是我们要做的事情的想法。我们将计算通过触摸点和椭圆中心绘制的线与通过中心绘制的水平线之间的角度。
               o <- touch point
              /
             / \ <- angle
  center->  o-----  <- horizontal line

现在,知道了角度之后,我们将使用适应于椭圆的圆的方程,重新计算新图标的坐标,使其粘贴到该椭圆上。下面进入算法。
为了检测触摸角度,我们将使用atan2函数。
double atan2Angle = Math.atan2(touchY - centerY, touchX - centerX);

这将根据角度给我们以下数值。
        -π
   -2*π      -0
         o          <- center 
    2*π       0
         π

为了在圆的方程中使用这个角度,我们需要将它转换成更传统的表示方式,就像下面这个例子一样。
        270°

   180°  o    0°/360°

         90°

可以这样做。

    float angleFactor = (float) (atan2Angle / 2 * Math.PI);
    if (angleFactor < 0) {
        angleFactor += 1f;
    }

    float touchAngle = angleFactor * 360f;
    if (angle < 0) {
        angle += 360f;
    }

现在,我们已经得到了触摸角度的度数,我们可以使用椭圆方程计算图标的新坐标。
double touchAngleRad = Math.toRadians(touchAngle);
float iconX = centerX + (float) (radiusX * Math.cos(touchAngleRad));
float iconY = centerY + (float) (radiusY * Math.sin(touchAngleRad));

// centerX, centerY - coordinates of the center of ellipses
// radiusX, radiusY - horizontal and vertical radiuses of the ellipse, to which 
//                    the touched icon belongs
// iconX, iconY     - new coordinates of the icon lying on that ellipse

如果你根据这个算法在每个TOUCH_MOVE上重新计算图标位置并使视图无效,那么你的图标将沿着其椭圆形移动。

使用弧度而不是角度可以进一步优化代码,但我认为角度更适合解释。希望这有所帮助。如果在实现过程中遇到任何问题,请发布你的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接