2

ObjectAnimator.ofFloat(Object target, String xPropertyName, String yPropertyName, Path path) seems to work in API 21 but not 23? Simple reproduction: Here's the activity and layout. Try long press on the text view, both level will work. But, just tap on the view: 21 works but not 23.

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final View hello = findViewById(R.id.hello);
        final Path p = new Path();
        p.addCircle(-200, 0, 100, Path.Direction.CW);
        hello.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator a = ObjectAnimator.ofFloat(hello, "translationX", "translationY", p);
                a.setDuration(1000);
                a.start();
            }
        });
        hello.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                ObjectAnimator a = ObjectAnimator.ofFloat(hello, "scaleX", 2);
                a.setDuration(1000);
                a.start();
                return true;
            }
        });
    }
}

--

<?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_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="letstwinkle.com.pathtest.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_marginEnd="63dp"
        android:layout_marginTop="210dp"
        android:id="@+id/hello"
        android:background="#880033"/>
</RelativeLayout>

This may be the same as this bug reported in June. https://code.google.com/p/android/issues/detail?id=212776

androidguy
  • 3,005
  • 2
  • 27
  • 38
  • I tested with AppCompatActivity and emulator versions 22 and 23: works on 22, starts to move on 23 but freezes right after that. – Bö macht Blau Sep 21 '16 at 09:46
  • 1
    I'd added an update listener, and looked through the animated value - on API 21 it differentiates, but on API 23 it is stable, always the same "-100" - to be exact. – Yurii Tsap Sep 21 '16 at 09:55
  • And with path.lineTo(200,200) - it is working perfectly. Need to dive into the circle stuff, or even something with textview has changed. – Yurii Tsap Sep 21 '16 at 10:00
  • 1
    The same behavior for ImageView, so it's not just about TextView – Bö macht Blau Sep 21 '16 at 10:12
  • I didn't use TextView for any particular reason; actually, it was just a view that came in the Android Studio default activity, and I just used it to test. I assume that the specific View class doesn't matter at all. – androidguy Sep 21 '16 at 16:27
  • 1
    Also happens with partial arcs, which is what I actually care about; just used a circle to test. – androidguy Sep 21 '16 at 16:36
  • yes, it seems to be broken, you need to use `PathMeasure` i'm afraid – pskink Sep 22 '16 at 05:55
  • One possible workaround using `PathMeasure` and a `Handler` can be found [here on SO](http://stackoverflow.com/a/33625969/5015207) I'll add another version based on ValueAnimator and a custom AnimatorUpdateListener – Bö macht Blau Sep 22 '16 at 07:00

1 Answers1

4

As a workaround, you can use a ValueAnimator with a custom AnimatorUpdateListener.

First you need a PathMeasure for your Path. This will give you access to data like path length and coordinates on the path for a specific distance (0 <= distance <= path length).

Path p = new Path();
p.addCircle(-200, 0, 100, Path.Direction.CW);
PathMeasure pathMeasure = new PathMeasure(p, true);

Then you set the ValueAnimator to animate from 0 to the path length.

final ValueAnimator a = ValueAnimator.ofFloat(0,pathMeasure.getLength());
a.setDuration(1000);

Next, you add an AnimatorUpdateListener to actually manipulate the View. I'll show first how to add it:

MyAnimatorUpdateListener myListener = new MyAnimatorUpdateListener(hello, pathMeasure);
a.addUpdateListener(myListener);

Continue with the OnClickListener:

hello.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        a.start();
    }
});

Please note that I changed the code and declared/ initialized the ValueAnimator outside of the OnClickListener to get a better performance: this way, it will not be newly created each time I click the TextView and so the garbage collector will be less busy :).

Finally, the AnimatorUpdateListener code:

class MyAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener
{
    private View view;
    private PathMeasure pathMeasure;
    private float[] coordinates = new float[2];

    public MyAnimatorUpdateListener(View view, PathMeasure pathMeasure)
    {
        this.view = view;
        this.pathMeasure = pathMeasure;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float distance = (float) animation.getAnimatedValue();
        pathMeasure.getPosTan(distance, coordinates, null);
        view.setTranslationX(coordinates[0]);
        view.setTranslationY(coordinates[1]);
    }
}
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
  • Looks promising. What I hacked was just to animate x and y separately, but one with an acceleration interpolator and one with a deceleration, to produce something kind of circular. Definitely doesn't look as good. I'll loop back and implement this when I can. – androidguy Sep 23 '16 at 00:40