1

I have an ImageView which is a child of a LinearLayout. If I animate the ImageView anywhere inside the LinearLayout, the onTouchListener() is responsive after the animation, but becomes unresponsive if the animation moves the view outside of the direct LinearLayout parent.

In other words, whether ImageView onTouchListener() is fired or not, depends entirely on whether the x and y offset of my animation are within bounds of the LinearLayout parent.

ObjectAnimator translateX =
        ObjectAnimator.ofFloat(view, "translationX",
                offSet_X);

ObjectAnimator translateY =
        ObjectAnimator.ofFloat(view, "translationY",
                offSet_Y);

translateX.setDuration(translationDurationMill);
translateX.start();
translateY.setDuration(translationDurationMill);
translateY.start();

Is this behavior expected, or could it be a bug in in my code?

edit

So for example, I've reduced the problem into a simple app, and verified the behavior is not a bug, but just the way Android works, but I'm not sure how to work around the behavior.

So here is a simple layout for the main activity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:clipChildren="false"
    android:clipToPadding="false"
    >

    <LinearLayout
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/colorPrimary"
        android:text="Hello World!"
        android:gravity="center"
        >

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            android:text="hello world" />

    </LinearLayout>
</LinearLayout>

Here is the main activity code

public class MainActivity extends AppCompatActivity
        implements View.OnTouchListener{

    int mTranslationDurationMill = 500;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
        tv.setOnTouchListener(this);

        animateTextView(tv,10,10);
    }

    private void animateTextView(View view, int offSet_X,int offSet_Y)
    {
        ObjectAnimator translateX =
                ObjectAnimator.ofFloat(view, "translationX",
                        offSet_X);

        ObjectAnimator translateY =
                ObjectAnimator.ofFloat(view, "translationY",
                        offSet_Y);

        translateX.setDuration(mTranslationDurationMill);
        translateX.start();
        translateY.setDuration(mTranslationDurationMill);
        translateY.start();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("TAG","onTouch()");
        return false;
    }
}

Above I have animateTextView(tv,10,10); to animate the text view, and after animation I can confirm the log indicates the touch event fired, and here is picture

enter image description here

but If do animateTextView(tv,400,400); then the view moves outside the direct parent LinearLayout and touch event does not register. Here is picture.

enter image description here

the_prole
  • 8,275
  • 16
  • 78
  • 163

2 Answers2

0

it works for me. here is the whole code

in the layout, I have an ImageView and a TextView (text change from not clicked to clicked once a touch event is triggered)

<FrameLayout
    android:id="@+id/child_view_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/child_view"
        android:layout_width="match_parent"
        android:layout_height="300px"
        app:srcCompat="@color/colorAccent"
        tools:layout_editor_absoluteX="216dp"
        tools:layout_editor_absoluteY="143dp" />

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:text="not clicked"
        android:textSize="30sp"
        tools:layout_editor_absoluteX="119dp"
        tools:layout_editor_absoluteY="230dp" />
</FrameLayout>

in the code: childView is the ImageView textView is the TextView the listener will listen for a touch event and if detected it will change textView text to "clicked"

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView = findViewById(R.id.textview);

    childView = findViewById(R.id.child_view);
    childView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            textView.setText("clicked");
            return false;
        }
    });

}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    if(hasFocus){
        View containerView = findViewById(R.id.child_view_container);
        childView.setTranslationX(containerView.getWidth());
        childView.animate()
                .translationXBy(-containerView.getWidth())
                .setDuration(10000)
                .setInterpolator(new AccelerateDecelerateInterpolator())
                .start();
    }
}

I hope this is helpful.

  • Doesnt look like ur moving the view outside of the parent layout which is the frame layout which looks like the root view for main activity in ur example. – the_prole Dec 03 '18 at 00:05
0

I might have found a work around. It appears that the touch event will not be propagated to the animated view if the touch even occurs on the animated view, but outside of the bounds of the direct parent of the animated view, so I override the activity dispatch event handler, and pass the motion event directly to the animated view.

public class MainActivity extends AppCompatActivity
        implements View.OnTouchListener {

    int mTranslationDurationMill = 500;
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = findViewById(R.id.tv);
        tv.setOnTouchListener(this);

        animateTextView(tv, 400, 400);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("TAG", "onTouch()");
        Log.d("TAG", "event.getAction() "+event.getAction());
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent me) {

        Log.d("TAG", "dispatchTouchEvent");

        // Send the motion event directly to the view.

        this.onTouch(tv, me);

        // Don't propagate the motion event to the children.
        return true;
    }

    private void animateTextView(View view, int offSet_X, int offSet_Y) {
        ObjectAnimator translateX =
                ObjectAnimator.ofFloat(view, "translationX",
                        offSet_X);

        ObjectAnimator translateY =
                ObjectAnimator.ofFloat(view, "translationY",
                        offSet_Y);

        translateX.setDuration(mTranslationDurationMill);
        translateX.start();
        translateY.setDuration(mTranslationDurationMill);
        translateY.start();
    }
}

So here are the logs for when I touch the animated view when the view it outside the bound of it's parent:

D/TAG: dispatchTouchEvent
D/TAG: onTouch()
D/TAG: event.getAction() 0
D/TAG: dispatchTouchEvent
D/TAG: onTouch()
D/TAG: event.getAction() 2
D/TAG: dispatchTouchEvent
D/TAG: onTouch()
D/TAG: event.getAction() 2
D/TAG: dispatchTouchEvent
D/TAG: onTouch()
D/TAG: event.getAction() 2
D/TAG: dispatchTouchEvent
D/TAG: onTouch()
D/TAG: event.getAction() 1 
the_prole
  • 8,275
  • 16
  • 78
  • 163