#はじめに
Androidには、標準のファイルブラウズのUIが無かったので、個々に実装する必要があった。
新たなストレージアクセスフレームワークを使用すると、ファイルの読み書きを簡単に実装できたので、覚えとして記す。
GoogleDrive、SDカード、内部共有ストレージにアクセスできる。
サンプルとして作成したのは、テキストファイルをロードして編集し保存するアプリである。
#実装
Activityとして、「Basic Activity」を選択し、MainActivityの中に全てのコードを記述した。プロジェクト名は「FileSample」とした。FloatingButton関係の記述は削除した。
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を全画面表示する。
<?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 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時の「開く」表示は「削除」表示にしたいが、変更方法が分からない。ご存知の方はご教示くだれば幸いです。