Android アプリ開発「MATRIX」

Androidアプリの開発に役立つサンプル集


マトリックス(Matrix)を使ってビットマップ画像を連続回転させる方法

マトリックスでビットマップ画像を連続回転させる

今回は、オリジナルの View(MyNewView.class)に配置した画像を、マトリックス(Matrix)を使用し同じ位置で連続回転させるサンプルです。View に配置したビットマップ画像の回転方法がわからない・・・という方はぜひ参考にしてください。

サンプルコード①(MainActivity.java

このサンプルは、画面を描画するオリジナルの View(MyNewView.class) を別ファイルにせず MainActivity の中に含めて「一体型」にしてあります。なお、サンプルコードの詳しい説明はコード内のコメントを参照してください。

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//オリジナルの ViewMyNewView.class)をセットして使用
setContentView(new MyNewView(this));
}

//オリジナルのViewMyNewView
public class MyNewView extends View {
//各オブジェクト・変数を準備
private Bitmap bitmap;
private BitmapFactory.Options options;
private Resources resources;
private float kakudo;

public MyNewView(Context context) {
super(context);
//タイマーのインスタンス
Timer timer1 = new Timer();
//角度変数の初期化
kakudo = 0;
//プロジェクトのリソースにアクセスするための準備
resources = context.getResources();
//ビットマップを読み込む際のオプションを準備
options = new BitmapFactory.Options();
//スケーリングを無効にセット(原寸大表示にする)
options.inScaled = false;
//リソース(drawable)から画像を読み込んで、同時にオプションを適用
bitmap = BitmapFactory.decodeResource(resources, R.drawable.donbei, options);

//タイマーにスケジュールを登録してスタート
timer1.schedule(new TimerTask() {
@Override
public void run() {
//角度の計算(2度ずつ回転)
kakudo += 2;
//表示の更新(描画用メソッドを呼び出している)
invalidate();
}
},100,100); //100ms後に100ms間隔で実行
}

//描画用メソッド
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

//マトリックスを生成してインスタンス
Matrix matrix = new Matrix();
//画像の中心を基準に画像を回転させる
matrix.setRotate(kakudo,bitmap.getWidth() / 2,bitmap.getHeight() / 2);
//回転させた画像をx=200,y=200の位置に移動
matrix.postTranslate(200,200);

Paint paint = new Paint(); //ペイントの生成とインスタンス
paint.setAntiAlias(true); //アンチエイリアスを有効
paint.setColor(Color.BLACK); //黒色に設定
paint.setTextSize(50); //文字サイズを50

//matrixを適用して画像を表示 + 現在角度の表示
canvas.drawBitmap(bitmap, matrix, null);
canvas.drawText("現在の角度:" + kakudo + "",10,50, paint);
}
}
}

サンプルコード②(AndroidManifest.xml

マニフェストファイルの記述はありません。

サンプルコード③(activity_main.xml

画面表示にオリジナルの View(MyNewView.class)をセットするので、レイアウトファイルは使用しません。

実行結果

① 起動すると X座標200px、Y座標200px の位置に画像が表示され 100ms 間隔で2度ずつ回転を始めます。

アンドロイド開発「マトリックスで画像を回転させる」①

② 画像はアプリを終了するまで回り続けます。

アンドロイド開発「マトリックスで画像を回転させる」②

備考・その他

マトリックス(Matrix)の使い方がわかると、ちょっと変わったおもしろい動きを表現できるようになりますので、今回のサンプルのコードを変更していろいろとテストしてみてください。

END

画面の回転(縦位置 ⇔ 横位置)に対応する方法

画面の回転に対応する方法

アンドロイドアプリは画面を回転(横位置 ⇔ 縦位置)させると、自動でアクティビティが再起動される仕組み(仕様)になっているため、変数のデータも破棄されてしまいます。

そのため、画面の回転に対応するには、回転の直前に呼び出される「onSaveInstanceState」メソッド内でデータを一旦保存し、次に画面の回転が完了すると呼び出される「onRestoreInstanceState」メソッド、または「onCreate」メソッドの中で、画面回転前に保存したデータを読み込み再び画面上にセットするという処理が必要になります。

アプリの縦横回転対応

サンプルコード①(MainActivity.java

今回のサンプルでは1秒間隔でカウントをアップして画面に表示します。サンプルコードの詳しい説明はコード内のコメントを参照してください。

public class MainActivity extends AppCompatActivity {

private Timer timer1; //タイマーオブジェクト
private Handler handler1; //ハンドラーオブジェクト
private TextView textView; //テキストビューオブジェクト
private int counter; //カウンター変数

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//レイアウトファイルをビューにセット
setContentView(R.layout.activity_main);

//レイアウトファイルのテキストビューを取得
textView = (TextView)findViewById(R.id.counter);
//タイマーをインスタンス
timer1 = new Timer();
//ハンドラーをインスタンス
handler1 = new Handler();
//カウントアップ用変数を初期化
counter = 0;

//タイマーを生成+スケジュールを実行
timer1.schedule(new TimerTask() {
@Override
public void run() {
//ハンドラー経由で画面を更新 ※直接更新するとエラーになる
handler1.post(new Runnable() {
@Override
public void run() {
//カウントアップ
counter += 1;
//テキストビューにカウント値をセット
textView.setText(String.valueOf(counter));
}
});
}
}, 1000, 1000); //1秒後から1秒間隔で実行
}

//画面の回転時に呼び出される(データを保存する)
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

//保存領域に「CONTENT_VALUE」というキーの数値を保存
outState.putInt("COUNT_VALUE", counter);
}

//アクティビティが再起動するときに呼び出される(データ読み出し)
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);

//保存領域から「CONTENT_VALUE」というキーの数値を取得
counter = savedInstanceState.getInt("COUNT_VALUE");
//レイアウトファイルのテキストビューにセット
textView.setText(String.valueOf(counter));
}
}

サンプルコード②(AndroidManifest.xml

今回はマニフェストファイルへの記述はありません。

サンプルコード③(activity_main.xml

レイアウトファイルは画面の中央に大きめの TextView を配置しただけの簡単なものです。

<TextView
android:id="@+id/counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="100sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

① 起動するとカウントアップ(1秒間隔)が始まります。

アンドロイド開発「画面回転対応」① 

② 画面の回転に対応しているので、端末を横位置にしてアプリが再起動してもカウント用の変数は保持されカウントが継続されます。

アンドロイド開発「画面回転対応」④

<画面の回転に対応していない場合>

端末を横位置にするとアクティビティの再起動と同時にカウント用の変数もクリアーされ「0」からの再スタートになってしまいます。

f:id:vw-dsg:20190119084621j:plain

備考・その他

他に、画面の回転に対応する方法として「マニフェストファイルに記述する属性で画面が回転してもアクティビティを再起動させないようにする(android:configChanges="orientation")」というのがありますが、この方法はアンドロイドの公式開発サイトで「この属性の使用はなるべく避けて、あくまで最後の手段として使用する必要がある」とされているため、少々手間がかかりますが今回紹介した方法を利用した方が良いでしょう。

END

Android Studio 3.3 の「レイアウトエディタ」のプレビュー表示を以前の状態に戻す方法

レイアウトエディタのプレビュー表示が変わった

Android Studio 3.3 になって「レイアウトエディタ」のプレビュー表示が変わりました。

以前はタイトルバー(アクションバー)や、上段の通知バー、下段にホームボタン、戻るボタンなどがありましたが、Android Studio 3.3 では、それら(デコレーション)がデフォルトでは表示されず、スクリーンの必要な部分だけになっています。

デザイン自体はできるのでこのままでも問題はありませんが、バーやボタンが表示されないため画面全体としてのイメージがしにくく、また、プレビューと背景の境界もやや分かりづらいため以前の表示に戻したいと思います。

Android Studio 3.3 メイン画面

レイアウトエディタのプレビュー表示の変更方法

プレビューにタイトルバー(アクションバー)、通知バーなどを表示するには、プレビュー表示の左上にある「目」のアイコンをクリックし、表示されるメニュー項目にある「Show Layout Decoration」にチェックを入れます。

Android Studio 3.3 レイアウトエディタ「プレビュー表示」の変更方法

「Show Layout Decoration」にチェックを入れると少し間をおいてプレビューが更新され、以前のようなタイトルバー(アクションバー)や、通知バーなども表示されるようになります。

Android Studio 3.3 レイアウトエディタ「プレビュー表示」の変更方法

備考・その他

Android Studio 3.3 は、プレビュー表示のタイトルバー(アクションバー)や通知バーを表示したり隠したりすことができるようになったので、デザイン自体はしやすくなるかもしれませんね。

END

端末のバッテリー充電レベルを監視する方法

端末のバッテリー充電レベルを監視する

バッテリーの充電レベルを監視するには、バッテリーの充電レベルが変化するたびに端末から発信されるバッテリー充電レベルのインテントをブロードキャストレシーバーで受信する必要があります。

<流れ>

1.端末のバッテリー受電レベルが変化
       ↓
2.システムがバッテリー充電レベルの変化インテントを発信する
       ↓
3.アプリがブロードキャストレシーバーでインテントを受信
       ↓
4.アプリがバッテリー充電レベルの表示を更新する

サンプルコード①(MainActivity.java

ブロードキャストレシーバーのコードは別ファイルではなく MainActivity.java 内に記述します。(※詳しい説明はコード内のコメントを見てください。)

public class MainActivity extends Activity {

//インテントフィルターの箱を用意
private IntentFilter intentFilter1;
//テキストビューの箱を用意
public static TextView textView1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//レイアウトファイルをViewにセットする
setContentView(R.layout.activity_main);
//レイアウトファイルにあるテキストビュー「disp_level」を取得して格納
textView1 = (TextView)findViewById(R.id.disp_level);
//インテントフィルターをインスタンス
intentFilter1 = new IntentFilter();
//インテントフィルターにバッテリー残量のアクションを追加
intentFilter1.addAction(Intent.ACTION_BATTERY_CHANGED);

//ブロードキャストレシーバーとインテントフィルターを登録
registerReceiver(mbroadcastReceiver, intentFilter1);
}

//アプリが破棄(終了)される時に呼び出される
@Override
protected void onDestroy() {
super.onDestroy();
//ブロードキャストレシーバーの登録を解除
unregisterReceiver(mbroadcastReceiver);
}

////////////////////////////////////////////////////////////////////////////////
////////////////////ブロードキャストレシーバー//////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
private static BroadcastReceiver mbroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//バッテリー関連のインテントを受信した場合
if (intent.getAction() == Intent.ACTION_BATTERY_CHANGED) {
//テキストビューにバッテリー残量レベルの値をセット
textView1.setText(intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) + "%");
}
}
};
}

サンプルコード②(AndroidManifest.xml

今回はマニフェストファイルにブロードキャストレシーバーを登録する必要はありません。

サンプルコード③(activity_main.xml

レイアウトファイルは画面の中央に大きめの TextView を配置しただけの単純なものです。

<TextView
android:id="@+id/disp_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0%"
android:textSize="100sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

① 起動すると画面の中央にテキストが表示されるので、そのままバッテリー充電レベルが変化するのを待つか、エミュレーターであれば、エミュレーターの「拡張コントロール」でバッテリーの充電レベルをマウスで操作します。

アンドロイド開発「バッテリー残量の監視」①

② バッテリー充電レベルが増えたり減ったりすると、画面中央のバッテリー残量レベルの数値もそれに合わせて更新されます。

アンドロイド開発「バッテリー残量の監視」②

備考・その他

今回のサンプルは「ホームボタン」や「バックボタン」でバックグラウンドに移行しても動作し続けます。バックグラウンドから削除(onDestroy)されると完全に終了します。

END

ブロードキャストレシーバーの基本的な使い方(その2)

ブロードキャストレシーバーの使い方(その2)

ブロードキャストレシーバーは端末が発信している様々なメッセージや情報を受信するクラスです。これを利用すると、時間やタイムゾーンの変化、バッテリーの残量や充電状態など、細かな端末の状態を知ることができます。また、自作の情報を発信してそれを受信することも可能です。

前回は自作のインテントを受信しましたが、今回は端末自らが発信(ブロードキャスト)しているインテント情報を受信する方法を説明したいと思います。

サンプルコード①(MainActivity.java

今回はブロードキャストレシーバーに関するコードを別ファイルに記述するのではなく MainActivity.java に記述します。サンプルコードの詳しい説明はコード内のコメントを参照してください。

public class MainActivity extends Activity {

//オブジェクト・変数の準備
private static TextView textView1;
private static int kaisu;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//Viewにレイアウトファイルをセットして使用する
setContentView(R.layout.activity_main2);

//レイアウトファイルのTextViewを取得
textView1 = (TextView)findViewById(R.id.textView);
//変数kaisu0をセット
kaisu = 0;

//インテントフィルターを準備
IntentFilter intentFilter1 = new IntentFilter();
//現在時刻が変更された(分単位)
intentFilter1.addAction(Intent.ACTION_TIME_TICK);
//レシーバーを登録
registerReceiver(mbroadcastReceiver, intentFilter1);

//TextViewの更新メソッドを実行
koushin();
}

//TextView更新メソッド
public static void koushin() {

//TextViewの更新
textView1.setText("受信回数:" + kaisu + "");
}

///////////////////////////////////////////////////////////////////////////////
////////////ブロードキャストレシーバー部///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
private static BroadcastReceiver mbroadcastReceiver = new BroadcastReceiver() {

//受信した時に呼び出されるメソッド
@Override
public void onReceive(Context context, Intent intent) {

//受信回数+1
kaisu += 1;
//更新メソッドを呼び出す
koushin();
}
};
}

サンプルコード②(AndroidManifest.xml

「ブロードキャストレシーバーの基本的な使い方(その1)」ではマニフェストファイルにブロードキャストレシーバーを登録しましたが、今回は登録する必要がありません。

<注意点>
今回の使い方で受信できるのは端末が自ら発信しているブロードキャストのみで、自作のインテントを受信することができません。自作のインテントを受信するには、マニフェストファイルに登録する「ブロードキャストレシーバーの基本的な使い方(その1)」を参考にしてください。

サンプルコード③(activity_main.xml

レイアウトファイルは画面の中央に大きめの TextView を配置しただけの簡単なものです。

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

① 起動すると画面の中央にテキストが表示されるので、現在時刻の変更のブロードキャストがあるまでそのまま待ちます。

ブロードキャストレシーバー①

② 現在時刻が変更(1分間隔)されるたびにブロードキャストレシーバーがそれを受信して「受信回数」がひとつずつ加算されていきます。

ブロードキャストレシーバー②

備考・その他

ブロードキャストレシーバーで受信できる端末情報の種類はとても多いのですが、知っていると非常に便利なので一度目を通しておいた方が良いと思います。

Android Developers|Intent> Intent  |  Android Developers

END

ブロードキャストレシーバーの基本的な使い方(その1)

ブロードキャストレシーバーの使い方(その1)

ブロードキャストレシーバーは端末が送信している様々なメッセージや情報を受信するクラスです。これを利用すると、時間やタイムゾーンの変化、バッテリーの残量や充電状態など、細かな端末の状態を知ることができます。また、自分で情報を発信してそれを受信することも可能です。

今回は自分で情報(インテント)を発信(ブロードキャスト)して、それをブロードキャストレシーバーで受信する方法の説明です。

サンプルコード①(MainActivity.java

詳しい説明はコード内のコメントで記述してあります。

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//レイアウトファイルをViewにセット
setContentView(R.layout.activity_main);

//レイアウトファイルのボタンに直にクリックイベントリスナーを設定
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

//ブロードキャストレシーバーに自作インテントを送信する
Intent intent1 = new Intent(); //空のインテントを生成
intent1.setAction("MY_INTENT"); //インテントのアクション名を設定
sendBroadcast(intent1); //インテントを送信
}
});
}
}

サンプルコード②(mBroadcastReceiver.java

詳しい説明はコード内のコメントで記述してあります。

public class mBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {

//送信されたブロードキャストを受信してトーストで表示
Toast.makeText(context, "受信しました…", Toast.LENGTH_SHORT).show();
}
}

サンプルコード③(AndroidManifest.xml

マニフェストファイルにブロードキャストレシーバーを登録します。以下の <receiver> タグを追記してください。追記する場所は <activity> タグと同じ並びです。

<!-- ブロードキャストレシーバーと受け取るアクション名「MY_INTENT」を登録 -->
<receiver android:name=".mBroadcastReceiver">
<intent-filter>
<action android:name="MY_INTENT"></action>
</intent-filter>
</receiver>

サンプルコード④(activity_main.xml

画面構成は中央に以下のボタンを配置しただけの簡単なものです。

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

① 起動すると画面の中央にボタンが表示されるのでそれをクリックして自作のインテントを発信(ブロードキャスト)します。

アンドロイド開発「ブロードキャストレシーバー」①

② 送信されたインテントをブロードキャストレシーバーが受信してトーストで「受信しました…」というメッセージを表示します。

アンドロイド開発「ブロードキャストレシーバー」②

備考・その他

ブロードキャストレシーバーの使い方は特に難しいものではありませんが、ついうっかりマニフェストファイルに登録するのを忘れて「あれ、動かないぞ・・・」となることがよくあるので、それには注意してください。次回は、自作のインテントではなく端末の状態などの情報をブロードキャストレシーバーで受信する方法を説明する予定です。

END

画面をタッチしたまま指を動かす「ムーブイベント」を検出する方法

「ムーブイベント」を検出する

スマートフォンタブレットの画面をタッチしたまま指を動かす「ムーブイベント」を検出するには View クラスの「onTouchEvent」メソッドの中で「MotionEvent.ACTION_MOVE」アクションを取得します。

サンプルコード①(MainActivity.java

詳しい説明はコード内のコメントに記述してあります。 

public class MainActivity extends AppCompatActivity {

//アプリを動かすと必ず最初に実行される
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//オリジナルのView(画面)をセット
setContentView(new TouchView(this));
}

//オリジナルのView(画面)
public class TouchView extends View {

//XY座標用の変数を準備
private float x_zahyou;
private float y_zahyou;
private String mess;
private final static String def_mess = "ムーブしてください";

public TouchView(Context context) {
super(context);

//変数の初期化
x_zahyou = 0;
y_zahyou = 0;
mess = def_mess;
}

//描画するところ
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

//文字の表示スタイルを設定
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(50);
paint.setStrokeWidth(30);

//画面に文字列などを表示する
canvas.drawText(mess,50,100, paint);
canvas.drawText("X座標:" + x_zahyou + " Y座標:" + y_zahyou,50,160, paint);
canvas.drawPoint(x_zahyou, y_zahyou, paint);
}

//画面(View)が操作されると呼び出される
@Override
public boolean onTouchEvent(MotionEvent event) {

//画面(View)が押されたかを判定
if(event.getAction() == MotionEvent.ACTION_MOVE) {

mess = "ムーブ中・・・"; //
x_zahyou = event.getX(); //X座標を変数にセット
y_zahyou = event.getY(); //Y座標を変数にセット

} else if(event.getAction() == MotionEvent.ACTION_UP) {

//メッセージを元に戻す
mess = def_mess;
}

//Viewの更新(onDrawの呼び出し)
invalidate();
return true;
}
}
}

サンプルコード②(activity_main.xml

今回は、オリジナルの「View」を作って表示しているので、レイアウトXMLファイル「activity_main.xml」は使用していません。

実行結果

アプリを起動すると「ムーブしてください」というメッセージと、現在マーカー「■」がある座標(X:0、Y:0)が表示されます。

アンドロイド開発「ムーブイベントを検出する」①

画面(スクリーン)内の適当なところをタッチしてそのまま指を移動すると、表示が「ムーブ中・・・」に変わり、X座標とY座標の値が連続で変わります。

アンドロイド開発「ムーブイベントを検出する」②

画面(スクリーン)から指を離すと、表示が「ムーブしてください」に戻ります。

アンドロイド開発「ムーブイベントを検出する」③

備考・その他

アプリだけでなくゲームなどにも活用できるのでムーブイベントを検出する方法を知っておくととても便利ですね。

END

画面がタッチされた時にX座標とY座標を取得する方法

画面がタッチされた時にX座標とY座標を取得する

スマートフォンタブレットの画面がタッチされた時にX座標とY座標を取得するには「View」クラスの「onTouchEvent」メソッドを使用します。

サンプルコード①(MainActivity.java

詳しい説明はコード内のコメントに記述してあります。 

public class MainActivity extends AppCompatActivity {

//アプリを動かすと必ず最初に実行される
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//オリジナルのViewをセット
setContentView(new TouchView(this));
}

//オリジナルのView
public class TouchView extends View {

//XY座標用の変数を準備
private float x_zahyou;
private float y_zahyou;

public TouchView(Context context) {
super(context);

//XY座標の初期化
x_zahyou = 0;
y_zahyou = 0;
}

//描画するところ
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

//文字の表示スタイルを設定
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(50);
paint.setStrokeWidth(30);

//画面に文字列などを表示する
canvas.drawText("タッチしてください",50,100, paint);
canvas.drawText("X座標:" + x_zahyou + " Y座標:" + y_zahyou,50,160, paint);
canvas.drawPoint(x_zahyou, y_zahyou, paint);
}

//画面(View)が操作されると呼び出される
@Override
public boolean onTouchEvent(MotionEvent event) {

//画面(View)が押されたのかを判定
if(event.getAction() == MotionEvent.ACTION_DOWN) {

x_zahyou = event.getX(); //X座標を変数にセット
y_zahyou = event.getY(); //Y座標を変数にセット
}

//画面の更新(onDrawの呼び出し)
invalidate();

return true;
}
}
}

サンプルコード②(activity_main.xml

今回は、オリジナルの「View」を作って表示しているので、レイアウトXMLファイル「activity_main.xml」は使用しません。

実行結果

起動すると「タッチしてください」というメッセージと、現在マーカーがある座標(X:0、Y:0)が表示されます。

アンドロイド開発「タッチ座標を取得する」①

スクリーン内の適当なところをタッチします。

アンドロイド開発「タッチ座標を取得する」②

タッチした位置にマーク「■」が表示され、XとY座標の位置が表示されます。

アンドロイド開発「タッチ座標を取得する」③

備考・その他

「onTouchEvent」の中でキャッチするイベントを「MotionEvent.ACTION_MOVE」にすると連続して座標を取得することが可能です。

END

 

押されたキーの「キーコード」を取得する方法

押されたキーの「キーコード」を取得する

スマートフォンタブレットの物理的な「キー」が押された時に、そのキーコードを取得するには「View」クラスの「onKeyDown」イベントを利用します。この「onKeyDown」イベントはソフトウェアキーボードのコードも取得できる場合がありますが、確実ではありませんのでおすすめすることはできません。

サンプルコード①(MainActivity.java

アプリの詳しい説明はコード内のコメントに記述してあります。 

public class MainActivity extends AppCompatActivity {

//オブジェクト・変数を準備
private TextView textView;
private String str;
private String default_message = "キーを押してください";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//オリジナルViewMyView)をセット
setContentView(new MyView(this));
//デフォルトメッセージをセット
str = default_message;
}

//オリジナルViewMyView
public class MyView extends View {

public MyView(Context context) {
super(context);

//このViewにフォーカスさせる
setFocusable(true);
setFocusableInTouchMode(true);
}

//キーが押された時に呼び出される
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

//キーコードの取得と表示する文字列の作成
str = "コード「" + String.valueOf(KeyCode) + "」のキーが押されました";
//表示の更新(onDrawの自動呼び出し)
invalidate();

return super.onKeyDown(keyCode, event);
}

//キーが離された時に呼び出される
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {

//デフォルトメッセージをセット
str = default_message;
//表示の更新(onDrawの自動呼出し)
invalidate();

return super.onKeyUp(keyCode, event);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

//ペイントの生成とパラメーターのセット
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(50);
paint.setColor(Color.RED);
//テキストを画面に描画
canvas.drawText(str,20,100,paint);
}
}
}

サンプルコード②(activity_main.xml

レイアウトは、画面の中央にスピードを表示するテキストビューがあるだけのシンプルな構造です。 

<TextView
android:id="@+id/keyevent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

実行するとアプリは入力待ち状態になります。

f:id:vw-dsg:20181229001113j:plain

ボリュームキー(アップ、またはダウン)を押すと、押されたキーのキーコードが表示されました。

アンドロイド開発「キーコードを取得する」2

押したボリュームキーを離すとまた入力待ちの状態に戻ります。

f:id:vw-dsg:20181229001113j:plain

備考・その他

。 

END

Google Play Instant Development SDK(1.6.0)のアップデートがエラーで失敗する場合の対処方法

Google Play Instant Development SDK のアップデートエラー

Android StudioSDKマネージャーで「Google Play Instant Development SDK」のリビジョンを「1.5.0」→「1.6.0」にアップデートしようとすると、原因不明のエラーが発生してアップデートできない場合がありますが、確認したところ、このトラブルは前回の「1.4.0」→「1.5.0」にアップデートしようとした時のエラーと同じ方法で対処できることが分かりましたので、お困りの方は下記の対処方法を参考にしてください。

Google Play Instant Development SDK(revision 1.5.0)のアップデートがエラーで失敗する場合の対処方法

アップデートのエラー画面

Android Studio エラーメッセージ

備考・その他

次回「1.7.0」へのアップデートでも同じようなエラーが発生するかもしれませんので、その場合は再度この対処方法を試してみてください。

END

音楽を再生するアプリを作ってみよう ~ メディアプレイヤーの基本的な使い方 ~

音楽を再生するアプリの作り方

今回はメディアプレイヤーを使って音楽を再生するアプリを作ってみます。音楽を再生するアプリと聞くといろいろと難しいのでは・・・と考えてしまいがちですが、一つの曲を単純に再生するだけならとくに難しい技術は必要ありません。

下準備(プロジェクトに音楽ファイルを追加する)

プログラムを作成するまえにプロジェクトの「res」フォルダ内に「raw」フォルダを新規に追加作成して、その中に再生したい音楽ファイル(mp3,wav,mpeg4,ogg形式など)を追加します。

手順①

「res」フォルダを右クリックして「New」→「Android Resource Directory」を選択します。

f:id:vw-dsg:20181031192054p:plain

手順②

「New Resource Directory」という画面が表示されたら「Resource Type」を「raw」に変更し「OK」ボタンを押します。すると「res」フォルダの中に「raw」フォルダが作成されます。

f:id:vw-dsg:20181031192254p:plain

手順③

好きな音楽ファイル上で右クリック。表示されたメニューから「コピー」を選択しクリップボードにコピーします。

f:id:vw-dsg:20181031192608p:plain

手順④

「raw」フォルダ上で右クリックして表示されたメニューから「Paste」を選択。クリップボードにコピーしておいた音楽ファイルをペーストします。

f:id:vw-dsg:20181031191906p:plain

手順⑤

「raw」フォルダ内に音楽ファイルが追加されました。これでアプリから利用できるようになります。

f:id:vw-dsg:20181031190410p:plain

サンプルコード① MainActivity.java

プログラムは「メイン処理」「音楽を再生する処理」「音楽を停止する処理」の3つに分かれていますが、処理内容はとてもシンプルです。なお、詳しい内容についてはコード中のコメントを参照してください。 

public class MainActivity extends AppCompatActivity {

private MediaPlayer mediaPlayer1; //メディアプレイヤー用オブジェクト
private Button button_play; //プレイボタン用オブジェクト

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//レイアウトファイル(activity_main.xml)をビューにセット
setContentView(R.layout.activity_main);

//レイアウトファイル中のボタンを取得する
button_play = (Button)findViewById(R.id.play_button);

//上で取得したボタンにクリックイベントの監視を設定
button_play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

//メディアプレイヤーが未生成、または、メディアプレイヤーが未再生だったら
if(mediaPlayer1 == null || !mediaPlayer1.isPlaying()) {
//音楽を再生する処理を実行
music_start();
} else { //メディアプレイヤーが再生中だったら
//音楽を停止する処理を実行
music_stop();
}
}
});
}

//アプリがユーザーから見えなくなった時
@Override
protected void onStop() {
super.onStop();

//音楽を停止する処理を実行
music_stop();
}

//音楽の再生する処理
private void music_start() {

//メディアプレイヤーを生成
mediaPlayer1 = MediaPlayer.create(getBaseContext(), R.raw.music);
//曲の先頭から再生するように設定
mediaPlayer1.seekTo(0);
//再生開始
mediaPlayer1.start();
//ボタンの表示を「STOP」に変更
button_play.setText("STOP");
}

//音楽を停止する処理
private void music_stop() {

//メディアプレイヤーが生成されていない場合は何もせずメソッドを抜ける
if(mediaPlayer1 == null) return;
//音楽の再生をストップ
mediaPlayer1.stop();
//メディアプレイヤーを解放
mediaPlayer1.release();
//メディアプレイヤーにNULLをセット
mediaPlayer1 = null;
//ボタンの表示を「PLAY」に変更
button_play.setText("PLAY");
}
}

サンプルコード② activity_main.xml

画面に「PLAY」と「STOP」が切り替わるボタンがひとつ配置されているだけの単純な構成です。

<Button
android:id="@+id/play_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PLAY" />

結果画面

起動すると「PLAY」と書かれたボタンが表示されます。

f:id:vw-dsg:20181031185326j:plain

「PLAY」ボタンを押すとあらかじめセットしておいた音楽が再生され、ボタンのラベル表示が「STOP」に変わります。

f:id:vw-dsg:20181031185339j:plain

「STOP」ボタンを押すと音楽の再生が停止して、ボタンのラベル表示が再び「PLAY」に変わります。

f:id:vw-dsg:20181031185326j:plain

備考・その他

今回はひとつの音楽ファイルを再生するだけのアプリでしたが、音楽ファイルを複数読み込んで次から次へと連続で再生するようにできれば「音楽プレイヤー」のようなアプリが作れると思いますので、ぜひチャレンジしてみてください。

END

Google Play Instant Development SDK(revision 1.5.0)のアップデートがエラーで失敗する場合の対処方法

Google Play Instant Development SDK のアップデートエラー

Android Studio で Google Play Instant Development SDK のリビジョンを「1.4.0」から「1.5.0」にアップデートしようとするとエラーが発生してアップデートできない場合があります。

f:id:vw-dsg:20181025224057p:plain
Android Studio の右下に表示されるアップデート案内をクリックし、表示された選択画面で「Update Now」を選択する。

f:id:vw-dsg:20181025224133p:plain
アップデートが開始されダウンロードが始まる・・・しかし・・・

f:id:vw-dsg:20181025224112p:plain
原因不明のエラーが発生して「Google Play Instant Development SDK」のリビジョンアップが停止してしまう・・・。何度試しても失敗してしまうのでお手上げ状態に・・・

エラーの対処方法

海外の掲示板に「古いリビジョンのファイルが邪魔をしているので SDK のインストール先にある instantapps というフォルダを削除して再度アップデートすれば解決する…」とう書き込みがあったので、それを試してみたのですが何度トライしても結局ダメ・・・。

一時は途方に暮れてしまったのですが、しばらくしてから少し方法を変えてトライしてみたところ、なぜか無事にアップデートを完了することができました。

f:id:vw-dsg:20181025224341p:plain
① まず、SDK マネージャーを表示し「SDK Tools」タブをクリックして「Google Play Instant Development SDK」のチェックを外します。

f:id:vw-dsg:20181025224505p:plain
② 次に、SDK がインストールされているフォルダを開いて、その中にある「instantapps 」というフォルダを削除する。※削除してしまうと失敗した時に元に戻すのが面倒になるので「instantapps」を「instantapps_backup」などにリネームしても良いですね。

f:id:vw-dsg:20181025224133p:plain
Android Studio を再起動して再びアップデートを試みます。

f:id:vw-dsg:20181025224203p:plain
④ 今度は Google Play Instant Development SDK のリビジョン「1.5.0」のインストールが成功しました。

f:id:vw-dsg:20181025224247p:plain
⑤ 最後にSDKマネージャーを表示して「Google Play Instant Development SDK」にチェックを入れて完了です。

備考・その他

もしこの方法で失敗する場合は、⑤のSDKマネージャーの「Google Play Instant Development SDK」にチェックを入れるタイミングを少し変えてみてください。

END

サービスの基本的な使い方を知っておこう

基本的なサービスの使い方

今回は「サービス」の基本的な使い方です。「サービス」というのは長時間バックグラウンドで動きインターフェース画面を持たないアプリケーションです。基本的な作り方は意外に簡単で、サービスを継承したクラスを作った後、それを他から呼び出して実行させるだけです。

「サービス」を利用すれば画面を閉じても動き続ける必要がある「タイマー」アプリや「音楽プレイヤー」アプリなどが作れるようになりますね。

サンプルコード①(MainActivity.java

MainActivity クラスの中に「Service」を継承した「TestService」が入っていますが、コード増えると長くなってしまうので、その場合はそれぞれ別々のファイルに分けた方が良いですね。

public class MainActivity extends AppCompatActivity {

private Button button, button2;
private Intent intent;
private static TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//サービス用のインテントを生成
intent = new Intent(getBaseContext(), TestService.class);
//レイアウトから表示用のテキストビューを取得
textView = (TextView)findViewById(R.id.infom);

//レイアウトからボタンを取得してクリックイベントを設定
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startService(intent);
}
});

//レイアウトからボタン2を取得してクリックイベントを設定
button2 = (Button)findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopService(intent);
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
// アプリを終了したらサービス停止
stopService(intent);
}

//サービス本体
public static class TestService extends Service {

// サービスが作られると呼び出される
@Override
public void onCreate() {
super.onCreate();
}

// サービスが停止すると呼び出される
@Override
public void onDestroy() {
super.onDestroy();
// 画面に「サービス停止中」と表示
textView.setText("サービス停止中");
}

// バインドされると呼び出される(※内容がなくても省略不可能)
@Override
public IBinder onBind(Intent intent) {
// バインドしていないので内容はなし
return null;
}

// サービスが開始されると呼び出される
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 画面に「サービス実行中」と表示
textView.setText("サービス実行中");
// 強制終了したら再起動させる戻り値
return START_REDELIVER_INTENT;
}
}
}

サンプルコード②(AndroidManifest.xml

「サービス」を継承したクラスを作ったら必ずマニフェストファイルにその存在を登録する必要があります。※下の方にある赤文字の行を追加します。

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service android:name=".MainActivity$TestService" />
</application>

サンプルコード③(activity_main.xml

特に複雑なものではありません。サービスの状態を表示するテキストビューと、サービスの起動と停止をする2つのボタンがあるだけのシンプルな構造です。

<TextView
android:id="@+id/infom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="7dp"
android:text="サービス停止中"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="START"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="STOP"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button" />

実行結果

実行すると「サービス停止中」というテキスト表示と「START」と「STOP」というラベルのボタンが画面上に表示されます。

f:id:vw-dsg:20181019000238g:plain

「START」ボタンを押してサービスを起動すると「サービス停止中」という表示が「サービス実行中」という表示に変わります。

f:id:vw-dsg:20181019000303g:plain

開発者ツールで実行中のサービスを確認すると今回作った「Service Test」というアプリの名前が実行中のサービスリストの中にありました。

f:id:vw-dsg:20181019000319g:plain

「STOP」ボタンを押したりサンプルアプリを終了すると・・・実行中のサービス一覧からサンプルアプリ「Service Test」が削除されました。

f:id:vw-dsg:20181019000333g:plain

その他・備考

この方法の他にバインドしてサービスを動かす方法がありますが、少しだけ複雑になりますのでまた次の機会にサンプルを作って説明したいと思います。 

END

GPSを利用したスピードメーター(速度計)の作り方

GPSを利用したスピードメーター(速度計)

今回はGPSを利用したシンプルなスピードメーター(速度計)の作り方です。

Android Java には現在の速度を「m/s」単位で取得できる関数「getSpeed」があるので、GPSの位置情報からいちいち速度を計算する必要はありませんが、取得できる間隔が「1秒」と長いので、短時間で速度が変化するような場合の速度測定には向かないかもしれません。

サンプルコード①(MainActivity.java

詳しい説明はコード内のコメントを見てください。 

//メインアクティビティ(implementsGPSを監視できるようにしてある)
public class MainActivity extends AppCompatActivity implements LocationListener {

private LocationManager locationManager; //ロケーションマネージャー
private TextView textView; //速度表示用
private float speed = 0f; //速度用の変数

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//レイアウトファイルをセット
setContentView(R.layout.activity_main);
//アクションバーを隠す
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
//ロケーションマネージャーに端末のロケーションサービスを関連付ける
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
//レイアウトファイルからテキストビューを取得する
textView = (TextView) findViewById(R.id.speed_text);
}

@Override
protected void onPause() {
super.onPause();
//ポーズ時にはGPS(位置情報)の取得を解除する
locationManager.removeUpdates(this);
}

@Override
protected void onResume() {
super.onResume();
//GPS(位置情報)の取得が許可があるのかをチェック
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

//GPSの使用が許可されていなければパーミッションを要求し、その後再度チェックが行われる
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);

return;
}
//ロケーションマネージャーにGPS(位置情報)のリクエストを開始する
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}

@Override
public void onLocationChanged(Location location) {
  //
速度が出ている?
if(location.hasSpeed()) {
//速度が出ている時(km/hに変換して変数speedへ
speed = location.getSpeed() * 3.6f;
} else {
//速度が出ていない時
speed = 0;
}
//速度を表示する
textView.setText(speed + " km/h");
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
//必要に応じてプログラムを書く(省略はできない)
}

@Override
public void onProviderEnabled(String s) {
//必要に応じてプログラムを書く(省略はできない)
}

@Override
public void onProviderDisabled(String s) {
//必要に応じてプログラムを書く(省略はできない)
}
}

サンプルコード②(activity_main.xml

レイアウトは、画面の中央にスピードを表示するテキストビューがあるだけのシンプルな構造です。 

<TextView
android:id="@+id/speed_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0 km/h"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

実行するとアプリが「位置情報(GPS)」使用の許可を要求してくる。

f:id:vw-dsg:20181008223619j:plain

「OK(ALLOW)」を選択するとすぐに速度の測定が開始されます。

f:id:vw-dsg:20181008223055j:plain

備考・その他

このサンプルアプリは、バックグラウンドに移動するとGPSを停止して速度の測定を一時中断する仕組みになっているので、バックグラウンドに残してあってもGPSで電力を消費することはありません。 

END

ブラウザを起動するボタンを作る方法

ブラウザを起動するボタンを作る

今回はボタンをクリックするとブラウザが起動して「Yahoo!」サイトを表示するサンプルを作ります。

動作の流れは、Uriに「Yahoo!」のアドレスをセットし、UriをセットしたIntent(ACTION_VIEW)を作成して、最後にそのIntentをstartActivityで開始します。

手続きが少々面倒ですが知っておいて損はありませんので、このサンプルで流れをしっかり覚えてしまいましょう。

サンプルコード①(MainActivity.java

詳しい説明はコード内のコメントを参考にしてください。

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//レイアウトファイルをセット
setContentView(R.layout.activity_main);
//アクションバーを取得
ActionBar actionBar = getActionBar();
//アクションバーを隠す
actionBar.hide();

//レイアウトファイルのボタンを取得
Button button = (Button)findViewById(R.id.button);
//取得したボタンにクリックイベントをセット
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//ブラウザで開きたいサイトをuriにセット
Uri uri = Uri.parse("http://yahoo.co.jp");
//uriを開くインテントを作成
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
//uriを開くアクティビティをスタートします
startActivity(intent);
}
});
}
}

サンプルコード②(activity_main.xml) 

レイアウトファイルは中央にボタンがあるだけの単純な構造です。

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Yahoo!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

実行結果

中央のボタンをクリックすると…

f:id:vw-dsg:20181005211902p:plain

 

Yahoo!」のウェブサイトが表示されました。

f:id:vw-dsg:20181005211923p:plain

END