0

我的应用支持 API 级别 10(姜饼)以上。其中一个活动绘制了一个可以在更高版本上完美运行的图表,但是在运行 10 级模拟器时,我会使用错误的画布 ID 额外调用 View.onDraw,这会导致屏幕变为空白。(不仅仅是模拟器 - 有人在他们的 Gingerbread 手机上运行报告了这个问题。)

正常操作是调用两次 onDraw - 第一次来自框架,我从中获取画布 ID,第二次来自我调用 invalidate(),它传递相同的画布 ID。这两个调用发生在 10 级仿真器中,但随后有第三个调用具有不同的画布 ID - 即不属于视图,这会将其空白。

该活动是从 SherlockActivity 派生的,以提供一个操作栏,我相信这就是导致问题的原因。

我的活动课程中的相关代码:

public class Chart extends SherlockActivity implements OnGestureListener, OnDoubleTapListener, OnScaleGestureListener
{
    public static boolean   mDataSet = false;

    private ChartView              mView;
    private Menu                   mMenu;
    private GestureDetector        mDetector;
    private ScaleGestureDetector   mScaleDetector;
    private ActionBar              mActionBar;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        mDataSet = false;

        mActionBar = getSupportActionBar();
        mActionBar.setHomeButtonEnabled(true);
        mActionBar.setDisplayHomeAsUpEnabled(true);
        mActionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

        mView = new ChartView(this, mCentreLat, mCentreLong, mRadius);
        setContentView(mView);
        setTitle("");

        Context context = getApplicationContext();

        mDetector   = new GestureDetector(context, this);
        mScaleDetector = new ScaleGestureDetector(context, this);
    }

    @Override
    public void onConfigurationChanged(Configuration config)
    {
        super.onConfigurationChanged(config);
        mDataSet = false;
    }

    // Menu handling
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        mMenu = menu;
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.chart_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        boolean handled = true;

        switch (item.getItemId())
        {
        case android.R.id.home:
        finish();
        break;

        case R.id.viewOptions:
            mDataSet = false;
            Intent i = new Intent(getBaseContext(), ChartSettings.class);
            startActivity(i);
            break;

        // Other menu options here...

        default:
            handled = false;
        }
        return handled;
    }

我的 View 类的相关代码:

public class ChartView extends View
{

    public ChartView(Context context, float centreLat, float centreLong, float radius)
    {
        super(context);
        mContext    = context;
        mCentreLat  = centreLat;
        mCentreLong = centreLong;
        mRadius = radius;
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        // First time
        // (We pick up the canvas id mCanvas from the parameter.)
        // (Nothing much else relevant here, except this is where stuff
        // gets initialized, then in setDataInfo(), mDataSet is set true
        // and invalidate() is called at the end.)
        if (!Chart.mDataSet)
        {
            setBackgroundColor(Color.WHITE);
            mCanvas = canvas;
            initPaint();
            setDataInfo();      // invalidate() called at end
        }
        else
        {
            // Second call to onDraw() with same canvas id comes here,
            // then an unwanted third call with a different canvas id, only
            // with the Level 10 (Gingerbread) emulator.

            // This is where the various lines and shapes are plotted
            // on the canvas (mCanvas)
            plotLinesAndShapes();
        }

请任何人都可以解释为什么姜饼模拟器(或电话)会发生第三次通话?效果是屏幕空白(全白)。

当它到达这个函数的时候已经太晚了,调用不能被忽略——当调用堆栈展开时屏幕变成空白。

有一个解决方法 - 如果用户从菜单中选择视图选项,然后立即返回图表,它会重新绘制并从那时起正常运行。

4

1 回答 1

4

问题是您保留对作为参数接收的 Canvas 的引用。无法保证此 Canvas 实例在当前帧完成后仍然有效。例如,您可以在每一帧上接收不同的 Canvas 实例。当视图被渲染为位图时,您通常会收到不同的画布(例如,View.setDrawingCacheEnabled()。)

无需在 mCanvas 中保留引用,只需将收到的画布传递给 plotLinesAndShapes()。

于 2012-11-21T17:32:00.210 回答