web-dev-qa-db-ja.com

Android)でキャンバスをスムーズにスクロールする

私はAndroidを初めて使用します。

ビューのOnDraw(Canvasキャンバス)メソッド内のCanvasにビットマップ、線、および形状を描画しています。ユーザーによるドラッグに応じてスムーズなスクロールを実装する方法についてのヘルプを探しています。検索しましたが、これに役立つチュートリアルは見つかりませんでした。

Canvasのリファレンスでは、Canvasがビットマップ(たとえばbmpBufferと呼ばれる)から構築されている場合、Canvasに描画されるものはすべてbmpBufferにも描画されると書かれているようです。 bmpBufferを使用してスクロールを実装することは可能でしょうか...おそらく一度に数ピクセルずつシフトしてCanvasにコピーして戻しますか?しかし、Canvas.drawBitmapを使用してbmpBufferを数ピクセルシフトしたCanvasに描画し直した場合、bmpBufferは破損しませんか?したがって、おそらく、bmpBufferをbmpBuffer2にコピーしてから、bmpBuffer2をCanvasに描画する必要があります。

より簡単なアプローチは、線や形状などをバッファビットマップに直接描画してから、そのバッファを(シフトを使用して)Canvasに描画することですが、さまざまなメソッドを見ることができる限り、drawLine()、drawShape()などは、ビットマップへの描画には使用できません...キャンバスへの描画のみです。

キャンバスを2枚いただけますか?そのうちの1つはバッファビットマップから作成され、線や形状などをプロットするために使用され、バッファビットマップはビューに表示するために他のキャンバスに描画されますか?

アドバイスを歓迎します!

ここ(および他のWebサイト)での同様の質問への回答は、「ブリッティング」を参照しています。概念は理解していますが、Androidドキュメントで「blit」または「bitblt」について何も見つかりません。Canvas.drawBitmapおよびBitmap.CopyAndroidと同等のものですか?

24
prepbgg

私もこの問題を抱えていました、

私はこのように絵を描きました:

Canvas BigCanvas = new Canvas();
Bitmap BigBitmap = new Bitmap(width,height);

int ScrollPosX , ScrollPosY  // (calculate these with the onScrollEvent handler)

void onCreate()
{
   BigCanvas.SetBitmap(BigBitmap);
}

onDraw(Canvas TargetCanvas)
{
   // do drawing stuff
   // ie.  BigCanvas.Draw.... line/bitmap/anything

   //draw to the screen with the scrolloffset

   //drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)
   TargetCanvas.DrawBitmap(BigBitmap(new Rect(ScrollPosX,ScrollPosY,ScrollPosX + BigBitmap.getWidth(),ScrollPosY + BigBitmap.getHeight(),new Rect(0,0,ScreenWidth,ScreenHeight),null);
}

スムーズなスクロールを行うには、スクロール後に数ポイント(つまり、最初のスクロールポイントと10番目)を取得する何らかのメソッドを作成し、それらを減算して、ループごとにその数だけスクロールする必要があります。これにより、ループが徐々に遅くなります(ScrollAmount -ターン-摩擦)。

これがもう少し洞察を与えることを願っています。

1
Mervin

答えを見つけたようです。描画コードの大部分(以前はonDraw()にあった)を新しいdoDrawing()メソッドに配置しました。この方法は、画面よりも大きい(完全な図面を保持するのに十分な大きさの)新しいビットマップを作成することから始まります。次に、詳細な描画を行うための2番目のキャンバスを作成します。

    BufferBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
    Canvas BufferCanvas = new Canvas(BufferBitmap);

DoDrawing()メソッドの残りの部分は、BufferCanvasへの詳細な描画で処理されます。

OnDraw()メソッド全体が次のようになります。

    @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(BufferBitmap, (float) -posX, (float) -posY, null);
}

位置変数posXおよびposYは、アプリケーションのonCreate()メソッドで0に初期化されます。アプリケーションはOnGestureListenerを実装し、OnScroll通知で返されたdistanceX引数とdistanceY引数を使用して、posXとposYをインクリメントします。

スムーズなスクロールを実装するために必要なのはこれだけのようです。それとも私は何かを見落としていますか!?

12
prepbgg

Viktorへの返信の続き.。

実際、状況はもっと複雑です。 doDrawingプロセスは非常に遅いので(私の遅い古いHTC Hero電話では2〜3秒かかります)、トーストメッセージをポップアップして、それが起こっていることをユーザーに通知し、理由を示すことが望ましいと思いました。これを行うための明白な方法は、2行だけを含む新しいメソッドを作成することでした。

public void redrawBuffer(String strReason) {
    Toaster.Toast(strReason, "Short");`
    doDrawing();
}

そして、doDrawing()の代わりに私のプログラムの他の場所からこのメソッドを呼び出すこと。

しかし、トースターが表示されないか、短時間点滅して読み取れなかったことがわかりました。私の回避策は、タイムチェックハンドラーを使用して、Toastを表示してからdoDrawing()を呼び出すまでの間にプログラムを200ミリ秒スリープさせることでした。これは再描画の開始をわずかに遅らせますが、ユーザーは何が起こっているのかを知っているので、これはプログラムの使いやすさの観点から支払う価値のある価格だと思います。 reDrawBuffer()は次のようになります。

public void redrawBuffer(String strReason) {
    Toaster.Toast(strReason, "Short");
    mTimeCheckHandler.sleep(200);    
}`

ハンドラーコード(私のViewクラス内にネストされています)は次のとおりです。

private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();

class timeCheckHandler extends Handler {
@Override
    public void handleMessage(Message msg) {
        doDrawing();
    }
    public void sleep(long delayMillis) {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
}`
1
prepbgg

アクティビティを再開する必要はありません! (prepbggのJan 2710による彼のJan17 10'answer 'への返信)ビットマップをリサイクルしてアクティビティを再ロードするオーバーヘッドを発生させるのではなく、以下に示す' Android:configChanges '属性を配置することでアプリケーションのロードを回避できます。アプリのAndroidManifest.xmlファイルの「activity」要素にあります。これは、アプリが向きの変更を処理し、アプリを再起動する必要がないことをシステムに通知します。

<activity Android:name=".ANote"
    Android:label="@string/app_name"
    Android:configChanges="orientation|screenLayout">
    <intent-filter>
       <action Android:name="Android.intent.action.MAIN" />
       <category Android:name="Android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

このメソッドは、向きが変更されたときに通知を受け取るために使用できます。

public void onConfigurationChanged(Configuration  newConfig) {
    super.onConfigurationChanged(newConfig);
    prt("onConfigurationChanged: "+newConfig);

    if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
      prt("  PORTRAIT");
    } else {
      prt("  LANDSCAPE");
    }
} // end of onConfigurationChanged
1
Ribo

prepbgg:canvas.drawBitmapはビットマップに描画せず、ビットマップをキャンバスに描画するため、コードが機能するとは思いません。

私が間違っている場合は私を訂正してください!

0
Gaurav Vaish