0

私のアプリは API レベル 10 (Gingerbread) 以上をサポートしています。アクティビティの 1 つは、新しいバージョンで完全に機能するチャートを描画しますが、レベル 10 エミュレーターを実行すると、間違ったキャンバス ID で View.onDraw への余分な呼び出しが発生し、画面が空白になります。(これは単なるエミュレーターではありません。この問題は、Gingerbread フォンを実行している誰かによって報告されました。)

通常の操作では、onDraw が 2 回呼び出されます。1 回目はキャンバス ID を取得するフレームワークから、2 回目は同じキャンバス ID を渡す invalidate() への呼び出しからです。これら 2 つの呼び出しはレベル 10 エミュレーターで発生しますが、別のキャンバス ID (ビューに属していない) を持つ 3 番目の呼び出しがあり、これにより空白になります。

このアクティビティは、アクション バーを提供するために 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();
        }

Gingerbread エミュレーター (または電話) で 3 番目の呼び出しが発生する理由を誰か説明できますか? その効果は、画面がブランク (完全に白) になることです。

この関数に到達するまでには手遅れであり、呼び出しを無視することはできません。呼び出しスタックが巻き戻されると、画面が空白になります。

回避策があります。ユーザーがメニューから表示オプションを選択し、すぐにチャートに戻ると、チャートは再描画され、それ以降は正常に動作します。

4

1 に答える 1

4

問題は、パラメータとして受け取ったCanvasへの参照を保持していることです。現在のフレームが完了した後、このCanvasインスタンスが有効になるという保証はありません。たとえば、フレームごとに異なるCanvasインスタンスを受け取ることができます。ビューがビットマップにレンダリングされると、通常は別のキャンバスを受け取ります(たとえば、View.setDrawingCacheEnabled())。

mCanvasに参照を保持する代わりに、受け取ったキャンバスをplotLinesAndShapes()に渡すだけです。

于 2012-11-21T17:32:00.210 に答える