Creating a customized and animated toggle

Asked

Viewed 1,646 times

0

I am creating an app for Android in which I am using a ToggleButton as one of the interface elements. I am trying to make it have images instead of the button itself and there is a transition between them (when the button is on/off).

Giving a search, I read that to use images in the button the best would be using an xml file, which is here:

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/btn_off" android:state_checked="false" />
    <item android:drawable="@drawable/btn_on" android:state_checked="true" />
</selector>

What I couldn’t find how to do is how to associate this file to the button and how to put a transition between on/off. What is the best way to do this? I had already managed to switch background images by code, as follows:

// botão ligado
mToggle.setBackgroundDrawable(getResources().getDrawable(R.drawable.btn_on));

// botão desligado
mToggle.setBackgroundDrawable(getResources().getDrawable(R.drawable.btn_off));

But it seems to me an unorthodox way, providing Android with more specific features to do the same.

  • Renan, just use the attribute android:background with this drawable state list. It didn’t work?

  • Thanks @Wakim, it worked! The article I found added 3 steps after creating this XML to achieve something similar. Now I have only one question: how do I encourage this change of images?

  • Would it be applying an animation of transition between these two states? I think only programmatically, making a component that extends the Togglebutton and inside the onTouch or some more specific method that makes the backgroundDrawable change and make an animation (could use an Alphaanimation (1 to 0), swap the background and execute another Alphaanimation (0 to 1)). There is also Drawableanimation, but it is as if it transitions between "frames" (http://developer.android.com/guide/topics/graphics/drawable-animation.html) and still needs to be started programmatically.

  • Exactly, it would apply a transition between the two states. It is not possible to do without creating a new class?

  • You can set a OnTouchListener on that button from the Activity and do the animation on it.

  • Thank you, it worked perfectly. Only one thing is not right: when the animation starts, the state image disappears (for example: if it is off, the off state image disappears) to then begin the transition animation. How can I make the image remain Teriror? (something like 'off' image > fade > 'on' image, not 'off' image > some > fade > 'on image')

  • Using the OnTouchListener I believe that two things may be happening: Or the Drawable is changed before the call onTouch and there just extending to View. Or he’s switching later, but the fact of starting the Animation, it does after the next pass on layout, in the startAnimation it only configures and invalidates the View (need to confirm the correct time). In the second case would have to make the change manually (returns false in the onTouch, starts alpha animation for 0 change the background, starts another alpha to 1).

Show 3 more comments

1 answer

1


To do this effect, I made an auxiliary class to listen for events of Motion in the ToggleButton, that in sequence:

  1. Starts an alpha animation from 1 to 0, leaving the ToggleButton transparent.
  2. With the ToggleButton transparent modifies its state using the setChecked
  3. Then start an alpha animation from 0 to 1 to display the new state.

The auxiliary class is:

public class DrawableTransition extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener, Animation.AnimationListener {

    WeakReference<ToggleButton> mToggle;
    GestureDetector mGestureDetector;
    AlphaAnimation mAlphaTo0, mAlphaTo1;

    public DrawableTransition(Context context, ToggleButton toggle) {
        mToggle = new WeakReference<ToggleButton>(toggle);

        mGestureDetector = new GestureDetector(context, this);

        mAlphaTo1 = new AlphaAnimation(0f, 1f);

        mAlphaTo1.setDuration(1000l);

        mAlphaTo0 = new AlphaAnimation(1f, 0f);

        mAlphaTo0.setDuration(1000l);
        mAlphaTo0.setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) {}

    @Override
    public void onAnimationEnd(Animation animation) {
        // Animacao de alpha para 0 terminou, vamos mostrar o botao
        show();
    }

    @Override
    public void onAnimationRepeat(Animation animation) {}

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // Delega para o GestureDetector
        mGestureDetector.onTouchEvent(event);
        return true;
    }

    // Single tap foi confirmado
    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        return hide();
    }

    // Troca o estado, com isso alterando o background
    // depois iniciando uma animacao de alpha para 1, exibindo o botao novamente
    boolean show() {
        ToggleButton toggle = mToggle.get();

        if(toggle == null) {
            return false;
        }

        toggle.setChecked(! toggle.isChecked());

        toggle.startAnimation(mAlphaTo1);

        return true;
    }

    // Faz a animacao de alpha para 0, escondendo o botao
    boolean hide() {
        ToggleButton toggle = mToggle.get();

        if(toggle == null) {
            return false;
        }

        toggle.startAnimation(mAlphaTo0);

        return true;
    }

    /**
     * Limpar recursos, a fim de evitar memory leak
     * Chamar no onDestroy ou onDestroyView
     */
    public void clean() {
        mGestureDetector = null;
        mToggle.clear();
        mToggle = null;
        mAlphaTo0 = mAlphaTo1 = null;
    }
}

I ended up using the GestureDetector, why work only on onTouch there may be false positives of ACTION_UP. The ACTION_UP also occurs when he lifts his finger off the button, and this is characterized as a cancellation. The GestureDetector can do this treatment, so it is easier to use it.

To use:

ToggleButton toggle = findViewById(...);

toggle.setOnTouchListener(new DrawableTransition(getApplicationContext(), toggle));
  • I think I’m doing something wrong... using the class you posted, the button just fades between states, but it doesn’t perform what it should perform... I must put the codes of what I want it to do inside some place of the class?

  • What do you mean you don’t do what you should? It would be the processing of OnClickListener? In that case yes, with the return true he doesn’t, so you just call right after the hide within the onSingleTapConfirmed. Or would it be the exchange of background?

  • Sorry for the huge delay in responding, I put this project aside for a while on account of other projects. The fade I want is the following: the initial (off) state becomes visible during the transition and the fade occurs only in the second (on) state. It would be like an overlay with a fade-in. Your class is causing the initial state (off) to disappear and then the final state (on) to appear. I could try to do it myself based on your class, but as I said, I ended up stopping the project for a while.

  • @Renanlazarotto, now understood. The way I did, just using the state of itself View can’t do it. To do it the way you want, you’ll have to do a Custom View, using a FrameLayout (to have depth) and two ImageView's or two Toggle's, applying AlphaAnimation to make the transition.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.