Search code examples
javaandroidandroid-asynctaskandroid-animationandroid-view

AsyncTask: invalidating view does not take effect


Update

My small showcase is stored on Bitbucket

https://bitbucket.org/solvapps/animationtest

I have an Activity with a view in it. Contentview is set to this view.

public class MainActivity extends AppCompatActivity {

    private MyView myView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myView = new MyView(this);
        setContentView(myView);
        startMovie();
    }

    public void startMovie(){
        MovieTask movieTask = new MovieTask(myView, this);
        movieTask.doInBackground(null);
    }

}

A MovieTask is an Asynctask and refreshes the view periodically. But invalidate() doesn't refresh the view.

public class MovieTask extends AsyncTask<String, String, String> {

    MyView drawingView;
    MainActivity mainActivity;

    public MovieTask(MyView view, MainActivity mainActivity){
        this.mainActivity = mainActivity;
        this.drawingView =view;
    }

    @Override
    protected String doInBackground(String... strings) {

        for(int i=20;i<100;i++){
            drawingView.myBall.goTo(i,i);
            publishProgress();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);
        mainActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.v("DEBUG_DRAW","in  onProgressUpdate()");
                drawingView.invalidate();
            }
        });

    }

}

Can someone help ?


Solution

  • See how you are launching the AsyncTask:

    
        public void startMovie() {
            MovieTask movieTask = new MovieTask(myView, this);
            movieTask.doInBackground(null);
        }
    
    

    You are manually calling a method inside some class called MovieTask, thus you are running a code on the same thread. Obviously, that is not your intention, you intended to run the computation code on a background thread.

    Correct way to launch AsyncTask is using execute(Params...):

    
        public void startMovie() {
            MovieTask movieTask = new MovieTask(myView, this);
            movieTask.execute("");
        }
    
    

    Now you will get the desired effect.


    P.S.

    Please, do not use that code: you do not need to launch a background thread in order to do that kind of stuff. As an alternative consider Animators API.

    Declare setBall(int pos) method inside MyBall class:

    
        public class MyView extends View {
            ...
            public void setBall(int pos) {
                myBall.setX(pos);
                myBall.setY(pos);
                invalidate();
            }
        }
    
    

    Then change startMovie() to following:

    
        public void startMovie() {
            // "ball" means, that Animators API will search for `public setBall(int)` method inside MyView.java and call that method
            ObjectAnimator ball = ObjectAnimator.ofInt(myView, "ball", 20, 100);
            ball.setDuration(1000);
            ball.start();
        }
    
    

    You'll get the same animation without a nasty code.