Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 384
mcfly soft Avatar asked Mar 16 '18 08:03

mcfly soft


1 Answers

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.

like image 186
azizbekian Avatar answered Sep 30 '22 06:09

azizbekian