web-dev-qa-db-fra.com

Android Bouton de forme personnalisée

Comment créer une vue ou un bouton cliquable de forme personnalisée dans Android?

Lorsque je clique, je veux éviter de toucher une zone vide.

enter image description here

veuillez aider. Merci.

41
Erhan Demirci

Question interessante. J'ai essayé quelques solutions et c'est ce que j'ai trouvé qui a le même résultat que ce que vous essayez de réaliser. La solution ci-dessous résout 2 problèmes:

  1. Forme personnalisée telle que vous l'avez présentée
  2. Le côté supérieur droit du bouton ne doit pas être cliquable

Voici donc la solution en 3 étapes:

Étape 1

Créez deux formes.

  • Première forme de rectangle simple pour le bouton: shape_button_beer.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:Android="http://schemas.Android.com/apk/res/Android" >
    
        <gradient
            Android:angle="90"
            Android:endColor="#C5D9F4"
            Android:startColor="#DCE5FD" />
    
        <corners
            Android:bottomLeftRadius="5dp"
            Android:bottomRightRadius="5dp"
            Android:topLeftRadius="5dp" >
        </corners>
    
    </shape>
    
  • La deuxième forme est utilisée comme masque pour le côté supérieur droit du bouton: shape_button_beer_mask.xml. C'est un simple cercle avec une couleur unie noire.

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:shape="oval" >
    
        <solid Android:color="#000000" />
    
    </shape>
    

Étape 2

Dans votre mise en page principale, ajoutez le bouton par approche suivante:

  • RelativeLayout est le conteneur de ce bouton personnalisé
  • Le premier LinearLayout est le bouton bleu avec l'icône de la bière et le texte à l'intérieur
  • Second ImageView est le masque au-dessus du bouton bleu. Et voici le sale tour:
    1. Les marges sont négatives pour placer le masque au bon endroit
    2. Nous définissons id pour pouvoir remplacer le clic (voir étape 3)
    3. Android:soundEffectsEnabled="false" - de sorte que l'utilisateur ne sentira pas qu'il a appuyé sur quelque chose.

Le XML:

    <!-- Custom Button -->
    <RelativeLayout
        Android:layout_width="120dp"
        Android:layout_height="80dp" >

        <LinearLayout
            Android:id="@+id/custom_buttom"
            Android:layout_width="100dp"
            Android:layout_height="100dp"
            Android:background="@drawable/shape_button_beer" >

            <!-- Beer icon and all other stuff -->

            <ImageView
                Android:layout_width="40dp"
                Android:layout_height="40dp"
                Android:layout_marginLeft="5dp"
                Android:layout_marginTop="15dp"
                Android:src="@drawable/beer_icon" />
        </LinearLayout>

        <ImageView
            Android:id="@+id/do_nothing"
            Android:layout_width="120dp"
            Android:layout_height="100dp"
            Android:layout_alignParentRight="true"
            Android:layout_alignParentTop="true"
            Android:layout_marginRight="-50dp"
            Android:layout_marginTop="-50dp"
            Android:background="@drawable/shape_button_beer_mask"
            Android:soundEffectsEnabled="false" >
        </ImageView>
    </RelativeLayout>
    <!-- End Custom Button -->

Étape 3

Dans votre activité principale, vous définissez les événements de clic pour le bouton et le masque comme suit:

LinearLayout customButton = (LinearLayout) findViewById(R.id.custom_buttom);
customButton.setOnClickListener(new View.OnClickListener()
{
    @Override
    public void onClick(View arg0)
    {
        Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_SHORT).show();
    }
});

// Mask on click will do nothing
ImageView doNothing = (ImageView) findViewById(R.id.do_nothing);
doNothing.setOnClickListener(new View.OnClickListener()
{
    @Override
    public void onClick(View arg0)
    {
        // DO NOTHING
    }
});

C'est ça. Je sais que ce n'est pas une solution parfaite, mais dans votre cas d'utilisation décrit, cela pourrait aider. Je l'ai testé sur mon mobile et voici à quoi il ressemble lorsque vous cliquez sur la zone bleue et rien ne se passera sur les autres zones:

  • enter image description here

J'espère que cela a aidé d'une manière ou d'une autre :)

39
sromku

Utilisez OnTouch au lieu d'OnClick et vérifiez la valeur alpha de l'image que vous avez utilisée dans le bouton.Si ce n'est pas égal à zéro, faites ce que vous voulez. Vérifiez le code suivant,

final Bitmap bitmap;  //Declare bitmap     
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.TheImage);


public boolean onTouch(View v, MotionEvent event) {

        int eventPadTouch = event.getAction();
        float iX=event.getX();
    float iY=event.getY();

        switch (eventPadTouch) {

            case MotionEvent.ACTION_DOWN:
                if (iX>=0 & iY>=0 & iX<bitmap.getWidth() & iY<bitmap.getHeight()) { //Makes sure that X and Y are not less than 0, and no more than the height and width of the image.                
                    if (bitmap.getPixel((int) iX, (int) iY)!=0) {
                        // actual image area is clicked(alpha not equal to 0), do something 
                    }               
                }
                return true;                
        }           
        return false;
}
15
Basim Sherif

utiliser la liste des calques, vous pouvez concevoir n'importe quelle forme n'importe quel haut de bouton de dégradé ici est un exemple

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item>
        <shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
            Android:shape="rectangle">
            <corners

                Android:topLeftRadius="0dp"
                Android:topRightRadius="0dp"
                Android:bottomLeftRadius="2dp"
                Android:bottomRightRadius="15dp"
                />            
            <!-- The border color -->
            <solid Android:color="#ffffff" />
        </shape>
    </item>

    <item Android:right="2dp"
        Android:left="2dp"
        Android:bottom="2dp">
            <shape>            
            <gradient                
                Android:startColor="#002a36"
                Android:centerColor="#457c8e"
                Android:endColor="#e6ffff"               
                Android:angle="90" 
                  Android:centerY="1"
                Android:centerX="0.5"           
                />          

            <corners

                Android:topLeftRadius="0dp"
                Android:topRightRadius="0dp"
                Android:bottomLeftRadius="2dp"
                Android:bottomRightRadius="15dp"
                />            
            <padding
                Android:left="10dp"
                Android:top="10dp"
                Android:right="10dp"
                Android:bottom="10dp" 
                />                        
        </shape>
    </item>
</layer-list> 

utilisez des valeurs de rayon -ve pour faire la forme du bouton comme mentionné

3
Charan Pai

vous pouvez essayer celui-ci:

        <Button
        Android:id="@+id/logout"
        Android:layout_width="240dp"
        Android:layout_height="28dp"
        Android:layout_weight="1"
        Android:gravity="center"
        Android:text="ContactsDetails"
        Android:textColor="#ffffff" Android:layout_marginLeft="50dp" Android:background="@drawable/round"/>

et créez un fichier round.xml dans un dossier dessinable:

        <?xml version="1.0" encoding="utf-8"?>
      <shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
      Android:shape="rectangle" Android:padding="0dp" Android:useLevel = "false">
    <!-- you can use any color you want I used here gray color-->
     <solid Android:color="#ABABAB"/> 
       <corners
       Android:bottomRightRadius="0dp"
         Android:bottomLeftRadius="0dp"
       Android:topLeftRadius="0dp"
      Android:topRightRadius="70dp"/>
       </shape>
3
G M Ramesh

J'ai eu un problème similaire mais je ne voulais pas dépendre du code derrière pour examiner la valeur en pixels. Je voulais un moyen simple (pas de surcharge de classe) pour contraindre un événement tactile à seulement une sous-partie d'un dessinable. Ci-dessous j'utilise un LinearLayout pour le dessinable puis à l'intérieur je mets un bouton transparent (avec du texte). Je peux ajuster la marge du bouton pour positionner la zone cliquable.

    <LinearLayout
        Android:layout_width="0dp"
        Android:layout_weight="1"
        Android:layout_height="match_parent"
        Android:orientation="horizontal"
        Android:background="@drawable/circle">
        <Button
            Android:layout_height="match_parent"
            Android:layout_width="match_parent"
            Android:id="@+id/btnTimer1"
            Android:text="0:00"
            Android:textColor="#ffffff"
            Android:textSize="22dp"
            Android:layout_margin="20dp"
            Android:background="@Android:color/transparent"/>
    </LinearLayout>
1
Mark Owens

Plutôt que de faire toutes ces modifications, vous devez utiliser la disposition du cadre dans la partie qui entoure le bouton, et masquer la partie supérieure droite avec quelque chose (circulaire, comme un bouton arrondi) et n'assigner aucun écouteur de clic à cette partie. Cela masque en fait le cadre inférieur (c'est-à-dire votre bouton d'origine) et le masque avec la partie non active.

0
Abhijeet Soni

Meilleure et plus simple solution (as4me) J'ai trouvé ici - c'est un bouton sous-classé et donc il prend en charge le sélecteur. Donc, tout ce que vous devez faire est de dessiner/ajouter des pngs correspondants pour chaque état de bouton pour utiliser le sélecteur et déclarer onClick en xml ou ajouter OnClickListener dans le code et vous êtes prêt à partir.

0
Stan

J'ai essayé la réponse de @Basim Sherif ( link ) et cela fonctionne très bien cependant seulement si la taille du bouton est la même que l'image d'origine. Si le bouton a été étiré, la zone cliquable sera plus petite et si le bouton a été défini sur une taille plus petite, la région cliquable sera plus grande que le bouton réel.

La solution est simple qui consiste à mettre à l'échelle les valeurs iX et iY pour qu'elles correspondent au bitmap d'origine.

Et voici ma version modifiée du code:

final Bitmap bitmap;  //Declare bitmap     
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.TheImage);

public boolean onTouch(View v, MotionEvent event) {

    int eventPadTouch = event.getAction();
    float iX=event.getX();
    float iY=event.getY();

    // Get the dimensions used in the view
    int realW = this.getWidth();
    int realH = this.getHeight();

    // Get the dimensions of the actual image
    int bitmapW = bitmap.getWidth();
    int bitmapH = bitmap.getHeight();

    // Scale the coordinates from the view to match the actual image
    float scaledX = iX * bitmapW / realW;
    float scaledY = iY * bitmapH / realH;

    switch (eventPadTouch) {
        case MotionEvent.ACTION_DOWN:
            if (scaledX >= 0 & scaledY >= 0 & scaledX < bitmap.getWidth() & scaledY < bitmap.getHeight()) { //Makes sure that X and Y are not less than 0, and no more than the height and width of the image.                
                if (bitmap.getPixel((int) scaledX, (int) scaledY)!=0) {
                    // actual image area is clicked(alpha not equal to 0), do something 
                }
            }
            return true;
    }
    return false;
}
0
Fahad Alduraibi