GridViewにフォルダ内の画像一覧を表示させる

#　はじめに
Android開発をはじめて2週間弱ではまったポイントについてまとめておきたいと思います。
Javaのプログラミングについては経験なしで、初めの1週間で下記の参考書を一通り読みました。
> はじめてのAndroidプログラミング改訂版 https://www.amazon.co.jp/dp/B071RLTTT7/

初心者が書いた初心者向けのメモです。

# やること
GridViewにフォルダ内の画像一覧を表示させるためには、以下の内容を実施します。
    1. permissionを追加する。
    2. layoutにGridViewを用意する。
    3. フォルダ内の画像ファイル一覧を取得する。
    4. GridView用のアダプターを用意する。
    5. permissionの結果を受け取ってフォルダ内の画像一覧を表示させる。

今回は画像一覧を表示させ、選択画像の枠を変化するまでやりました。
   
# 1.permissionを追加する。
端末内のデータにアクセスするためには、パーミッションの設定が必要です。
パーミッションの設定をせずにファイルを取得しようとすると、nullで返され「何で取得できないの？」という気持ちになります。
（Logに表示されていたのかもしれませんが、わかりませんでした。）

permissionの設定は、下記を[app]-[manifests]-[AndroidManifest.xml]ファイルのmanifestタグ内に記載してください。
> \<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

マニフェストファイルに追加するだけでは、端末内のファイルにアクセスすることができません。
以下のような権限確認ウィンドウを表示させ、許可してもらう必要があります。
![kengen.PNG](https://qiita-image-store.s3.amazonaws.com/0/228736/31c1b119-9697-b18a-f382-44cea8c62171.png)

[app]-[java]-[com.example.<username>.<projectname>]-[MainActivity]ファイルに以下のように権限ウィンドウを表示させます。

``` MainActivity.java
   @RequiresApi(api = Build.VERSION_CODES.M)
    private void requestReadStorage(){
        if(ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED){
            if(shouldShowRequestPermissionRationale(
                    Manifest.permission.READ_EXTERNAL_STORAGE)){

            }

            requestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE
            }, PERMISSON_REQUEST_CODE);
        }else{
            // ここに許可済みの時の動作を書く
        }
    }
```
\*PERMISSION_REQUEST_CODEはクラス定数(int 2)です。
それぞれのメソッドはについて簡単に説明します。
 > 1. ContextCompat.checkSelfPermissionメソッドで権限を持っているか確認する
 > 2. shouldShowRequestPermissionRationaleメソッドでユーザが「許可しない」をタップしたときにTrueが返されます。つまり、このif内で許可しないを選択したときの動作、例えば「～～のため許可が必要です。」などのメッセージを表示させます。
 > 3. requestPermissionsメソッドで権限を要求するダイアログを表示させる。

# 2.layoutにGridViewを用意する。
[app]-[res]-[layout]-[activity_main.xml]を以下のようなGridViewにします。

``` activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<GridView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/myGrid"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp"
    android:verticalSpacing="1dp"
    android:horizontalSpacing="1dp"
    android:numColumns="auto_fit"
    android:columnWidth="85dp"
    android:stretchMode="columnWidth"
    android:gravity="center" />
```

# 3.フォルダ内の画像ファイル一覧を取得する。
例えば、こんな感じです。Androidと特に関係ないので省略します。

``` MainActivity.java
private ArrayList<String> getLocalPhotos(String directoryName, String[] extensions)  {
        String fullpath = "";
        String filename = "";
        String fileExtension = "";
        int lastDotPosition = 0;
        ArrayList<String> retFilePaths = new ArrayList<String>() ;
        File dir = Environment.getExternalStoragePublicDirectory(directoryName);
        if(!dir.exists()){
            Toast.makeText(this, directoryName+ "は存在しません", Toast.LENGTH_LONG).show();
            return null;
        }

        File[] files = dir.listFiles();

        for(int fileCount = 0; fileCount < files.length; fileCount++){
            fullpath = String.valueOf(files[fileCount]);
            filename = String.valueOf(files[fileCount].getName());

            if(files[fileCount].isDirectory()){
                // ディレクトリのため対象外
                continue;
            }

            // 拡張子取得
            fileExtension = "";
            lastDotPosition = filename.lastIndexOf(".");
            if(lastDotPosition != -1){
                fileExtension = filename.substring(lastDotPosition + 1);
            }
            if(Arrays.asList(extensions).contains(fileExtension)){
                retFilePaths.add(fullpath);
            }

        }
        return retFilePaths;

    }
``` 

# 4.GridView用のアダプターを用意する。
BaseAdapterを拡張してGridView用のGridAdapterを用意します。

![gridadapter.PNG](https://qiita-image-store.s3.amazonaws.com/0/228736/721cd2d8-53df-906e-d746-f4f15b8d486c.png)


定数やgetCount()などを追加、修正していきます。


``` GridAdapter.java
public class GridAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<Boolean> mIsSelected;  // 選択/非選択
    private ArrayList<String> mPhotos;       // 画像のファイルパス

    @Override
    public int getCount() {
        return mPhotos.size();
    }

    @Override
    public Object getItem(int i) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public GridAdapter(Context context, ArrayList<String> Photos, ArrayList<Boolean> isSelected){
        super();
        mContext = context;
        mPhotos = Photos;
        mIsSelected = isSelected;
    }
``` 

getViewで画像を表示させます。
画像の表示にはPicassoを使ってます。

``` GridAdapter.java
    public View getView(int position, View convertView, ViewGroup parent) {
        /* 画像表示用のImageView */
        ImageView imageView;

        if(convertView == null){
            // imageViewの新規生成
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(150, 150));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(10,10,10,10);
        }else{
            imageView = (ImageView) convertView;
        }

        // ImageViewに画像ファイルを設定(Picassoを使って画像を表示)
        Picasso.with(mContext).load("file://" + mPhotos.get(position)).into(imageView);

        mIsSelected.add(false);  //初期は非選択状態


        return imageView;
    }
```

また、画像の選択時の枠の変化はこんな感じにしています。

``` GridAdapter.java
public void isSelected(View view, int position){
        mIsSelected.set(position, !mIsSelected.get(position));
        GradientDrawable bgShape = new GradientDrawable();
        if(mIsSelected.get(position)){
            bgShape.setStroke(5, Color.BLUE);
            bgShape.setCornerRadius(2);
            bgShape.setColor(Color.WHITE);
            ((ImageView) view).setBackground(bgShape);
        }else{
            ((ImageView) view).setBackground(null);
        }
    }
``` 

# 5.permissionの結果を受け取ってフォルダ内の画像一覧を表示させる。
「1. permissionを追加する。」の「3. requestPermissionsメソッドで権限を要求するダイアログを表示させる。」の結果を受け取って画像一覧を表示させます。requestPermissionの結果を受け取るのは、onRequestPermissionsResultメソッドです。

``` MainActivity.java
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
        if(requestCode == PERMISSON_REQUEST_CODE){
            if(grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED){
                //拒否された時の動作

            }else{
                // 許可された時の動作
                mPhotos = getLocalPhotos(Environment.DIRECTORY_DOWNLOADS, mExtensions);
                mGrid = (GridView) findViewById(R.id.myGrid);
                final GridAdapter adapter = new GridAdapter(this, mPhotos, mIsSelected);
                mGrid.setAdapter(adapter);

                mGrid.setOnItemClickListener(new AdapterView.OnItemClickListener(){
                    @Override
                    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                        adapter.isSelected(view, position);
                    }
                });

            }
        }
    }
``` 

また、アプリ起動時に既に許可されている場合の動作も追加します。

``` MainActivity.java
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void requestReadStorage(){
        if(ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED){
            ～～省略～～
        }else{
            // アプリ起動時に既に許可済みの動作
            mPhotos = getLocalPhotos(Environment.DIRECTORY_DOWNLOADS, mExtensions);
            mGrid = (GridView) findViewById(R.id.myGrid);
            final GridAdapter adapter = new GridAdapter(this, mPhotos, mIsSelected);
            mGrid.setAdapter(adapter);

            mGrid.setOnItemClickListener(new AdapterView.OnItemClickListener(){
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                    adapter.isSelected(view, position);
                }
            });
        }
    }
``` 

# 結果
こんな感じになります。
![result.PNG](https://qiita-image-store.s3.amazonaws.com/0/228736/17aba549-caad-807f-4fc7-bbd5cfaec5d4.png)

ダウンロードフォルダ内の画像一覧が表示され、選択時に青色で枠ができるようになりました。
一番右の画像の青枠の上下がうまく表示されていないので直したいですが、方法がよくわからないです。。。

今後は、画像を複数選択してスライドショーさせるなどしてみたいですね。