2011-04-09 51 views
3

据我现在所知(这很少),Android中的所有视图都有正方形或矩形形状。几乎所有的时间都很好,直到你想要 - 我真正想要的 - 创建可以处理事件的非方形形状。可以处理Android上不规则形状的事件吗?

我的目标是将一个圆圈分成三部分,每部分120°。圆圈的每一部分应该像一个按钮。问题是,如果我们看一个圆圈的3rds,并将它们放在一个严格包含它们的方框中,它们就会彼此重叠:不知道用户想要点击哪个方块。

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

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

THX,保罗

回答

2

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

由于无法在形状上触发事件,因此在包含形状的视图上使用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> 

任何意见感谢! Paul :)

+0

而不是使用这种坐标比较(当然哪些工作),如果你需要更多的区域,我提出的数学方法会更容易适应;) – 2011-04-14 07:27:57

2

我相信来处理这个问题的方法是重写onTouch上的每个控件,执行自己的几何支票上的触摸坐标,如果它的自定义区域return false置身其中将经过这个事件到另一种观点。否则返回调用方法的超级版本。我不是100%肯定的,所以有人请纠正我,如果我错了,但它可能是值得一试。

+1

这应该很好。要覆盖的View方法是'onTouchEvent',这意味着你正在使用你上面概述的方法 - 使每个部分都是自己的View,并定位它们,使它们的矩形边界重叠。您也只想尝试点击测试并在ACTION_DOWN上返回false - Android会执行命中定位并锁定手势目标。 – adamp 2011-04-09 21:03:53

2

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

首先,您需要通过设置OnTouchListener,更多here来挂钩OnTouch事件。最简单的方法是只对ACTION_UP行动MotionEvent作出反应,可通过MotionEvent.getAction()访问,详情通过here。在此MotionEvent中,您可以获得X和Y坐标,其中事件发生在getX()getY()之间。

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

我们称之为事件的X和Y坐标分别与eventXeventYcenterXcenterY是圆中心坐标以及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应该是辐射点顺时针角度的点击,你可以核对你的部分;)纠正我,如果有什么问题^^

+0

好的解决方案。谢谢你。尽管如此小的修正(我想!):应该是'Math.atan(...)'[或'Math.atan2(...)']而不是'Math.asin(...)'。 (相反,相反,'tan'而不是'sin')。 – 2012-09-14 16:31:37