Reputation: 183
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
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:
Upvotes: 0
Reputation: 58
Kotlin Solution
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
}
}
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
Reputation: 119
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>
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
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
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
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
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
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
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