ダイアログを非同期処理する
昨日のダイアログをAsynkTaskを使って処理する。
ボタンを押すとダイアログが立ち上がる仕様。
キャンセル機能も搭載した。
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
import 色々; public class MainActivity extends Activity { TextView tv; Button btn; final static int sumMin = 1; final static int sumMax = 10000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findView(); addEvent(); } private void findView() { tv = (TextView) findViewById(R.id.textView1); btn = (Button) findViewById(R.id.button1); } private void addEvent() { btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { MyTask task = new MyTask(); task.execute(sumMin,sumMax); } }); } class MyTask extends AsyncTask<Integer, Integer, Integer> { ProgressDialog pd = new ProgressDialog(MainActivity.this); int sum = 0; @Override protected void onPreExecute() { super.onPreExecute(); pd.setTitle("計算中"); pd.setMessage("計算終了までお待ちください。"); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setCancelable(false); pd.setMax(sumMax); pd.setButton(DialogInterface.BUTTON_NEGATIVE, "キャンセル", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { pd.cancel(); } }); pd.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { MyTask.this.cancel(true); } }); pd.show(); } @Override protected Integer doInBackground(Integer... params) { for (int i = params[0]; i <= params[1]; i++) { if (isCancelled()) { break; } try { sum += i; publishProgress(i,sum); Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } return sum; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); pd.setProgress(values[0]); pd.setMessage(String.format("現在の合計:%d", sum)); } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); tv.setText(String.format("%dから%dの合計は%dです。", sumMin, sumMax, result)); pd.dismiss(); } @Override protected void onCancelled(Integer result) { super.onCancelled(result); } } } |
setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener)
ダイアログにボタンを設置する。
第一引数:どのボタンかの設定
BUTTON_POSITIVE、BUTTON_NEGATIVE、BUTTON_NEUTRALのいずれか
第二引数:ボタンに表示する文字列
第三引数:ボタンを押した時のリスナー登録。DialogInterface.OnClickListenerを使用すること(公式リファレンス)
cancel()
ダイアログをキャンセルする。
本質的にはdismiss()と同じだがDialogInterface.OnCancelListenerを呼び出す。
setOnCancelListener (DialogInterface.OnCancelListener listener)
ダイアログをキャンセルした時に呼び出されるリスナー。
開発者がダイアログの終了(dismiss)を知りたいときはsetOnDismissListener (DialogInterface.OnDismissListener listener)を利用する。
cancel(boolean mayInterruptIfRunning)
AsynkTaskのメソッド。
指定インスタンスのタスクをキャンセルする。
doInBackGroundメソッドのisCancelled()をチェックする。
今回は「もしisCancelled()がtrueならループを終了する」処理をしている。
イメージ的にはcancelメソッドでisCancelledがtrueになるといった感じ?
isCancelled()
doInBackgroundが定期的にチェックしているメソッド。
cancel(boolean)が実行されるとtrueになる。
公式リファレンスによると「タスクが完了する前にキャンセルされたらtrueになる」(訳者:私)ということらしい。
独自Viewの作成
MainActivityとは別に新規クラスを作成する。
Viewを継承する。
Viewのコンストラクタを実装する。
今回は、XML内にレイアウトパラメータを定義したViewを利用する為、
引数が2つのコンストラクタ
View (Context context, AttributeSet attrs)
を実装する。
第二引数にXML内に定義した情報が渡ってくるため。
作ったViewはactivity_main.xmlのカスタム&ライブラリー・ビューに出現するのでレイアウトに配置しておくこと。
xmlに直接書くと
<example.MyView android:id="@+id/myView1" android:layout_width="幅" android:layout_height="高さ" />
といった感じ
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
import 色々; public class MainActivity extends Activity { Button up; Button down; Button left; Button right; MyView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); up = (Button) findViewById(R.id.bt_up); down = (Button) findViewById(R.id.bt_down); left = (Button) findViewById(R.id.bt_left); right = (Button) findViewById(R.id.bt_right); myView = (MyView) findViewById(R.id.myView1); up.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.vx = 0; myView.vy = -1; } }); down.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.vx = 0; myView.vy = 1; } }); left.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.vx = -1; myView.vy = 0; } }); right.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.vx = 1; myView.vy = 0; } }); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import 色々; public class MyView extends View { int x = 100; int y = 100; int vx = 0; int vy = 0; public MyView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 描画処理の記述 Paint paint = new Paint(); paint.setColor(Color.BLUE); canvas.drawText("Hello Android", 100, 100, paint); // 画像の表示 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); canvas.drawBitmap(bitmap, x, y, null); // 画像を動かす x += vx; y += vy; if (x <= 0) { vx = 0; } if (x >= (getWidth()-bitmap.getWidth())) { vx = 0; } if (y <= 0) { vy = 0; } if (y >= (getHeight()-bitmap.getHeight())) { vy = 0; } invalidate(); } } |
描画処理の記述
onDraw(Canvas canvas)
Viewのメソッド。
Viewが描画されるときに実行される。
Viewの描画領域を示すCanvasが引数として渡ってくる。
Paint
グラフィックを描画する情報を保持するためのクラス。
描画用のペン的なオブジェクトを生成できる。
setColor (int color)
ペイントの色を設定する。
引数は色の値を示す整数。
drawText (String text, float x, float y, Paint paint)
ペイントオブジェクトでテキストを描画する。
第一引数:描画する文字列
第二引数:描画した文字列の原点となるX座標
第三引数:描画した文字列の原点となるY座標
第四引数:利用するペイントオブジェクト
画像の表示
Bitmap
イメージを取り扱うクラス。画像を読み込んだり作成したりする。
このクラスにはコンストラクタが存在しない。
decodeResource (Resources res, int id)
BitmapFactoryのメソッド。
画像をBitmapオブジェクトにする。
第一引数:画像データを含むリソースオブジェクト
第二引数:画像データのリソースID
getResources()
Contextクラスのメソッド
リソースインスタンスを返す・・・らしいよ・・・(よく分かってない
drawBitmap (Bitmap bitmap, float left, float top, Paint paint)
画像を描画する
第一引数:描画するビットマップオブジェクト
第二引数:ビットマップの左側の位置
第三引数:ビットマップの上側の位置
第四引数:利用するペイントオブジェクト(nullの場合もある)
画像を動かす
invalidate()
再描画するメソッド。
onDrawメソッド内で使うと繰り返しonDrawが実行される。
こちらのサイトによると
「UIスレッドが次回,アイドル状態になり次第,できるだけ早く再描画するメソッド」らしい。
このメソッド使ってるうちに何か波乱を巻き起こしそうな予感っ!
幅と高さを取得する
文字面で意味が分かりそうなので簡単にまとめ。
オブジェクトの指定がないgetWidthとgetHeightはViewの幅と高さ。
指定があるのは指定オブジェクトの幅と高さ。
実行画面
SurfaceViewを使った独自Viewの作成
SurfaceViewって何?
っていうのがよく分からんかったので公式リファレンスのSurfaceViewクラスの概要をGoogle翻訳さんに訳してもらったぞ!
ビュー階層の中に埋め込まれた専用の描画面を提供しています。あなたは、そのサイズがお好みであれば、このサーフェスのフォーマットを制御することができ、 SurfaceViewは、画面上の正しい位置で表面を置くことの面倒を見る
表面は、そのSurfaceViewを保持するウィンドウの背後になるようにZオーダーであり、 SurfaceViewは、その表面が表示されるように、そのウィンドウ内に穴を開ける。ビュー階層が正しく表面と、通常、その上に現れるSurfaceViewのいずれか兄弟を合成するの世話をします。ノートは、しかし、それは完全なアルファブレンド合成するので、パフォーマンスに影響を与える可能性があることをするたびに表面変化を行いますが、これは、そのような表面の上にボタンなどのオーバーレイを配置するために使用することができます。
表面が見えるように透明領域は、ビュー階層内の配置位置に基づいています。レイアウト後の特性は、 SurfaceViewの上に兄弟ビューを描画するために使用するトランスフォームする場合、ビューが適切に表面に合成されない場合があります。
下にある表面へのアクセスは、 getHolder ()を呼び出すことによって取得できますSurfaceHolderインタフェースを介して提供される。
SurfaceView ‘sのウィンドウが表示されている間に表面が自動的に作成されます。あなたは表面が示され、非表示になって作成され、ウィンドウとして破棄されたときに発見するためにsurfaceCreated ( SurfaceHolder )とsurfaceDestroyed ( SurfaceHolder )を実装する必要があります。
このクラスの目的の一つは、二次スレッドが画面内に描画可能な表面を提供することである。あなたはこのようにそれを使用しようとしている場合は、いくつかのスレッドの意味を認識する必要があります。
すべてのSurfaceViewとSurfaceHolder.Callback方法はSurfaceView ‘sのウィンドウ(アプリケーションの通常メインスレッド)を実行しているスレッドから呼び出されます。したがって、これらは正しくも、描画スレッドがタッチされた任意の状態と同期する必要があります。
SurfaceHolder.Callback.surfaceCreated ( )とSurfaceHolder.Callback.surfaceDestroyed間( ) – あなたはそれが有効な間は、描画スレッドが唯一の下にある表面に触れるようにする必要があります。
・・・・うん!よく分からん!
色んなサイトを巡ってみたぞ!
描画用スレッドを実装できる SurfaceView の利用方法
AndroidのSurfaceViewの基礎
SurfaceViewならAndroidで高速描画ゲームが作れる
おぼろげながら分かってきた・・・・ぁ?
どうやら描画専用のスレッドが作れるクラスということの模様?
描画パフォーマンスが必要なもの、アニメーションとかゲームなど動きの激しいものを作る時には利用すべきクラスらしい。
ダブルバッファリングが使えるのが強み。
ダブルバッファリング!これがSurfaceViewを利用するメリットそのものかもしれない!やっと何か分かった気がする。
って訳で書いたコード
コード
MainActivity.javaは前のコードのMyViewの呼び出しをSurfaceViewを継承したクラスに変えるだけ。
下記はSurfaceViewを継承したクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
import android.view.SurfaceView; public class MySurfaceView extends SurfaceView implements Runnable,SurfaceHolder.Callback { boolean isLoop = true; int x = 100; int y = 100; int vx = 0; int vy = 0; public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); getHolder().addCallback(this); } @Override public void run() { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); while (isLoop) { Canvas canvas = getHolder().lockCanvas(); if (canvas != null) { canvas.drawColor(Color.WHITE); x += vx; y += vy; if (x <= 0) { vx = 0; } if (x >= (getWidth()-bitmap.getWidth())) { vx = 0; } if (y <= 0) { vy = 0; } if (y >= (getHeight()-bitmap.getHeight())) { vy = 0; } canvas.drawBitmap(bitmap, x, y, null); getHolder().unlockCanvasAndPost(canvas); } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { Thread thread = new Thread(this); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { isLoop = false; } } |
Viewと同じ要領でSurfaceViewを継承したクラスを作って、同じようにコンストラクタを実装する。
SurfaceHolder.Callback(インターフェイス)
SurfaceHolderはSurfaceViewの変更を監視している。
つまり、その変更の通知を受けられるようにするためのインターフェイス。
実装しなければならないメソッドは3つ。
surfaceChanged(SurfaceHolder holder, int format, int width, int height)
surfaceCreated(SurfaceHolder holder)
surfaceDestroyed(SurfaceHolder holder)
上から変化があったとき、作られた時、破棄されたとき。
getHolder()でSurfaceViewのホルダーが取得できる。
addCallback(SurfaceHolder.Callback callback)
コールバックを受け取る為の登録処理を行う。
引数は「新しいコールバックインターフェース」(直訳)らしいよ・・・
まあ作ったインスタンスがそのものだからthisで間違いないですな。新しいってのが引っかかる。他のコールバックを実装してるオブジェクトはどうなんだ?「新しい」なのか?すでにあるものを指定することになるとなると「新しい」のか?混乱。
とにかくコレを記述しないとせっかくインターフェイス実装してもコールバック受け取れないので必須ですね。
lockCanvas()
描画を開始する。と同時に名前の通りロックを掛ける。
スレッドセーフに作られていて、unlockCanvasAndPost(canvas)が呼ばれるまでlockCanvas()中のスレッド以外は描画が出来ない。みたいな?
unlockCanvasAndPost(canvas)
描画の終了と反映。
引数に指定したCanvasオブジェクトの行った処理を返す。つまり画面に反映する。
書ききったぞ!しんど・・・・
今日のひとこと
処理のヤツじゃないんすか?
コメント
No Trackbacks.