Уменьшить размер кнопки при нажатии и восстановить ее размер при отпускании

Я хочу создать кнопку, которая меняет размер (немного меньше) при нажатии, и после того, как кнопка будет отпущена, размер должен вернуться к нормальному размеру. Я использую Scale xml, чтобы добиться этого, но он переместился даже если я не отпущу кнопку.

здесь я имею в виду просмотр изображений.

вот мой исходный код:-

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;
            }
        });

мой масштаб 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>

мой 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>

Я попытался с paramLayout, но, как я установил вес в XML, чтобы он не дал мне эффект на анимацию.

я уже использую этот подход:-

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 всегда возвращает мне ноль, поэтому я пытался сделать это с весом, используя вес, мой глюк зрения на клик.. я хочу, чтобы он был гладким, поэтому я предпочитаю масштабирование

Второй подход:-

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;

это работает, но не гладко

Подскажите пожалуйста как сделать

Спасибо

9 ответов

Решение

Вот что вы хотите:-

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;
            }
        });

И просто внесите несколько изменений в ваш 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" />

используйте scaleDown.play (scaleDownX).with (scaleDownY), он сохранит анимированную позицию и не вернет ее к исходному

Надеюсь, это решит ваши проблемы

Android Best Practice (для вашего требования):

1- Создайте два XML-файла в /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>

II. 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- Использование

я. Когда MotionEvent.ACTION_DOWN

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

II. Когда MotionEvent.ACTION_UP

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

Это решение тщательно протестировано и соответствует вашим требованиям.

Начиная с API 21 (Lollipop) доступно только xml-решение на основе атрибута stateListAnimator. Вот пример макета, который при нажатии масштабируется до 0,9 от исходного размера:

       <!-- 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>

Вы также можете найти пример в официальном приложении с образцами материалов Owl. Взгляните на onboarding_topic_item.xml.

Несколько похожих вопросов:

Вы можете сделать это программно следующим образом на 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);
    }
  } 
}

Это очень просто, но вы должны знать, как Android API обрабатывает вызов каждого слушателя.

Вот уловка:

1- метод onTouch в TouchListener будет вызываться первым.
1.1- Если метод onTouch возвращает true, то onLongClickLongClickListener) или onClickClickListener) будут проигнорированы (оба больше не будут вызываться).
2- Если onTouch метод возвращает ложь, а затем проверить, если onLongClick возвращает истину, так OnClick не будет называться, но если она возвращает ложь так OnClick будет называться так же.

Вот пример:

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;
    });

В этом примере я пытаюсь увеличить размер кнопки, если я обнаруживаю долгое нажатие, в противном случае я делаю что-то в методе onClick, и если пользователь отпускает кнопку, я снова уменьшаю размер.

Если вы хотите, чтобы onClick вызывался каждый раз, когда вызывается onLongClick, просто сделайте так, чтобы onLongClick возвращал false;

Вы можете добавить свойство fillafter: 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>

Котлин Решение

  1. Создать функцию расширения:
            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. Вызов созданной функции в представлении:
            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
            }
        }
    }
    

Надеюсь, это помогло вам

<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" 
/>

Вот код, который сработал у меня:

      <!--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>

В принципе то же самое, что и в ответе Валерия Каткова , но я добавилandroid:state_pressed="false"без чего в моем случае это не сработало.

И вы можете использовать его так:

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

Вот как это выглядит:

Другие вопросы по тегам