Android アプリ開発 「MATRIX」

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



「通知」をタップした時に任意の処理を実行する方法

通知をタップして処理を実行

今回は「通知」をタップした時に画面(Activity)を表示するのではなく、「任意の処理を実行する」というサンプルを作ってみたいと思います。基本的には以下のようなフローになっています。

<流れ>

① テキストボタンを持った「通知」を作成して表示する
        ↓
② 通知のテキストをタップした時に Intent を発信する
        ↓
③ 待機していたサービスが Intent を受け取りブロードキャストレシーバーに送信
        ↓
④ ブロードキャストレシーバーがフィルターを判別して任意の処理を実行 

 

サンプルコード①(Main.activity)

ここには通知を発行する「onCreate」だけでなく、通知から Intent を受け取りブロードキャストレシーバーに処理を依頼する「サービス」と、サービスからの依頼を受け取り処理を実行する「ブロードキャストレシーバー」など、必要なものがすべて含まれています。※詳細はコード内のコメントを参考にしてください。 

public class MainActivity extends AppCompatActivity {

private NotificationCompat.Builder builder;
private NotificationManager manager;
private static TextView textView;

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////メイン///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

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

//レイアウトXMLをセット
setContentView(R.layout.activity_main);

//表示用のテキストビューを操作するための準備
textView = (TextView)findViewById(R.id.text);

//通知の準備
builder = new NotificationCompat.Builder(getBaseContext(), "c1"); //通知ビルダーのインスタンスを生成
builder.setSmallIcon(R.drawable.bar_icon); //バーに表示するアイコンの指定
builder.setContentTitle("Sample Title"); //通知タイトル
builder.setContentText("Sample Text"); //通知する内容
builder.setWhen(System.currentTimeMillis()); //通知時間
builder.setAutoCancel(true); //オートキャンセル(これだけでは動作しない)

//通知にタップで反応するテキストを追加
Intent test_intent = new Intent(); //空のインテントを準備
test_intent.setAction("test_action"); //ブロードキャストが受信した時に識別する「タグ」のようなもの
test_intent.putExtra("sample_name", "sample value"); //ブロードキャストするデータをセット
PendingIntent test_pendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, test_intent, PendingIntent.FLAG_UPDATE_CURRENT); //インテントペンディングインテントに組み込む
builder.addAction(R.drawable.bar_icon, "ここを押すとブロードキャストする", test_pendingIntent); //上で作ったペンディングインテントActionとして追加する

manager = (NotificationManager)getBaseContext().getSystemService(Service.NOTIFICATION_SERVICE); //通知マネージャーを準備
manager.notify(1, builder.build()); //通知開始

Intent intent = new Intent(getBaseContext(), MyService.class); //サービス開始用のインテントを生成
startService(intent); //サービスを開始して待機
}

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////サンプル処理/////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

public static void sample() {
textView.setText("完了しました!"); //テキストを変更する
}

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////サービス////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////

public static class MyService extends Service {

//自動作成される
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

//最初に実行される
@Override
public int onStartCommand(Intent intent, int flags, int startId) {

IntentFilter filter = new IntentFilter(); //空のインテントフィルターを準備
filter.addAction("test_action"); //受信するActionを指定("test_action"というアクションを受信してブロードキャストレシーバーに送る)
registerReceiver(MyReceiver, filter); //レシーバーとフィルターを登録してサービス開始

return super.onStartCommand(intent, flags, startId); //自動生成のまま
}
}

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

public static BroadcastReceiver MyReceiver = new BroadcastReceiver() {

//サービスから処理のオーダーを受け取る
@Override
public void onReceive(Context context, Intent intent) {

if(intent.getAction().equals("test_action")) { //"test_action"というインテントフィルターが付いた依頼を処理

//ここは別スレッドなのでHandlerを利用してUIスレッドにアクセスしなければならない
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
sample(); //任意の処理(この場合はサンプルメソッドを実行
}
});
}
}
};
}

 

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

マニフェストファイルには「サービス」と「ブロードキャストレシーバー」の情報を <application></application> タグ内に追加する必要があります。これを書かないと正しく動かないので注意してください。※赤文字の2行。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="htt p://schemas.android.com/apk/res/android"
package="sample.test.co.jp">

<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$MyService" />
<receiver android:name=".MyReceiver" />

</application>

</manifest>

 

サンプルコード③(activity_main.xml)※抜粋

レイアウトファイルにはテキストを表示する「TextView」があるだけです。通知のテキストをタップした時に、この TextView のテキストを「Hello World!」から「完了しました!」に変更します。 

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textColor="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

 

サンプルの実行結果

① 起動すると画面の中央に「Hello World!」が表示され、すでに通知が届いている状態です。

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

 

② 通知を下に引き出して確認すると、下側にタップ可能のテキスト(「ここを押すとブロードキャストする」)が表示されます。 

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

 

③ 通知を収納すると画面中央のテキストが「完了しました!」に変更されています。

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

まとめ

ネット上には、通知をタップした時に「画面(Activity)を起動して表示する」という処理をもったサンプルは数多く存在しますが、「任意の処理を実行する」といったものは意外に少ないので、もし似たような処理を実現しようといろいろ模索している場合は、ぜひ今回のサンプルを参考にしてください。

END

Androido 端末で使用可能なセンサーの一覧(2017年12月26日現在)

Android 端末のセンサー一覧(2017年12月26日現在)

Sensor | Android Developers より抜粋した Android 端末で使用可能なセンサー(定数)の一覧です。各センサー下の説明は「Google 翻訳」で翻訳した後、若干の修正を加えたものとなっています。

全部で29種類のセンサーがありますが、この中のどのセンサーが実際に使用できるのかというのは、機種によってそれぞれ違いますのでアプリを作る前に、一度調べておく必要があるでしょう。

1.TYPE_ACCELEROMETER
加速度センサー

2.TYPE_ACCELEROMETER_UNCALIBRATED
較正されていない加速度センサー

3.TYPE_ALL
すべてのセンサー

4.TYPE_AMBIENT_TEMPERATURE
周囲温度センサー

5.TYPE_DEVICE_PRIVATE_BASE
ベンダーによって定義されたセンサー

6.TYPE_GAME_ROTATION_VECTOR
較正されていない回転ベクトルセンサー

7.TYPE_GEOMAGNETIC_ROTATION_VECTOR
地磁気の回転ベクトル

8.TYPE_GRAVITY
重力センサー

9.TYPE_GYROSCOPE
ジャイロセンサー

10.TYPE_GYROSCOPE_UNCALIBRATED
較正されていないジャイロセンサー

11.TYPE_HEART_BEAT
動き検出センサー

12.TYPE_HEART_RATE
心拍数センサー

13.TYPE_LIGHT
光センサー

14.TYPE_LINEAR_ACCELERATION
線形加速度センサー

15.TYPE_LOW_LATENCY_OFFBODY_DETECT
低遅延のオフボディ検出センサー

16.TYPE_MAGNETIC_FIELD
磁界センサー

17.TYPE_MAGNETIC_FIELD_UNCALIBRATED
較正されていない磁界センサー

18.TYPE_MOTION_DETECT
動き検出センサー

19.TYPE_ORIENTATION
方位センサー。この定数は、APIレベル8で廃止されました。代わりにSensorManager.getOrientation()を使用してください。

20.TYPE_POSE_6DOF
姿勢センサー

21.TYPE_PRESSURE
圧力センサー

22.TYPE_PROXIMITY
近接センサー

23.TYPE_RELATIVE_HUMIDITY
相対湿度センサー

24.TYPE_ROTATION_VECTOR
回転ベクトルセンサー

25.TYPE_SIGNIFICANT_MOTION
モーショントリガーセンサー

26.TYPE_STATIONARY_DETECT
固定検出センサー

27.TYPE_STEP_COUNTER
ステップカウンターセンサー

28.TYPE_STEP_DETECTOR
ステップ検出器センサーを記述する定数。

29.TYPE_TEMPERATURE
温度センサー。この定数はAPIレベル14では廃止されました。代わりにSensor.TYPE_AMBIENT_TEMPERATUREを使用してください。

備考

センサーからの値の取得については、「SensorEvent(SensorEvent | Android Developers)」を参考にしてください。

END

レイアウトXML に配置した SurfaceView 内でカウンターを動かしてみよう

今回は SurfaceView のサンプル

今回はレイアウトXML の一部に SurfaceView を配置し、その SurfaceView の中でカンターを動かしてみたいと思います。MainActivity と SurfaceView の関係が若干わかりにくいかもしれませんが、 一度覚えてしまえば楽に実装できるようになると思います。

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

使用するレイアウトは、Android Stuido が自動で生成したレイアウトXMLに SurfaceView を追加しただけのシンプルなものです。高さと幅は自由に確保してください。

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="sample.mysurfacetest.MainActivity">

<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="textView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

※デザインビューで見るとこんな感じになります。

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

サンプルコード②(MainActivity.class)

レイアウト(activity_main)は普通に読み込みます。その後、レイアウトXML に配置した SurfaceView を取得して、 それを context と一緒に MySurfaceView.class に渡します。

public class MainActivity extends AppCompatActivity {

//オブジェクト・変数を用意
private TextView textView;
private SurfaceView mSurfaceView;
private MySurfaceView mSfV;

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

//レイアウトxmlをセット
setContentView(R.layout.activity_main);

//レイアウトXMLから textView を取得する
textView = (TextView)findViewById(R.id.textView);
//文字色をグリーンに指定
textView.setTextColor(Color.BLUE);
//文字サイズを50に指定
textView.setTextSize(30);
//文字をセットして表示
textView.setText("SurfaceView カウンター");

//レイアウトXMLから SurfaceView を取得
mSurfaceView = (SurfaceView)findViewById(R.id.surfaceView);
//MySurfaceView.java context mSurfaceView を送って実体化する
mSfV = new MySurfaceView(this, mSurfaceView);
}
}

サンプルコード③(MySurfaceView.class)

SurfaceView を継承し、SurfaceHolder のコールバックと Runnable のインターフェースを実装します。重要ポイイントは、コンストラクターが受け取る引数に「SurfaceHolder sv」を追加するところです。これを追加しないと MainActivity から送られてくる SurfaceView を受け取ることができず、レイアウトXMLに配置した SurfaceView にアクセスすることができません。

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

//オブジェクト・変数を用意
private SurfaceHolder holder1;
private Thread thread;
private Canvas canvas;
private Paint paint;
private int count;

//コンストラクターで初期化(ここが最初に実行される)
public MySurfaceView(Context context, SurfaceView sv) {
super(context);

//サーフェースフォルダーを取得
holder1 = sv.getHolder();
//取得したサーフェースホルダーにコールバック機能を追加
holder1.addCallback(this);
//別スレッドを用意
thread = new Thread(this);
//count という名前の変数に 0 をセット
count = 0;
}

@Override
public void surfaceCreated(SurfaceHolder holder2) {
//SurfaceView が作られると同時にスレッドをスタート
thread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder3, int format, int width, int height) {
//SurfaceView が変更されると実行されるメソッド
//今回はここを使用しませんがこれが無いとエラーになるためとりあえす存在している
}

@Override
public void surfaceDestroyed(SurfaceHolder holder4) {
//SurfaceView が破棄される時にスレッドを停止
thread = null;
}

@Override
public void run() { //run() はスレッドが開始されると実行される

//SurfaceView に表示する文字の各詳細を paint で設定している
paint = new Paint();
paint.setTextSize(80);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);

//スレッドが存在する限りループする
while (thread != null) {

//ダブルバッファリングを準備
canvas = holder1.lockCanvas();
//用意しておいた canvas に文字色と表示文字を設定
canvas.drawColor(Color.RED);
canvas.drawText("カウント" + count + " 回",10,80,paint);
//ここで SurfaceView canvas を描画する
holder1.unlockCanvasAndPost(canvas);

//カウントを1アップ
count += 1;
}
}

<結果>

画面の一部に SurfaceView が表示され、その中でカウンターが動作しました。

f:id:vw-dsg:20171222090107p:plainEND 

 

Matrix の基本「Translate」の使い方を知ろう(その2)

Matrix の Translate を知る

Matrix 関数の中に 「Translate」 というメソッドがあります。Translate を利用すると画像を任意の場所に移動することができますが、その使い方や使い道がよくわかりませんので、今回からその「Translate」について少しずつ調べてみることにします。

Translate は3種類ある

Translate には「setTranslate」「preTranslate」「postTranslate」の3種類があり、「移動する」という基本動作は同じですが、それぞれ微妙に動きが違います。

  • setTranslate・・・指定場所に移動する
  • preTranslate・・・あらかじめ移動する
  • postTranslate・・・現状に続けて移動する

と、簡単に説明するとこんな感じになるかとは思います。 

サンプルコード(MyView)

今回使うサンプルコード(オリジナルのView)です。動きは「画像を読み込んで表示する」だけの単純なものです。このコードを MainActivity で読み込んで表示させますので、レイアウトXMLは使いません。※Matrix の命令を記述するのはコード中の赤文字の部分になります。

public class MyView extends View {

//各オブジェクト・変数を準備
private Bitmap bitmap;
private BitmapFactory.Options options;
private Matrix matrix;
private Resources resources;

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

//リソース(drawable にある画像)にアクセスするための準備
resources = context.getResources();

//ビットマップを読み込む際のオプションを準備
options = new BitmapFactory.Options();
//スケーリングを無効にセット(原寸大表示にする)
options.inScaled = false;

//リソース(drawable)から画像を読み込んで、同時にオプションを適用
bitmap = BitmapFactory.decodeResource(resources, R.drawable.image, options);

//背景色をライトグレーにする
setBackgroundColor(Color.LTGRAY);
}

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

//matrix に新規のマトリックスを準備
matrix = new Matrix();

//ここに matrix の命令を記述
matrix.postTranslate(100,100);

//matrix を適用し画像を表示
canvas.drawBitmap(bitmap, matrix, null);
}

サンプルコード(MainActivity)

セットする View をレイアウトXML(activity_main)からオリジナルの「MyView」に書き換えますが、この時必ず頭に new を付けなければなりません。

public class MainActivity extends AppCompatActivity {

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

//オリジナルの ViewMyView.class)をセットして使用
setContentView(new MyView(getBaseContext()));
}
}

調査 「複合パターンの動作」

まずは「setTranslate」と「preTranslate」を組み合わせた場合の動作です。

matrix.setTranslate(100,100);
matrix.preTranslate(200,200);

<結果>

どん兵衛の画像が左上の角から X方向に300px、Y方向に300px 移動しました。「preTranslate」は、”あらかじめ移動する”という命令なので、preTranslate であらかじめ X方向に100px、Y方向に100px 移動した後、そこを基準点にして setTranslate が実行され、最終的に、X方向に300px、Y方向に300px の位置に移動したと考えられます。 

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

次に「setTranslate」と「postTranslate」を組み合わせた場合の動作です。

matrix.setTranslate(100,100);
matrix.postTranslate(200,200);

<結果>

「setTranslate」と「preTranslate」の組み合わせと同じように、どん兵衛の画像が左上の角から X方向に300px、Y方向に300px 移動しました。

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

次に「preTranslate」と「postTranslate」を組み合わせた場合の動作です

matrix.preTranslate(100,100);
matrix.postTranslate(200,200);

<結果>

こちらも前の例と同じ結果になりました。preTranslate と postTranslate の組み合わせは、どちらが先でも継続して移動するようです。

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

次は「setTranslate」と「preTranslate」を逆の順番で組み合わせた場合の動作です。

matrix.preTranslate(100,100);
matrix.setTranslate(200,200);

<結果>

今度は、どん兵衛の画像が「setTranslate」で指定した左上の角から X方向に200px、Y方向に200px の位置に移動しました。これは、あらかじめ移動する preTranslate でX方向に100px、Y方向に100px 移動した後に、setTranslate で置き換えられてしまったと考えられます。

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

次は「postTranslate」と「setTranslate」の組み合わせです。

matrix.postTranslate(100,100);
matrix.setTranslate(200,200); 

<結果>

結果は、前回同様「setTranslate」で指定した左上の角から X方向に200px、Y方向に200px の位置に移動しました。 

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

最後は「postTranslate」と「preTranslate」の組み合わせです。

matrix.postTranslate(100,100);
matrix.preTranslate(200,200); 

<結果>

どん兵衛の画像が「postTranslate」と「preTranslate」の数値を足した、左上の角から X方向に300px、Y方向に300px の位置に移動しました。 preTranslate と postTranslate の組み合わせは、どちらが先でも継続して移動するようです。

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

今回のまとめ

今回は「setTranslate」「preTranslate」「postTranslate」をそれぞれ一つずつ組み合わせてみましたが、これだけでは Matrix の正しい使い方や使い道を知ることはできないようです。

次回「その3」ではこの3つの命令をもう少し複雑に組み合わせて、その動きを調べてみたいと思います。

END

Matrix の基本「Translate」の使い方を知ろう(その1)

Matrix の Translate を知る

Matrix 関数の中に 「Translate」 というメソッドがあります。Translate を利用すると画像を任意の場所に移動することができますが、その使い方や使い道がよくわかりませんので、今回からその「Translate」について少しずつ調べてみることにします。

Translate は3種類ある

Translate には「setTranslate」「preTranslate」「postTranslate」の3種類があり、「移動する」という基本動作は同じですが、それぞれ微妙に動きが違います。

  • setTranslate・・・指定場所に移動する
  • preTranslate・・・あらかじめ移動する
  • postTranslate・・・現状に続けて移動する

と、簡単に説明するとこんな感じになるかとは思います。 

サンプルコード(MyView)

今回使うサンプルコード(オリジナルのView)です。動きは「画像を読み込んで表示する」だけの単純なものです。このコードを MainActivity で読み込んで表示させますので、レイアウトXMLは使いません。※Matrix の命令を記述するのはコード中の赤文字の部分になります。

public class MyView extends View {

//各オブジェクト・変数を準備
private Bitmap bitmap;
private BitmapFactory.Options options;
private Matrix matrix;
private Resources resources;

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

//リソース(drawable にある画像)にアクセスするための準備
resources = context.getResources();

//ビットマップを読み込む際のオプションを準備
options = new BitmapFactory.Options();
//スケーリングを無効にセット(原寸大表示にする)
options.inScaled = false;

//リソース(drawable)から画像を読み込んで、同時にオプションを適用
bitmap = BitmapFactory.decodeResource(resources, R.drawable.image, options);

//背景色をライトグレーにする
setBackgroundColor(Color.LTGRAY);
}

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

//matrix に新規のマトリックスを準備
matrix = new Matrix();

//ここに Matrix の命令を記述する
matrix.setTranslate(100,100);

//matrix を適用し画像を表示
canvas.drawBitmap(bitmap, matrix, null);
}
}

サンプルコード(MainActivity)

セットする View をレイアウトXML(activity_main)からオリジナルの「MyView」に書き換えますが、この時必ず頭に new を付けなければなりません。 

public class MainActivity extends AppCompatActivity {

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

//オリジナルの ViewMyView.class)をセットして使用
setContentView(new MyView(getBaseContext()));
}
}

調査① 単独での動き

まず、「setTranslate」を単独で使ってみます。

matrix.setTranslate(100,100);

<結果>

単純に、画像が左上の角から X方向に100px、Y方向に100px 移動しました。これは、「preTranslate」「postTranslate」についても同じでしたのでこの2つの結果は省略します。

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

調査② 複数回続けた場合の動き

まず、「setTranslate」を数値を変えつつ複数回続けて使ってみます。

matrix.setTranslate(100,100);
matrix.setTranslate(200,200);
matrix.setTranslate(300,300);

<結果>

最終的に最後に命令した座標に移動しました。どうやら一番目と二番目の命令は次々と置き換えられてしまうようです。

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

次に、「preTranslate」で同様に複数回続けて命令してみます。

matrix.preTranslate(100,100);
matrix.preTranslate(200,200);
matrix.preTranslate(300,300);

<結果>

今度は、XとYそれぞれの数値を合計した X方向に600px、Y方向に600px 移動しました。「preTranslate」は命令が重複しても上書きされることはなく、継続的に移動するようです。

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

最後に、「postTranslate」を同様に使ってみます。

matrix.postTranslate(100,100);
matrix.postTranslate(200,200);
matrix.postTranslate(300,300); 

<結果>

結果は 「preTranslate」と同じでした。

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

今回のまとめ

単独ではどれも同じ結果になりましたが、複数回重ねた場合は「setTranslate」は次の「setTranslate」によって置き換えられ、「preTranslate」と「postTranslate」は継続的に移動するという違いがあることがわかりました。

今回の調査で同じ結果を出した「preTranslate」「postTranslate」の動き違いについては次回の「その2」でいろいろと調べてみたいと思います。

END

ウィジェットにレイアウトした ImageView 画像が表示されないトラブルの対処方法

今回は、自分が最近ウィジェットアプリを作っている時に見舞われてしまった、『ウィジェットにレイアウトした ImageView 画像が表示されない』というトラブルの対処方法です。

トラブルの原因は仕様変更

結局、このトラブルの原因はプログラムのミスや何かのバグの類ではなく、Android Studio の仕様がアップデートで変更されたことによるものだとわかりましたが、「仕様変更」という気が付きにくい原因だったため、解決するまでに二日近くという時間を費やしてしまいました。

対処方法

対処方法は至って簡単で、ImageView が登録される「属性」を変更するだけです。

ウィジェットのレイアウトに「ImageView」の画像を追加すると、Android Stuido 3.0 の仕様ではデフォルトで「srcCompat」という属性に登録されますが、なぜか、この srcCompat で登録されてしまうとウィジェットに配置した画像が表示されません。

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

そのまま実行(Run)すると、配置したはずの ImageView 画像が表示されない。

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

ウィジェットの配置した ImageView の画像を表示させるには、登録する属性を「srcCompat」から「src」に変更する必要があります。src 属性は srcCompat と同じ Attribute 一覧の下の方にあります。

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

今度は ImageView で配置した画像が正しくウィジェットに表示されました。

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

まとめ

なぜ、ImageView が登録されるデフォルトの属性が以前の「src」から「srcCompat」に変更され、さらに、画像が表示されなくなったのかはわかりませんが、間違いなく何か理由があってのことだと思いますので、今後のために srcCompat 属性について一度調べておいた方が良いかもしれませんね。

END

端末で利用できるセンサーの一覧と詳細を取得してスクロール表示する方法

今回は、端末で利用できるセンサーの一覧を取得して表示してみたいと思います。

スマートフォンに内蔵されているセンサー数は10種類前後と多く、詳細を含めて表示すると画面からはみ出してしまいますので、TextViewをスクロールさせる必要があります。

サンプルコード(MainActivity) 

端末に内蔵されているセンサーの一覧はセンサーマネージャー(SensorManager)が持っていますので、そこからList形式で取り出しレイアウトファイルに配置してあるテキストビュー(TextView)で表示します。 

import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.List;

public class MainActivity extends AppCompatActivity {

private SensorManager sensorManager;
private TextView list;

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

//表示に使用するレイアウトをセット
setContentView(R.layout.activity_main);

//センサー一覧表示用のテキストボックスを取得
list = (TextView)findViewById(R.id.sensorlist);

//センサー情報にアクセスするためにセンサーマネージャーを取得
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

//センサーマネージャーから端末のすべてのセンサー情報をList形式で取得しsensorListに格納
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);

//表示用のString変数strを用意
String str = "";

//センサーリストからセンサーと詳細をすべて取り出してstr変数に格納
for(Sensor s : sensorList) {
str += "センサー名:" + s.getName() + "\n";
str += "メーカー:" + s.getVendor() + "\n";
str += "バージョン:" + s.getVersion() + "\n";
str += "消費電力:" + s.getPower() + "mA\n";
str += "最大レンジ:" + s.getMaximumRange() + "\n";
str += "最小遅延:" + s.getMinDelay() + "μs\n";
str += "最大遅延:" + s.getMaxDelay() + "μs\n";
str += "----------------------------------------" + "\n"; //区切り線
}

//最後にテキストボックスに文字列strをセットして表示
list.setText(str);
}

 

レイアウトコードサンプル(activity_main.xml) 

ポイントはセンサーの一覧を表示するテキストビュー(TextView)をスクロールビューの中に入れることです。こうすることで、テキストビューからはみ出た部分もスクロールして確認することができます。

<?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="sample.sensorlist.MainActivity">

<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/sensorlist"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>

</RelativeLayout

 

実行した結果

端末で利用できるセンサーの一覧が表示されました。画面からはみ出た部分もスクロールすることで確認できます。

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

 

END