2011-11-07 12 views
16

L'app in cui sto lavorando utilizza molte ImageViews come pulsanti. La grafica di questi pulsanti utilizza il canale alfa per sfumare i bordi del pulsante e renderli irregolari. Attualmente, dobbiamo generare 2 elementi grafici per ciascun pulsante (1 per lo stato selezionato/focalizzato/premuto e l'altro per lo stato predefinito non selezionato) e utilizzare un oggetto StateListDrawable definito in un file XML per ogni pulsante.Tonalità/dim. Selezionabile al tocco

Mentre funziona correttamente, sembra estremamente dispendioso poiché tutti i grafici selezionati sono semplicemente versioni colorate dei pulsanti non selezionati. Questi richiedono tempo per produrre (anche se poco) e occupare spazio nell'APK finale. Sembra che ci dovrebbe essere un modo semplice per questo automaticamente.

La soluzione perfetta, a quanto pare, è utilizzare ImageViews per ciascun pulsante e specificare nell'attributo tinta un ColorStateList. Questo approccio ha il vantaggio che è necessario un solo ColorStateList XML per tutti i pulsanti (che condividono la stessa tinta). Comunque, non funziona. Come accennato a here, ImageView genera una NumberFormatException quando il valore fornito per la tinta è diverso da un singolo colore.

Il mio prossimo tentativo era usare un LayerDrawable per il drawable selezionato. All'interno dell'elenco dei livelli, avremmo l'immagine originale nella parte inferiore della pila coperta da un rettangolo semitrasparente. Questo ha funzionato sulle parti solide della grafica del pulsante. Tuttavia, i bordi che dovevano essere completamente trasparenti ora erano dello stesso colore dello strato superiore.

Qualcuno ha riscontrato questo problema prima e ha trovato una soluzione ragionevole? Vorrei attenermi agli approcci XML ma probabilmente codificherò una sottoclasse ImageView semplice che eseguirà la tinta del codice richiesta.

risposta

6

È possibile combinare StateListDrawable e LayerDrawable per questo.

public Drawable getDimmedDrawable(Drawable drawable) { 
     Resources resources = getContext().getResources(); 
     StateListDrawable stateListDrawable = new StateListDrawable(); 
     LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{ 
       drawable, 
       new ColorDrawable(resources.getColor(R.color.translucent_black)) 
     }); 
     stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, layerDrawable); 
     stateListDrawable.addState(new int[]{android.R.attr.state_focused}, layerDrawable); 
     stateListDrawable.addState(new int[]{android.R.attr.state_selected}, layerDrawable); 
     stateListDrawable.addState(new int[]{}, drawable); 
     return stateListDrawable; 
} 

Presumo i nostri colori.xml assomiglia a questo

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <color name="translucent_black">#80000000</color> 
</resources> 
+0

Come faccio a farlo funzionare bene per i file con 9 patch, in modo che l'intera area non trasparente dell'immagine venga oscurata al tocco degli eventi? Attualmente ciò che fa per le immagini con 9 patch è di cambiare solo la parte che è definita come il contenuto dell'immagine, e non l'intera immagine stessa ... –

17

Per coloro che hanno riscontrato un'esigenza simile, risolvere questo codice è abbastanza pulito. Ecco un esempio:

public class TintableButton extends ImageView { 

    private boolean mIsSelected; 

    public TintableButton(Context context) { 
     super(context); 
     init(); 
    } 

    public TintableButton(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(); 
    } 

    public TintableButton(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     init(); 
    } 

    private void init() { 
     mIsSelected = false; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     if (event.getAction() == MotionEvent.ACTION_DOWN && !mIsSelected) { 
      setColorFilter(0x99000000); 
      mIsSelected = true; 
     } else if (event.getAction() == MotionEvent.ACTION_UP && mIsSelected) { 
      setColorFilter(Color.TRANSPARENT); 
      mIsSelected = false; 
     } 

     return super.onTouchEvent(event); 
    } 
} 

Non è finito ma funziona bene come prova del concetto.

+0

Questo è, IMHO, la soluzione migliore e dovrebbe essere la risposta accettata, poiché funziona bene e crea un componente riutilizzabile che può essere utilizzato in qualsiasi layout per sostituire semplicemente un ImageView. Bella soluzione! – GreyBeardedGeek

+1

Grazie, molto bello! ho modificato l'onTouchEvent un po 'come questo: 'else if ((event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) && mIsSelected)' Questo assicura che la tinta scompare quando l'utente tiene premuto il pulsante, trascina il dito all'esterno e rilascia. – Jaykob

+0

'init()' deve essere chiamato solo nel 'TintableButton (contesto Context, AttributeSet attrs, int defStyle)' costruttore –

6

Your answer era eccellente :)

Tuttavia, invece di creare un oggetto pulsante, io sto solo chiamando un OnTouchlistener per cambiare lo stato di ogni vista.

public boolean onTouch(View v, MotionEvent event) { 
    if (event.getAction() == MotionEvent.ACTION_DOWN) { 
     Drawable background = v.getBackground(); 
     background.setColorFilter(0xBB000000, PorterDuff.Mode.SCREEN); 
     v.setBackgroundDrawable(background); 
    } else if (event.getAction() == MotionEvent.ACTION_UP) { 
     Drawable background = v.getBackground(); 
     background.setColorFilter(null); 
     v.setBackgroundDrawable(background); 
    } 

    return true; 

} 

dà gli stessi risultati anche se

+2

Anche questo funziona. Il motivo per cui ho scelto di estendere le classi necessarie è che posso gestire tutto questo in un XML di layout dopo che la classe è pronta. In questo modo il codice che utilizza questi widget tintabili non differisce dal codice che utilizza componenti standard. – zienkikk

1

Anch'io ho cercando pulsanti irregolari e serviva la tinta. Alla fine, ho appena fatto ricorso a una lista colori dello stato come sfondo ImageButton. Dà l'impressione che il colore del pulsante stia cambiando in stampa ed è molto semplice e richiede meno calcoli. So che questa non è esattamente una tinta, ma dà che dà un feedback visivo necessario all'utente in quello che di solito è un momento fugace.

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

Your answer era anche eccellente;)

sto modificherò po ', vi darà risultato come questo:

button_normalbutton_pressed

Rect rect; 
Drawable background; 
boolean hasTouchEventCompleted = false; 

@Override 
public boolean onTouch(View v, MotionEvent event) { 
    if (event.getAction() == MotionEvent.ACTION_DOWN) { 
     rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), 
       v.getBottom()); 
     hasTouchEventCompleted = false; 
     background = v.getBackground(); 
     background.setColorFilter(0x99c7c7c7, 
       android.graphics.PorterDuff.Mode.MULTIPLY); 
     v.setBackgroundDrawable(background); 
    } else if (event.getAction() == MotionEvent.ACTION_UP 
      && !hasTouchEventCompleted) { 
     background.setColorFilter(null); 
     v.setBackgroundDrawable(background); 

    } else if (event.getAction() == MotionEvent.ACTION_MOVE) { 
     if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() 
       + (int) event.getY())) { 
      // User moved outside bounds 
      background.setColorFilter(null); 
      v.setBackgroundDrawable(background); 
      hasTouchEventCompleted = true; 
     } 
    } 


    //Must return false, otherwise you need to handle Click events yourself. 
    return false; 
} 
0

penso questa soluzione è abbastanza semplice:

final Drawable drawable = ... ; 
    final int darkenValue = 0x3C000000; 
    mButton.setOnTouchListener(new OnTouchListener() { 
     Rect rect; 
     boolean hasColorFilter = false; 

     @Override 
     public boolean onTouch(final View v, final MotionEvent motionEvent) { 
      switch (motionEvent.getAction()) { 
       case MotionEvent.ACTION_DOWN: 
        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); 
        drawable.setColorFilter(darkenValue, Mode.DARKEN); 
        hasColorFilter = true; 
        break; 
       case MotionEvent.ACTION_MOVE: 
        if (rect.contains(v.getLeft() + (int) motionEvent.getX(), v.getTop() 
          + (int) motionEvent.getY())) { 
         if (!hasColorFilter) 
          drawable.setColorFilter(darkenValue, Mode.DARKEN); 
         hasColorFilter = true; 
        } else { 
         drawable.setColorFilter(null); 
         hasColorFilter = false; 
        } 
        break; 
       case MotionEvent.ACTION_UP: 
       case MotionEvent.ACTION_CANCEL: 
        drawable.setColorFilter(null); 
        hasColorFilter = false; 
        break; 
      } 
      return false; 
     } 
    }); 
2

Penso che questa soluzione sia semplice!

Passaggio 1: Creare res/drawable/dim_image_view.xml

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:state_focused="true" android:drawable="@drawable/dim_image_view_normal"/> <!-- focused --> 
    <item android:state_pressed="true" android:drawable="@drawable/dim_image_view_pressed"/> <!-- pressed --> 
    <item android:drawable="@drawable/dim_image_view_normal"/> <!--default --> 
</selector> 

Fase 2: Creare res/drawable/dim_image_view_normal.xml

<?xml version="1.0" encoding="utf-8"?> 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item><bitmap android:src="@mipmap/your_icon"/></item> 
</layer-list> 

Fase 3: Creare res/drawable/dim_image_view_pressed. xml

<?xml version="1.0" encoding="utf-8"?> 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > 
    <item><bitmap android:src="@mipmap/your_icon" android:alpha="0.5"></bitmap></item> 
</layer-list> 

Fase 4: Provare a impostare l'immagine nel layout xml come:

android:src="@drawable/dim_image_view" 
Problemi correlati