按下时动态缩小按钮大小,并在释放时恢复其大小

15

我想创建一个按钮,在按下时可以改变大小(稍微缩小一点),并在按钮被释放后再次将其大小更改回正常大小。我正在使用Scale xml来实现这一点,但即使我没有释放按钮,它也会重新定位。

这里我指的是ImageView作为按钮。

这是我的源代码:

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总是返回零,因此我尝试使用weight来解决这个问题,但是使用weight后我的视图在点击时出现故障...我希望它能够平滑运行,所以我更喜欢使用缩放。

第二种方法:

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个回答

20

这里是您想要的内容:

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)来缩小元素,它会保持动画位置,并不会将其恢复到原始大小。

希望这能解决你的问题,祝好。


11
  1. 在 /res/animator 中创建两个 XML 文件

    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>

恢复大小.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>
  • 用法

    MotionEvent.ACTION_DOWN

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

    MotionEvent.ACTION_UP事件中

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

    8
    自API 21(棒棒糖)以来,基于stateListAnimator属性的仅限于XML的解决方案可用。以下是一个布局示例,当按下时缩小为其原始大小的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
    一些类似的问题:

    顺便说一下,这个解决方案存在一个问题,就是集合内的顺序。按下的状态需要在默认项之前列出。如果顺序不正确,动画将无法正常工作。 - undefined

    2
    您可以在触摸监听器上按以下方式以编程方式执行此操作。
    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);
        }
      } 
    }
    

    2

    这很简单,但你需要了解Android API如何处理每个监听器的调用。

    这是诀窍:

    1- TouchListener中的onTouch方法将首先被调用。
    1.1- 如果onTouch方法返回true,则LongClickListener中的onLongClickClickListener中的onClick都将被忽略(不再被调用)。
    2- 如果onTouch方法返回false,则检查onLongClick是否返回true,如果是,则onClick将不会被调用,但如果它返回false,则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方法中执行某些操作,如果用户释放按钮,则再次减小大小。
    如果你想每次调用onLongClick时都调用onClick,只需让onLongClick返回false即可。

    1
    你可以添加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>
    

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

    3
    最好添加解释。你的代码/帖子的目的是什么。不要使用HTML代码片段来编写Android XML布局代码。 - Shashanth
    @Shashanth 我已经删除了代码片段并将其设置为代码块,但仍需要 Rohit Shewale 进行编辑以获得更多解释。 - TessavWalstijn

    0

    Kotlin解决方案

    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 {
                  // 单击事件
              }
          }
      }
      
    希望这对你有所帮助。

    0
    这是对我有效的代码:
    <!--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>
    

    基本上和Valeriy Katkov的回答一样,但是我添加了android:state_pressed="false",没有这个的话在我的情况下无法工作。
    你可以像这样使用它:
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:stateListAnimator="@animator/scale_pressed"/>
    

    这是它的样子:

    GIF


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接