Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Thread to Update View Inside Window

I have a Service that can draw a Canvas on top of all applications using a SYSTEM_ALERT_WINDOW which contains a custom View.

I am attempting to animate the Canvas object using a Thread which calls Canvas.draw(...) and postInvalidate() - I was hoping this would "move" the shape across the screen. It does not work.

I tried putting my custom View inside a ViewGroup container and added this to the WindowManager object - based on the following posts:

Animate system alert type view

WindowManager with Animation (is it possible?)

The Canvas object position does not change - what am I doing wrong?

Here is my code...

CursorService.java

    public class CursorService extends Service {

    private WindowManager windowManager;
    private ViewGroup cursorContainer;
    private Cursor cursor;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    public void onCreate() {
        super.onCreate();

       go();
    }

    public void go(){

        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                PixelFormat.TRANSLUCENT
        );

        cursor = new Cursor(this);
        cursorContainer = new LinearLayout(this);
        cursorContainer.addView(cursor);
        windowManager.addView(cursorContainer, params);


        new Thread(new Runnable() {
            @Override
            public void run() {
                    cursor.x+=1;
                    cursor.y+=1;
                    cursor.radius=100;
               }
        }).start();
    }


    public void onDestroy() {
        super.onDestroy();
        if (cursorContainer!=null) windowManager.removeView(cursorContainer);
    }
}

Cursor.java

public class Cursor extends View {

    public float x;
    public float y;
    public float radius;
    public Paint paint;

    public Cursor(Context context) {
        super(context);

        x=0;
        y=0;
        radius=0;
        paint=new Paint();
        paint.setColor(Color.RED);

        new Thread(new Runnable() {
            public void run() {
                while (true){
                    try{
                        Thread.sleep(100);
                        postInvalidate();
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    @Override
    protected void onDraw(Canvas canvas){
        canvas.drawCircle(x, y, radius, paint);
    }
}

1 Answers

First of all you need smth like while(true){} cycle in your runnable because in your code postInvalidate() method would be called just once.

But would be much better to call invalidate() method in onDraw and calculate your circle position by using the current time.

public class Cursor extends View {    
    public Cursor(Context context) {
        super(context);
        startTime = System.currentTimeMills();
    }

    @Override
    protected void onDraw(Canvas canvas){
        long delta = System.currentTimeMills() - startTime;
        // ... calculate x and y using delta
        canvas.drawCircle(x, y, radius, paint);
        invalidate();
    }
}
like image 96
Stepango Avatar answered Jul 16 '26 08:07

Stepango