When creating an overlay from a TextView and moving it using updateViewLayout with changed WindowManager.LayoutParams the overlay unexpectedly moves after changing the size of the TextView (e.g. by changing the text or size).
It first instantly moves back to the old position and then moves to the new position with an animation.
See below for a minimal example overlay with the issue and video.
The expected behaviour would be to stay at the new position without any animated or additional movement.
I tried to step through it with the debugger starting from the setText, but the animation does not seem to be triggered directly, but rather as a result of a layout update being triggered directly.
Additionally I tried disabling the animations by creating a theme with animations set to false for the overlay and calling clearAnimation() on the TextView after changing the text.
Video showing the issue (on Android 14 emulator)
Explanation of the video:
- Intial text in starting position
- Changed position using
wm.updateViewLayout(view,params);, which works instantly as expected - Called
setText("Long Text Message");, which results in unwanted movement back to initial position and then an animation to the new position. (This only happens when the dimensions of the TextView change, so replacing two same size letters does not trigger the animation)
Simple sample activity used to create the video (manually grant the allow overlay permission, if you want to test this):
package com.example.textmovetest;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
TYPE_APPLICATION_OVERLAY,
0,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.TOP;
WindowManager wm = (WindowManager) createDisplayContext(getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY))
.createWindowContext(TYPE_APPLICATION_OVERLAY, null)
.getSystemService(WINDOW_SERVICE);
TextView view = new TextView(this);
view.setTextColor(Color.BLACK);
view.setTextSize(20f);
wm.addView(view, params);
Handler timeoutHandler = new Handler(Looper.getMainLooper());
timeoutHandler.post(new Runnable() {
int step = 0;
@Override
public void run() {
if(step == 0) {
//set initial position and text
params.x = 100;
params.y = 200;
wm.updateViewLayout(view,params);
view.setText("Initial text");
step = 1;
}
else if(step == 1) {
//First change only the position: Text moves instantly as expected
params.x = 500;
params.y = 1000;
wm.updateViewLayout(view,params);
step = 2;
}
else {
/*Next change the text to other text with different length:
This causes the text to jump back to the initial position and move back to the new position with an animation*/
view.setText("Long Text Message");
step = 0;
}
timeoutHandler.postDelayed(this, 2000);
}
});
}
}