学习总结--属性动画

关于属性动画的学习,郭大婶已经有过很经典的分享,这里只是对自己这些天学习属性动画的总结,传送门:郭大婶属性动画分享

这篇总结分为以下几个方面:
1. 基本使用
2. 自定义属性
3. 实例

基本使用

属性动画,用的较多的就是 ObjectAnimator、ValueAnimator、AnimatorSet 这三个,这里就不浪费口水了,看这:Android属性动画完全解析(上),初识属性动画的基本用法

这里补充一些常用的可以直接使用的属性动画的属性值:

来自 Android 群英传:

  • translationX 和 translationY:这两个属性作为一种增量来控制着 View 对象从它的布局器的左上角坐标开始的位置。
  • rotation、rotationX 和 rotationY:这三个属性控制 View 对象围绕支点进行 2D 和 3D 旋转。
  • scaleX 和 scaleY:这两个属性控制着 View 对象围绕它的支点进行 2D 缩放。
  • pivotX 和 pivotY:这两个属性控制着 View 对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是 View 对象的中心点。
  • x 和 y:这是两个简单实用的属性,它描述了 View 对象在它的容器中的最终位置,它是最初的左上角坐标与translationX、translationY 值的累计和。
  • alpha:它表示 View 对象的 alpha 透明度。默认值是 1(不透明), 0 代表完全透明(不可见)。

自定义属性

系统提供的动画属性值足以完成很多功能了,但是如果想要些更炫的效果,系统的那些属性值就不够用了,只能自己来实现咯。这里提供两种方案:

1、通过自定义一个属性类或者包装类

如果想要通过动画来实现某个 View 宽度的变化该怎么做呢?系统提供的属性动画的属性中并没有 width 这个属性,那么只能自己来定义一个 width 属性咯。首先新建一个类,在这个类中提供一个构造方法,并且提供想要定义的属性的 get、set 方法:

public class WrapperView {

    private View mTarget;

    public WrapperView(View mTarget) {
        this.mTarget = mTarget;
    }

    /**
     * 获取宽度
     * @return
     */
    public int getWidth() {
        return mTarget.getLayoutParams().width;
    }

    /**
     * 设置宽度
     * @param width
     */
    public void setWidth(int width) {
        mTarget.getLayoutParams().width = width;
        // 重新刷新
        mTarget.requestLayout();
        }
    }

这个包装类还是很简单的,目的是改变某个 View 的 width。接下来就可以拿来用了:

public class ObjectActivity extends AppCompatActivity {

  private Button button;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_object);

      button = (Button) findViewById(R.id.button);
      // 自定义属性的属性动画
      WrapperView view = new WrapperView(button);
      ObjectAnimator.ofInt(view, "width", 500).setDuration(5000).start();
  }
}

这个都没什么难度的,布局文件就只是添加了个 Button。附上布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_object"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="cn.ljuns.androidgrowing.practice.ObjectActivity">

    <Button
            android:text="Button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"
            android:id="@+id/button" />
</RelativeLayout>

最终的效果是这样的:

2、通过 ValueAnimator 来实现

如果想要点击一个 View 来实现另一个 View 的展示和隐藏,设置 View 的 visibility 为 GONE 或 VISIBLE 就可以实现,但是这样的话显示和隐藏都是瞬间完成的,如何让它在显示和隐藏时增加一个动画效果呢?先来看看布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/activity_fade"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  tools:context="cn.ljuns.androidgrowing.example.FadeActivity">

  <LinearLayout
      android:id="@+id/click"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="@color/colorAccent"
      android:gravity="center_vertical"
      android:orientation="horizontal">

      <ImageView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:src="@mipmap/ic_launcher" />

      <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Click Me" />
  </LinearLayout>

  <LinearLayout
      android:id="@+id/hide"
      android:layout_width="match_parent"
      android:layout_height="50dp"
      android:background="@color/colorPrimary"
      android:gravity="center_vertical"
      android:orientation="horizontal"
      android:visibility="gone">

      <ImageView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:src="@mipmap/ic_launcher" />

      <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="I am hide" />
  </LinearLayout>
</LinearLayout>

这里有两个 LinearLayout,第二个默认为不可见。点击第一个 LinearLayout 实现第二个 LinearLayout 的显示和隐藏,接下来看看 Acitvity 的代码:

public class FadeActivity extends AppCompatActivity {

  private LinearLayout mClick;
  private LinearLayout mHide;
  private int mLayoutHeight;
  private float mDensity;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_fade);

      mDensity = getResources().getDisplayMetrics().density;
      mLayoutHeight = (int) (mDensity * 50 + 0.5);

      mHide = (LinearLayout) findViewById(R.id.hide);
      mClick = (LinearLayout) findViewById(R.id.click);
      mClick.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              if (mHide.getVisibility() == View.GONE) {
                  // 展开
                  OpenAnim(mHide);
              } else {
                  // 隐藏
                  CloseAnim(mHide);
              }
          }
      });
  }

  /**
   * 展开动画
   */
  private void OpenAnim(View view) {
   Toast.makeText(this,"open",Toast.LENGTH_SHORT).show();
      view.setVisibility(View.VISIBLE);
      ValueAnimator animator = createAnimaor(view, 0,
      mLayoutHeight);
      animator.start();
  }

  /**
   * 隐藏动画
   */
  private void CloseAnim(final View view) {
      Toast.makeText(this,"close",Toast.LENGTH_SHORT).show();
      int height = view.getHeight();
      ValueAnimator animator = createAnimaor(view, height, 0);
      animator.addListener(new AnimatorListenerAdapter() {
          @Override
          public void onAnimationEnd(Animator animation) {
              view.setVisibility(View.GONE);
          }
      });
      animator.start();
  }

  /**
   * 创建动画
   * @param view
   * @param start:起始位置
   * @param end:结束位置
   * @return
   */
  private ValueAnimator createAnimaor(final View view, int start, int end) {
      ValueAnimator animator = ValueAnimator.ofFloat(start, end);
      animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
          @Override
          public void onAnimationUpdate(ValueAnimator valueAnimator) {
              // 改变高度来实现动态显示
              float value = (float) valueAnimator.getAnimatedValue();
              ViewGroup.LayoutParams params = view.getLayoutParams();
              params.height = (int) value;
              view.setLayoutParams(params);
          }
      });
      animator.setDuration(3000);
      return animator;
  }
}

这里有个小技巧:给动画添加一个监听事件,在事件中通过 valueAnimator.getAnimatedValue() 获取到变化的值再赋值给 View 的高度,这样就可以实现 View 是通过动画慢慢出现的。效果图:

其实这个例子和上面那个例子很相似,上面是通过动画显示 View 的宽度,这里是通过动画显示和隐藏 View 的高度,两种方案都可以实现一样的效果。这里再给一个郭大婶属性动画系列中对 ValueAnimator 的高级用法:Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法

实例

最后这里拿个小例子来对属性动画的学习进行结尾,要实现的效果是这样子的:

其实这种效果真的很简单,就是将所有图片放在一起,然后给最上面的图片设置点击事件,通过动画来显示和隐藏其他图片。下面先来看看布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout                         
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_menu"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="cn.ljuns.androidgrowing.example.MenuActivity">

    <ImageView
            android:id="@+id/img_b"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/b" />

    <ImageView
            android:id="@+id/img_c"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/c" />

    <ImageView
            android:id="@+id/img_d"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/d" />

    <ImageView
            android:id="@+id/img_e"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/e" />

    <ImageView
            android:id="@+id/img_a"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/a" />
</RelativeLayout>

布局文件就只是在 RelativeLayout 中放了几个要作为菜单显示的图片,接下来是 Activity :

public class MenuActivity extends AppCompatActivity implements
        View.OnClickListener{
    //  图片集合
    private int[] mRes = {R.id.img_a,
     R.id.img_b, R.id.img_c, R.id.img_d, R.id.img_e};
    private List<ImageView> mImageView;
    private boolean isOpen;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_menu);

        init();
    }

    /**
     * 初始化
     */
    private void init() {
        isOpen = false;
        mImageView = new ArrayList<>();
        for (int i = 0; i < mRes.length; i++) {
            ImageView imageView = (ImageView) findViewById(mRes[i]);
            // 给每个 ImageView 设置点击事件
            imageView.setOnClickListener(this);
            mImageView.add(imageView);
        }
    }

    /**
     * 点击事件
     * @param view
     */
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.img_a:
                if (isOpen) {
                    closeMenu();
                    isOpen = false;
                } else {
                    openMenu();
                    isOpen = true;
                }
                break;
            default:
                Toast.makeText(this,"子菜单",Toast.LENGTH_SHORT).show();
                break;
        }
    }

    /**
     * 打开菜单
     */
    private void openMenu() {
        ObjectAnimator animator0 =
        ObjectAnimator.ofFloat(mImageView.get(0), "alpha", 1f, 0.5f);
        ObjectAnimator animator1 =
        ObjectAnimator.ofFloat(mImageView.get(1), "translationX", 0, -200f);
        ObjectAnimator animator2 =
        ObjectAnimator.ofFloat(mImageView.get(2), "translationX", 0, 200f);
        ObjectAnimator animator3 =
        ObjectAnimator.ofFloat(mImageView.get(3), "translationY", 0, -200f);
        ObjectAnimator animator4 =
        ObjectAnimator.ofFloat(mImageView.get(4), "translationY", 0, 200f);
        AnimatorSet set = new AnimatorSet();
        set.playTogether(animator0, animator1,
        animator2, animator3, animator4);
        set.setInterpolator(new BounceInterpolator());
        set.setDuration(1000);
        set.start();
    }

    /**
     * 关闭菜单
     */
    private void closeMenu() {
        ObjectAnimator animator0 =
        ObjectAnimator.ofFloat(mImageView.get(0), "alpha", 0.5f, 1f);
        ObjectAnimator animator1 =
        ObjectAnimator.ofFloat(mImageView.get(1), "translationX", -200f, 0);
        ObjectAnimator animator2 =
        ObjectAnimator.ofFloat(mImageView.get(2), "translationX", 200, 0);
        ObjectAnimator animator3 =
        ObjectAnimator.ofFloat(mImageView.get(3), "translationY", -200, 0);
        ObjectAnimator animator4 =
        ObjectAnimator.ofFloat(mImageView.get(4), "translationY", 200, 0);
        AnimatorSet set = new AnimatorSet();
        set.playTogether(animator0, animator1,
        animator2, animator3, animator4);
        set.setInterpolator(new BounceInterpolator());
        set.setDuration(1000);
        set.start();
    }
  }

初始化的时候把图片装进 list 集合,并给每个图片设置点击事件。打开和关闭的时候给每个图片设置一个动画,再用 AnimatorSet 来统一执行。打开和关闭的动画不同点就是起始的位置和结束的位置刚好相反,有兴趣可以根据这个原理自己计算角度设置更炫的效果。源码:属性动画相关

通过几天的学习,属性动画算是告一段落了,加紧时间不断学习提升自己,争取每周都来一篇总结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值