Symfoware

Symfowareについての考察blog

Android 入力値不正なときに警告を表示する(validation)

Androidアプリで入力データをチェックし、警告を表示したい。


setError



こちらが参考になりました。
Edittext Validation in Android Example

setErrorというメソッドにエラー内容を設定すればOK


・activity_main.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.     <EditText
  11.         android:id="@+id/editText"
  12.         android:layout_width="match_parent"
  13.         android:layout_height="wrap_content"
  14.         android:ems="10"
  15.         android:hint="デフォルトで表示されるテキスト"
  16.         android:padding="12dp" />
  17.     <Button
  18.         android:layout_width="wrap_content"
  19.         android:layout_height="wrap_content"
  20.         android:text="テスト"
  21.         android:onClick="test" />
  22. </LinearLayout>




・MainActivity.java


  1. import android.support.v7.app.AppCompatActivity;
  2. import android.os.Bundle;
  3. import android.view.View;
  4. import android.widget.EditText;
  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 test(View view) {
  12.         EditText text = (EditText)findViewById(R.id.editText);
  13.         if(text.getText().toString().isEmpty()) {
  14.             text.setError("文字を入力してください");
  15.         }
  16.     }
  17. }




実行結果

784_01.png


なるほど。



ライブラリ



探してみると、バリデーション用のライブラリがいくつか作成されているようです。

Android における EditText のクールなバリデーション実装

AwesomeValidation


大掛かりなアプリ開発では、こういったライブラリを導入したほうが楽できそうです。

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

  1. 2017/08/20(日) 18:33:46|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Android 戻るボタンを押した時、積み上げたBackStackを1つ飛ばした画面に戻りたい

初期画面から新規登録画面へ遷移。
新規登録後、リスト表示画面へ遷移。
リスト表示画面から戻るボタンを押された時、初期画面に戻ってほしいのに新規登録画面が表示されて困った。


初期表示:Fragment1
次の表示:Fragment2
更に次の表示:Fragment3

この状態で戻るボタンを押した時Fragment1に戻したい。



popBackStack



Skip some fragments onBackPressed
こちらが参考になりそうなのですが、理屈がよくわからん。

作成していたプログラムはこんな感じ。


  1. import android.support.v4.app.Fragment;
  2. import android.support.v4.app.FragmentManager;
  3. import android.support.v4.app.FragmentTransaction;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.os.Bundle;
  6. import android.view.View;
  7. public class MainActivity extends AppCompatActivity {
  8.     @Override
  9.     protected void onCreate(Bundle savedInstanceState) {
  10.         super.onCreate(savedInstanceState);
  11.         setContentView(R.layout.activity_main);
  12.         // 初期表示はFragment1
  13.         FragmentManager fragmentManager = getSupportFragmentManager();
  14.         FragmentTransaction transaction = fragmentManager.beginTransaction();
  15.         Fragment newFragment = new Fragment1();
  16.         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
  17.         transaction.add(R.id.content, newFragment);
  18.         transaction.commit();
  19.     }
  20.     public void show2(View view) {
  21.         // Fragment1で次へが押されるとFragment2を表示
  22.         FragmentManager fragmentManager = getSupportFragmentManager();
  23.         FragmentTransaction transaction = fragmentManager.beginTransaction();
  24.         Fragment newFragment = new Fragment2();
  25.         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
  26.         transaction.replace(R.id.content, newFragment);
  27.         // backstackに追加
  28.         transaction.addToBackStack(null);
  29.         transaction.commit();
  30.     }
  31.     public void show3(View view) {
  32.         // Fragment2で次へが押されるとFragment3を表示
  33.         FragmentManager fragmentManager = getSupportFragmentManager();
  34.         FragmentTransaction transaction = fragmentManager.beginTransaction();
  35.         Fragment newFragment = new Fragment3();
  36.         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
  37.         transaction.replace(R.id.content, newFragment);
  38.         // backstackに追加
  39.         transaction.addToBackStack(null);
  40.         transaction.commit();
  41.     }
  42. }




画面遷移

783_01.png

783_02.png

783_03.png


VIEW3の時、戻るボタンを押したらVIEW1にしたい。


試行錯誤の末、理解したのは

VIEW3を表示する前に、popBackStackで1つ戻す。
これでStackにはVIEW2が積まれていない状態となる。
VIEW3を表示してStackに積む。
戻るを押したら、VIEW2の存在はStackから消えているので、VIEW1に戻ることになる。


  1. import android.support.v4.app.Fragment;
  2. import android.support.v4.app.FragmentManager;
  3. import android.support.v4.app.FragmentTransaction;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.os.Bundle;
  6. import android.view.View;
  7. public class MainActivity extends AppCompatActivity {
  8.     @Override
  9.     protected void onCreate(Bundle savedInstanceState) {
  10.         super.onCreate(savedInstanceState);
  11.         setContentView(R.layout.activity_main);
  12.         // 初期表示はFragment1
  13.         FragmentManager fragmentManager = getSupportFragmentManager();
  14.         FragmentTransaction transaction = fragmentManager.beginTransaction();
  15.         Fragment newFragment = new Fragment1();
  16.         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
  17.         transaction.add(R.id.content, newFragment);
  18.         transaction.commit();
  19.     }
  20.     public void show2(View view) {
  21.         // Fragment1で次へが押されるとFragment2を表示
  22.         FragmentManager fragmentManager = getSupportFragmentManager();
  23.         FragmentTransaction transaction = fragmentManager.beginTransaction();
  24.         Fragment newFragment = new Fragment2();
  25.         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
  26.         transaction.replace(R.id.content, newFragment);
  27.         // backstackに追加
  28.         transaction.addToBackStack(null);
  29.         transaction.commit();
  30.     }
  31.     public void show3(View view) {
  32.         // Fragment2で次へが押されるとFragment3を表示
  33.         FragmentManager fragmentManager = getSupportFragmentManager();
  34.         FragmentTransaction transaction = fragmentManager.beginTransaction();
  35.         // 追加する前に一つ戻って、Fragment1が積まれた状態にしておく
  36.         // これでFragment2は消える
  37.         fragmentManager.popBackStack();
  38.         Fragment newFragment = new Fragment3();
  39.         transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
  40.         transaction.replace(R.id.content, newFragment);
  41.         // backstackに追加
  42.         transaction.addToBackStack(null);
  43.         transaction.commit();
  44.     }
  45. }




これで狙い通りの動きになりました。



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

  1. 2017/08/20(日) 18:09:47|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Volleyを使用したファイルのアップロード

Android + Volleyで画像ファイルのアップロードを行ってみます。

Android + Volleyでローカルファイルの読み取りはこちらで行ってみました。
Android ローカルストレージにアクセスし、画像ファイルを表示する
Android 画像のサムネールを正方形に繰り抜いて表示する
Volleyによるローカルファイル(サムネール画像)のキャッシュ


アップロード方式



データを受け取るプログラムはPHPで書いてみようと思います。
最初、fileのpostを想定していたのですが、地味にめんどくさそう。

How to upload file using Volley library in android?

boundaryを自前で実装しろとのこと。
Upload file with Multipart Request Volley Android

これはちょっとしんどいなと思ったので、bodyにファイルのバイナリデータを指定する方法で試してみることにしました。



サンプル




StringRequestを継承。
getBodyメソッドをオーバーライドして、アップロードしたいファイルのバイト配列を返すようにしてやります。

ソースの抜粋はこのようになりました。


  1.     public void upload(View view) {
  2.         GridView gv = (GridView)findViewById(R.id.gridView1);
  3.         MyGridViewAdapter adapter = (MyGridViewAdapter)gv.getAdapter();
  4.         // 表示しているデータの先頭を取得
  5.         Bundle bundle = (Bundle)adapter.getItem(0);
  6.         final String filePath = bundle.getString("path");
  7.         String url = "http://192.168.1.103:8000/upload.php";
  8.         StringRequest sr = new StringRequest(
  9.                 Request.Method.POST, url,
  10.                 new Response.Listener<String>() {
  11.                     @Override
  12.                     public void onResponse(String response) {
  13.                         showInfo();
  14.                     }
  15.                 },
  16.                 new Response.ErrorListener() {
  17.                     @Override
  18.                     public void onErrorResponse(VolleyError error) {
  19.                         error.printStackTrace();
  20.                     }
  21.                 }
  22.         )
  23.         {
  24.             @Override
  25.             public String getBodyContentType() {
  26.                 return "image/jpeg";
  27.             }
  28.             @Override
  29.             public byte[] getBody() {
  30.                 byte[] fileBytes = null;
  31.                 byte[] b = new byte[8192];
  32.                 try {
  33.                     FileInputStream fis = new FileInputStream(filePath);
  34.                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  35.                     while (fis.read(b) > 0) {
  36.                         baos.write(b);
  37.                     }
  38.                     baos.close();
  39.                     fis.close();
  40.                     fileBytes = baos.toByteArray();
  41.                 } catch (Exception e) {
  42.                     e.printStackTrace();;
  43.                 }
  44.                 return fileBytes;
  45.             }
  46.         };
  47.         RequestQueue queue = VolleySingleton.getInstance(this).getRequestQueue();
  48.         queue.add(sr);
  49.     }
  50.     private void showInfo() {
  51.         Toast.makeText(this, "アップロード完了", Toast.LENGTH_SHORT).show();
  52.     }







ソースの全体像

・activity_main.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.         <Button
  20.             android:layout_width="wrap_content"
  21.             android:layout_height="wrap_content"
  22.             android:text="アップロード"
  23.             android:onClick="upload" />
  24.     </LinearLayout>
  25.     <GridView
  26.         android:id="@+id/gridView1"
  27.         android:layout_width="match_parent"
  28.         android:layout_height="match_parent"
  29.         android:columnWidth="100dp"
  30.         android:horizontalSpacing="1dp"
  31.         android:verticalSpacing="1dp"
  32.         android:numColumns="auto_fit"
  33.         android:stretchMode="columnWidth"
  34.         android:gravity="center">
  35.     </GridView>
  36. </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 com.android.volley.Request;
  15. import com.android.volley.RequestQueue;
  16. import com.android.volley.Response;
  17. import com.android.volley.VolleyError;
  18. import com.android.volley.toolbox.StringRequest;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.File;
  21. import java.io.FileInputStream;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24. public class MainActivity extends AppCompatActivity {
  25.     // この定数は0以上の値をアプリ内で適当に決めて良い
  26.     private final int EXTERNAL_STORAGE_REQUEST_CODE = 1;
  27.     @Override
  28.     protected void onCreate(Bundle savedInstanceState) {
  29.         super.onCreate(savedInstanceState);
  30.         setContentView(R.layout.activity_main);
  31.     }
  32.     public void show(View view) {
  33.         checkPermission();
  34.     }
  35.     // Permissionの確認
  36.     @TargetApi(Build.VERSION_CODES.M)
  37.     public void checkPermission() {
  38.         // 既に許可している
  39.         if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED) {
  40.             readLocalStrage();
  41.             return;
  42.         }
  43.         // 許可していない場合、パーミッションの取得を行う
  44.         // 以前拒否されている場合は、なぜ必要かを通知し、手動で許可してもらう
  45.         if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  46.             Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT).show();
  47.         }
  48.         // パーミッションの取得を依頼
  49.         requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
  50.     }
  51.     // 結果の受け取り
  52.     @Override
  53.     @TargetApi(Build.VERSION_CODES.M)
  54.     public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  55.         // requestPermissionsの引数に指定した値が、requestCodeで返却される
  56.         if (requestCode != EXTERNAL_STORAGE_REQUEST_CODE) {
  57.             return;
  58.         }
  59.         // 自分がリクエストしたコードで戻ってきた場合
  60.         // 使用が許可された
  61.         if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  62.             // ローカルファイルの読み取り処理実行
  63.             readLocalStrage();
  64.             return;
  65.         }
  66.         // 拒否されたが永続的ではない場合
  67.         if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
  68.             Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT).show();
  69.             // パーミッションの取得を依頼
  70.             requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
  71.             return;
  72.         }
  73.         // 永続的に拒否された場合
  74.         Toast.makeText(this, "許可されないとアプリが実行できません\nアプリ設定>権限をチェックしてください", Toast.LENGTH_SHORT).show();
  75.     }
  76.     // ローカルストレージの読み取り処理
  77.     private void readLocalStrage() {
  78.         ContentResolver contentResolver = getContentResolver();
  79.         Cursor cursor = null;
  80.         // 取得するフィールド
  81.         String[] projection = {
  82.                 MediaStore.Images.Media._ID,
  83.                 MediaStore.Images.Media.DATA,
  84.                 MediaStore.Images.Media.WIDTH,
  85.                 MediaStore.Images.Media.HEIGHT,
  86.                 MediaStore.Images.Media.SIZE
  87.         };
  88.         // 検索条件
  89.         // mimetypeで検索 ?はプレースフォルダーで、値はselectionArgsで指定
  90.         String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";;
  91.         // 検索条件に指定する値
  92.         // まずjpgのmimetypeを取得
  93.         String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg");
  94.         // 検索条件の値として設定
  95.         String[] selectionArgs = new String[]{ mimeType };
  96.         // _IDの降順でソート
  97.         String sortOrder = MediaStore.Images.Media._ID + " desc";
  98.         try {
  99.             cursor = contentResolver.query(
  100.                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder);
  101.         } catch (Exception e) {
  102.             e.printStackTrace();
  103.             Toast.makeText(this, "例外が発生、Permissionを許可していますか?", Toast.LENGTH_SHORT).show();
  104.             return;
  105.         }
  106.         if (cursor == null) {
  107.             return;
  108.         }
  109.         List<Bundle> rows = new ArrayList<>();
  110.         while (cursor.moveToNext()) {
  111.             // カーソルから値を取り出し
  112.             String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
  113.             long _id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
  114.             // ※必要なら、ここでサイズや容量のチェックも実行できる
  115.             // arrayに転送
  116.             Bundle bundle = new Bundle();
  117.             bundle.putLong("_id", _id);
  118.             bundle.putString("path", filePath);
  119.             rows.add(bundle);
  120.         }
  121.         cursor.close();
  122.         // データを表示
  123.         MyGridViewAdapter adapter = new MyGridViewAdapter(this, rows);
  124.         GridView gv = (GridView)findViewById(R.id.gridView1);
  125.         gv.setAdapter(adapter);
  126.     }
  127.     // 画像のアップロード
  128.     public void upload(View view) {
  129.         GridView gv = (GridView)findViewById(R.id.gridView1);
  130.         MyGridViewAdapter adapter = (MyGridViewAdapter)gv.getAdapter();
  131.         // 表示しているデータの先頭を取得
  132.         Bundle bundle = (Bundle)adapter.getItem(0);
  133.         final String filePath = bundle.getString("path");
  134.         String url = "http://192.168.1.103:8000/upload.php";
  135.         StringRequest sr = new StringRequest(
  136.                 Request.Method.POST, url,
  137.                 new Response.Listener<String>() {
  138.                     @Override
  139.                     public void onResponse(String response) {
  140.                         showInfo();
  141.                     }
  142.                 },
  143.                 new Response.ErrorListener() {
  144.                     @Override
  145.                     public void onErrorResponse(VolleyError error) {
  146.                         error.printStackTrace();
  147.                     }
  148.                 }
  149.         )
  150.         {
  151.             @Override
  152.             public String getBodyContentType() {
  153.                 return "image/jpeg";
  154.             }
  155.             @Override
  156.             public byte[] getBody() {
  157.                 byte[] fileBytes = null;
  158.                 byte[] b = new byte[8192];
  159.                 try {
  160.                     FileInputStream fis = new FileInputStream(filePath);
  161.                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
  162.                     while (fis.read(b) > 0) {
  163.                         baos.write(b);
  164.                     }
  165.                     baos.close();
  166.                     fis.close();
  167.                     fileBytes = baos.toByteArray();
  168.                 } catch (Exception e) {
  169.                     e.printStackTrace();;
  170.                 }
  171.                 return fileBytes;
  172.             }
  173.         };
  174.         RequestQueue queue = VolleySingleton.getInstance(this).getRequestQueue();
  175.         queue.add(sr);
  176.     }
  177.     private void showInfo() {
  178.         Toast.makeText(this, "アップロード完了", Toast.LENGTH_SHORT).show();
  179.     }
  180. }





・MyGridViewAdapter.java


  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 com.android.volley.toolbox.ImageLoader;
  12. import java.util.List;
  13. public class MyGridViewAdapter extends BaseAdapter {
  14.     private Context mContext;
  15.     private ContentResolver mContentResolver;
  16.     private LayoutInflater mRayoutInflater = null;
  17.     private List<Bundle> mList;
  18.     public MyGridViewAdapter(Context context, List<Bundle> list) {
  19.         this.mContext = context;
  20.         this.mContentResolver = this.mContext.getContentResolver();
  21.         this.mList = list;
  22.     }
  23.     @Override
  24.     public int getCount() {
  25.         if (mList == null) {
  26.             return 0;
  27.         }
  28.         return mList.size();
  29.     }
  30.     @Override
  31.     public Object getItem(int position) {
  32.         return mList.get(position);
  33.     }
  34.     @Override
  35.     public long getItemId(int position) {
  36.         return position;
  37.     }
  38.     @Override
  39.     public View getView(int i, View convertView, ViewGroup viewGroup) {
  40.         View view;
  41.         if (convertView != null) {
  42.             view = convertView;
  43.         }
  44.         else {
  45.             view = new SquareImageView(mContext);
  46.         }
  47.         ImageView thumbnail = (ImageView)view;
  48.         Bundle item = mList.get(i);
  49.         long _id = item.getLong("_id");
  50.         // 画像のローダークラスを取得
  51.         ImageLoader loader = VolleySingleton.getInstance(mContext).getImageLoader();
  52.         // リクエストのキャンセル処理
  53.         ImageLoader.ImageContainer imageContainer = (ImageLoader.ImageContainer)thumbnail.getTag();
  54.         if (imageContainer != null) {
  55.             imageContainer.cancelRequest();
  56.         }
  57.         // _idを指定してサムネール画像のロード
  58.         ImageLoader.ImageListener listener = loader.getImageListener(thumbnail,
  59.                 R.mipmap.ic_launcher, R.mipmap.ic_launcher);
  60.                 thumbnail.setTag(loader.get("id://" + _id, listener));
  61.         //Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, _id, MediaStore.Images.Thumbnails.MINI_KIND, null);
  62.         //thumbnail.setImageBitmap(bmp);
  63.         return view;
  64.     }
  65. }





アップロードされたファイルを受け取るphp側のプログラムです。
bodyを素直に受け取ってそのままファイル出力しています。

・upload.php


  1. <?php
  2. $body = file_get_contents('php://input');
  3. file_put_contents('test.jpg', $body);





サムネール画像を表示した後アップロードボタンをクリック。

782_01.png


サーバー側に先頭に表示している画像ファイルが保存できました。

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

  1. 2017/08/20(日) 17:40:15|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Volleyによるローカルファイル(サムネール画像)のキャッシュ

Androidでローカルの画像ファイルにアクセス。
サムネール画像を表示してみました。
Android ローカルストレージにアクセスし、画像ファイルを表示する
Android 画像のサムネールを正方形に繰り抜いて表示する

サムネール画像の取得、ネットワーク経由で取得する場合と同じく遅延ロードしたい。
自分で処理書くのは手間なので、Volleyにまるなげできないか。

こちらを参考にさせていただきました。
Volleyを無理矢理file schemeに対応してみる

使用しているVolleyのバージョンは1.0.0です。

Volleyに関する過去記事はこちら。
Android ネットワークライブラリ「Volley」を使用してJSONデータ取得
Android ネットワークライブラリ「Volley」を使用して画像データ取得



CustomNetwork



BasicNetworkを継承したクラスを作成。
http(s)から始まるリクエストの場合は親クラスに処理を依頼。
id://から始まる場合はローカルストレージの画像取得とみなし自分で処理。としてみます。

まず、サムネール画像を読みだす機能を追加したクラスを作成。

・CustomNetwork.java


  1. import java.io.ByteArrayOutputStream;
  2. import java.util.Collections;
  3. import android.content.ContentResolver;
  4. import android.graphics.Bitmap;
  5. import android.provider.MediaStore;
  6. import android.util.Log;
  7. import com.android.volley.NetworkResponse;
  8. import com.android.volley.Request;
  9. import com.android.volley.VolleyError;
  10. import com.android.volley.toolbox.BasicNetwork;
  11. import com.android.volley.toolbox.ByteArrayPool;
  12. import com.android.volley.toolbox.HttpStack;
  13. public class CustomNetwork extends BasicNetwork {
  14.     private static final String TAG = CustomNetwork.class.getSimpleName();
  15.     private ContentResolver mContentResolver;
  16.     public CustomNetwork(HttpStack stack, ContentResolver contentResolver) {
  17.         super(stack);
  18.         this.mContentResolver = contentResolver;
  19.     }
  20.     public CustomNetwork(HttpStack stack, ByteArrayPool pool, ContentResolver contentResolver) {
  21.         super(stack, pool);
  22.         this.mContentResolver = contentResolver;
  23.     }
  24.     @Override
  25.     public NetworkResponse performRequest(Request<?> request)
  26.             throws VolleyError {
  27.         // file schemeの場合は自前で処理
  28.         String uri = request.getUrl();
  29.         if (uri.startsWith("id://")) {
  30.             return performFileRequest(uri.substring(5));
  31.         }
  32.         // file scheme以外の処理は親クラスに丸投げ
  33.         return super.performRequest(request);
  34.     }
  35.     private NetworkResponse performFileRequest(String id) throws VolleyError {
  36.         // ContentResolver経由でサムネール画像を取得
  37.         long _id = Long.parseLong(id);
  38.         Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, _id, MediaStore.Images.Thumbnails.MINI_KIND, null);
  39.         NetworkResponse response;
  40.         if (bmp != null) {
  41.             // Bitmapをバイナリ配列に変換
  42.             ByteArrayOutputStream stream = new ByteArrayOutputStream();
  43.             bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream);
  44.             byte[] data = stream.toByteArray();
  45.             response = new NetworkResponse(data);
  46.         } else {
  47.             // 404エラーを返す
  48.             if (BuildConfig.DEBUG)
  49.                 Log.w(TAG, "performFileRequest: file not found: " + _id);
  50.             response = new NetworkResponse(404, null,
  51.                     Collections.<String, String> emptyMap(), false);
  52.         }
  53.         return response;
  54.     }
  55. }




リクエストやImageLoaderを管理するクラスを作成。
参考:Use a Singleton Pattern


・VolleySingleton.java


  1. import android.content.Context;
  2. import android.graphics.Bitmap;
  3. import android.support.v4.util.LruCache;
  4. import com.android.volley.Network;
  5. import com.android.volley.Request;
  6. import com.android.volley.RequestQueue;
  7. import com.android.volley.toolbox.DiskBasedCache;
  8. import com.android.volley.toolbox.HttpStack;
  9. import com.android.volley.toolbox.HurlStack;
  10. import com.android.volley.toolbox.ImageLoader;
  11. import java.io.File;
  12. public class VolleySingleton {
  13.     private static VolleySingleton mInstance;
  14.     private RequestQueue mRequestQueue;
  15.     private ImageLoader mImageLoader;
  16.     private static Context mCtx;
  17.     private VolleySingleton(Context context) {
  18.         mCtx = context;
  19.         mRequestQueue = getRequestQueue();
  20.         mImageLoader = new ImageLoader(mRequestQueue,
  21.                 new ImageLoader.ImageCache() {
  22.                     private final LruCache<String, Bitmap>
  23.                             cache = new LruCache<String, Bitmap>(100);
  24.                     @Override
  25.                     public Bitmap getBitmap(String url) {
  26.                         return cache.get(url);
  27.                     }
  28.                     @Override
  29.                     public void putBitmap(String url, Bitmap bitmap) {
  30.                         cache.put(url, bitmap);
  31.                     }
  32.                 });
  33.     }
  34.     public static synchronized VolleySingleton getInstance(Context context) {
  35.         if (mInstance == null) {
  36.             mInstance = new VolleySingleton(context);
  37.         }
  38.         return mInstance;
  39.     }
  40.     public RequestQueue getRequestQueue() {
  41.         if (mRequestQueue == null) {
  42.             //mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
  43.             mRequestQueue = createRequestQueue(mCtx.getApplicationContext());
  44.         }
  45.         return mRequestQueue;
  46.     }
  47.     public RequestQueue createRequestQueue(Context context) {
  48.         File cacheDir = new File(context.getCacheDir(), "volley");
  49.         HttpStack stack = new HurlStack();
  50.         // ここで作成したCustomNetworkを指定
  51.         Network network = new CustomNetwork(stack, mCtx.getContentResolver());
  52.         RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
  53.         queue.start();
  54.         return queue;
  55.     }
  56.     public <T> void addToRequestQueue(Request<T> req) {
  57.         getRequestQueue().add(req);
  58.     }
  59.     public ImageLoader getImageLoader() {
  60.         return mImageLoader;
  61.     }
  62. }





サムネール画像を幼児するAdapterはこうなりました。

・MyGridViewAdapter.java


  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 com.android.volley.toolbox.ImageLoader;
  12. import java.util.List;
  13. public class MyGridViewAdapter extends BaseAdapter {
  14.     private Context mContext;
  15.     private ContentResolver mContentResolver;
  16.     private LayoutInflater mRayoutInflater = null;
  17.     private List<Bundle> mList;
  18.     public MyGridViewAdapter(Context context, List<Bundle> list) {
  19.         this.mContext = context;
  20.         this.mContentResolver = this.mContext.getContentResolver();
  21.         this.mList = list;
  22.     }
  23.     @Override
  24.     public int getCount() {
  25.         if (mList == null) {
  26.             return 0;
  27.         }
  28.         return mList.size();
  29.     }
  30.     @Override
  31.     public Object getItem(int position) {
  32.         return mList.get(position);
  33.     }
  34.     @Override
  35.     public long getItemId(int position) {
  36.         return position;
  37.     }
  38.     @Override
  39.     public View getView(int i, View convertView, ViewGroup viewGroup) {
  40.         View view;
  41.         if (convertView != null) {
  42.             view = convertView;
  43.         }
  44.         else {
  45.             view = new SquareImageView(mContext);
  46.         }
  47.         ImageView thumbnail = (ImageView)view;
  48.         Bundle item = mList.get(i);
  49.         long _id = item.getLong("_id");
  50.         // 画像のローダークラスを取得
  51.         ImageLoader loader = VolleySingleton.getInstance(mContext).getImageLoader();
  52.         // リクエストのキャンセル処理
  53.         ImageLoader.ImageContainer imageContainer = (ImageLoader.ImageContainer)thumbnail.getTag();
  54.         if (imageContainer != null) {
  55.             imageContainer.cancelRequest();
  56.         }
  57.         // _idを指定してサムネール画像のロード
  58.         ImageLoader.ImageListener listener = loader.getImageListener(thumbnail,
  59.                 R.mipmap.ic_launcher, R.mipmap.ic_launcher);
  60.                 thumbnail.setTag(loader.get("id://" + _id, listener));
  61.         //Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, _id, MediaStore.Images.Thumbnails.MINI_KIND, null);
  62.         //thumbnail.setImageBitmap(bmp);
  63.         return view;
  64.     }
  65. }




thumbnail.setImageBitmap(bmp)していた箇所をImageLoaderを介して画像を取得するよう修正します。


見た目は変わりませんが、ロード前にドロイド君の画像(画像取得までに表示するよう指定した画像)が一瞬表示された後、
サムネール画像が表示されるようになりました。

781_01.png

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

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

Android 画像のサムネールを正方形に繰り抜いて表示する

Androidでローカルファイルを読み取り、サムネール画像を表示してみました。
Android ローカルストレージにアクセスし、画像ファイルを表示する

正方形に切り抜いて表示してみようと思います。


ImageView



こちらを参考にしました。
ImageViewを丸く表示するViewを作ってみた

ImageViewを継承し、正方形の大きさを保持するクラスを作成。
ScaleTypeは常にCENTER_CROPとしておきます。


  1. import android.content.Context;
  2. import android.util.AttributeSet;
  3. public class SquareImageView extends android.support.v7.widget.AppCompatImageView {
  4.     public SquareImageView(Context context) {
  5.         super(context, null);
  6.         setScaleType(ScaleType.CENTER_CROP);
  7.     }
  8.     public SquareImageView(Context context, AttributeSet attrs) {
  9.         super(context, attrs);
  10.         setScaleType(ScaleType.CENTER_CROP);
  11.     }
  12.     public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
  13.         super(context, attrs, defStyle);
  14.         setScaleType(ScaleType.CENTER_CROP);
  15.     }
  16.     @Override
  17.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  18.         // 常に横幅と同じ縦幅を持った矩形のサイズを要求する
  19.         setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
  20.     }
  21. }





プログラム中で固定せず、レイアウトファイルで指定する場合は、このようになります。


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <SquareImageView
  3.     xmlns:android="http://schemas.android.com/apk/res/android"
  4.     android:layout_width="match_parent"
  5.     android:layout_height="match_parent"
  6.     android:scaleType="centerCrop"
  7. />





作成したSquareImageViewを使用して、GridView内を表示するよう修正。


  1.     @Override
  2.     public View getView(int i, View convertView, ViewGroup viewGroup) {
  3.         View view;
  4.         if (convertView != null) {
  5.             view = convertView;
  6.         }
  7.         else {
  8.             view = new SquareImageView(mContext);
  9.         }
  10.         ImageView thumbnail = (ImageView)view;
  11.         Bundle item = mList.get(i);
  12.         long _id = item.getLong("_id");
  13.         Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, _id, MediaStore.Images.Thumbnails.MINI_KIND, null);
  14.         thumbnail.setImageBitmap(bmp);
  15.         return view;
  16.     }




これで狙い通りの表示になりました。

780_01.png

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

  1. 2017/08/20(日) 16:32:35|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
前のページ 次のページ