円や四角を描画する時に、グラデーションでデザインをいただく事が多いのですが、グラデーションで描画をする事が多いのです
2回ほど前に作成したリボンですが
Viewで作成していたのですが、今回はsurfaceViewを使ってより早く描画できるようにしてみます。
・まずは簡単にViewとSurfaceViewの違いから。
viewに関しては、基本的には静止でinvalidate()を利用することで、再描画をする事ができました。上記のサンプルを作成した際には連続描画をTimerを用いて作成していました。実際にTimerで設定した時間よりも遅くなってしまいましたが、そこまでスピードが遅くなることもなく実装ができていました。
surfaceviewに関してはimplementでSurfaceHolder.Callback,Runnableを読み込む事で、内部にThreadを持ち、surfaceholderで描画用のデータへの描画を簡単にしてくれます。基本の形としては、
surfaceCreated surfaceChanged surfaceDestroyed
の三段階を実装します。
この中でthreadを実装する際に
Canvas canvas = sufaceHolder.lockCanvas(); canvas.draw・・・ sufaceHolder.unlockCanvasAndPost(canvas);
としてcanvasのロックを調整する事で描画用の最適化をしてくれるようです。
で、今回作成するのは
で前回の赤一色ではまったく同じものになってしまうので、少し色を変える仕組みを入れてみました。
マニフェストは
特に変更しません。
レイアウトは
直接surfaceviewを配置する
surfaceviewは
public class MainView extends SurfaceView implements SurfaceHolder.Callback,Runnable { private SurfaceHolder sufaceHolder; private Thread thread; public int _x = 200, _y = 100; public float currentX1 = 200, currentY1 = -100; public float _xV1 = 0,_yV1 = 0; Bitmap _bitmap; Canvas _canvas; Paint paint1; public MainView(Context context) { super(context); sufaceHolder = null; thread = null; getHolder().addCallback(this); paint1 = new Paint(); paint1.setAntiAlias(true); paint1.setStrokeWidth(6); paint1.setStrokeCap(Paint.Cap.ROUND); } public void surfaceCreated(SurfaceHolder holder) { this.sufaceHolder = holder; thread = new Thread(this); } public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { _bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); _canvas = new Canvas(_bitmap); _canvas.drawColor(Color.WHITE); if (thread != null) { thread.start(); } } public void surfaceDestroyed(SurfaceHolder holder) { thread = null; BOOL = false; } Boolean BOOL = true; int _count1 = (int)(300*Math.random()); int _count2 = (int)(300*Math.random()); int _count3 = (int)(300*Math.random()); public void run() { while (BOOL) { _count1++; _count2 += 2; _count3 += 3; paint1.setColor(Color.rgb((int)(128+127*Math.sin(Math.PI*2/360*_count1)), (int)(128+127*Math.sin(Math.PI*2/360*_count2)), (int)(128+127*Math.sin(Math.PI*2/360*_count3)))); _xV1 += (_x - currentX1)/12.0f; _yV1 += (_y - currentY1)/12.0f; _xV1 *= 0.9; _yV1 *= 0.9; currentX1 += _xV1; currentY1 += _yV1; paint1.setStrokeWidth(Math.abs(_yV1)/10.0f+Math.abs(_xV1)/10.0f); _canvas.drawLine((int)currentX1, (int)currentY1, (int)(currentX1-_xV1),(int)(currentY1-_yV1), paint1); Canvas canvas = sufaceHolder.lockCanvas(); canvas.drawBitmap(_bitmap, 0, 0, null); sufaceHolder.unlockCanvasAndPost(canvas); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: _x = (int) event.getX(); _y = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: _x = (int) event.getX(); _y = (int) event.getY(); break; case MotionEvent.ACTION_UP: break; default: break; } return true; } }
メインのクラスは
setContentView(R.layout.main); MainView surfaceView = new MainView(this); setContentView(surfaceView);
以上です。
今回のアプリは頑張ればアプリ化に到達できそうな、予感もありつつ、考え付かないので、書いてしまいます。
描画をするにはやはり、Viewを使うのがお手軽感があります。
↑こんなの作ります。
点で書くの難しいなぁ。と思いながらも、スクリプトは簡単でした。
マニフェストは
特に変更しません。
レイアウトは
直接viewを表示します。
描画用viewは小さすぎる円と大きすぎる円はしきい値を引いたのがポイントでしょうか。
public class TouchCircle extends View { public int _x = 200, _y = -100; public int _xOld = 200, _yOld = -100; public float currentX = 200, currentY = -100; public float _xV = 0,_yV = 0; public TouchCircle(Context context) { super(context); } Bitmap _bitmap; Canvas _canvas; protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); _bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); _canvas = new Canvas(_bitmap); } int _count = 0; protected void onDraw(Canvas canvas) { _count++; Paint paint = new Paint(); int _ran = (int)(Math.random()*200); paint.setColor(Color.rgb(255, (int)(_ran+(255-_ran)/8*Math.random()), (int)(_ran+(255-_ran)/8*Math.random()))); paint.setAntiAlias(true); int _size = (int) Math.sqrt((_x - _xOld)*(_x - _xOld)+(_y - _yOld)*(_y - _yOld)); _size /= 2; if(_size<5){ _canvas.drawCircle((int)(_x+20-40*Math.random()), (int)(_y+20-40*Math.random()), 5, paint); }else if(_size<50){ _canvas.drawCircle((int)(_x+20-40*Math.random()), (int)(_y+20-40*Math.random()), _size, paint); }else{ _canvas.drawCircle((int)(_x+20-40*Math.random()), (int)(_y+20-40*Math.random()), 10, paint); } canvas.drawBitmap(_bitmap, 0, 0, null); _xOld = _x; _yOld = _y; } public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: _x = (int) e.getX(); _y = (int) e.getY(); break; case MotionEvent.ACTION_MOVE: _x = (int) e.getX(); _y = (int) e.getY(); invalidate(); break; case MotionEvent.ACTION_UP: break; default: break; } return true; } }
メインのクラスから描画クラスを呼び出します。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TouchCircle _tCircle = new TouchCircle(getApplicationContext()); setContentView(_tCircle); }
はいできた!
今回はViewの限界まで挑戦をしたいと思い、リボンでくるくる回転させてみました。
動作の滑らかさを求めるのであれば、viewよりsurfaceview,surfaceviewよりopenglといわれるのですが、viewのソースコードが割と楽しかったのでその連続で作成してみました。それと最近作った新しいサイトで少しviewを掘り下げたので、ちょっと挑戦をしてみました。
で、できたのがこれです。
まあ、見栄え的には悪くないのかな。と思います。
指を動かすとそのゆびの周りをリボンが回るように、描画していきます。リボンの速度が速くなると、リボンが太くなります。楽しいのですが、使いようがないかも。
作り方↓
マニフェスト
変更なし
レイアウト
main.xmlすらなし
メインクラス
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); DrawLine _dLine = new DrawLine(getApplicationContext()); setContentView(_dLine); }
描画クラス
public class DrawLine extends View { public int _x = 200, _y = 100; public float currentX1 = 200, currentY1 = -100; public float _xV1 = 0,_yV1 = 0; private Handler mHandler = new Handler(); public DrawLine(Context context) { super(context); paint1 = new Paint(); paint1.setColor(Color.rgb(234, 14, 50)); paint1.setAntiAlias(true); paint1.setStrokeWidth(6); paint1.setStrokeCap(Paint.Cap.ROUND); Timer mTimer = new Timer(); mTimer.schedule( new TimerTask(){ @Override public void run() { // mHandlerを通じてUI Threadへ処理をキューイング mHandler.post( new Runnable() { public void run() { invalidate(); } }); } }, 16, 16); } Bitmap _bitmap; Canvas _canvas; Paint paint1; protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); _bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); _canvas = new Canvas(_bitmap); _canvas.drawColor(Color.WHITE); } protected void onDraw(Canvas canvas) { _xV1 += (_x - currentX1)/12.0f; _yV1 += (_y - currentY1)/12.0f; _xV1 *= 0.92; _yV1 *= 0.92; currentX1 += _xV1; currentY1 += _yV1; paint1.setStrokeWidth(Math.abs(_yV1)/10.0f+Math.abs(_xV1)/10.0f); _canvas.drawLine((int)currentX1, (int)currentY1, (int)(currentX1-_xV1),(int)(currentY1-_yV1), paint1); canvas.drawBitmap(_bitmap, 0, 0, null); } public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: _x = (int) e.getX(); _y = (int) e.getY(); break; case MotionEvent.ACTION_MOVE: _x = (int) e.getX(); _y = (int) e.getY(); break; case MotionEvent.ACTION_UP: break; default: break; } return true; } }
フレームレートも30くらい出てる感じですが、もう少しあった方が気持ちよさの面でグレードアップするので残念なんですが、5分くらいはずっとくるくるしてたので、意外に面白いと自分では思っています。
以上です。