Android: Using ObjectAnimator to translate a View with fractional values of the View's dimension

前端 未结 5 1028
执笔经年
执笔经年 2020-11-28 05:56

It appears that the old view animations (translate, scale, and so on) are no longer accepted by the AnimationInflater, at least as of

相关标签:
5条回答
  • 2020-11-28 06:10

    Here is an important gotcha for those who are trying to create view animations like translate and scale.

    Property animations are located in a directory named "animator": res/animator/filename.xml

    BUT, view animations must be put in a directory simply called "anim": res/anim/filename.xml

    I first put my view animation in an "animator" folder, and was confused about Android Studio complaining about how translate was not a valid element. So, NO, view animations are not deprecated. They just have their own location for some confusing reason.

    0 讨论(0)
  • 2020-11-28 06:10

    View animations are not deprecated. If you are using Eclipse you can find them under tween animation as a resource type when creating a new Android XML file.

    0 讨论(0)
  • 2020-11-28 06:21

    here is complete working example (works as outlined by author of accepted answer.)

    Pressing the button toggles between 2 fragments A and B (by slide animation right to left). The fragments are just stupid text (AAAAAA and BBBBB) with different backgrounds.

    MainActivity.java

    package com.example.slidetrans;
    
    import android.app.Activity;
    import android.app.Fragment;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    
    
    public class MainActivity extends Activity {
    
        private static final String TAG = "Main";
    
        boolean showingA = true;
        Button button;
    
        A a;
        B b;
    
        private void incarnate(FragmentManager fm){
            int layoutId = R.id.frame;
            boolean fragmentWasNull = false;
            Fragment f = fm.findFragmentById(layoutId);
            if (f == null){
                Log.i(TAG, "fragment is null");
                if (showingA){
                    f = a = new A();
                } else {
                    f = b = new B();
                }
                fragmentWasNull = true;
            } else {
                Log.i(TAG, "fragment is not null");
                showingA = (f instanceof A);
                updateButtonText();
            }
            if (fragmentWasNull){
                FragmentTransaction ft = fm.beginTransaction();
                ft.add(layoutId, showingA ? a : b,  "main").commit(); 
            }
        }
        private void updateButtonText(){
            button.setText(showingA ? "slide in B" : "slide in A");
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            FragmentManager fm = getFragmentManager();
            button = (Button)findViewById(R.id.button);
            incarnate(fm);
            OnClickListener listener = new OnClickListener() {
                @Override
                public void onClick(View v) {
                    FragmentManager fm = getFragmentManager();
                    FragmentTransaction transaction = fm.beginTransaction();
                    transaction.setCustomAnimations(R.anim.in, R.anim.out);
                    transaction.replace(R.id.frame, showingA ? new B() : new A()).commit();
                    showingA = !showingA;
                    updateButtonText();
                }
            };
            button.setOnClickListener(listener);
        }
    }
    

    LL.java

    package com.example.slidetrans;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.widget.LinearLayout;
    
    public class LL extends LinearLayout {
    
        public LL(Context context) {
            super(context);
        }
    
        public LL(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public float getXFraction() {
            final int width = getWidth();
            if (width != 0) return getX() / getWidth();
            else return getX();
        }
    
        public void setXFraction(float xFraction) {
            final int width = getWidth();
            float newWidth = (width > 0) ? (xFraction * width) : -9999;
            setX(newWidth);
        }
    }
    

    main.xml (layout)

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
    >
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="slide in B" />
    
        <FrameLayout
            android:id="@+id/frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
    
        </FrameLayout>
    
    </LinearLayout>
    

    a.xml (layout)

    <?xml version="1.0" encoding="utf-8"?>
    <com.example.slidetrans.LL xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#00FF00"
    >
    
        <TextView
            android:id="@+id/aText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="AAAAAAAAAAAAAAAAAA"
            android:textSize="30sp"
            android:textStyle="bold"
        />
    
    </com.example.slidetrans.LL>
    

    b.xml (layout)

    <?xml version="1.0" encoding="utf-8"?>
    <com.example.slidetrans.LL xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#FFFF00"
    >
    
        <TextView
            android:id="@+id/bText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="30sp"
            android:textStyle="bold"
            android:text="BBBBBBBBBB"
        />
    
    </com.example.slidetrans.LL>
    

    in.xml (anim)

    <?xml version="1.0" encoding="utf-8"?>
    <set  xmlns:android="http://schemas.android.com/apk/res/android">
        <objectAnimator
        android:duration="500"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="xFraction"
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:valueType="floatType" />
    
    </set>
    

    out.xml (anim)

    <?xml version="1.0" encoding="utf-8"?>
    <set  xmlns:android="http://schemas.android.com/apk/res/android">
        <objectAnimator
        android:duration="500"
        android:interpolator="@android:anim/linear_interpolator"
        android:propertyName="xFraction"
        android:valueFrom="0.0"
        android:valueTo="-1.0"
        android:valueType="floatType" />
    
    </set>
    
    0 讨论(0)
  • 2020-11-28 06:27

    Android SDK implementation of FragmentTransaction expects an Animator while, for some obscure reason, the support library implementation expects an Animation, if you use Fragment implementation from support library your translate animation will work

    0 讨论(0)
  • 2020-11-28 06:31

    Actually object animators accept fractional values. But maybe you didn't understand the underlying concept of an objectAnimator or more generally a value animator. A value animator will animate a value related to a property (such as a color, a position on screen (X,Y), an alpha parameter or whatever you want). To create such a property (in your case xFraction and yFraction) you need to build your own getters and setters associated to this property name. Lets say you want to translate a FrameLayout from 0% to 25% of the size of your whole screen. Then you need to build a custom View that wraps the FrameLayout objects and write your getters and setters.

    public class SlidingFrameLayout extends FrameLayout
    {
        private static final String TAG = SlidingFrameLayout.class.getName();
    
        public SlidingFrameLayout(Context context) {
            super(context);
        }
    
        public SlidingFrameLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public float getXFraction()
        {
            int width = getWindowManager().getDefaultDisplay().getWidth();
            return (width == 0) ? 0 : getX() / (float) width;
        }
    
        public void setXFraction(float xFraction) {
            int width = getWindowManager().getDefaultDisplay().getWidth();
            setX((width > 0) ? (xFraction * width) : 0);
        }
    }
    

    Then You can use the xml way to declare the object animator putting xFraction under the property xml attribute and inflate it with a AnimatorInflater

    <?xml version="1.0" encoding="utf-8"?>
    <objectAnimator 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="xFraction" 
    android:valueType="floatType"
    android:valueFrom="0"
    android:valueTo="0.25" 
    android:duration="500"/>
    

    or you can just use the java line code

    ObjectAnimator oa = ObjectAnimator.ofFloat(menuFragmentContainer, "xFraction", 0, 0.25f);
    

    Hope it helps you!

    Olivier,

    0 讨论(0)
提交回复
热议问题