Android 애니메이션 컬렉션 속성 애니메이션 - 우선 살펴보기


이 섹션 소개:

이 섹션에서는 Android 애니메이션의 세 번째 유형인 속성 애니메이션을 소개합니다. 이전 섹션에서 기억하세요 8.4.2 Android 애니메이션 컬렉션 Tween Animation 은 조각입니다 전환 애니메이션을 설정할 때 App 패키지와 V4 패키지 아래의 Fragment가 그에 따라 setCustomAnimations()를 호출한다고 언급했습니다. v4 패키지에 있는 것은 Animation이고, 앱 패키지에 있는 것은 Animator입니다.

애니메이션 일반 애니메이션은 앞서 배운 프레임 애니메이션과 트윈 애니메이션입니다! Animator는 이 섹션에서 속성 애니메이션에 대해 이야기할 것입니다!

속성 애니메이션과 관련하여 위대한 사람인 Guo 삼촌이 세 가지 훌륭한 요약 기사를 작성했습니다. 매우 잘 작성되었습니다. 하지만 여기서는 대부분의 내용이 다음 세 가지 기사를 참조합니다.

Android 속성 애니메이션 전체 분석(1부), 처음으로 속성 애니메이션의 기본 사용법

Android 속성 전체 분석 animation(2부), ValueAnimator 및 ObjectAnimator의 고급 사용법

Android 속성 애니메이션에 대한 전체 분석(2부), Interpolator 및 ViewPropertyAnimator

의 사용법이 잘 작성되어 있거나 이 기사를 직접 건너뛰고 위의 세 가지 기사를 읽어보세요~

물론, 제 강연을 보시고 싶으시다면 환영합니다. 자, 이 섹션을 시작하겠습니다~


1. 속성 애니메이션의 개념


BB가 아니라 그냥 가세요. 사진으로 보면 너무 폭력적이네요~

1.jpg


2. ValueAnimator는 사용이 간단합니다.

사용 프로세스 :

  • 1. ValueAnimator의 ofInt(), ofFloat()을 호출합니다. 오브젝트 () ValueAnimator 인스턴스를 생성하는 정적 메서드
  • 2. setXxx 메서드는 애니메이션 지속 시간, 보간 방법, 반복 횟수 등을 설정합니다.
  • 3 인스턴스의 addUpdateListener를 호출하여 AnimatorUpdateListener를 추가합니다. 리스너, 리스너 속에서 ValueAnimator에서 계산된 값을 가져와서 지정된 객체에 적용할 수 있습니다~
  • 4. 인스턴스의 start() 메소드를 호출하여 애니메이션을 시작합니다! 또한, ofInt와 ofFloat 모두 다음과 같은 매개변수를 가지고 있음을 알 수 있습니다. float/int... 값은 여러 값을 나타냅니다!

사용 예:

2.gif

코드 구현:

레이아웃 파일: activity_main.xml, 매우 간단함, 버튼 4개, ImageView 1개

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ly_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画1" />

    <Button
        android:id="@+id/btn_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画2" />

    <Button
        android:id="@+id/btn_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画3" />

    <Button
        android:id="@+id/btn_four"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画4" />

    <ImageView
        android:id="@+id/img_babi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="@mipmap/img_babi" /></LinearLayout>

그런 다음 MainActivity.java로 이동하세요. 먼저 뷰 위치를 수정하는 메서드가 필요합니다. 여기에서는 moveView()를 호출하여 왼쪽과 위쪽의 시작 좌표와 너비와 높이를 설정합니다.

그런 다음 선형 이동, 크기 조정, 투명도를 사용한 회전, 원형 회전이라는 네 가지 애니메이션이 정의됩니다!

그런 다음 버튼을 통해 해당 애니메이션을 실행하세요~

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_one;
    private Button btn_two;
    private Button btn_three;
    private Button btn_four;
    private LinearLayout ly_root;
    private ImageView img_babi;
    private int width;
    private int height;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
    }

    private void bindViews() {
        ly_root = (LinearLayout) findViewById(R.id.ly_root);
        btn_one = (Button) findViewById(R.id.btn_one);
        btn_two = (Button) findViewById(R.id.btn_two);
        btn_three = (Button) findViewById(R.id.btn_three);
        btn_four = (Button) findViewById(R.id.btn_four);
        img_babi = (ImageView) findViewById(R.id.img_babi);

        btn_one.setOnClickListener(this);
        btn_two.setOnClickListener(this);
        btn_three.setOnClickListener(this);
        btn_four.setOnClickListener(this);
        img_babi.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_one:
                lineAnimator();
                break;
            case R.id.btn_two:
                scaleAnimator();
                break;
            case R.id.btn_three:
                raAnimator();
                break;
            case R.id.btn_four:
                circleAnimator();
                break;
            case R.id.img_babi:
                Toast.makeText(MainActivity.this, "不愧是coder-pig~", Toast.LENGTH_SHORT).show();
                break;
        }
    }


    //定义一个修改ImageView位置的方法
    private void moveView(View view, int rawX, int rawY) {
        int left = rawX - img_babi.getWidth() / 2;
        int top = rawY - img_babi.getHeight();
        int width = left + view.getWidth();
        int height = top + view.getHeight();
        view.layout(left, top, width, height);
    }


    //定义属性动画的方法:

    //按轨迹方程来运动
    private void lineAnimator() {
        width = ly_root.getWidth();
        height = ly_root.getHeight();
        ValueAnimator xValue = ValueAnimator.ofInt(height,0,height / 4,height / 2,height / 4 * 3 ,height);
        xValue.setDuration(3000L);
        xValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 轨迹方程 x = width / 2
                int y = (Integer) animation.getAnimatedValue();
                int x = width / 2;
                moveView(img_babi, x, y);
            }
        });
        xValue.setInterpolator(new LinearInterpolator());
        xValue.start();
    }

    //缩放效果
    private void scaleAnimator(){
    
        //这里故意用两个是想让大家体会下组合动画怎么用而已~
        final float scale = 0.5f;
        AnimatorSet scaleSet = new AnimatorSet();
        ValueAnimator valueAnimatorSmall = ValueAnimator.ofFloat(1.0f, scale);
        valueAnimatorSmall.setDuration(500);

        ValueAnimator valueAnimatorLarge = ValueAnimator.ofFloat(scale, 1.0f);
        valueAnimatorLarge.setDuration(500);

        valueAnimatorSmall.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float scale = (Float) animation.getAnimatedValue();
                img_babi.setScaleX(scale);
                img_babi.setScaleY(scale);
            }
        });
        valueAnimatorLarge.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float scale = (Float) animation.getAnimatedValue();
                img_babi.setScaleX(scale);
                img_babi.setScaleY(scale);
            }
        });

        scaleSet.play(valueAnimatorLarge).after(valueAnimatorSmall);
        scaleSet.start();

        //其实可以一个就搞定的
//        ValueAnimator vValue = ValueAnimator.ofFloat(1.0f, 0.6f, 1.2f, 1.0f, 0.6f, 1.2f, 1.0f);
//        vValue.setDuration(1000L);
//        vValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//            @Override
//            public void onAnimationUpdate(ValueAnimator animation) {
//                float scale = (Float) animation.getAnimatedValue();
//                img_babi.setScaleX(scale);
//                img_babi.setScaleY(scale);
//            }
//        });
//        vValue.setInterpolator(new LinearInterpolator());
//        vValue.start();
    }


    //旋转的同时透明度变化
    private void raAnimator(){
        ValueAnimator rValue = ValueAnimator.ofInt(0, 360);
        rValue.setDuration(1000L);
        rValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int rotateValue = (Integer) animation.getAnimatedValue();
                img_babi.setRotation(rotateValue);
                float fractionValue = animation.getAnimatedFraction();
                img_babi.setAlpha(fractionValue);
            }
        });
        rValue.setInterpolator(new DecelerateInterpolator());
        rValue.start();
    }

    //圆形旋转
    protected void circleAnimator() {
        width = ly_root.getWidth();
        height = ly_root.getHeight();
        final int R = width / 4;
        ValueAnimator tValue = ValueAnimator.ofFloat(0,
                (float) (2.0f * Math.PI));
        tValue.setDuration(1000);
        tValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 圆的参数方程 x = R * sin(t) y = R * cos(t)
                float t = (Float) animation.getAnimatedValue();
                int x = (int) (R * Math.sin(t) + width / 2);
                int y = (int) (R * Math.cos(t) + height / 2);
                moveView(img_babi, x, y);
            }
        });
        tValue.setInterpolator(new DecelerateInterpolator());
        tValue.start();
    }
}

알겠습니다. 과정은 매우 간단합니다. 먼저 ValueAnimator 개체를 만들고 ValueAnimator.ofInt/ofFloat를 호출하세요. 애니메이션 지속 시간을 가져오고 설정하고, addUpdateListener추가 AnimatorUpdateListener 이벤트 수신을 추가하고, 그런 다음 animation 매개변수의 getAnimatedValue()를 사용하여 현재 값을 얻은 다음 이 값을 유지할 수 있습니다. 소위 애니메이션 효과를 형성하기 위해 뷰의 일부 속성을 수정한 다음 setInterpolator 애니메이션 렌더링 모드를 설정하려면, 마지막으로 start()를 호출해 애니메이션 재생을 시작~

맙소사, 직선의 방정식, 원의 매개방정식, 이제 슬슬 이해가 되기 시작하는데, 이게 고급 수학 아닌가요? 과학적 쓰레기는 삼각 함수조차 잊어버렸습니다...3.gif

github의 예제 참조:MoveViewValueAnimator


3. ObjectAnimator는 사용하기 쉽습니다

ValueAnimator에 비해 ObjectAnimator는 사용하기 더 쉽습니다 직접 모든 개체의 속성에 애니메이션을 적용 ! 맞습니다. View 객체뿐만 아니라 모든 객체입니다. 객체에 특정 속성값을 지속적으로 부여한 후, 객체 속성값의 변화에 ​​따라 이를 어떻게 표시할지 결정합니다. 나오다! 예를 들어 TextView에 대해 다음 애니메이션을 설정합니다. ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f)
여기서 alpha 값은 1f - 0f에서 지속적으로 변경되고 개체는 인터페이스를 새로 고칩니다. 속성값의 변화에 ​​따라 표시됩니다. 페이드 인 및 아웃 효과를 보여 주지만 TextView 클래스에는 알파 속성이 없습니다. ObjectAnimator의 내부 메커니즘은 다음과 같습니다. 속성 값을 찾는 대신 전송된 속성 이름에 해당하는 get 및 set 메서드를 찾습니다. ! 믿을 수 없다면 TextView의 소스 코드를 확인하여 알파 속성이 있는지 확인할 수 있습니다! 자, ObjectAnimator를 사용하여 4가지 트윈 애니메이션 효과를 구현해 보겠습니다~

Running renders:

4.gif

코드 구현:

레이아웃은 위 레이아웃을 직접 사용하고, ImageView를 대체하는 버튼을 추가합니다. TextView를 사용하는 경우 여기에 코드를 게시하지 않겠습니다. 코드의 MainActivity.java 부분으로 바로 가보겠습니다. 사실 다 비슷해요~

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_one;
    private Button btn_two;
    private Button btn_three;
    private Button btn_four;
    private Button btn_five;
    private LinearLayout ly_root;
    private TextView tv_pig;
    private int height;
    private ObjectAnimator animator1;
    private ObjectAnimator animator2;
    private ObjectAnimator animator3;
    private ObjectAnimator animator4;
    private AnimatorSet animSet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
        initAnimator();
    }

    private void bindViews() {
        ly_root = (LinearLayout) findViewById(R.id.ly_root);
        btn_one = (Button) findViewById(R.id.btn_one);
        btn_two = (Button) findViewById(R.id.btn_two);
        btn_three = (Button) findViewById(R.id.btn_three);
        btn_four = (Button) findViewById(R.id.btn_four);
        btn_five = (Button) findViewById(R.id.btn_five);
        tv_pig = (TextView) findViewById(R.id.tv_pig);

        height = ly_root.getHeight();
        btn_one.setOnClickListener(this);
        btn_two.setOnClickListener(this);
        btn_three.setOnClickListener(this);
        btn_four.setOnClickListener(this);
        btn_five.setOnClickListener(this);
        tv_pig.setOnClickListener(this);
    }

    //初始化动画
    private void initAnimator() {
        animator1 = ObjectAnimator.ofFloat(tv_pig, "alpha", 1f, 0f, 1f, 0f, 1f);
        animator2 = ObjectAnimator.ofFloat(tv_pig, "rotation", 0f, 360f, 0f);
        animator3 = ObjectAnimator.ofFloat(tv_pig, "scaleX", 2f, 4f, 1f, 0.5f, 1f);
        animator4 = ObjectAnimator.ofFloat(tv_pig, "translationY", height / 8, -100, height / 2);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_one:
                animator1.setDuration(3000l);
                animator1.start();
                break;
            case R.id.btn_two:
                animator2.setDuration(3000l);
                animator2.start();
                break;
            case R.id.btn_three:
                animator3.setDuration(3000l);
                animator3.start();
                break;
            case R.id.btn_four:
                animator4.setDuration(3000l);
                animator4.start();
                break;
            case R.id.btn_five:
                //将前面的动画集合到一起~
                animSet = new AnimatorSet();
                animSet.play(animator4).with(animator3).with(animator2).after(animator1);
                animSet.setDuration(5000l);
                animSet.start();
                break;
            case R.id.tv_pig:
                Toast.makeText(MainActivity.this, "不愧是coder-pig~", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

위에서 언급한 결합 애니메이션에 대해서도 아주 간단하게 말씀드리겠습니다~


4. 및 AnimatorListener

위의 두 가지 예에서 우리 모두는 AnimatorSet 클래스를 사용하여 애니메이션 조합을 경험했습니다!

play() 메서드를 호출한 다음 실행할 첫 번째 애니메이션을 전달합니다. 이때 Builder 클래스가 반환됩니다.

5.png

다음으로 Builder에서 제공하는 네 가지 메서드를 호출할 수 있습니다. 다른 애니메이션을 결합하려면:

  • after(Animator anim) 기존 애니메이션을 들어오는 애니메이션에 삽입하고
  • after(긴 지연) 후에 실행합니다. 지정된 밀리초
  • before 후에 기존 애니메이션을 실행합니다. (Animator anim) 들어오는 애니메이션 앞에 기존 애니메이션을 삽입하고 실행합니다
  • with(Animator anim) 기존 애니메이션과 들어오는 애니메이션을 동시에 실행

자, 아주 간단합니다. 다음에 얘기하겠습니다. 애니메이션 이벤트 모니터링의 경우 위 ValueAnimator의 리스너는 AnimatorUpdateListener입니다. 값 상태가 변경되면 onAnimationUpdate 메서드가 호출됩니다.

이런 종류의 이벤트 외에도 애니메이션 진행 상황 모니터링도 있습니다~ AnimatorListener, addListener 메서드를 호출할 수 있습니다. 리스너를 추가한 후 다음 네 가지 콜백 메서드를 재정의합니다.

  • onAnimationStart(): 애니메이션 시작
  • onAnimationRepeat(): 애니메이션 반복
  • onAnimationEnd(): 애니메이션 종료
  • onAnimationCancel( ) : 애니메이션 취소

네, 실제로 AnimatorListener를 사용한다면 네 가지 방법을 모두 다시 작성해야 합니다. 물론 이전 동작 섹션과 동일합니다. Android는 AnimatorListenerAdapter라는 어댑터 클래스를 제공했습니다. 메소드가 모두 구현되었으므로 여기에 콜백 메소드를 작성하면 됩니다!


5. XML을 사용하여 애니메이션 작성

XML을 사용하여 애니메이션을 작성합니다. Java 코드보다 그리는 데 시간이 조금 더 걸릴 수 있지만 재사용하기가 훨씬 쉽습니다! 해당 XML 태그는 다음과 같습니다: <animator><objectAnimator><set> 관련 속성은 다음과 같이 설명됩니다.

  • android:ordering: 애니메이션의 재생 순서를 지정합니다: 순차적으로(순차 실행), 함께(동시 실행)
  • android:duration: 애니메이션의 지속 시간
  • android:propertyName ="x": x 여기서 위의 "알파"를 기억하시나요? 애니메이션을 로드하는 객체에 필요합니다. objectAnimator가 객체의 값을 수정하는 방법인 getx 및 setx의 메소드를 정의하십시오!
  • android:valueFrom="1": 애니메이션 시작 시 초기 값
  • android:valueTo="0": 애니메이션 종료 시 최종 값
  • android:valueType="floatType": 값 변경 데이터 유형

사용 예는 다음과 같습니다:

0에서 100으로 부드럽게 전환되는 애니메이션:

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="intType"/>

뷰의 알파 속성을 1에서 0으로 변경하세요.:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>

3애니메이션 사용 데모 설정:

 <set android:ordering="sequentially" >
    <set>
        <objectAnimator
            android:duration="500"
            android:propertyName="x"
            android:valueTo="400"
            android:valueType="intType" />
        <objectAnimator
            android:duration="500"
            android:propertyName="y"
            android:valueTo="300"
            android:valueType="intType" />
    </set>
    <objectAnimator
        android:duration="500"
        android:propertyName="alpha"
        android:valueTo="1f" /></set>

애니메이션 파일 로드:

AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(mContext, 
             R.animator.property_animator);  
animator.setTarget(view);  
animator.start();

6에서 샘플 코드를 다운로드하세요. 섹션 :

AnimatorDemo1.zip

AnimatorDemo2.zip


이 섹션 요약:

좋습니다. 이 섹션에서는 Android에서 속성 애니메이션의 기본 사용법을 요약해 보겠습니다. 하지만 내용은 매우 간단합니다. 예, 그리고 예제는 매우 흥미롭습니다. 모두가 좋아할 것이라고 믿습니다. 글쎄, 그게 다입니다. 감사합니다~

기사를 작성해 주셔서 감사합니다Guo Shen~

6.jpg