AsyncTask:无效视图不生效

AsyncTask: invalidating view does not take effect

更新

我的小陈列柜存储在 Bitbucket 上

https://bitbucket.org/solvapps/animationtest

我有一个 Activity,其中有一个视图。 Contentview 设置为此视图。

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);
    }

}

MovieTask 是一个 Asynctask,它会定期刷新视图。 但是 invalidate() 不会刷新视图。

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();
            }
        });

    }

}

有人可以帮忙吗?

有两种可能的情况,第一种如documents所述:

void invalidate () Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future.

所以尝试 运行 你在 onResume 中的代码,有可能 View 还不可见。

其次,View#invalidate 告诉系统在主 UI 线程空闲时立即重绘视图。也就是说,调用 invalidate 会安排您的视图在所有其他直接工作完成后重新绘制。

如果您希望定期更新视图,请在单独的线程中使用 Handler#postDelay 或 运行 并使用 View#postInvalidate 更新 View 并触发调用 onDraw.

看看你是如何启动 AsyncTask:


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

您在某些 class 中手动调用一个名为 MovieTask 的方法,因此您 运行 在同一个线程上调用了一个代码。显然,这不是您的意图,您打算 运行 后台线程上的计算代码。

启动 AsyncTask 的正确方法是使用 execute(Params...):


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

现在你会得到想要的效果。


P.S.

请不要使用该代码:您不需要启动后台线程来执行此类操作。作为替代方案,请考虑 Animators API.

MyBall 中声明 setBall(int pos) 方法 class:


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

然后将 startMovie() 更改为以下内容:


    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();
    }

您将获得相同的动画,而无需讨厌的代码。