8
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Androidのファイルブラウズを簡単実装

Last updated at Posted at 2018-09-01

#はじめに
Androidには、標準のファイルブラウズのUIが無かったので、個々に実装する必要があった。
新たなストレージアクセスフレームワークを使用すると、ファイルの読み書きを簡単に実装できたので、覚えとして記す。
GoogleDrive、SDカード、内部共有ストレージにアクセスできる。
サンプルとして作成したのは、テキストファイルをロードして編集し保存するアプリである。

#実装
Activityとして、「Basic Activity」を選択し、MainActivityの中に全てのコードを記述した。プロジェクト名は「FileSample」とした。FloatingButton関係の記述は削除した。

Javaコード記述

MainActivity.java
package xx.xx.xx.xx.filesample;

import android.content.ClipData;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends AppCompatActivity implements View.OnTouchListener {

    static final int REQUEST_OPEN_FILE = 1001;
    static final int REQUEST_CREATE_FILE = 1002;
    static final int REQUEST_DELETE_FILE = 1003;
    enum Mode {OPEN, CREATE, DELETE};
    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        editText = findViewById(R.id.edit_text);
        editText.setOnTouchListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        switch (id) {
            case R.id.action_load:
                startFileBrowser(Mode.OPEN);
                return true;
            case R.id.action_save:
                startFileBrowser(Mode.CREATE);
                return true;
            case R.id.action_delete:
                startFileBrowser(Mode.DELETE);
                return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void startFileBrowser(Mode mode) {
        Intent intent = null;
        try {
            switch (mode) {
                case OPEN:
                    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                    intent.setType("text/plain");   //TEXT file only
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    startActivityForResult(Intent.createChooser(intent, "Open a file"), REQUEST_OPEN_FILE);
                    break;
                case CREATE:
                    intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
                    intent.setType("text/plain");   //TEXT file only
                    intent.putExtra(Intent.EXTRA_TITLE, "newfile.txt");
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    startActivityForResult(Intent.createChooser(intent, "Create a file"), REQUEST_CREATE_FILE);
                    break;
                case DELETE:
                    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                    intent.setType("text/plain");   //TEXT file only
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                    startActivityForResult(Intent.createChooser(intent, "Delete a file"), REQUEST_DELETE_FILE);
                    break;
                default:
            }
        } catch (android.content.ActivityNotFoundException ex) {
            // Potentially direct the user to the Market with a Dialog
            Toast.makeText(this, "Please install a File Browser/Manager", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // File load
        if (requestCode == REQUEST_OPEN_FILE) {
            if (resultCode == RESULT_OK && data != null) {
                Uri uri = data.getData();
                if (uri != null) {
                    editText.setText(loadStrFromUri(uri));
                }
            }
        }
        // File save
        else if (requestCode == REQUEST_CREATE_FILE) {
            if (resultCode == RESULT_OK && data != null) {
                Uri uri = data.getData();
                if (uri != null) {
                    saveStrToUri(uri, editText.getText().toString());
                }
            }
        }
        // File delete
        else if (requestCode == REQUEST_DELETE_FILE) {
            if (resultCode == RESULT_OK && data != null) {
                ClipData clipData = data.getClipData();
                if(clipData==null){  // single selection
                    Uri uri = data.getData();
                    deleteUri(uri);
                }else {  // multiple selection
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        Uri uri = clipData.getItemAt(i).getUri();
                        deleteUri(uri);
                    }
                }
            }
        }
    }

    String loadStrFromUri(Uri uri) {
        String str = "";
        Boolean loop=true;
        try {
            if (uri.getScheme().equals("content")) {
                InputStream iStream = getContentResolver().openInputStream(uri);
                if(iStream==null) loop=false;
                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                while (loop) {
                    int len = iStream.read(buffer);
                    if (len < 0) break;
                    bout.write(buffer, 0, len);
                }
                str = bout.toString();
            } else {
                Toast.makeText(this, "Unknown scheme", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Toast.makeText(this, "Cannot read the file:" + e.toString(), Toast.LENGTH_LONG).show();
        }
        return str;
    }

    void saveStrToUri(Uri uri, String str) {
        try {
            if (uri.getScheme().equals("content")) {
                OutputStream oStream = getContentResolver().openOutputStream(uri);
                if(oStream!=null) oStream.write(str.getBytes());
            } else {
                Toast.makeText(this, "Unknown scheme", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Toast.makeText(this, "Cannot write the file:" + e.toString(), Toast.LENGTH_LONG).show();
        }
    }

    void deleteUri(Uri uri) {
        try {
            DocumentsContract.deleteDocument(getContentResolver(), uri);
        } catch(FileNotFoundException e){
            Toast.makeText(this, "Cannot find the file:" + e.toString(), Toast.LENGTH_LONG).show();
        }
    }

    // 以下は、仮想キーボードをタッチで再表示するためのコードである。
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        view.performClick();
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if(view == editText) showKeyboard();
                break;
        }
        return false;
    }
    void showKeyboard() {
        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        if(inputMethodManager != null) inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    }
}

Intent.ACTION_OPEN_DOCUMENTまたはIntent.ACTION_CREATE_DOCUMENTを呼ぶと、ファイルブラウズのUIが出る。そのUIでユーザがテキストファイルを選択すると、対応するURIが返されるので、それを使ってファイルの読み書きを行う。

上記はファイルを選択するUIだが、ディレクトリを選択するUIは、Intent.ACTION_OPEN_DOCUMENT_TREEを使えばよい。

レイアウトファイル記述

「Basic Activity」では、MainActivity.javaに対応して「activity_main.xml」と「content_main.xml」の2つのレイアウトファイルが作られる。そのうち、content_main.xmlを以下のように記述し、テキストの表示・編集のUIを全画面表示する。

content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <EditText
            android:id="@+id/edit_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:inputType="textMultiLine"
            android:textIsSelectable="true" />
    </ScrollView>

</android.support.constraint.ConstraintLayout>

メニュー記述

メインメニューではファイルのREADとWRITEが選択できる。READを選択するとファイルからテキストを読んで画面に表示する。WRITEを選択すると画面に表示しているテキストをファイルに書く。

menu_main.xml
<menu 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"
    tools:context="me.osdn.users.watanaby.filesample.MainActivity">
    <item
        android:id="@+id/action_load"
        android:orderInCategory="100"
        android:title="Load"
        app:showAsAction="never" />
    <item
        android:id="@+id/action_save"
        android:orderInCategory="100"
        android:title="Save"
        app:showAsAction="never" />
    <item
        android:id="@+id/action_delete"
        android:orderInCategory="100"
        android:title="Delete"
        app:showAsAction="never" />
</menu>

#スクリーンショット

#操作方法
アプリを開始すると編集画面を表示する。
画面右上端のメニューアイコンから、Load、Save、Deleteを呼ぶ(スクリーンショット1)。
ファイルブラウズ画面では、左上端のメニューからストレージを選択できる(スクリーンショット2,3)。
Saveでは、ファイル名をキー入力して新規作成するか、既存ファイル名をタッチして書き換える(スクリーンショット4)。
LoadとDeleteでは、キー入力フィールドは出ない(スクリーンショット5)。
Deleteでは、長押しすると複数ファイルを選択できる(スクリーンショット6)。選択後に画面上部の「開く」をタッチすると削除が実行される。
*Delete時の「開く」表示は「削除」表示にしたいが、変更方法が分からない。ご存知の方はご教示くだれば幸いです。

8
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?