Android アプリ開発「MATRIX」

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


他のクラスの変数を参照して変更する方法

他のクラスの変数を参照して変更する方法

今回は他のクラスにある変数を直接的に参照して変更する方法を説明します。メインの MainActivity.class から 文字を表示しているオリジナルの View クラス(MyView.class)にある変数「yy」を参照して変更します。 

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

MainActivity.class では、オリジナル View クラス(MyView.class)をインスタンス化し、それを経由して MyView.class の変数にアクセスします。詳細な説明はコード内のコメントを見てください。

public class MainActivity extends AppCompatActivity {

private MyView myView; // MyView クラスをインスタンス化するための箱
private Timer timer; // Timer クラスをインスタンス化するための箱
private Handler handler; // Handler クラスをインスタンス化するための箱

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

// MyView クラスをインスタンス化して mView に格納
myView = new MyView(getBaseContext());
// myView を渡して表示する
setContentView(myView);

// MyView UI にアクセスするための Handler インスタンス化して handler に格納
handler = new Handler();

// Timer インスタンス化して timer に格納
timer = new Timer();
// timer にスケジュールを設定すると同時に処理内容を記述
timer.schedule(new TimerTask() {

@Override
public void run() {
// myView クラスの変数 yy 3 を加える
myView.yy += 3;
// UI にアクセスする場合は handler を経由する
handler.post(new Runnable() {
@Override
public void run() {
// myView onDraw 処理を更新する
myView.invalidate();
}
});
}
}, 1000, 1000);
}

}

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

参照して変更される変数をもっているクラスです。重要ポイントは参照・変更させたい変数のアクセス修飾子を「public」にすることです。※このクラス内では変数「yy」の値を変更していません。 

public class MyView extends View {

// Paint インスタンス化して paint に格納
private Paint paint = new Paint();
// Y 座標の位置 yy 100f を格納
public float yy = 100f;

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

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

// paint にテキストサイズ 100f をセット
paint.setTextSize(100f);
// キャンバスに文字を出力する。位置は X=10f, Y=yy。スタイルは paint
canvas.drawText("TEST", 10f, yy, paint);
}

③結果

MainActivity.class から MyView.class の変数「yy」が変更され、一秒ごとに「TEST」という文字が下方に移動します。 

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

④備考・その他

今回は MyView.class で描画しているので、レイアウトXLMファイルは使用していません。 

END

Android Studio 3.1.3 でデザインのレイアウトが編集できない場合の対処方法

デザインのレイアウトが編集できない

いつものように新規にプロジェクトを作成したにもかかわらず、ある日突然デザインがまったく表示されなくなりレイアウトの編集ができなくなる場合があります。

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

よく見ると、デザインエリアの右上にある「!」のエラーアイコンが赤く点灯しています。さっそくクリックしてみましょう。

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

赤い「!」マークをクリックするとエラーの内容が下部に表示されました。翻訳してみるとレンダリングに関係するエラーだというのはわかりましたが、内容が漠然としていていまひとつ理解できません。

対処方法(手順)

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

以前にも同じようなトラブルがあったのを思い出して、まだコードネームが付けられていない SDKプラットフォーム「Android API 28」のチェックを外してアンインストールしてみます。

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

Android SDK Platform 28」を削除しますか?と聞かれるので「OK」をクリック。

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

Android SDK Platform 28」のアンインストールが開始されます。

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

Android SDK Platform 28」のアンインストールが完了したら「Finish」をクリックして画面を閉じます。

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

Android SDK Platform 28」をアンインストールした後に、さっそく新規にプロジェクトを作成してみると、今度は正しくデザインのレイアウトが表示され編集も可能になりました。やっぱり原因は「Android SDK Platform 28」にあったようです。

備考・まとめ

今回の、デザインレイアウトが表示されなくなって編集も不可能になるというトラブルは、まだコードネームが付けられていない最新の「SDK Platform」(今回は Android SDK Platform 28)を含めて開発しようとして起きてしまったようです。

最新の SDKプラットフォームは、Android Studio のアップデートを経由してインストールされる可能性があるので、もしアップデート後に今回と似たようなトラブルが起きた場合は「SDKマネージャー」からコードネームのないプラットフォームがインストールされていないかを一度チェックしてみて下さい。

END

音声を認識して結果をテキスト表示する方法(音声認識アプリ)

簡単な音声認識アプリを作る

今回は、グーグルが提供している音声認識システムを使用した簡単な音声認識アプリを作ってみたいと思います。 詳細についてはコード内にコメントで記載してありますので、そちらをご覧ください

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

ポイントは、音声認識が完了した後の処理をする「onActivityResult」をオーバーライドするところです。これがないと音声認識の結果を受け取ることができませんので、必ず追加してください。 

import android.content.Intent;
import android.speech.RecognizerIntent;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

private TextView textView, textView2;
private Button button;
//任意の識別番号
private static final int REQUEST_CODE = 12345;

//アプリが開始されると最初に処理されるメソッド
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//このアプリで使用するレイアウトファイルをセット
setContentView(R.layout.activity_main);

//画面の上にあるアクションバーを隠す(隠さなくても良い)
ActionBar actionBar = getSupportActionBar();
actionBar.hide();

//レイアウトにある2つのテキストビューを取得
textView = (TextView)findViewById(R.id.textView);
textView2 = (TextView)findViewById(R.id.textView2);

//レイアウトにあるボタンを取得
button = (Button)findViewById(R.id.button);

//上で取得したボタンにクリックイベントを実装
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

//音声認識用のインテントを作成
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
//認識する言語を指定(この場合は日本語)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.JAPANESE.toString());
//認識する候補数の指定
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 10);
//音声認識時に表示する案内を設定
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "話してください");
//音声認識を開始
startActivityForResult(intent, REQUEST_CODE);
}
});
}

//音声認識が終わると自動で呼び出されるメソッド
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if(requestCode == REQUEST_CODE && resultCode == RESULT_OK) {

//data から音声認識の結果を取り出す(リスト形式で)
ArrayList<String> kekka = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);

//認識結果が一つ以上ある場合はテキストビューに結果を表示する
if (kekka.size() > 0) {
//一番最初にある認識結果を表示する
textView2.setText(kekka.get(0));
} else {
//何らかの原因で音声認識に失敗した場合はエラーメッセージを表示
textView2.setText("音声の認識に失敗しました…");
}
}
}
}

 

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

テキストビュー2つとボタンが1つあるだけのレイアウトで、特に注意する点はありません。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_margin="10dp"
tools:context="test.MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="認識結果"
android:textSize="18sp" />

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/textView"
android:text="結果"
android:textSize="18sp" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="音声認識スタート" />
</RelativeLayout

実行結果 

① 起動直後の画面です。

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

② 「音声認識スタート」ボタンを押すと音声認識が開始されます。音声認識の小画面にはプログラムコード内で設定した「話してください」という案内が表示されます。

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

③ 適当な言葉を話しかけると「認識結果」のすぐ下に話しかけた内容が表示されます。

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

まとめ

音声認識機能はポイントさえ押さえてしまえば意外と簡単に実装できますので、これから音声認識機能を使ったアプリを作ろうと考えている方は、ぜひ今回のサンプルを参考にしてください。 

END

最初からバナー広告の表示スペースを確保した新規プロジェクトを作る方法

広告の後付けでひと苦労してしまう・・・

アプリに表示するバナー広告は、過去の記事にあるようにアプリを作ってからでも「後付け」することができますが、広告用のアドオンを追加したり、マニフェストファイルを変更したり、コードを追加する必要があるため少し面倒です。

また、アプリによっては、すんなりとバナー広告を表示するスペースを確保することができず、デザインの見直しで不要な苦労してしまう場合があります。

完成間近になってからのひと仕事ほどつらいものはありません。

後から広告で苦労しないために

この苦労を回避するのは簡単で、プロジェクトを新規作成する際に、バナー広告を含んだアクティビティ「Google AdMob Ads Activity」を選択し、あらかじめバナー広告のスペースが確保されたレイアウトを自動生成して、それからアプリの中身を作り始めると良いでしょう。

f:id:vw-dsg:20180302094552p:plain
新規プロジェクトを作成するときに広告を含んだアクティビティを選択します。

f:id:vw-dsg:20180302094610p:plain
設定項目をスクロールさせると広告の種類を選択するドロップダウンメニュー「Ad Format」が現れるので、そこで「Banner」を選択します。

f:id:vw-dsg:20180302095308p:plain
新規作成したプロジェクトのレイアウトファイルには、バナー広告のスペースが確保されています。

f:id:vw-dsg:20180302095752p:plain
プロジェクトを実行すると、無事画面の下にインターネット経由で AdMob から取得されたサンプルのバナー広告が表示されました。

まとめ

最初に読んだ Android アプリの参考書でバナー広告を後付けする方法を解説していたこともあって、自分も以前はアプリを作ってからコードを書いてバナー広告を後付けするという方法をとっていましたが、Android Studio で自動生成できることが分かってからはこの方法を使って楽をしています。

最後になってしまいましたが、バナー広告をサンプルから正式なものへ変更する場合は「res/values/strings.xml」の「banner_ad_unit_id」の値(ca-app-pub-3940256099942544/6300978111の部分)を正式に取得した広告IDに変更してください。

 

END

 

Android Developers サイトでキーワードの「検索」ができなくなってしまった時の対処方法

突然「検索機能」が使えなくなる?

アンドロイドの開発者向けサイト(Android Developers)にはキーワードからリファレンスマニュアルを検索する便利な機能がありますが、何かの原因でまったく反応しなくなり検索ができなくなってしまう場合があります。

リファレンスマニュアルは膨大な量ですので、この検索機能が使えなくなってしまうと大きな時間のロスを生み出してしまうでしょう。

※このトラブルはグーグルの「chrome」とマイクロソフトの「Edge」の両方で確認できました。

f:id:vw-dsg:20180226153453j:plain
この検索窓にキーワードを入力しても全く反応しなくなる

対処方法

検索ができなくなってしまった時は、ブラウザの設定から「閲覧履歴」や「Cookieと他のサイトデータ」や「キャッシュされた画像とファイル」などを消去してみてください。

トラブルの原因がここにあれば(というか、通常はここしかない)、検索機能が無事に復活するはずです。

f:id:vw-dsg:20180226155458j:plain
ロームの例。右端のメニューから設定を選択し、設定画面の一番したの「詳細設定」を開きます。その中に「閲覧履歴データを消去する」という項目があるので、それをクリック。

f:id:vw-dsg:20180226155540j:plain
消去するデータの選択画面で、「基本」項目の3つのデータを「全期間」消去します。

f:id:vw-dsg:20180226155603j:plain
その後、アンドロイド開発者サイト(Android Developers)のタブだけ更新すれば、今度は検索窓のキーワード入力に反応するようになります。

f:id:vw-dsg:20180226155620j:plain
これで無事に「検索機能」が復活しました。

このトラブルの原因は?

chrome」と「Edge」どちらのブラウザもキャッシュの消去によって復旧していますので、おそらく、サイトを制御している既存スクリプトの中身が部分的に変更されたことが原因になっていると思いますが、もしかしたら違うかもしれません。

END

 

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

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

今回は「通知」をタップした時に画面(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

画面の「タッチ中」を検出する方法

今回は画面がタッチされている状態を検出する方法です。

ユーザーの画面操作を検出するには「onTouchEvent」メソッドを利用し、この onTouchEvent が受け取る MotionEvent の種類で処理を振り分けます。

 

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

MotionEvent の 「ACTION_DOWN(画面にタッチ)」で、メッセージを「タッチ中!」に変更して、「ACTION_UP(タッチが終わった)」で、メッセージを元に戻しています。 

public class MainActivity extends AppCompatActivity {

private TextView textView1;

//最初に実行されるメソッド
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//レイアウトファイルにあるテキストビューを取得して操作可能にする
textView1 = (TextView)findViewById(R.id.textview1);
//テキストビューに任意の文字を設定
textView1.setText("タッチしてません!");
}

//画面の操作を取得するメソッド
@Override
public boolean onTouchEvent(MotionEvent event) {

//イベントの種類によって処理を振り分ける
switch (event.getAction()) {

//画面にタッチした時の処理
case MotionEvent.ACTION_DOWN: {
textView1.setText("タッチ中!");
break;
}
//画面のタッチが終わった時の処理
case MotionEvent.ACTION_UP: {
textView1.setText("タッチしてません!");
break;
}
}

//returnは最後に書かないとエラーになる
return super.onTouchEvent(event);
}
}

 

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

レイアウトは画面の上下左右の中央に「textview1」というIDの 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.tanronsan.myapplication.MainActivity">

<TextView
android:id="@+id/textview1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@android:color/holo_red_dark"
android:padding="10dp"
android:text="textview1"
android:textColor="@android:color/background_light" />
</RelativeLayout>

 

<実行結果> 

画面にタッチしていない時は画面中央のTextViewに「タッチしてません!」を表示。

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

 

タイトルやOSのメッセージ領域を除いた白い領域をタッチすると「タッチ中!」に変わります。

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

 

画面のタッチを終えるとメッセージが再び「タッチしてません!」に戻ります。

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

ユーザーの画面操作はクリックの検出とは違って、リスナーを登録する必要がないので実装が簡単ですね。

END

 

TextToSpeech の speak で音声読み上げに失敗する原因と対処方法

原因は音声データの未インストール

TextToSpeech の speak で音声読み上げに失敗する原因はいろいろあると思いますが、自分がハマったのは、端末に「音声データ」をインストールしていなかったために、ネットワークを無効(オフライン)にしてしまうと音声データがダウンロードできず、音声の読み上げがエラーではなく「無言」になってしまうというトラブル。

てっきり端末に日本語の「音声データ」くらいはインストールされているとばかり思っていたので、この初歩的なミスに何時間もハマってしまった。

もし、アプリを作っていてオフラインで音声読み上げができない場合は、コードのバグはもちろんですが「音声データ」がインストールされているのかを一度確認した方が良いでしょうね。

設定・音声データのインストール方法

1.設定から「システム」を選択。

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

 

2.「言語と入力」を選択。

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

 

3.一番下の「詳細設定」を選択。

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

 

4.一番下の「テキスト読み上げの出力」を選択。

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

 

5.「優先するエンジン」の右の「ギヤマーク」を選択。

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

 

6.一番上の「音声データをインストール」を選択。

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

 

7.言語の一覧から必要なものを選択。

※テスト端末には、日本語(日本)が既にインストールされているので、サンプルとして「広東語(香港)」を選択しています。

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

 

8.右のダウンロードマークを選択。

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

 

9.ダウンロードとインストールが終わると「ダウンロードマーク」が「ゴミ箱マーク」に変わる。不要になった場合はこの「ゴミ箱マーク」を選択すれば音声言語データは削除されます。

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

以上です。

何度確認してもコードは間違っていないのに音声読み上げが実行されない・・・という場合は、一度端末にインストールされている「音声データ」を確認してみると良いかもしれません。

 

END

Android Studio 3.0 にアップデートしてレイアウトXMLのプレビューに不具合がでた場合の対処方法

問題の内容

Android Studio のバージョンを「3.0」にアップデートしたところ、レイアウトXMLファイルのプレビューがグレーだけになってしまい、プレビューしながらの編集できなくなってしまった。※ちなみに、テキスト表示による編集は可能。

f:id:vw-dsg:20171030154740j:plain
プレビューがグレーに塗りつぶされ、中央に「android...ActionBarOverlayLayout」というメッセージがある。

対処方法

調べたところ「styles.xml」を少し変更することでプレビュー表示が復旧することが判明。それで対処して無事レイアウトXMLファイルのプレビュー表示が復活できた。

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

「styles.xml」ファイルの <style> タグの中を以下のように変更する。具体的には「Theme.AppCompat.Light.DarkActionBar」の頭に「Base.」を付け加える

<変更前>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

              ↓↓↓↓↓↓↓↓↓

<変更後>

<style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">

以上

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

もう一度レイアウトファイルを確認してみると、無事レイアウトXMLファイルのプレビューが復活していました。

END