2011-04-09 48 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