user2028
user2028

Reputation: 183

Animatedly reduce button size on press and regain it's size on release

I want a to create a button that changes size (little bit smaller) when it's pressed, and after the button is released again the size should change back to normal size.I am using Scale xml to achieve this but it re-positioned itself even if i do not release button.

button here i am referring to imageview.

here is my source code :-

imgSpin = (ImageView) findViewById(R.id.iv_spins);
    imgSpin.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    imgSpin.startAnimation(mAnimation);
                    spinslot();
                    break;

                case MotionEvent.ACTION_UP:
                    imgSpin.clearAnimation();
                    imgSpin.setEnabled(false);
                    break;
                }
                return true;
            }
        });

my scale Xml :-

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromXScale=".8"
    android:fromYScale=".8"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="1.0"
    android:toYScale="1.0" >

</scale>

my XML:-

 <LinearLayout
                android:id="@+id/layout_status"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight=".2"
                android:orientation="horizontal" >

                <View
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight=".5" />

                <ImageView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="2.1"
                    android:background="@drawable/bootser" />

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="2.5"
                    android:background="@drawable/bet_bg"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:weightSum="1" >

                    <ImageView
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_gravity="center_vertical"
                        android:layout_weight=".2"
                        android:src="@drawable/leftarrow" />

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="1dp"
                        android:layout_weight=".6"
                        android:gravity="center"
                        android:text="Bet"
                        android:textColor="@android:color/white" />

                    <ImageView
                        android:layout_width="0dp"
                        android:layout_height="40dp"
                        android:layout_gravity="center_vertical"
                        android:layout_weight=".2"
                        android:src="@drawable/rightarrow" />
                </LinearLayout>

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="2.5"
                    android:background="@drawable/container"
                    android:gravity="center" >

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="2dp"
                        android:text="winning"
                        android:textColor="@android:color/white" />
                </LinearLayout>

                <ImageView
                    android:id="@+id/iv_spins"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1.9"
                    android:background="@drawable/spin" />

                <View
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight=".5" />
            </LinearLayout>

I tried with paramLayout but as i set the weights in XML so it do not give me the effect on animation.

i already use this approach :-

case MotionEvent.ACTION_DOWN:
                ViewGroup.LayoutParams params = imgSpin.getLayoutParams();
                // Button new width
                params.width = params.width - (params.width *90/100);

                imgSpin.setLayoutParams(params);

                break;

params.width always return me zero so i tried to do this with weight , by using weight my view glitch on click ..i want it to be smooth so i am preferring scaling

Second approach :-

case MotionEvent.ACTION_DOWN:
                LinearLayout.LayoutParams params = (android.widget.LinearLayout.LayoutParams) imgSpin
                        .getLayoutParams();
                // Button new width
                params.weight = 1.7f;

                imgSpin.setLayoutParams(params);
                // imgSpin.startAnimation(mAnimation);
                // spinslot();
                break;

            case MotionEvent.ACTION_UP:
                LinearLayout.LayoutParams params2 = (android.widget.LinearLayout.LayoutParams) imgSpin
                        .getLayoutParams();
                // Button new width
                params2.weight = 1.9f;

                imgSpin.setLayoutParams(params2);
                // imgSpin.clearAnimation();
                imgSpin.setEnabled(false);
                break;
            }
            return true;

it works but not smooth

Please tell me how to do

Thanks

Upvotes: 15

Views: 13110

Answers (9)

Kuki
Kuki

Reputation: 59

Here's the code that worked for me:

<!--res/animator/scale_pressed.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
    <set>
        <objectAnimator
            android:propertyName="scaleX"
            android:valueTo="1.0"
            android:duration="@android:integer/config_shortAnimTime"
            android:interpolator="@android:interpolator/fast_out_slow_in"/>
        <objectAnimator
            android:propertyName="scaleY"
            android:valueTo="1.0"
            android:duration="@android:integer/config_shortAnimTime"
            android:interpolator="@android:interpolator/fast_out_slow_in"/>
    </set>
</item>
<item android:state_pressed="true">
    <set>
        <objectAnimator
            android:propertyName="scaleX"
            android:valueTo="0.90"
            android:duration="@android:integer/config_shortAnimTime"
            android:interpolator="@android:interpolator/fast_out_slow_in"/>
        <objectAnimator
            android:propertyName="scaleY"
            android:valueTo="0.90"
            android:duration="@android:integer/config_shortAnimTime"
            android:interpolator="@android:interpolator/fast_out_slow_in"/>
    </set>
</item>
</selector>

Basically it's the same as in Valeriy Katkov's answer, but I added android:state_pressed="false" without which it didn't work in my case.

And you can use it like that:

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    android:stateListAnimator="@animator/scale_pressed"/>

Here's how it looks:

GIF

Upvotes: 0

rustam_safarov
rustam_safarov

Reputation: 58

Kotlin Solution

  1. Create extension function:
    fun View.addElasticTouchEffect() {
        this.setOnTouchListener { view, motionEvent ->
            when (motionEvent.action) {
                MotionEvent.ACTION_DOWN -> {
                    this.animate().scaleX(0.96f).scaleY(0.96f).setDuration(100).start()
                }
                MotionEvent.ACTION_UP,
                MotionEvent.ACTION_CANCEL -> {
                    this.animate().scaleX(1f).scaleY(1f).setDuration(100).start()
                    if (motionEvent.action == MotionEvent.ACTION_UP) {
                        this.performClick()
                    }
                }
            }
            return@setOnTouchListener true
        }
    }
    
  2. Call created function in view:
    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
            binding.ivAddress.addElasticTouchEffect()
            binding.ivAddress.setOnClickListener {
                // on click
            }
        }
    }
    

Hope this helped you

Upvotes: 0

HAFIZ MAAZ AHMAD
HAFIZ MAAZ AHMAD

Reputation: 119

  1. Create two XMLs in /res/animator

    reduce_size.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set android:ordering="sequentially"
        xmlns:android="http://schemas.android.com/apk/res/android">
        <set>
            <objectAnimator
                android:propertyName="scaleX"
                android:duration="300"
                android:valueTo="0.9f"
                android:valueType="floatType"/>
            <objectAnimator
                android:propertyName="scaleY"
                android:duration="300"
                android:valueTo="0.9f"
                android:valueType="floatType"/>
        </set>
    </set>
    

    regain_size.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set android:ordering="sequentially"
        xmlns:android="http://schemas.android.com/apk/res/android">
        <set>
            <objectAnimator
                android:propertyName="scaleX"
                android:duration="1000"
                android:valueTo="1f"
                android:startOffset="300"
                android:valueType="floatType"/>
            <objectAnimator
                android:propertyName="scaleY"
                android:duration="1000"
                android:valueTo="1f"
                android:startOffset="300"
                android:valueType="floatType"/>
        </set>
    </set>
    
  2. Usage

    On MotionEvent.ACTION_DOWN

    AnimatorSet reducer = (AnimatorSet) AnimatorInflater.loadAnimator(mContext,R.animator.squeeze_in);
    reducer.setTarget(view);
    reducer.start();
    

    On MotionEvent.ACTION_UP

    AnimatorSet regainer = (AnimatorSet) AnimatorInflater.loadAnimator(mContext,R.animator.squeeze_out);
    regainer.setTarget(view);
    regainer.start();
    

Upvotes: 11

Valeriy Katkov
Valeriy Katkov

Reputation: 40572

Since API 21 (Lollipop) an xml-only solution is available based on the stateListAnimator attribute. Here's an example of a layout which scale to 0.9 of its original size when it's pressed:

<!-- res/layout/my_layout.xml -->

<LinearLayout ...
  android:stateListAnimator="@animator/zoom_out_animator">...</layout>
<!-- res/animator/zoom_out_animator -->

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <set>
      <objectAnimator
          android:propertyName="scaleX"
          android:valueTo="1.0"
          android:duration="@android:integer/config_shortAnimTime"
          android:interpolator="@android:interpolator/fast_out_slow_in"/>
      <objectAnimator
          android:propertyName="scaleY"
          android:valueTo="1.0"
          android:duration="@android:integer/config_shortAnimTime"
          android:interpolator="@android:interpolator/fast_out_slow_in"/>
    </set>
  </item>
  <item android:state_pressed="true">
    <set>
      <objectAnimator
          android:propertyName="scaleX"
          android:valueTo="0.9"
          android:duration="@android:integer/config_shortAnimTime"
          android:interpolator="@android:interpolator/fast_out_slow_in"/>
      <objectAnimator
          android:propertyName="scaleY"
          android:valueTo="0.9"
          android:duration="@android:integer/config_shortAnimTime"
          android:interpolator="@android:interpolator/fast_out_slow_in"/>
    </set>
  </item>
</selector>

You can also find an example in the official material sample app Owl. Take a look at onboarding_topic_item.xml.

A few similar questions:

Upvotes: 8

Khalid Ali
Khalid Ali

Reputation: 373

It's very simple but you have to know how the Android API handles the call of each listener.

Here is the trick:

1- onTouch method in TouchListener will be called first.
1.1- If onTouch method returns true, so onLongClick(in LongClickListener) or onClick(in ClickListener) will be ignored(both will not be called any more).
2- If onTouch method returns false, then check if onLongClick returns true so onClick will not be called but if it returns false so onClick will be called as well.

Here is an example:

targetButton.setOnLongClickListener(v -> {
        targetButton.animate().scaleX(1.2f).scaleY(1.2f).setDuration(0);
        Log.d("Button", "onLong: ");
        return true;
    });
    targetButton.setOnClickListener(v -> {
        Log.d("Button", "onClick: ");
    });
    targetButton.setOnTouchListener((v, event) -> {
        Log.d("Button", "onTouch: ");
        if (event.getAction() == MotionEvent.ACTION_UP) {
            recordButton.animate().scaleX(1.0f).scaleY(1.0f).setDuration(0);
        } 
          // if you want to make it pretty increase the size when click also
          /* else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            targetButton.animate().scaleX(1.2f).scaleY(1.2f).setDuration(0);
            } */
        return false;
    });

In this example I am trying to increase the size of the button if I detect a long press, otherwise do something into onClick method, and if the user release the button I reduce the size again.

If you want the onClick called every time the onLongClick called just make the onLongClick returns false;

Upvotes: 2

rohit Shewale
rohit Shewale

Reputation: 1

<ImageView
  android:id="@+id/iv_spins"
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  android:layout_weight="1.9"
  android:adjustViewBounds="true"
  android:background="@drawable/spin"
  android:scaleX="1"
  android:scaleY="1" 
/>

Upvotes: 0

N. Kyalo
N. Kyalo

Reputation: 67

You can do this programmatically as follows on the touchlistener

new View.OnTouchListener() {
  public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction == MotionEvent.ACTION_DOWN) {
      // scale your value
      float reducedvalue = (float)0.7;
      v.setScaleX(reducedvalue);
      v.setScaleY(reducedvalue);
    }
    else if (event.getAction == MotionEvent.ACTION_UP) {
      v.setScaleX(1);
      v.setScaleY(1);
    }
  } 
}

Upvotes: 3

sid
sid

Reputation: 1116

Here is what you want :-

imgSpin.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    ObjectAnimator scaleDownX = ObjectAnimator.ofFloat(imgSpin,
                            "scaleX", 0.8f);
                    ObjectAnimator scaleDownY = ObjectAnimator.ofFloat(imgSpin,
                            "scaleY", 0.8f);
                    scaleDownX.setDuration(1000);
                    scaleDownY.setDuration(1000);

                    AnimatorSet scaleDown = new AnimatorSet();
                    scaleDown.play(scaleDownX).with(scaleDownY);

                    scaleDown.start();

                    spinslot();
                    break;

                case MotionEvent.ACTION_UP:
                    ObjectAnimator scaleDownX2 = ObjectAnimator.ofFloat(
                            imgSpin, "scaleX", 1f);
                    ObjectAnimator scaleDownY2 = ObjectAnimator.ofFloat(
                            imgSpin, "scaleY", 1f);
                    scaleDownX2.setDuration(1000);
                    scaleDownY2.setDuration(1000);

                    AnimatorSet scaleDown2 = new AnimatorSet();
                    scaleDown2.play(scaleDownX2).with(scaleDownY2);

                    scaleDown2.start();

                    imgSpin.setEnabled(false);
                    break;
                }
                return true;
            }
        });

And just make this few changes in your xml :-

<ImageView
                    android:id="@+id/iv_spins"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1.9"
                    android:adjustViewBounds="true"
                    android:background="@drawable/spin"
                    android:scaleX="1"
                    android:scaleY="1" />

make use of scaleDown.play(scaleDownX).with(scaleDownY),it will keep the animated position and will not restore it back to original

Hope it solves your problem cheers

Upvotes: 20

xtr
xtr

Reputation: 5960

You can add fillafter property: android:fillAfter="true"

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromXScale=".8"
    android:fromYScale=".8"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fillAfter="true"
    android:toXScale="1.0"
    android:toYScale="1.0" >
</scale>

Upvotes: 1

Related Questions