安卓视图 Canvas onDraw 方法未执行

3

我正在开发一个自定义视图,它在画布上绘制一些瓦片。 这些瓦片从几个文件中加载,并在需要时加载。

它们将由AsyncTask加载。如果它们已经加载,它们将只被绘制在画布上。这部分工作正常!

如果这些图片被加载,AsyncTask将触发view.postInvalidate()。问题在于,我的自定义View 没有每次都触发 onDraw(Canvas canvas) 方法,即使我反复调用 view.postInvalidate()。

view.postInvalidate 只有在第一次加载图片时才会触发 onDraw() 方法,然后只有当我在 CustomView 的 onTouchEvent 中调用 this.invalidate() 时才会触发。

是不是有可能 View 决定是否要重新绘制画布?有没有一种方法可以 强制 View 重绘?我认为 invalidate 方法只是告诉 View 它可以考虑重新绘制。

这些 invalidate 方法有限制吗?

希望你们中的任何人都可以更多地了解这个问题。

编辑:

我刚把所有的 postInvalidate() 都改为 invalidate(),因为所有的图片都是由主线程执行的 AsyncTask 加载。但仍然存在一个问题,即执行 invalidate() 并没有触发 onDraw() 方法。我发现 view.invalidate() 是通过覆盖原始方法来触发的。

@Override
public void invalidate() {
    super.invalidate();
    Log.d(TAG, "invalidate executed");
}

我现在不知道该怎么办。我正在使用view.invalidate()和view.postInvalidate()这两个方法,但是无论怎样组合都没有用。

5个回答

2
这里有一些误解。invalidate 和 postInvalidate 方法用于告诉 View 它需要在最早的绘图周期中进行刷新和重绘。区别在于,invalidate 方法应该从 UI 线程内部调用,而 postInvalidate 方法应该从 UI 线程外部调用。
这些方法在这里简要描述: 另一方面,AsyncTask 是一个专门为你面临的问题设计的类。当你需要在后台异步执行一个大任务时,你需要使用 AsyncTask,但是!AsyncTask 的回调方法在 UIThread 中运行!请看这里关于 AsyncTask 方法的解释:
当一个异步任务被执行时,该任务经过 4 个步骤:
onPreExecute():在任务执行后立即在UI线程上调用。通常用于设置任务,例如在用户界面中显示进度条。
doInBackground(Params...):在onPreExecute()完成后立即在后台线程上调用。该步骤用于执行可能需要很长时间的后台计算。异步任务的参数传递给此步骤。计算结果必须由此步骤返回,并将传递回最后一步。此步骤还可以使用publishProgress(Progress...)发布一个或多个进度单元。这些值在UI线程上发布,在onProgressUpdate(Progress...)步骤中显示任何形式的进度。
onProgressUpdate(Progress...):在调用publishProgress(Progress...)后在UI线程上调用。执行的时间是未定义的。此方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。例如,它可用于动画进度条或在文本字段中显示日志。
onPostExecute(Result):在后台计算完成后在UI线程上调用。将后台计算的结果作为参数传递给此步骤。
这意味着在onPostExecute方法中,你应该尝试使用invalidate方法而不是postInvalidate方法,因为它是从UIThread调用的。

好的,谢谢。我还没有考虑过这个问题。现在我对于每个AsyncTask都使用view.invalidate()。 - user1494865

1

你听说过一个名为setWillNotDraw(boolean)的方法吗?

点击这里查看

将它设置为false,你就会看到效果。


0
在我的情况下,目标自定义视图在那个时候具有高度=0的属性。(我的错误) postInvalidate()和invalidate没有触发onDraw()和Log。 当我正确地改变了高度时,所有问题都得到了解决。

0

只是为了将来保存和可能的谷歌搜索者;)

我创建了一个新的类,为我的视图提供数据。这个类扩展了Thread并在后台运行。它检查我的视图必须显示哪些瓷砖。

长话短说:每次需要显示新瓷砖时,我的提供程序类都会收集一些更新请求。如果收集到3个或更多的更新请求,或者自第一个更新请求以来已经超过1秒钟,则视图将获得view.postInvalidate()。

问题是,如果您同时触发几个无效请求,视图会忽略其中一些无效请求。如果您收集这些无效请求并仅使用一个invalidate()触发其中的几个,您可以在短时间内看到最新的信息 - 但您可以看到它;)


0

我已经解决了这个问题,请求view.invalidate()view.postInvalidate()不是为了逐个使每个子视图无效(每个子视图都包含自己的AsyncTask),而是为了使它们共同的父ViewGroup无效。

现在详细说明一下。我有

public class mViewGroup extends RelativeLayout {...}

还有几个视图,每个视图如下:

public class mView extends View {

private mViewGroup mContext = null;

//with a little bit modified standard constructors:
public mView (Context context, mViewGroup ParentViewGroup) {
        super(context);
        mContext = ParentViewGroup;
}

//and also with an AsyncTask:
private class mViewAsyncTask extends AsyncTask<Integer, Void, Void> {
public mAsyncTask() {
   @Override
   protected Void doInBackground(Integer... a) {
       //which does its work and sometimes publishes progress:
       publishProgress();
       return null;
   }

   protected void onProgressUpdate(Void... progress) {
       invalidate();
   }

}

invalidate();的调用有时有效,有时无效(可能是因为从不同的AsyncTask属于不同的mView进行调用,但在非常接近的时间内)。

更改中

invalidate(); //i.e. invalidating individual View

解决问题的方法是:

mContext.invalidate(); //即使整个ViewGroup无效

返回。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接