2012-06-18 21 views
8

Utworzono ImageButton za pomocą selektora dla stanów wciśniętych i wciśniętych, co działa dobrze.Tworzenie nieregularnego kształtu ImageButton z różnymi stuknięciami kliknięć

Ale przycisk ma nieregularny kształt i chcę, aby był klikalny tam, gdzie ukryty prostokątny obraz jest nieprzezroczysty.

Więc zaimplementowałem OnTouchListener, który sprawdza współrzędne punktów dotyku względem wartości pikseli Bitmapy (jak opisano w pierwszej odpowiedzi tutaj: link). Działa to w zakresie logiki decydującej, czy przycisk został naciśnięty, ale teraz obraz przycisku nie zmienia się już w naciśnięty obraz.

Oto co mam:

Selector plik XML:

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android" > 
    <item android:state_pressed="true" android:drawable="@drawable/button_start_call_pressed" /> 
    <item android:drawable="@drawable/button_start_call_normal" /> 
</selector> 

częściowo przezroczyste ImageButton w układzie:

<ImageButton 
    android:id="@+id/dashboardStartCallButton" 
    android:background="@null" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:src="@drawable/start_call_button_selector" 
    /> 

W działalność:

public void onCreate(Bundle savedInstance) { 
    super.onCreate(savedInstance); 
    ... 
    ImageButton startCallButton = (ImageButton) this.findViewById(R.id.dashboardStartCallButton); 
    startCallButton.setOnTouchListener(new OnTouchListener() { 
     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      return OnStartCallButtonTouch(v,event); 
     }   
    }); 
} 


public boolean OnStartCallButtonTouch(View v, MotionEvent event) 
{ 
    Bitmap TheBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.button_start_call_normal); 
    int eventPadTouch = event.getAction(); 
    int iX = (int) event.getX(); 
    int iY = (int) event.getY(); 
    switch (eventPadTouch) { 
     case MotionEvent.ACTION_DOWN: 
      if (iX>=0 & iY>=0 & iX<TheBitmap.getWidth() & iY<TheBitmap.getHeight()) {     
       if (TheBitmap.getPixel(iX,iY)!=0) { 
        onStartCallButtonClicked(v); 
        return false; 
       } 
      } 
    }   
    return true; 
} 
+0

dlaczego nie 'return OnStartCallButtonTouch (v, zdarzenie) || startCallButton.onTouch() '? –

Odpowiedz

10

Myślę, że ty „Re faktycznie patrząc na to:

View.dispatchTouchEvent(...)

Trzeba zastąpić tę metodę w swojej przycisku niestandardowego powrotu fałszywe jeśli punkt nie jest w pożądanym zakresie. Proponuję udać się o nim tak:

public static class MyButton extends ImageButton { 
    ... 
    @Override 
    public boolean dispatchTouchEvent(MotionEvent event) { 
     int iX = (int) event.getX(); 
     int iY = (int) event.getY(); 
     // TODO Or use a more sophisticated, pixel value-based condition 
     if (!(iX>=0 & iY>=0 & iX<TheBitmap.getWidth() & iY<TheBitmap.getHeight())) { 
      return false; 
     } 
     return super.dispatchTouchEvent(event) 
    } 
} 

Dlaczego nie skorzystać OnTouchListener: Bo trzeba zadzwonić aż do View.onTouchEvent() (która odbywa się w super.dispatchTouchEvent (zdarzenia)), aby pozwolić View uchwyt stany rozciągliwej (to się robi w onTouchEvent() przy użyciu metody View.refreshDrawableState(), i tam również kilka dość skomplikowana logika tam)

Dlaczego nie zastępują onTouchEvent(): Ponieważ w View.dispatchTouchEvent() jest warunek, że jeśli istnieje OnTouchListener, pozwól, aby słuchający obsłużył wszystko i zwrócił true. Więc jeśli później ustawisz OnTouchListener na przycisku z jakiegoś powodu, twój stan współrzędnych w onTouchEvent() nie zostanie sprawdzony, ponieważ onTouchEvent() nigdy nie zostanie wywołany. Przez przesłonięcie metody dispatchTouchEvent() filtrujesz dotknięcia na samej górze i pozostawiasz całą logikę na swoim miejscu - możesz dodać do przycisku dowolnych słuchaczy i będą działać tak samo, jak normalnie, ale tylko wtedy, gdy twój warunek jest prawdziwy.

+0

Hej, jak leci? Jeśli odpowiedź ci to zrobiła, rozważ zaakceptowanie tego. –

1

Myślę, że trzeba zmodyfikować wykrywanie kolizji, aby sprawdzić, czy dotyk znajduje się wewnątrz przycisku, czy nie. Użyj następującego podejścia.

public boolean OnStartCallButtonTouch(View v, MotionEvent event) 
{ 
    Bitmap TheBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.button_start_call_normal); 
    int eventPadTouch = event.getAction(); 
    int iX = (int) event.getX(); 
    int iY = (int) event.getY(); 

    int[] location = new int[2]; 
    v.getLocationOnScreen(location); 

    int viewX = location[0]; 
    int viewY = location[1]; 


    switch (eventPadTouch) { 
     case MotionEvent.ACTION_DOWN: 
      if (iX>=viewX & iY>=viewY & iX<=(viewX+TheBitmap.getWidth()) & iY<=(viewY+TheBitmap.getHeight())) {     
       if (TheBitmap.getPixel(iX,iY)!=0) { 
        onStartCallButtonClicked(v); 
        showPressedState(); 
        return false; 
       } 
      } 
      break; 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      showNormalState(); 
      break; 
    }   
    return true; 
} 

private void showNormalState() { 
    // set your button background drawable to show normal state 
} 

private void showPressedState() { 
    // set your button background drawable to show pressed state 
}