Symfoware

Symfowareについての考察blog

Android ローカルストレージにアクセスし、画像ファイルを表示する

Androidで端末内に保存されている画像ファイルのサムネールを表示してみたいと思います。


こちらを参考にしました。
[Android] MediaStore スマホの画像を検索
初心者のためのM PERMISSIONS入門
Android 6.0のRuntime Permissionに対応する
MediaStore - Uri to query all types of files (media and non-media)




アクセス許可




ローカルストレージにアクセスするには、android.permission.READ_EXTERNAL_STORAGEの指定と、
ユーザーの許可が必要です。

まず、AndroidManifest.xmlにパーミッションの指定を追加。


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3.     package="com.example.baranche.myapplication">
  4.     <uses-permission android:name="android.permission.INTERNET" />
  5.     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  6.     <application
  7.         android:allowBackup="true"
  8.         android:icon="@mipmap/ic_launcher"
  9.         android:label="テスト"
  10.         android:roundIcon="@mipmap/ic_launcher_round"
  11.         android:supportsRtl="true"
  12.         android:theme="@style/AppTheme">
  13.         <activity android:name=".MainActivity">
  14.             <intent-filter>
  15.                 <action android:name="android.intent.action.MAIN" />
  16.                 <category android:name="android.intent.category.LAUNCHER" />
  17.             </intent-filter>
  18.         </activity>
  19.     </application>
  20. </manifest>



「uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"」が追加した箇所です。



次に、ローカルストレージへアクセスする前に、許可されているか。
許可されていなければ、requestPermissionsでストレージへの許可を取得します。


  1. import android.Manifest;
  2. import android.annotation.TargetApi;
  3. import android.content.pm.PackageManager;
  4. import android.os.Build;
  5. import android.support.v7.app.AppCompatActivity;
  6. import android.os.Bundle;
  7. import android.view.View;
  8. import android.widget.Toast;
  9. public class MainActivity extends AppCompatActivity {
  10.     // この定数は0以上の値をアプリ内で適当に決めて良い
  11.     private final int EXTERNAL_STORAGE_REQUEST_CODE = 1;
  12.     @Override
  13.     protected void onCreate(Bundle savedInstanceState) {
  14.         super.onCreate(savedInstanceState);
  15.         setContentView(R.layout.activity_main);
  16.     }
  17.     public void show(View view) {
  18.         checkPermission();
  19.     }
  20.     // Permissionの確認
  21.     @TargetApi(Build.VERSION_CODES.M)
  22.     public void checkPermission() {
  23.         // 既に許可している
  24.         if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED) {
  25.             readLocalStrage();
  26.             return;
  27.         }
  28.         // 許可していない場合、パーミッションの取得を行う
  29.         // 以前拒否されている場合は、なぜ必要かを通知し、手動で許可してもらう
  30.         if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  31.             Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT).show();
  32.         }
  33.         // パーミッションの取得を依頼
  34.         requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
  35.     }
  36.     // 結果の受け取り
  37.     @Override
  38.     @TargetApi(Build.VERSION_CODES.M)
  39.     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  40.         // requestPermissionsの引数に指定した値が、requestCodeで返却される
  41.         if (requestCode != EXTERNAL_STORAGE_REQUEST_CODE) {
  42.             return;
  43.         }
  44.         // 自分がリクエストしたコードで戻ってきた場合
  45.         // 使用が許可された
  46.         if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  47.             // ローカルファイルの読み取り処理実行
  48.             readLocalStrage();
  49.             return;
  50.         }
  51.         // 拒否されたが永続的ではない場合
  52.         if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  53.             Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT).show();
  54.             // パーミッションの取得を依頼
  55.             requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
  56.             return;
  57.         }
  58.         // 永続的に拒否された場合
  59.         Toast.makeText(this, "許可されないとアプリが実行できません\nアプリ設定>権限をチェックしてください", Toast.LENGTH_SHORT).show();
  60.     }
  61.     // ローカルストレージの読み取り処理
  62.     private void readLocalStrage() {
  63.     }
  64. }




requestPermissionsを実行すると、確認ダイアログが表示されます。

779_01.png


許可か拒否をクリックすると、onRequestPermissionsResultが呼び出されます。
requestCodeには、requestPermissionsで指定したIDが入っているので、呼び出した時と同じIDかチェック。

grantResults[0]がダイアログで選択された値の結果。
PackageManager.PERMISSION_GRANTEDの場合は許可されたので、処理を続行。

許可されなかった場合は、shouldShowRequestPermissionRationaleの戻り値を確認しています。

これがtrueの場合は、単に拒否しただけ。
falseの場合には、「再度表示しない」にチェックがつけられた状態で拒否したパターンになるようです。

「再度表示しない」をチェックして拒否された場合、アプリから再度許可のリクエストを行うことはできず、
ユーザーに設定画面から許可してもらわないといけないようです。

※一度設定した内容が端末内に保存されるようで、アンインストールしても失われない模様。
検証が難しい。


設定を変更するには、設定画面を開き、

779_02.png

アプリケーションの項目を選択。

779_03.png

設定を変更したいアプリケーションを選択。

779_04.png

パーミッションを選択。

779_05.png

これでやっと設定変更箇所にたどり着きます。

779_06.png





ContentResolver



ローカルストレージなどには、ContentResolverを介してアクセスします。
アクセスはデータベースと似た感じで、検索クエリーを実行してカーソルを取得。
カーソルをフェッチしながら中身を取り出すイメージです。


ContentResolver#query(取得領域, 取得するフィールド, 検索条件, 検索条件の値, ソート順)

のようにしてクエリーを実行します。
取得領域以外、nullを指定すると全件取得となるようです。


  1.         ContentResolver contentResolver = getContentResolver();
  2.         Cursor cursor = null;
  3.         String[] projection = null; // 取得するフィールド
  4.         String selection = null; // 検索条件
  5.         String[] selectionArgs = null; // 検索条件の値
  6.         String sortOrder = null; // ソート順
  7.         try {
  8.             cursor = contentResolver.query(
  9.                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder);
  10.         } catch (Exception e) {
  11.             e.printStackTrace();
  12.             Toast.makeText(this, "例外が発生、Permissionを許可していますか?", Toast.LENGTH_SHORT).show();
  13.             return;
  14.         }





検索条件を指定する例として、
・フィールドは、_ID(primary key)、DATA(ファイルパス)、WIDTH(画像の幅)、HEIGHT(画像の高さ)、SIZE(容量)を取得
・ファイルタイプはjpegのみ
・最新順
で取得してみます。


  1.         ContentResolver contentResolver = getContentResolver();
  2.         Cursor cursor = null;
  3.         // 取得するフィールド
  4.         String[] projection = {
  5.                 MediaStore.Images.Media._ID,
  6.                 MediaStore.Images.Media.DATA,
  7.                 MediaStore.Images.Media.WIDTH,
  8.                 MediaStore.Images.Media.HEIGHT,
  9.                 MediaStore.Images.Media.SIZE
  10.         };
  11.         // 検索条件
  12.         // mimetypeで検索 ?はプレースフォルダーで、値はselectionArgsで指定
  13.         String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";;
  14.         // 検索条件に指定する値
  15.         // まずjpgのmimetypeを取得
  16.         String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg");
  17.         // 検索条件の値として設定
  18.         String[] selectionArgs = new String[]{ mimeType };
  19.         // _IDの降順でソート
  20.         String sortOrder = MediaStore.Images.Media._ID + " desc";
  21.         try {
  22.             cursor = contentResolver.query(
  23.                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder);
  24.         } catch (Exception e) {
  25.             e.printStackTrace();
  26.             Toast.makeText(this, "例外が発生、Permissionを許可していますか?", Toast.LENGTH_SHORT).show();
  27.             return;
  28.         }









ContentProviderClient



今回は、1回のクエリ実行で全件データを取得していますが、データ件数が少ない場合は
ContentProviderClientを使用したほうが良いようです。

Android:高速化。ContentResolver?ContentProviderClient?

全件フェッチしてクローズする処理なので、今回は使用を見送りました。




サムネールの作成



MediaStore.Images.Thumbnails.getThumbnail(ContentResolver, _ID, 種類, BitmapFactory.Options)

これでサムネール画像が取得できます。
これらを総合して、ボタンをクリックしたらサムネールの一覧を表示するプログラムは以下のようになりました。

・activity_mail.xml


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout
  3.     xmlns:android="http://schemas.android.com/apk/res/android"
  4.     xmlns:app="http://schemas.android.com/apk/res-auto"
  5.     xmlns:tools="http://schemas.android.com/tools"
  6.     android:layout_width="match_parent"
  7.     android:layout_height="match_parent"
  8.     android:orientation="vertical"
  9.     >
  10.     <LinearLayout
  11.         android:layout_width="match_parent"
  12.         android:layout_height="wrap_content"
  13.         android:orientation="horizontal">
  14.         <Button
  15.             android:layout_width="wrap_content"
  16.             android:layout_height="wrap_content"
  17.             android:text="表示"
  18.             android:onClick="show" />
  19.     </LinearLayout>
  20.     <GridView
  21.         android:id="@+id/gridView1"
  22.         android:layout_width="match_parent"
  23.         android:layout_height="match_parent"
  24.         android:columnWidth="100dp"
  25.         android:horizontalSpacing="1dp"
  26.         android:verticalSpacing="1dp"
  27.         android:numColumns="auto_fit"
  28.         android:stretchMode="columnWidth"
  29.         android:gravity="center">
  30.     </GridView>
  31. </LinearLayout>





・MainActivity.java


  1. import android.Manifest;
  2. import android.annotation.TargetApi;
  3. import android.content.ContentResolver;
  4. import android.content.pm.PackageManager;
  5. import android.database.Cursor;
  6. import android.os.Build;
  7. import android.provider.MediaStore;
  8. import android.support.v7.app.AppCompatActivity;
  9. import android.os.Bundle;
  10. import android.view.View;
  11. import android.webkit.MimeTypeMap;
  12. import android.widget.GridView;
  13. import android.widget.Toast;
  14. import java.util.ArrayList;
  15. import java.util.List;
  16. public class MainActivity extends AppCompatActivity {
  17.     // この定数は0以上の値をアプリ内で適当に決めて良い
  18.     private final int EXTERNAL_STORAGE_REQUEST_CODE = 1;
  19.     @Override
  20.     protected void onCreate(Bundle savedInstanceState) {
  21.         super.onCreate(savedInstanceState);
  22.         setContentView(R.layout.activity_main);
  23.     }
  24.     public void show(View view) {
  25.         checkPermission();
  26.     }
  27.     // Permissionの確認
  28.     @TargetApi(Build.VERSION_CODES.M)
  29.     public void checkPermission() {
  30.         // 既に許可している
  31.         if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED) {
  32.             readLocalStrage();
  33.             return;
  34.         }
  35.         // 許可していない場合、パーミッションの取得を行う
  36.         // 以前拒否されている場合は、なぜ必要かを通知し、手動で許可してもらう
  37.         if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  38.             Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT).show();
  39.         }
  40.         // パーミッションの取得を依頼
  41.         requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
  42.     }
  43.     // 結果の受け取り
  44.     @Override
  45.     @TargetApi(Build.VERSION_CODES.M)
  46.     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  47.         // requestPermissionsの引数に指定した値が、requestCodeで返却される
  48.         if (requestCode != EXTERNAL_STORAGE_REQUEST_CODE) {
  49.             return;
  50.         }
  51.         // 自分がリクエストしたコードで戻ってきた場合
  52.         // 使用が許可された
  53.         if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  54.             // ローカルファイルの読み取り処理実行
  55.             readLocalStrage();
  56.             return;
  57.         }
  58.         // 拒否されたが永続的ではない場合
  59.         if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  60.             Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT).show();
  61.             // パーミッションの取得を依頼
  62.             requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
  63.             return;
  64.         }
  65.         // 永続的に拒否された場合
  66.         Toast.makeText(this, "許可されないとアプリが実行できません\nアプリ設定>権限をチェックしてください", Toast.LENGTH_SHORT).show();
  67.     }
  68.     // ローカルストレージの読み取り処理
  69.     private void readLocalStrage() {
  70.         ContentResolver contentResolver = getContentResolver();
  71.         Cursor cursor = null;
  72.         // 取得するフィールド
  73.         String[] projection = {
  74.                 MediaStore.Images.Media._ID,
  75.                 MediaStore.Images.Media.DATA,
  76.                 MediaStore.Images.Media.WIDTH,
  77.                 MediaStore.Images.Media.HEIGHT,
  78.                 MediaStore.Images.Media.SIZE
  79.         };
  80.         // 検索条件
  81.         // mimetypeで検索 ?はプレースフォルダーで、値はselectionArgsで指定
  82.         String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";;
  83.         // 検索条件に指定する値
  84.         // まずjpgのmimetypeを取得
  85.         String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg");
  86.         // 検索条件の値として設定
  87.         String[] selectionArgs = new String[]{ mimeType };
  88.         // _IDの降順でソート
  89.         String sortOrder = MediaStore.Images.Media._ID + " desc";
  90.         try {
  91.             cursor = contentResolver.query(
  92.                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder);
  93.         } catch (Exception e) {
  94.             e.printStackTrace();
  95.             Toast.makeText(this, "例外が発生、Permissionを許可していますか?", Toast.LENGTH_SHORT).show();
  96.             return;
  97.         }
  98.         if (cursor == null) {
  99.             return;
  100.         }
  101.         List<Bundle> rows = new ArrayList<>();
  102.         while (cursor.moveToNext()) {
  103.             // カーソルから値を取り出し
  104.             String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
  105.             long _id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
  106.             // ※必要なら、ここでサイズや容量のチェックも実行できる
  107.             // arrayに転送
  108.             Bundle bundle = new Bundle();
  109.             bundle.putLong("_id", _id);
  110.             bundle.putString("path", filePath);
  111.             rows.add(bundle);
  112.         }
  113.         cursor.close();
  114.         // データを表示
  115.         MyGridViewAdapter adapter = new MyGridViewAdapter(this, rows);
  116.         GridView gv = (GridView)findViewById(R.id.gridView1);
  117.         gv.setAdapter(adapter);
  118.     }
  119. }





・MyGridViewAdapter


  1. import android.content.ContentResolver;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.os.Bundle;
  5. import android.provider.MediaStore;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.ViewGroup;
  9. import android.widget.BaseAdapter;
  10. import android.widget.ImageView;
  11. import java.util.List;
  12. public class MyGridViewAdapter extends BaseAdapter {
  13.     private Context mContext;
  14.     private ContentResolver mContentResolver;
  15.     private LayoutInflater mRayoutInflater = null;
  16.     private List<Bundle> mList;
  17.     public MyGridViewAdapter(Context context, List<Bundle> list) {
  18.         this.mContext = context;
  19.         this.mContentResolver = this.mContext.getContentResolver();
  20.         this.mList = list;
  21.     }
  22.     @Override
  23.     public int getCount() {
  24.         if (mList == null) {
  25.             return 0;
  26.         }
  27.         return mList.size();
  28.     }
  29.     @Override
  30.     public Object getItem(int position) {
  31.         return mList.get(position);
  32.     }
  33.     @Override
  34.     public long getItemId(int position) {
  35.         return position;
  36.     }
  37.     @Override
  38.     public View getView(int i, View convertView, ViewGroup viewGroup) {
  39.         View view;
  40.         if (convertView != null) {
  41.             view = convertView;
  42.         }
  43.         else {
  44.             view = new ImageView(mContext);
  45.         }
  46.         ImageView thumbnail = (ImageView)view;
  47.         Bundle item = mList.get(i);
  48.         long _id = item.getLong("_id");
  49.         Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, _id, MediaStore.Images.Thumbnails.MINI_KIND, null);
  50.         thumbnail.setImageBitmap(bmp);
  51.         return view;
  52.     }
  53. }




実行結果

779_07.png

ちゃんとダウンロードした画像やカメラで撮影した画像が表示できました。




【参考URL】

[Android] MediaStore スマホの画像を検索
初心者のためのM PERMISSIONS入門
Android 6.0のRuntime Permissionに対応する
MediaStore - Uri to query all types of files (media and non-media)

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/19(土) 19:32:14|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Android Studio デバッグ時のAndroid Monitorが消えた場合

Android Studioでデバッグ時するとき、Android Monitorをよく見ているのですが、
気がついたら表示されなくなっていました。

778_01.png


辛い。

なんで消えたんだろ?と思っていたのですが、ふと左下のアイコンをクリックすると...

778_02.png


表示が隠れていただけでした。

778_03.png

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/17(木) 23:37:58|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Android いろいろな通知の表示方法(Toast, Snackbar, Dialog)

Androidで使える通知の方法をまとめてみました。


Toast



「保存しました」のような通知に使える方法だと思います。


  1. import android.support.v7.app.AppCompatActivity;
  2. import android.os.Bundle;
  3. import android.view.View;
  4. import android.widget.Toast;
  5. public class MainActivity extends AppCompatActivity {
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.activity_main);
  10.     }
  11.     public void show(View view) {
  12.         // 通知を表示
  13.         Toast.makeText(this, "テスト", Toast.LENGTH_LONG).show();
  14.     }
  15. }




こんな感じで表示されます。

777_01.png


長い文字列でも問題なし。

777_02.png


文字列に改行コードを入れれば、表示も改行されます。


  1. import android.support.v7.app.AppCompatActivity;
  2. import android.os.Bundle;
  3. import android.view.View;
  4. import android.widget.Toast;
  5. public class MainActivity extends AppCompatActivity {
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.activity_main);
  10.     }
  11.     public void show(View view) {
  12.         // 通知を表示
  13.         Toast.makeText(this, "祇園精舎の鐘の声\n諸行無常の響きあり\n沙羅双樹の花の色\n盛者必衰の理をあらわす", Toast.LENGTH_LONG).show();
  14.     }
  15. }



777_03.png


Toast.LENGTH_LONGで長時間、
Toast.LENGTH_SHORTで短時間表示となります。






Snackbar



Android の AppWidget で Snackbar を使う
こちらを参考にしました。
下に通知領域を表示します。

追加でライブラリが必要なので、gradleファイルに
「compile 'com.android.support:design:+'」
を追記。


  1. apply plugin: 'com.android.application'
  2. android {
  3.     compileSdkVersion 25
  4.     buildToolsVersion "26.0.1"
  5.     defaultConfig {
  6.         applicationId "com.example.baranche.myapplication"
  7.         minSdkVersion 21
  8.         targetSdkVersion 25
  9.         versionCode 1
  10.         versionName "1.0"
  11.         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  12.     }
  13.     buildTypes {
  14.         release {
  15.             minifyEnabled false
  16.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  17.         }
  18.     }
  19.     productFlavors {
  20.     }
  21. }
  22. dependencies {
  23.     compile fileTree(include: ['*.jar'], dir: 'libs')
  24.     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
  25.         exclude group: 'com.android.support', module: 'support-annotations'
  26.     })
  27.     compile 'com.android.support:appcompat-v7:25.+'
  28.     compile 'com.android.support.constraint:constraint-layout:1.0.2'
  29.     compile 'com.android.support:design:+'
  30.     compile 'com.android.volley:volley:1.0.0'
  31.     testCompile 'junit:junit:4.12'
  32.     testCompile 'org.robolectric:robolectric:3.4.2'
  33. }




プログラムサンプルです。


  1. import android.support.design.widget.Snackbar;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. public class MainActivity extends AppCompatActivity {
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.activity_main);
  10.     }
  11.     public void show(View view) {
  12.         // 通知を表示
  13.         Snackbar.make(view, "テスト", Snackbar.LENGTH_LONG).show();
  14.     }
  15. }



777_04.png


setActionでボタンを追加できます。


  1. import android.support.design.widget.Snackbar;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.activity_main);
  10.     }
  11.     public void show(View view) {
  12.         // 通知を表示
  13.         Snackbar.make(view, "テスト", Snackbar.LENGTH_LONG)
  14.                 .setAction("Action", this)
  15.                 .show();
  16.     }
  17.     @Override
  18.     public void onClick(View v) {
  19.         System.out.println("Actionクリック");
  20.     }
  21. }



777_05.png


それなりに長い文字列の表示。

777_06.png


改行すると、2行めまで表示されました。

777_07.png






Dialog




コピペしてすぐ使えるアラートダイアログ集
こちらを参考にしました。

サンプルはこんな感じになります。


  1. import android.content.DialogInterface;
  2. import android.support.v7.app.AlertDialog;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. public class MainActivity extends AppCompatActivity implements DialogInterface.OnClickListener {
  7.     @Override
  8.     protected void onCreate(Bundle savedInstanceState) {
  9.         super.onCreate(savedInstanceState);
  10.         setContentView(R.layout.activity_main);
  11.     }
  12.     public void show(View view) {
  13.         // 通知を表示
  14.         new AlertDialog.Builder(this)
  15.                 .setTitle("title")
  16.                 .setMessage("テスト")
  17.                 .setPositiveButton("OK", this)
  18.                 .show();
  19.     }
  20.     @Override
  21.     public void onClick(DialogInterface dialog, int which) {
  22.         System.out.println("okクリック");
  23.     }
  24. }



777_08.png


それなりに長い文字列。

777_09.png


改行もOKです。

777_10.png


テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/17(木) 23:14:39|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Bundleをテストで使用すると「Method putString in android.os.BaseBundle not mocked」

JSONObjectを使用したテストを実行してみました。
Android Studio JSONObjectをテストで使用すると「Method get in org.json.JSONObject not mocked」

続けて、Bundleを使用したメソッドのテストを実行したところこんなエラーが


java.lang.RuntimeException: Method putString in android.os.BaseBundle not mocked.
See http://g.co/androidstudio/not-mocked for details.

    at android.os.BaseBundle.putString(BaseBundle.java)
    at com.example.baranche.myapplication.ExampleUnitTest.test_bundle(ExampleUnitTest.java:35)

(略)






robolectric



こちらが参考になりました。
https://stackoverflow.com/questions/29100925/unit-testing-on-android-studio-not-mocked-error


robolectricを使えばOKとのこと

https://github.com/robolectric/robolectric


build.gradleを修正します。


  1. apply plugin: 'com.android.application'
  2. android {
  3.     compileSdkVersion 25
  4.     buildToolsVersion "26.0.1"
  5.     defaultConfig {
  6.         applicationId "com.example.baranche.myapplication"
  7.         minSdkVersion 21
  8.         targetSdkVersion 25
  9.         versionCode 1
  10.         versionName "1.0"
  11.         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  12.     }
  13.     buildTypes {
  14.         release {
  15.             minifyEnabled false
  16.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  17.         }
  18.     }
  19.     productFlavors {
  20.     }
  21. }
  22. dependencies {
  23.     compile fileTree(include: ['*.jar'], dir: 'libs')
  24.     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
  25.         exclude group: 'com.android.support', module: 'support-annotations'
  26.     })
  27.     compile 'com.android.support:appcompat-v7:25.+'
  28.     compile 'com.android.support.constraint:constraint-layout:1.0.2'
  29.     compile 'com.android.volley:volley:1.0.0'
  30.     testCompile 'junit:junit:4.12'
  31.     testCompile "org.robolectric:robolectric:3.4.2"
  32. }




「testCompile "org.robolectric:robolectric:3.4.2"」の行を追加。


テストプログラムはこうなりました。


  1. import android.os.Bundle;
  2. import org.json.JSONObject;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.robolectric.RobolectricTestRunner;
  6. import static org.junit.Assert.assertEquals;
  7. @RunWith(RobolectricTestRunner.class)
  8. public class ExampleUnitTest {
  9.     @Test
  10.     public void test_json() throws Exception {
  11.         JSONObject json = new JSONObject("{\"key\" : \"value\"}");
  12.         assertEquals("value", json.get("key"));
  13.     }
  14.     @Test
  15.     public void test_bundle() throws Exception {
  16.         Bundle bundle = new Bundle();
  17.         bundle.putString("key", "value");
  18.         assertEquals("value", bundle.getString("key"));
  19.     }
  20. }




無事テストが成功しました。
※初回の実行時、テスト用ライブラリのビルドが行われるようで時間がかかります。

robolectricを使用すれば、別途JSONライブラリを指定していなくてもJSONObjectのテストも実行できます。
これは便利。


テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/15(火) 23:16:51|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Android Studio JSONObjectをテストで使用すると「Method get in org.json.JSONObject not mocked」

Androidのプログラムで、JSONObjectを操作する汎用的なメソッドを作成しました。

テストコードを書いて実行しようとすると

776_01.png


java.lang.RuntimeException: Method get in org.json.JSONObject not mocked.
See http://g.co/androidstudio/not-mocked for details.

    at org.json.JSONObject.get(JSONObject.java)
    at com.example.baranche.myapplication.ExampleUnitTest.addition_isCorrect(ExampleUnitTest.java:19)

(略)


Process finished with exit code 255






テスト実行時に使用されるライブラリ




どうも、テスト実行時に読み込まれるライブラリはエミュレーター等で実行される時に
読み込まれるライブラリと異なるようです。

Android unit test not mocked

こちらを参考に、build.gradleを修正。


apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.example.baranche.myapplication"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors {
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.android.volley:volley:1.0.0'
    testCompile 'junit:junit:4.12'
    testCompile 'org.json:json:20140107'
}





最後の「testCompile 'org.json:json:20140107'」を追記しました。


これでJSONObjectを使用したテストも通るようになりました。
例)


  1. public class ExampleUnitTest {
  2.     @Test
  3.     public void test_json() throws Exception {
  4.         JSONObject json = new JSONObject("{\"key\" : \"value\"}");
  5.         assertEquals("value", json.get("key"));
  6.     }
  7.     
  8. }



テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/08/15(火) 22:58:39|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ