Canvas で描画した領域を動的に非表示にしたり表示したりする場合には、canvas.drawColor(0, PorterDuff.Mode.CLEAR) を使います。また同時に、クリアした後で再描画しますがそれには invalidate() を使います。
2021.2.1
Canvasの再描画
先の例でCanvasのViewをRelativeLayoutの中で使うことをやってみました。TextViewなどは表示、非表示ができますがCanvasの場合はどうでしょう。
Cavasの場合は一旦クリアーして、その後再描画が必要です。次に表示させる時にもまた再描画が必要です。
最初に簡単のためボタンで表示・非表示を実装してみたいと思います。その後でHandlerを使ってCanvasのViewをアニメーションさせてみます。
invalidate()
描画をクリアーするには、
canvas.drawColor(0, PorterDuff.Mode.CLEAR)
を使います。
また再描画のためにはinvalidate()を使います。Canvasをクリアしてから描画です。
ただし、invalidate()は再描画させるためにonDraw()を呼び出しますが、即呼び出されるわけではなく、都合がつくまで待たされるようです。ですから、即非表示を想定する場合はHandlerを使う方法が必要になります。
以前にCanvasを作成した方法 と同様に作成します。
例として package は 「package com.example.testcanvasclear」 で設定しました。
button によりフラグを切り替え
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
を実行して
invalidate();
で再描画します
サンプルコード
新しくMyViewクラスを作成
MyView.java
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 |
//package your.package.name; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.util.AttributeSet; import android.util.Log; import android.view.View; public class MyView extends View { private final Paint paint = new Paint(); private final Path path = new Path(); private Boolean viewflg; public MyView(Context context, AttributeSet attrs) { super(context, attrs); viewflg = true; } public void showCanvas(boolean flg){ viewflg = flg; // 再描画 invalidate(); } @Override protected void onDraw(Canvas canvas) { Log.d("TestView", " onDraw()"); if(viewflg){ Log.d("TestView", " viewflg = true"); // 背景、半透明 canvas.drawColor(Color.argb(125, 0, 0, 255)); // 円 paint.setColor(Color.argb(255, 68, 125, 255)); paint.setStrokeWidth(30); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); // (x1,y1,r,paint) 中心x1座標, 中心y1座標, r半径 canvas.drawCircle(450, 450, 200, paint); // 矩形 paint.setColor(Color.argb(255, 255, 0, 255)); paint.setStrokeWidth(10); paint.setStyle(Paint.Style.STROKE); // (x1,y1,x2,y2,paint) 左上の座標(x1,y1), 右下の座標(x2,y2) canvas.drawRect(480, 480, 850, 880, paint); // 三角形を書く float tx1 = 500; float ty1 = 820; float tx2 = 200; float ty2 = 1200; float tx3 = 700; float ty3 = 1200; paint.setStrokeWidth(10); paint.setColor(Color.YELLOW); path.moveTo(tx1, ty1); path.lineTo(tx2, ty2); path.lineTo(tx3, ty3); path.lineTo(tx1, ty1); canvas.drawPath(path, paint); } else{ Log.d("TestView", " viewflg = false"); // 描画クリア canvas.drawColor(0, PorterDuff.Mode.CLEAR); } } } |
MainActivity.java
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 |
//package your.package.name; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private boolean showCanvas; private MyView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView label = this.findViewById(R.id.label); label.setText(R.string.text); Log.d("MainActivity", "onCreate()"); myView = this.findViewById(R.id.my_view); myView.showCanvas(true); showCanvas = true; Button button = findViewById(R.id.button); button.setOnClickListener(v -> { Log.d("debug","button on click"); if (showCanvas) { myView.showCanvas(false); showCanvas = false; Log.d("debug"," showCanvas = false"); } else { myView.showCanvas(true); showCanvas = true; Log.d("debug"," showCanvas = true"); } }); } } |
activity_main.xml
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 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <!-- package name に合わせる --> <com.example.testcanvasclear.MyView android:id="@+id/my_view" android:layout_marginTop="120dp" android:layout_marginStart="20dp" android:layout_marginEnd="20dp" android:layout_marginBottom="100dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/label" android:textSize="20sp" android:textColor="#cccccc" android:layout_marginTop="100dp" android:layout_marginStart="30dp" android:layout_marginEnd="30dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/button" android:text="@string/button" android:layout_alignParentBottom="true" android:layout_margin="120dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> |
Stringのリソース
strings.xml
1 2 3 4 5 6 7 |
<resources> <string name="app_name">Your App Name</string> <string name="button">Button</string> <string name="text">"abcd123456789cdefg\n1234567893456978 \nhijklmnopq012698745rst4565848uvwxlmnopq \n\n\n\n012698745rst4565848\nuvwx"</string> </resources> |
マニュアルで表示・非表示ができました
Handlerを使う
invalidate()ですが、ちゃんと使うにはHandler等を利用する必要があります。
Event Handling and Threading
Note: The entire view tree is single threaded. You must always be on the UI thread when calling any method on any view. If you are doing work on other threads and want to update the state of a view from that thread, you should use a
Handler
.
Handlerを使ってアニメーションの作成
試しにHandlerとinvalidate()を使ってアニメーションを作ってみます。
Handlerで50msec毎にCanvas上のCircleのY軸座標を増加させて、Circleが落ちていくアニメーションです。
packageは com.example.testcanvasclearhandler
に変更しました。
また、Handler()はAPI30から非推奨になりnullにならないようにする手当が必要です。
Handler(Looper.getMainLooper())
MainActivity.java
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 |
//package com.example.testcanvasclearhandler; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private MyView myView; // 'Handler()' is deprecated as of API 30: Android 11.0 (R) private final Handler handler = new Handler(Looper.getMainLooper()); private Runnable runnable; boolean flg = false; int period = 50; private int pos; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView label = this.findViewById(R.id.label); label.setText(R.string.text); myView = this.findViewById(R.id.my_view); Button button = findViewById(R.id.button); button.setOnClickListener(v -> { if(!flg){ flg = true; moveCircle(); } else{ stopTask(); } }); } private void moveCircle(){ pos = 450; runnable = new Runnable() { @Override public void run() { if(flg){ pos += 10; myView.setPositon(pos); // 再描画のために無効にする myView.invalidate(); if(pos>1700){ stopTask(); pos = 0; } handler.postDelayed(this, period); } } }; handler.post(runnable); } private void stopTask(){ handler.removeCallbacks(runnable); runnable = null; flg = false; } } |
MyView.java
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 |
//package com.example.testcanvasclearhandler; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.View; public class MyView extends View { private final Paint paint = new Paint(); private final Path path = new Path(); private int yval; public MyView(Context context, AttributeSet attrs) { super(context, attrs); yval = 450; } public void setPositon(int pos) { yval = pos; } @Override protected void onDraw(Canvas canvas) { // 背景、半透明 canvas.drawColor(Color.argb(125, 0, 0, 255)); // 円 paint.setColor(Color.argb(255, 68, 125, 255)); paint.setStrokeWidth(30); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); // (x1,y1,r,paint) 中心x1座標, 中心y1座標, r半径 canvas.drawCircle(450, yval, 200, paint); // 矩形 paint.setColor(Color.argb(255, 255, 0, 255)); paint.setStrokeWidth(10); paint.setStyle(Paint.Style.STROKE); // (x1,y1,x2,y2,paint) 左上の座標(x1,y1), 右下の座標(x2,y2) canvas.drawRect(480, 480, 850, 880, paint); // 三角形を書く float tx1 = 500; float ty1 = 820; float tx2 = 200; float ty2 = 1200; float tx3 = 700; float ty3 = 1200; paint.setStrokeWidth(10); paint.setColor(Color.YELLOW); path.moveTo(tx1, ty1); path.lineTo(tx2, ty2); path.lineTo(tx3, ty3); path.lineTo(tx1, ty1); canvas.drawPath(path, paint); } } |
レイアウトとリソース
activity_main.xml
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 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <!-- package name に合わせる --> <com.example.testcanvasclearhandler.MyView android:id="@+id/my_view" android:layout_marginTop="120dp" android:layout_marginStart="20dp" android:layout_marginEnd="20dp" android:layout_marginBottom="100dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/label" android:textSize="20sp" android:textColor="#cccccc" android:layout_marginTop="100dp" android:layout_marginStart="30dp" android:layout_marginEnd="30dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/button" android:text="@string/button" android:layout_alignParentBottom="true" android:layout_margin="120dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> |
strings.xml
1 2 3 4 5 6 7 |
<resources> <string name="app_name">TestCanvasClearHandler</string> <string name="button">Button</string> <string name="text">"abcd123456789cdefg\n1234567893456978 \nhijklmnopq012698745rst4565848uvwxlmnopq \n\n\n\n012698745rst4565848\nuvwx"</string> </resources> |
サンプル動画
Handlerを使わないでAnimationを使ったやり方がこちらにあります。
- 関連記事
- Canvas と Paint で円や矩形を描く
- Custom Canvas をレイアウトに挿入する
- Canvas を Clear して再描画
- Canvas で画像と文字を表示する
- Canvas Animation で円弧を動かす
References:
Canvas | Android Developers
Canvas and Drawables | Android Developers