SurfaceView的onMeasure()方法没有通过调用setMeasuredDimension()设置测量尺寸。

6

我有一个SurfaceView。我从另一个线程手动绘制它。

它运行得很好。但是平均运行10-20分钟后,我会遇到这个异常:

01-14 08:51:25.000    3740-3740/com.myPackage E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.IllegalStateException: onMeasure() did not set the measured dimension by calling setMeasuredDimension()
            at android.view.View.measure(View.java:15200)
            at android.widget.RelativeLayout.measureChild(RelativeLayout.java:602)
            at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:415)
            at android.view.View.measure(View.java:15195)
            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4823)
            at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
            at android.view.View.measure(View.java:15195)
            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4823)
            at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1390)
            at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
            at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
            at android.view.View.measure(View.java:15195)
            at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4823)
            at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2340)
            at android.view.View.measure(View.java:15195)
            at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1850)
            at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1102)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1275)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4216)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
            at android.view.Choreographer.doCallbacks(Choreographer.java:555)
            at android.view.Choreographer.doFrame(Choreographer.java:525)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
            at android.os.Handler.handleCallback(Handler.java:615)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4813)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:559)
            at dalvik.system.NativeStart.main(Native Method)

Android文档说明:
测量视图及其内容以确定测量宽度和测量高度。此方法由measure(int, int)调用,应被子类重写以提供准确和高效的内容测量。

我认为Google的原始实现是这样的:

@Override
    protected void More ...onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
        int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

我不知道在这里还需要实现什么。

我认为SurfaceView的大小永远不会改变。

注意:
我的SurfaceView包含在一个RelativeLayout中。
异常是由一个子视图引起的,但它是一个视图(在异常中的第三行)

如果您知道如何解决这个烦人的错误,请帮助我。

编辑:
在Activity的onCreate中,我通过代码设置了我的视图(继承自SurfaceView)的大小。

回答评论:
这是我为所需大小设置的一些公共字段:

Parameters.initialize(getResources().getDisplayMetrics().widthPixels,
                getResources().getDisplayMetrics().heightPixels);

参数.initialize:
public static void initialize(int screenWidth, int screenHeight){
    FIELD_WIDTH = screenWidth;
    FIELD_HEIGHT = (int)(0.92f * screenHeight);
}

MyView是用xml编写的。
然后我在上面设置了一些东西:

public class MyViewManager {
    protected MyView mMyView;

    public FieldPainterComp(MainActivity activity) {
        super(activity);
        initialize();
    }

    private void initialize(){
        mMyView= (MyView) activity.findViewById(R.id.vMyView);
        mMyView.getLayoutParams().height = Parameters.FIELD_HEIGHT;
        mMyView.getLayoutParams().width = Parameters.FIELD_WIDTH;
    }
}

这个整个过程只在开始时发生一次。
android:screenOrientation="landscape"

屏幕永远不会变暗。游戏正在运行,但出现了异常。
我应该在哪里调用测量函数?
我不明白为什么长时间后才会出现这个问题。

可能与此相关的其他异常:

01-19 11:53:39.812  29666-31881/com.mypackage V/GAV4﹕ Thread[disconnect check,5,main]: Disconnecting due to inactivity
01-19 11:53:39.812  29666-31881/com.mypackage V/GAV4﹕ Thread[disconnect check,5,main]: Disconnected from service
01-19 11:57:17.732  29666-29666/com.mypackage D/AndroidRuntime﹕ Shutting down VM
01-19 11:57:17.732  29666-29666/com.mypackage W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x420face0)
01-19 11:57:17.812  29666-29666/com.mypackage V/GAV4﹕ Thread[main,5,main]: Tracking Exception: IllegalStateException (@View:measure:16533) {main}
01-19 11:57:17.832  29666-29666/com.mypackage V/GAV4﹕ Thread[main,5,main]: Dispatch call queued. Dispatch will run once initialization is complete.
01-19 12:19:27.982  29666-29670/com.mypackage I/dalvikvm﹕ threadid=3: reacting to signal 3
01-19 12:19:28.262  29666-29670/com.mypackage I/dalvikvm﹕ Wrote stack traces to '/data/anr/traces.txt'
01-19 12:19:32.452  29666-29666/com.mypackage A/libc﹕ Fatal signal 6 (SIGABRT) at 0x0000025a (code=0), thread 29666 (ames.adaptation)

GAV是指Google Analytics相关内容。

我提出了另一个关于ANR的问题:使用锁定产生的3个线程ANR


这可能是由于自定义视图引起的错误吗? - Denis Loh
是的,我从SurfaceView继承。我99%确定问题出在SurfaceView上,因为bug在我添加SurfaceView后开始出现(另外我在描述中添加了一些注释)。 - Tomi
不行,因为我会做与原始实现相同的事情(就像我在上面的描述中所写的那样)。 - Tomi
你如何改变视图的大小?如果你在不调用onMeasure()的情况下改变了视图边界,框架就不知道如何绘制它们。也许是由于屏幕方向改变时的配置更改导致的,需要重新绘制视图。配置更改还会调用你的活动的onCreate方法。 - Denis Loh
我更新了问题。我试图更好地描述你的问题。 - Tomi
显示剩余2条评论
4个回答

3
setMeasuredDimension()

必须在您的onMeasure()方法内调用。我曾经遇到类似的问题,在逻辑未达到setMeasuredDimension()时。我通过强制无论if/else语句如何都调用setMeasuredDimension()来解决了这个问题。


0

你使用了Fragment,但是没有在第一行写这句话

super.onCreate(savedInstanceState);

-1

私有双精度变量 aspectRatio = 1.0;

私有布尔变量 isCalledRecreate = true;

onMeasure 方法中:-

double localRatio = aspectRatio;

    if (localRatio == 0.0) {
        super.onMeasure(widthSpec, heightSpec);

    } else {
        int lockedWidth = MeasureSpec.getSize(widthSpec);
        int lockedHeight = MeasureSpec.getSize(heightSpec);

        if (lockedWidth == 0 && lockedHeight == 0) {

            widthSpec = MeasureSpec.makeMeasureSpec((int) (basicWidth), MeasureSpec.AT_MOST);
            heightSpec = MeasureSpec.makeMeasureSpec((int) (basicHeight), MeasureSpec.AT_MOST);

            lockedWidth = MeasureSpec.getSize(widthSpec);
            lockedHeight = MeasureSpec.getSize(heightSpec);

            Log.i(TAG, "onMeasure: onMeasure");

            callForRecreate();
        }

        // Get the padding of the border background.
        int hPadding = getPaddingLeft() + getPaddingRight();
        int vPadding = getPaddingTop() + getPaddingBottom();

        // Resize the preview frame with correct aspect ratio.
        lockedWidth -= hPadding;
        lockedHeight -= vPadding;

        if (lockedHeight > 0 && (lockedWidth > lockedHeight * localRatio)) {
            lockedWidth = (int) (lockedHeight * localRatio + .5);
        } else {
            lockedHeight = (int) (lockedWidth / localRatio + .5);
        }

        // Add the padding of the border.
        lockedWidth += hPadding;
        lockedHeight += vPadding;

        // Ask children to follow the new preview dimension.
        super.onMeasure(MeasureSpec.makeMeasureSpec(lockedWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(lockedHeight, MeasureSpec.EXACTLY));

注意:在调用callForRecreate()时,您需要创建或重新启动活动。


-1

请确保调用 super.onMeasure(widthMeasureSpec,heightMeasureSpec); //例如 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec,heightMeasureSpec); //然后是你的代码 }


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