Como definir o efeito de clique do botão no Android?

105

No Android, quando defino a imagem de fundo como Botão, não consigo ver nenhum efeito no botão.

Preciso de algum efeito no botão para que o usuário possa reconhecer que o botão foi clicado.

O botão deve estar escuro por alguns segundos quando for clicado. O que devo fazer para isso?

Obrigado.

Jignesh Ansodariya
fonte
O método a seguir permite que você use uma única classe de estilo para revestir todos os seus botões, independentemente do texto: stackoverflow.com/questions/5327553/…
Zack Marrapese
não, não estou usando o botão de imagem, estou usando o botão normal de alguma forma fácil para isso?
Jignesh Ansodariya
3
É um tutorial muito bom: dibbus.com/2011/02/gradient-buttons-for-android
Pratik Butani

Respostas:

154

Isso pode ser obtido criando um arquivo xml drawable contendo uma lista de estados para o botão. Então, por exemplo, se você criar um novo arquivo xml chamado "button.xml" com o seguinte código:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/YOURIMAGE" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/gradient" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/gradient" />
    <item android:drawable="@drawable/YOURIMAGE" />
</selector>

Para manter a imagem de fundo com uma aparência escurecida na impressão, crie um segundo arquivo xml e chame-o de gradient.xml com o seguinte código:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <bitmap android:src="@drawable/YOURIMAGE"/>
    </item>
    <item>
        <shape xmlns:android="http://schemas.android.com/apk/res/android">
            <gradient android:angle="90" android:startColor="#880f0f10" android:centerColor="#880d0d0f" android:endColor="#885d5d5e"/>
        </shape>
    </item>
</layer-list>

No xml do seu botão, defina o plano de fundo como o xml do botão, por exemplo

android:background="@drawable/button"

Espero que isto ajude!

Editar: Mudou o código acima para mostrar uma imagem (YOURIMAGE) no botão em oposição a uma cor de bloco.

Ljdawson
fonte
Então, onde posso definir a imagem para o fundo do botão, para definir a imagem é obrigatória
Jignesh Ansodariya
1
Editado para mostrar como usar uma imagem de fundo com o XML de estado. Substitua YOURIMAGE pelo recurso de imagem em seu diretório drawable.
Ljdawson
Isso só funciona se o src do botão for retangular. Se o drawable src tiver qualquer forma diferente daquela, o gradiente será aplicado ao fundo também, e isso é triste.
Renato Lochetti
Nem sempre, sempre poderia usar uma forma diferente na lista de camadas
Ljdawson
2
enquanto estou usando uma imagem, ela funciona para mim ... mas quando estou usando um plano de fundo XML. não está funcionando ...
DKV
88

É mais simples quando você tem muitos botões de imagem e não quer escrever xml-s para cada botão.

Versão Kotlin:

fun buttonEffect(button: View) {
    button.setOnTouchListener { v, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                v.background.setColorFilter(-0x1f0b8adf, PorterDuff.Mode.SRC_ATOP)
                v.invalidate()
            }
            MotionEvent.ACTION_UP -> {
                v.background.clearColorFilter()
                v.invalidate()
            }
        }
        false
    }
}

Versão Java:

public static void buttonEffect(View button){
    button.setOnTouchListener(new OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
                    v.invalidate();
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    v.getBackground().clearColorFilter();
                    v.invalidate();
                    break;
                }
            }
            return false;
        }
    });
}
András
fonte
2
Eu uso isso em meu código e o único problema que notei até agora é que o botão se comporta de maneira estranha quando colocado em um ScrollView.
Finnboy11 de
É uma solução realmente excelente e esta deve ser a resposta correta
Biraj Zalavadia
Como isso é melhor? O design deve ser mantido fora do código, não é?
Bugs acontecem em
É apenas uma solução simples para este problema. Quando você tem muitos botões e não quer criar xmls para cada um deles, isso pode ser útil.
András
AMD! Imagine ter 5 botões em um fragmento ... Você terá todo esse código clichê ... Como refatorá-lo então? Alterar o filtro de cor para todos os botões em todas as atividades? Imho, seu próximo desenvolvedor não ficará tão feliz ..
Leo Droidcoder
84

Crie seu AlphaAnimationobjeto que decide quanto será o efeito de esmaecimento do botão e, em seguida, deixe-o começar no onClickListenerde seus botões

Por exemplo :

private AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.8F);

// some code

public void onClick(View v) {
    v.startAnimation(buttonClick);
}

claro que esta é apenas uma forma, não a preferida, é apenas mais fácil

Ahmed Adel Ismail
fonte
5
O que eu gosto nesta solução, é que ela não requer a criação de muitos seletores, quando você tem muitos botões, cada um com suas próprias imagens personalizadas.
Pete
2
Isso não esmaece o botão ao tocar, apenas desvanece por uma fração de segundo depois que o clique é detectado e o dedo do usuário já foi levantado.
Doug Voss,
1
Bom e simples
@ Doug Voss, você pode definir a duração da animação para torná-la mais longa
Ahmed Adel Ismail,
que tal onLongClick e 'circularReveal'?
Acauã Pitta
44

Você pode simplesmente usar o primeiro plano para Viewobter um efeito clicável:

android:foreground="?android:attr/selectableItemBackground"


Para usar com o tema escuro, adicione também o tema ao seu layout(para que o efeito clicável fique claro):

android:theme="@android:style/ThemeOverlay.Material.Dark"
Francis
fonte
5
A maneira mais fácil de implementar. Para sua informação, Atributo android: o primeiro plano não tem efeito nos níveis de API inferiores a 23.
dolgom
1
Solução mais simples.
Jerry Chong
2
Esta deve ser a resposta aceita, muito mais simples e adiciona o bom efeito de animação ondulada.
Tyler
1
@dolgom Para nível de API <23, use-o no atributo "background". Então: android: background = "? android: attr / selectableItemBackground"
Z3R0
24

Para tornar o seu item consistente com a aparência do sistema, tente fazer referência ao atributo do sistema android:attr/selectableItemBackgroundno plano de fundo ou na tag de primeiro plano da visualização desejada:

<ImageView
    ...
    android:background="?android:attr/selectableItemBackground"      
    android:foreground="?android:attr/selectableItemBackground"
    ...
/>

Use os dois atributos para obter o efeito desejado antes / depois do nível 23 da API, respectivamente.

https://stackoverflow.com/a/11513474/4683601

Ely Dantas
fonte
2
De longe a solução mais simples. Obrigado!
tykom
9

Ou usando apenas uma imagem de fundo, você pode obter o efeito de clique usando setOnTouchListener

Dois caminhos

((Button)findViewById(R.id.testBth)).setOnTouchListener(new OnTouchListener() {

      @Override
        public boolean onTouch(View v, MotionEvent event) {
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN: {
                  Button view = (Button) v;
                  view.getBackground().setColorFilter(0x77000000, PorterDuff.Mode.SRC_ATOP);
                  v.invalidate();
                  break;
              }
              case MotionEvent.ACTION_UP:
                  // Your action here on button click
              case MotionEvent.ACTION_CANCEL: {
                  Button view = (Button) v;
                  view.getBackground().clearColorFilter();
                  view.invalidate();
                  break;
              }
          }
          return true;
        }
});

insira a descrição da imagem aqui

E se você não quiser usar setOnTouchLister, a outra maneira de conseguir isso é

    myButton.getBackground().setColorFilter(.setColorFilter(0xF00, Mode.MULTIPLY);

    StateListDrawable listDrawable = new StateListDrawable();
    listDrawable.addState(new int[] {android.R.attr.state_pressed}, drawablePressed);
    listDrawable.addState(new int[] {android.R.attr.defaultValue}, myButton);

    myButton.setBackgroundDrawable(listDrawable);
Vinayak Bevinakatti
fonte
existem erros no código da 2ª via. Qual é a função getCurrent ()? Além disso, myButton não é um Drawable, portanto não pode ser adicionado como um estado.
Ehtesham Hasan
Obrigado por apontar o erro. Eu o corrigi, e myButton é uma instância da classe Button que fornece o método setBackgroundDrawable e leva as instâncias de Drawable, portanto, isso funcionará.
Vinayak Bevinakatti
Confira minha resposta, que se baseia em sua segunda abordagem. stackoverflow.com/a/39430738/5229515
Ehtesham Hasan
Provavelmente, deve haver "return false" no final para que não consuma o evento e quaisquer onClickListeners adicionais possam ser chamados.
Doug Voss de
3

só quero adicionar outra maneira fácil de fazer isso: se seu ImageButton permanecer como plano de fundo e você não defini-lo como nulo, ele funcionará como um botão normal e mostrará a animação de clique enquanto clica exatamente como os outros botões. o fundo enquanto ainda está lá:

<ImageButton
    android:id="@+id/imageButton2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingBottom="1dp"
    android:paddingLeft="1dp"
    android:paddingRight="1dp"
    android:paddingTop="1dp"
    android:src="@drawable/squareicon" />

Os preenchimentos não deixam o fundo ficar visível e fazem com que o botão funcione como outros botões.

arianoo
fonte
1
isso manterá a borda do botão quadrado ao redor da imagem se ela for circular
MSaudi
3

Esta é a melhor solução que encontrei seguindo as dicas da resposta de @Vinayak. Todas as outras soluções têm desvantagens diferentes.

Em primeiro lugar, crie uma função como esta.

void addClickEffect(View view)
{
    Drawable drawableNormal = view.getBackground();

    Drawable drawablePressed = view.getBackground().getConstantState().newDrawable();
    drawablePressed.mutate();
    drawablePressed.setColorFilter(Color.argb(50, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);

    StateListDrawable listDrawable = new StateListDrawable();
    listDrawable.addState(new int[] {android.R.attr.state_pressed}, drawablePressed);
    listDrawable.addState(new int[] {}, drawableNormal);
    view.setBackground(listDrawable);
}

Explicação:

getConstantState (). newDrawable () é usado para clonar o Drawable existente, caso contrário, o mesmo drawable será usado. Leia mais aqui: Android: clonando um drawable para criar um StateListDrawable com filtros

mutate () é usado para fazer com que o clone do Drawable não compartilhe seu estado com outras instâncias do Drawable. Leia mais sobre isso aqui: https://developer.android.com/reference/android/graphics/drawable/Drawable.html#mutate ()

Uso:

Você pode passar qualquer tipo de View (Button, ImageButton, View etc) como parâmetro para a função e eles obterão o efeito de clique aplicado a eles.

addClickEffect(myButton);
addClickEffect(myImageButton);
Ehtesham Hasan
fonte
2
Step: set a button in XML with onClick Action:

 <Button
android:id="@+id/btnEditUserInfo"
style="?android:borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="@dimen/txt_height"
android:layout_gravity="center"
android:background="@drawable/round_btn"
android:contentDescription="@string/image_view"
android:onClick="edit_user_info"
android:text="Edit"
android:textColor="#000"
android:textSize="@dimen/login_textSize" />

Step: on button clicked show animation point
//pgrm mark ---- ---- ----- ---- ---- ----- ---- ---- -----  ---- ---- -----

    public void edit_user_info(View view) {

        // show click effect on button pressed
        final AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.8F);
        view.startAnimation(buttonClick);

        Intent intent = new Intent(getApplicationContext(),  EditUserInfo.class);
        startActivity(intent);

    }// end edit_user_info
Vinod Joshi
fonte
2

Use RippleDrawable para efeito pressionado / clicado no estado do Material Design.

Para conseguir isso, crie o item ondulado como um .xml na pasta / drawable e use-o em android: background para quaisquer visualizações.

  • Efeito para ícone pressionado / clicado, use efeito de ondulação circular, por exemplo:

    <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@android:color/darker_gray" />

  • Efeito para a visualização clicada com o limite do retângulo, podemos adicionar ondulação sobre o drawable existente como abaixo:

    <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#000000"> <item android:drawable="@drawable/your_background_drawable"/> </ripple>

giang nguyen
fonte
2

Para todas as visualizações

android:background="?android:attr/selectableItemBackground"

Mas para cardview que tem uso de elevação

android:foreground="?android:attr/selectableItemBackground"

Para efeito de clique circular como na barra de ferramentas

android:background="?android:attr/actionBarItemBackground"

Além disso, você precisa definir

 android:clickable="true"
 android:focusable="true"
Faizan Khan
fonte
0

Fazendo um pequeno acréscimo à resposta de Andràs:

Você pode usar postDelayedpara fazer o filtro de cor durar um pequeno período de tempo para torná-lo mais perceptível:

        @Override
        public boolean onTouch(final View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    v.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
                    v.invalidate();
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    v.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            v.getBackground().clearColorFilter();
                            v.invalidate();
                        }
                    }, 100L);
                    break;
                }
            }
            return false;
        }

Você pode alterar o valor do atraso 100L para atender às suas necessidades.

olhos escondidos02
fonte
0

Se você estiver usando fundo xml em vez de IMG, basta remover:

<item>
    <bitmap android:src="@drawable/YOURIMAGE"/>
</item>

da 1ª resposta que @Ljdawson nos deu.

RastaPopuloS
fonte