3

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);
    }
}
Community
  • 1
  • 1
  • The stack trace suggests you are calling `invalidate`, but the code you posted is calling `postInvalidate`. You must call `postInvalidate` to invalidate the view from a different thread. – pathfinderelite May 16 '15 at 14:23
  • I edited the post - turns out there aren't any errors after all! –  May 16 '15 at 15:29

2 Answers2

1

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();
    }
}
Stepango
  • 4,721
  • 3
  • 31
  • 44
  • I used a `while(true){}` cycle in my runnable and it works! How would I calculate x and y using delta time? –  May 16 '15 at 16:08
  • @calcha3991 for example, you want your circle to move by 100dp by X axis in one second. So there is circle speed 0.1dp in one millisecond. So position x in any time moment will be x = startPosition + delta * speed; if you want circle to stop in some point you could check if (x > maxPosition) x = maxPosition; and so on. – Stepango May 17 '15 at 08:08
-1

Since you are changing x, y and radius from a separate thread, you should be make them volatile. Otherwise, the changes you make to those variables (from the background thread) will not be seen by your view (on the UI thread).

public volatile float x;
public volatile float y;
public volatile float radius;
pathfinderelite
  • 3,047
  • 1
  • 27
  • 30
  • Sorry, this doesn't make any difference I'm afraid - I don't appear to be getting a `NullPointerException` –  May 16 '15 at 14:10
  • there is no race condition between threads so volatile really do nothing here – Stepango May 17 '15 at 08:09