Androidのファイルアクセス
by K.I
2014/10/15〜
Index
- Androidのというか、Javaのファイルアクセスは、Cと較べるとちょっと面倒だ。
- Cの、単純なfopenから、read,write、fcloseの流れとちょっと勝手が違う
- また、Androidの特殊な環境ゆえの制約もいろいろありそうだ。
- 最終的には、バイナリのデータの読み書きをやりたいので、
- Androidのファイル関連処理について、備忘録的に纏めて見た。
[top]
- まずは、基本として、テキストファイルの読み書きをやってみよう。
- Javaでは、ファイルアクセスのような処理では、必ず例外処理が必要になる。
- 基本的には、処理をtryで括って、catchで例外処理を記述する
- それ以外にも、ちょっと見慣れないものが多いので順番に見てみる
- これは、アプリケーションプログラムが、リソースとして使用するファイルを入れておくフォルダで、
- Eclipseで開発している時は、フォルダとしての実体があるけど、
- コンパイルすると、プログラムに組み込まれてしまうものらしい
AssetManager as = getResources().getAssets();
InputStream in1 = as.open("test1.txt");
- 普通のフォルダではないけど、同じように扱えるということなんだろうと思う
- どういう仕組みか分からないが、フォルダ管理や、プログラムからの追加とかも可能らしい。
- 非圧縮ファイルの場合、ファイルサイズ制限(1〜2M)がある
- 普通のファイルを開くならば、FileInputStreamを使えば良い
InputStream in = new FileInputStream(infile);
- resフォルダ下に、rawフォルダを作成して、assetsフォルダと同じ様に
- Stringは、基本的に固定長なので、文字列の連結は、自動的に
- 可変長文字列の、StringBuilderが使われる
- StringBuilderを、同期化してスレッドセーフにしたもの1が、
- StringBuilderは、同期化されてない分、StringBufferより少し速い様だ
参考: StringBuffer_vs_StringBuilder
- InputStreamは、データをbyte単位のストリームとして読込む
- InputStreamReaderで、文字単位のストリームに変換する2ことが出来る
- Readerは、データをデフォルトの文字コードで、文字単位のストリームとして読込む
- BufferedReaderは、いずれかの方法で読込まれた文字単位のストリームを、
- ただ文字を読込むだけで、いろいろな概念が出てきて、けっこう面倒だ。。
public void readwriteText() {
try {
writeText("sample1.txt", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
String str = readText("sample1.txt");
((TextView)findViewById(R.id.DrawText)).setText(str);
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeText(String fileName, String str) throws IOException {
OutputStream out = this.openFileOutput(fileName, MODE_PRIVATE);
out.write(str.getBytes());
out.close();
}
private String readText(String fileName) throws IOException {
InputStream in = this.openFileInput(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
return sb.toString();
}
- Assetsフォルダや、rawフォルダは基本的に、読込みしか出来ないので、
- このプログラムでは、Localフォルダに書き込んでから読込むようにしている
- String#getBytesメソッドは、文字コード変換をするメソッドらしい
- OutputStreamは、データをbyte単位のストリームとして書込む
- OutputStreamReaderで、文字単位のストリームに変換することも出来る
- Writerは、データをデフォルトの文字コードで、文字単位のストリームとして書き込む
- BufferedWriterは、いずれかの方法で文字単位のストリームを書き込む
private void writeText(String fileName, String str) throws IOException {
OutputStream out = this.openFileOutput(fileName, MODE_PRIVATE);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write(str);
bw.newLine();
bw.close();
}
- 何重にもラッピングされて、何がなんだか分からなくなりそうだが。。
- でもバッファされてるので、すぐにprintされなかったりする。
- なので、printfデバッグの時は、以下のようにすぐ出力させる
pw.flush();
- アプリケーション毎に用意されるlocalフォルダで、
- でも、このフォルダはアプリケーション以外からは、簡単にはアクセスできない。
- 他のアプリケーションから、簡単にアクセスされるとマズイ場合もあるとは思うけど、
- Androidのセキュリティは厳し過ぎるんじゃないかなぁ。。
1実際には、StringBufferが先にあって、StringBuilderが後から追加されたらしいが。
2文字コード変換が不要ならば、使う必要はないのかな。
[top]
- 外部ファイルを入れる場所として、SDcardが使える
- Nexus7の場合、SDcardは使えないが、sdcardフォルダはあるので、
- そこに入れておけば、PCとの接続時にSDcardと同様にアクセス可能
OutputStream out = new FileOutputStream(
Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +fileName);
InputStream in = new FileInputStream(
Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +fileName);
- SDcardをアプリケーションからアクセスする場合は、
- SDcardに書き込んだファイルが、他のアプリケーションで認識されない、或いはPCに接続した際に認識されない
- これはMediaにファイルを登録する必要があるらしい
- AndroidのOSを再起動すれば、SDcardがスキャンされて、認識されるようになるが、
- 以下のように、MediaScannerConnectionで、指定したファイルを登録することが出来る
MediaScannerConnection.scanFile(
context,
new String[] { Environment.getExternalStorageDirectory() + "/" + fileName },
new String[] { "text/plain" },
null);
参考: 追加した画像をギャラリーに表示させるには
- これでOKと思ったんだけど、これでもPC側から認識されない場合があるようだ。(151120追記)
[top]
- バイナリファイルの入出力をやってみる
- とりあえず、1byteづつ読んで、1byteづつ書くだけ
public static void fileInOut(String infile, String outfile) {
InputStream in=null;
OutputStream out=null;
try {
in = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +infile);
out = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +outfile);
int data;
while ((data = in.read()) != -1) {
out.write(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (in != null) in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null) out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- このルーチンを実行すると、プログラムが無反応の状態になってしまい、
- タイムアウトでプログラムを終了しなければならなくなってしまった。
- 短いファイルなら、特に問題なく終了するが、ある程度大きなファイルではそうなる様だ。
- 出力ファイルは、一応、出来ているみたいなんだけど。。(試したファイルは738KBぐらい)
- 最初、readは1byte読み込みなので、byteにしていたら、0xFFがあった処で当然終わってしまった。
- readメソッドは、読み込み完了で、0xFFFFFFFFを返すらしい
- 今度は、BufferedInputStreamと、BufferedOutputStreamを通して、読み書きしてみた。
public static void bufferedInOut(String infile, String outfile) {
InputStream in=null;
OutputStream out=null;
BufferedInputStream bin=null;
BufferedOutputStream bout=null;
try {
in = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +infile);
out = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +outfile);
bin = new BufferedInputStream(in);
bout = new BufferedOutputStream(out);
int data;
while ((data = bin.read()) != -1) {
bout.write(data);
}
bout.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (bin != null) bin.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bout != null) bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
これだと、すぐに終了する。ファイルもちゃんと出来ている。
- Bufferされない場合、read,writeの度にアクセスしなおすので時間が掛かるということなのかな
- でも、1MBにも満たないファイルで、そんなに時間が掛かるんだろうか?
- まぁ、とにかく、BufferedInputStreamとBufferedOutputStreamを使う方が良さそうだ。
[top]
- Androidには、標準のファイルダイアログって、無いらしい3。
- こちらのファイル選択ダイアログ記事がとても分かり易かったので、それを参考にさせて頂いた。
- ArrayAdapterは、ちょっとやり方を変えてるけど、あとはほとんどそのまま使わせてもらっている。
- まず、ファイル単体のファイル情報を保持するクラスを作成する。
- フィールド変数として、Fileオブジェクトと、ファイル名を保持するだけ
- ソートできるように、Comparableインターフェースをインプリメントして、compareToメソッドを実装(FileInfo.java)
public class FileInfo implements Comparable<FileInfo> {
private String m_strName; // 表示名
private File m_file; // ファイルオブジェクト
// コンストラクタ
public FileInfo( String strName, File file ) {
m_strName = strName;
m_file = file;
}
public String getName() {
return m_strName;
}
public File getFile() {
return m_file;
}
// 比較
public int compareTo( FileInfo another ) {
// ディレクトリ < ファイル とする
if( m_file.isDirectory() && !another.getFile().isDirectory() ) {
return -1;
}
if( !m_file.isDirectory() && another.getFile().isDirectory() ) {
return 1;
}
// ファイル同士、ディレクトリ同士の場合、ファイル名の大文字小文字区別なしで辞書順
return m_file.getName().toLowerCase().compareTo( another.getFile().getName().toLowerCase() );
}
}
- StringクラスのcompareToメソッドは、A.compareTo(B)というように使用した時、
- A < B の時はマイナス
- A = B の時はゼロ
- A > B の時はプラスを返すようにする
- ディレクトリより、ファイルの方が大きいと判断するので、
- Aがディレクトリ、Bがファイルの時は、マイナス
- Aがファイルで、Bがディレクトリの時、プラスを返すようにする
- ファイル情報といっても、これはファイル名しか保持していないが、
- いろいろ必要な属性を持つようにしても良いかもしれない。
- LinearLayoutをカスタマイズして、1行分のレイアウトの生成とファイル情報の設定を行う(FilelistLayout.java)
public class FilelistLayout extends LinearLayout {
TextView mTitleView;
public FilelistLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
// 新規にLayoutを生成するときのみ、findViewByIdを行う
protected void onFinishInflate() {
super.onFinishInflate();
mTitleView = (TextView) findViewById(R.id.list_textView1);
}
// Adapterから指定されたファイル情報をViewに設定する
public void bindView(FileInfo item, int pos) {
// ファイルとディレクトリで表示色を変更(奇数偶数行で色の濃さを変える)
if ( item.getFile().isDirectory() ) { // ディレクトリは「/」を付加
mTitleView.setText( item.getName() + "/" );
mTitleView.setBackgroundColor(Color.parseColor((pos%2==0)?"#8080D0":"#8080F0"));
}
else {
mTitleView.setText( item.getName() );
mTitleView.setBackgroundColor(Color.parseColor((pos%2==0)?"#D08080":"#F08080"));
}
}
}
- ファイル情報をListViewで表示するために、ArrayAdapterクラスをカスタマイズする
- カスタマイズしたFilelistLayoutクラスにファイル情報を渡すだけにしている(FilelistAdapter.java)
public class FilelistAdapter extends ArrayAdapter<FileInfo> {
private LayoutInflater inflater;
private int mItemLayoutResource;
private List<FileInfo> m_listFileInfo; // ファイル情報リスト
// コンストラクタ
public FilelistAdapter(Context context,int resource, List<FileInfo> object) {
super(context,resource, object);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mItemLayoutResource = resource;
m_listFileInfo = object;
}
// 指定行のファイル情報の取得
@Override
public FileInfo getItem( int position ) {
return m_listFileInfo.get( position );
}
// 1行分のビューを生成
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
final FilelistLayout view;
// viewが無かったら生成する
if( null == convertView ) {
view = (FilelistLayout)inflater.inflate(mItemLayoutResource, null);
}
else {
view = (FilelistLayout)convertView;
}
// ファイル情報はFilelistLayoutで設定する
FileInfo fileinfo = m_listFileInfo.get( position );
view.bindView(fileinfo, position);
return view;
}
}
- 作成したFilelistAdapterクラスを使って、指定したディレクトリのファイルリストを表示して選択させる
- 指定した拡張子のファイルのみ表示されるように、ファイルフィルタの指定も可能(FilelistDialog.java)
public class FilelistDialog implements OnItemClickListener {
private Context m_parent; // 呼び出し元
private OnFileSelectListener m_listener; // 結果受取先
private AlertDialog m_dlg; // ダイアログ
private FilelistAdapter m_filelistadapter; // ファイル情報配列アダプタ
private String[] m_astrExt; // フィルタ拡張子配列
// コンストラクタ
public FilelistDialog( Context context, OnFileSelectListener listener, String strExt ) {
m_parent = context;
m_listener = (OnFileSelectListener)listener;
// 拡張子フィルタを配列に
if( null != strExt ) {
m_astrExt = strExt.split(",",0);
int iCountToken = m_astrExt.length;
}
}
// ダイアログの作成と表示
public void show( File fileDirectory ) {
// タイトル
String strTitle = fileDirectory.getAbsolutePath();
// リストビュー
ListView listview = new ListView( m_parent );
listview.setScrollingCacheEnabled( false );
listview.setOnItemClickListener( this );
// 拡張子フィルタを使用して、ファイルリストを取得
File[] aFile = fileDirectory.listFiles( getFileFilter() );
List<FileInfo> listFileInfo = new ArrayList<FileInfo>();
if( null != aFile ) {
for( File fileTemp : aFile ) {
listFileInfo.add( new FileInfo( fileTemp.getName(), fileTemp ) );
}
Collections.sort( listFileInfo );
}
// 親フォルダに戻るパスの追加
if( null != fileDirectory.getParent() ) {
listFileInfo.add( 0, new FileInfo( "..", new File( fileDirectory.getParent() ) ) );
}
m_filelistadapter = new FilelistAdapter( m_parent, R.layout.filelistlayout, listFileInfo );
listview.setAdapter( m_filelistadapter );
Builder builder = new AlertDialog.Builder( m_parent );
builder.setTitle( strTitle );
builder.setPositiveButton( "Cancel", null );
builder.setView( listview );
m_dlg = builder.show();
}
// 拡張子フィルタ
private FileFilter getFileFilter() {
return new FileFilter() {
public boolean accept( File arg0 ) {
if( null == m_astrExt ) { // フィルタ指定無し→フィルタしない
return true;
}
if( arg0.isDirectory() ) { // ディレクトリの時は、true
return true;
}
for( String strTemp : m_astrExt ) {
if( arg0.getName().toLowerCase().endsWith( "." + strTemp ) ) {
return true;
}
}
return false;
}
};
}
// ListView内の項目をクリックしたときの処理
public void onItemClick( AdapterView<?> l, View v, int position, long id ) {
if( null != m_dlg ) {
m_dlg.dismiss();
m_dlg = null;
}
FileInfo fileinfo = m_filelistadapter.getItem( position );
if( true == fileinfo.getFile().isDirectory() ) {
// ディレクトリ選択時は、ディレクトリ内容を表示
show( fileinfo.getFile() );
}
else {
// ファイル選択時はOnFileSelectListener呼び出し
m_listener.onFileSelect( fileinfo.getFile() );
}
}
// 選択したファイルの情報を取り出すためのリスナーインターフェース
public interface OnFileSelectListener {
// ファイルが選択されたときに、onFileSelect関数をコールバック
public void onFileSelect( File file );
}
}
- ファイル選択を行うFilelistDialogクラスの使用例(FilelistDialogTest.java)
public class FilelistDialogTest extends Activity
implements FilelistDialog.OnFileSelectListener {
private static final int MENUID_FILE = 0; // OptionメニューID
private String m_strInitialDir = "/sdcard/"; // 初期フォルダはsdcard
private String m_strExtFilter = "db,str"; // 拡張子フィルタの指定
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
}
// オプションメニュー生成時の処理
@Override
public boolean onCreateOptionsMenu( Menu menu ) {
super.onCreateOptionsMenu( menu );
menu.add( 0, MENUID_FILE, 0, "Select File..." );
return true;
}
// オプションメニュー選択時の処理
@Override
public boolean onOptionsItemSelected( MenuItem item ) {
switch( item.getItemId() ) {
case MENUID_FILE:
// FilelistDialogクラスを使用して、ファイル名を選択させる
FilelistDialog dlg = new FilelistDialog( this, this, m_strExtFilter );
dlg.show( new File( m_strInitialDir ) );
return true;
}
return false;
}
// FilelistDialogでファイル選択されると、onFileSelectが呼び出される
public void onFileSelect( File file ) {
Toast.makeText( this, "File Selected : " + file.getPath(), Toast.LENGTH_LONG ).show();
m_strInitialDir = file.getParent();
}
}
- /sdcard/ディレクトリは、機種によってはパスが違うかもしれないので、 SDcardのパスを取得して使った方が良い。
- それから、SDカードをアクセスする場合は、AndroidManifest.xmlにパーミッションの記述が必要なので注意!
3というか、ファイルを弄らせたくないらしいのかも。
[top]
- ファイル選択は、 ファイルダイアログで十分なんだけど、選択した時点でファイルが確定してしまうので、ちょっと違和感がある。
- とりあえず、選択するとファイル名を表示して、選択し直せるような形にしてみよう。
- といっても、複雑になった割りに、動作はそんなに変わらないので、前項のファイルダイアログで十分かもしれない。
- こちらの複数ファイル選択アクティビティを基にしている。
- AndroidManifest等で設定しやすそうだったので、今度は、Activityにした。
public class FileInfo implements Comparable<FileInfo> {
private String m_strName; // 表示名
private File m_file; // ファイルオブジェクト
private boolean m_bSelected; // 選択状態
// コンストラクタ
public FileInfo( String strName, File file ) {
m_strName = strName;
m_file = file;
}
public String getName() {
return m_strName;
}
public File getFile() {
return m_file;
}
public boolean isSelected() {
return m_bSelected;
}
public void setSelected( boolean bSelected ) {
m_bSelected = bSelected;
}
// 比較
public int compareTo( FileInfo another ) {
// ディレクトリ < ファイル とする
if( m_file.isDirectory() && !another.getFile().isDirectory() ) {
return -1;
}
if( !m_file.isDirectory() && another.getFile().isDirectory() ) {
return 1;
}
// ファイル同士、ディレクトリ同士の場合、ファイル名の大文字小文字区別なしで辞書順
return m_file.getName().toLowerCase().compareTo( another.getName().toLowerCase() );
}
}
- ListViewの下に、EditTextのファイル名入力欄と、OK・Cancelのボタンを付けてみた。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" >
</ListView>
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Open file name"
android:textAppearance="?android:attr/textAppearanceSmall" />
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Input Open file path"
android:textAppearance="?android:attr/textAppearanceLarge" >
<requestFocus />
</EditText>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="32sp"
android:text="OK" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="32sp"
android:text="Cancel" />
</LinearLayout>
</LinearLayout>
- Openダイアログの、FilelistのAdapter
- List表示は、Layoutファイルでやるようにした。
public class FilelistAdapter extends ArrayAdapter<FileInfo> {
private LayoutInflater inflater;
private int mItemLayoutResource;
private List<FileInfo> m_listFileInfo; // ファイル情報リスト
// コンストラクタ
public FilelistAdapter(Context context,int resource, List<FileInfo> object) {
super(context,resource, object);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mItemLayoutResource = resource;
m_listFileInfo = object;
}
// 指定行のファイル情報の取得
@Override
public FileInfo getItem( int position ) {
return m_listFileInfo.get( position );
}
// 一番上の選択されたアイテムの位置の取得
public int getFirstSelectedItemPosition() {
ListIterator<FileInfo> it = m_listFileInfo.listIterator();
while( it.hasNext() ) {
FileInfo fileinfo = it.next();
if( fileinfo.isSelected() ) {
return it.nextIndex() - 1;
}
}
return -1;
}
// positionで指定したアイテムの次の選択されたアイテムの位置の取得
public int getNextSelectedItemPosition( int iPosition ) {
ListIterator<FileInfo> it = m_listFileInfo.listIterator( iPosition );
it.next(); // 一つ進める
while( it.hasNext() ) {
FileInfo fileinfo = it.next();
if( fileinfo.isSelected() ) {
return it.nextIndex() - 1;
}
}
return -1;
}
// 1行分のビューを生成
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
final FilelistLayout view;
// viewが無かったら生成する
if( null == convertView ) {
view = (FilelistLayout)inflater.inflate(mItemLayoutResource, null);
}
else {
view = (FilelistLayout)convertView;
}
// ファイル情報はFilelistLayoutで設定される
FileInfo fileinfo = m_listFileInfo.get( position );
view.bindView(fileinfo, position);
return view;
}
}
- 選択ファイルの位置を取得するメソッドが追加されている。
- Openダイアログの、Filelistのレイアウト表示をカスタマイズする
public class FilelistLayout extends LinearLayout {
TextView mTitleView;
public FilelistLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
// 新規にLayoutを生成するときのみ、findViewByIdを行う
protected void onFinishInflate() {
super.onFinishInflate();
mTitleView = (TextView) findViewById(R.id.list_textView1);
}
// Adapterから指定されたファイル情報をViewに設定する
public void bindView(FileInfo item, int pos) {
// ファイルとディレクトリで表示色を変更(奇数偶数行で色の濃さを変える)
if ( item.getFile().isDirectory() ) { // ディレクトリは「/」を付加
mTitleView.setText( item.getName() + "/" );
mTitleView.setBackgroundColor(Color.parseColor((pos%2==0)?"#8080D0":"#8080F0"));
mTitleView.setTextColor(Color.BLACK);
}
else if ( item.isSelected() ) {
mTitleView.setBackgroundColor(Color.parseColor("#FF0000"));
mTitleView.setTextColor(Color.WHITE);
}
else {
mTitleView.setText( item.getName() );
mTitleView.setBackgroundColor(Color.parseColor((pos%2==0)?"#D08080":"#F08080"));
mTitleView.setTextColor(Color.BLACK);
}
}
}
- 奇数偶数行で、色の濃さを変えるように
- 選択時は、色を変えるようにしている
- 一応、ファイルリスト1行分のリソースを示す。以下のような簡単なもの
<?xml version="1.0" encoding="utf-8"?>
<hm.orz.bluefish.gdsviewfile.FilelistLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/list_textView1"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="32sp"
android:text="FILELIST TEXTVIEW" />
</hm.orz.bluefish.gdsviewfile.FilelistLayout>
- Openダイアログクラス。実際はActivityだけど
- 元々、複数選択可能なソースだが、1ファイルのみ選択するようにしている4。
public class FilelistOpenActivity extends Activity
implements OnItemClickListener, OnClickListener {
// ボタン処理切替用タグ
private static final int BUTTONTAG_OK = 0;
private static final int BUTTONTAG_CANCEL = 1;
private ListView m_listview; // リストビュー
private FilelistAdapter m_filelistadapter; // ファイル情報配列アダプタ
private String[] m_astrExt; // フィルタ拡張子配列
private EditText m_fnameSelect; // 選択されたファイル名
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
// アクティビティの戻り値の初期化
setResult( Activity.RESULT_CANCELED );
// 呼び出し元からパラメータ取得
String strInitialDir = null;
String strExt = null;
Bundle extras = getIntent().getExtras();
if( null != extras ) {
strInitialDir = extras.getString( "initialdir" );
strExt = extras.getString( "ext" );
}
// 初期フォルダ
if( null == strInitialDir || false == new File( strInitialDir ).isDirectory() ) {
strInitialDir = "/";
}
// 拡張子フィルタを配列に
if( null != strExt ) {
StringTokenizer tokenizer = new StringTokenizer( strExt, "; " );
int iCountToken = 0;
while( tokenizer.hasMoreTokens() ) {
tokenizer.nextToken();
iCountToken++;
}
if( 0 != iCountToken ) {
m_astrExt = new String[iCountToken];
tokenizer = new StringTokenizer( strExt, "; " );
iCountToken = 0;
while( tokenizer.hasMoreTokens() ) {
m_astrExt[iCountToken] = tokenizer.nextToken();
iCountToken++;
}
}
}
// ファイルOPENダイアログのレイアウト
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
setContentView(R.layout.filelistopen);
m_listview = (ListView)this.findViewById(R.id.listView1);
m_listview.setScrollingCacheEnabled( false );
m_listview.setOnItemClickListener( this );
Button m_buttonOK = (Button)this.findViewById(R.id.button1);
m_buttonOK.setTag( BUTTONTAG_OK);
m_buttonOK.setOnClickListener( this );
Button m_buttonCancel = (Button)this.findViewById(R.id.button2);
m_buttonCancel.setTag(BUTTONTAG_CANCEL);
m_buttonCancel.setOnClickListener( this );
m_fnameSelect = (EditText)this.findViewById(R.id.editText1);
m_fnameSelect.setFocusable(false); // ファイル名は選択のみで、直接入力できないようにする
fill( new File( strInitialDir ) );
}
// 指定されたディレクトリのファイルリストを表示
private void fill( File fileDirectory ) {
// タイトル
setTitle( fileDirectory.getAbsolutePath() );
// ファイルリスト
File[] aFile = fileDirectory.listFiles( getFileFilter() );
List<FileInfo> listFileInfo = new ArrayList<FileInfo>();
if( null != aFile ) {
for( File fileTemp : aFile ) {
listFileInfo.add( new FileInfo( fileTemp.getName(), fileTemp ) );
}
Collections.sort( listFileInfo );
}
// 親フォルダに戻るパスの追加
if( null != fileDirectory.getParent() ) {
listFileInfo.add( 0, new FileInfo( "..", new File( fileDirectory.getParent() ) ) );
}
m_filelistadapter = new FilelistAdapter( this, R.layout.filelistlayout, listFileInfo );
m_listview.setAdapter( m_filelistadapter );
}
// FileFilterオブジェクトの生成
private FileFilter getFileFilter() {
return new FileFilter() {
public boolean accept( File arg0 ) {
if( null == m_astrExt ) { // フィルタしない
return true;
}
if( arg0.isDirectory() ) { // ディレクトリのときは、true
return true;
}
for( String strTemp : m_astrExt ) {
if( arg0.getName().toLowerCase().endsWith( "." + strTemp ) ) {
return true;
}
}
return false;
}
};
}
// ListViewをクリックしたときの処理
public void onItemClick( AdapterView<?> l, View v, int position, long id ) {
// 現状の選択を解除して、ListViewの表示を更新(1ファイルのみ選択のため)
int iPos = m_filelistadapter.getFirstSelectedItemPosition();
if (iPos != -1) {
m_filelistadapter.getItem( iPos ).setSelected(false);
m_filelistadapter.notifyDataSetChanged();
m_listview.invalidateViews();
}
FileInfo fileinfo = m_filelistadapter.getItem( position );
if( true == fileinfo.getFile().isDirectory() ) { // ディレクトリClickは、ファイルリスト表示
fill( fileinfo.getFile() );
m_fnameSelect.setText("");
}
else { // ファイルClickのときは選択して、クリック行の表示を更新
fileinfo.setSelected( true ); // 1ファイルのみ選択とする
l.getAdapter().getView( position, v, l );
// m_fnameSelect.setText(fileinfo.getName());
m_fnameSelect.setText(m_filelistadapter.getItem( position ).getFile().getPath()); // パス表示
}
}
// ボタンをクリックしたときの処理
public void onClick( View arg0 ) {
final int iTag = (Integer)arg0.getTag();
switch( iTag ) {
case BUTTONTAG_OK:
// ファイルが選択されているか
int iPosition_first = m_filelistadapter.getFirstSelectedItemPosition();
int iPosition = iPosition_first;
int iCount = 0;
while( -1 != iPosition ) {
iPosition = m_filelistadapter.getNextSelectedItemPosition( iPosition );
iCount++;
}
if( 0 == iCount ) { // ファイルが選択されていない
Toast.makeText( this, "No file selected.", Toast.LENGTH_LONG ).show();
}
else { // ファイルが選択されている
File[] aFile = new File[iCount];
iPosition = iPosition_first;
iCount = 0;
while( -1 != iPosition ) {
aFile[iCount] = m_filelistadapter.getItem( iPosition ).getFile();
iPosition = m_filelistadapter.getNextSelectedItemPosition( iPosition );
iCount++;
}
// 呼出元へファイル名を戻す
Intent intent = new Intent();
intent.putExtra( "file", aFile );
setResult( Activity.RESULT_OK, intent );
finish();
}
break;
case BUTTONTAG_CANCEL:
// アクティビティ終了
finish();
break;
}
}
}
- 作成した、FilelistOpenActivityクラスの使用例
public class FilelistOpenActivityTest extends Activity {
private static final int MENUID_FILE = 0; // オプションメニューID
private static final int REQUEST_FILESELECT = 0; // リクエストコード
// 初期フォルダ
private String m_strInitialDir = "/sdcard/";
private String m_strExtFilter = "db, str, gds";
/** Called when the activity is first created. */
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
}
// オプションメニュー生成時
@Override
public boolean onCreateOptionsMenu( Menu menu ) {
super.onCreateOptionsMenu( menu );
menu.add( 0, MENUID_FILE, 0, "Select Open File..." );
return true;
}
// オプションメニュー選択時
@Override
public boolean onOptionsItemSelected( MenuItem item ) {
switch( item.getItemId() ) {
case MENUID_FILE:
// ファイル選択アクティビティ
Intent intent = new Intent( this, FilelistOpenActivity.class );
intent.putExtra( "initialdir", m_strInitialDir );
intent.putExtra( "ext", m_strExtFilter );
startActivityForResult( intent, REQUEST_FILESELECT );
return true;
}
return false;
}
// アクティビティ呼び出し結果の取得
@Override
protected void onActivityResult( int requestCode,
int resultCode,
Intent intent ) {
if( REQUEST_FILESELECT == requestCode && RESULT_OK == resultCode ) {
Bundle extras = intent.getExtras();
if( null != extras ) {
Object[] aObject = (Object[])extras.getSerializable( "file" );
if( null == aObject ) {
return;
}
StringBuilder sb = new StringBuilder();
sb.append( "File Selected :\n" );
for( Object object : aObject ) {
sb.append( ( (File)object ).getPath() );
sb.append( "\n" );
}
Toast.makeText( this, sb.toString(), Toast.LENGTH_SHORT ).show();
m_strInitialDir = ( (File)aObject[0] ).getParent();
}
}
}
}
- 画面回転時にActivityが破棄されると、ちょっと面倒5なので、
- これで、ダイアログ表示中に画面を回転しても、Activityは再生成されない。
4ので、ちょっと冗長になっている。
5というか、いろいろやったけど、どうもよく分からないので。。
[top]
- これは実際作ってないんだけど、OpenダイアログはEditTextを使っているけど無効にしているので、
- FilelistOpenActivityの、これらの処理をコメントアウトすれば良い。
- まずソフトキーボードの無効化の処理をコメントアウト
// getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
- それから、フォーカスも無効化されてるので、それもコメントアウト
// m_fnameSelect.setFocusable(false);
- これで、ファイル入力欄をクリックすると、ソフトキーボードが表示され、入力可能となる。
- それだけじゃダメだと思うけど、少しの修正でSaveダイアログとして使えるんじゃないかと思う。
- 実際やってみると、Openダイアログは選択した内容をEditTextに入れているだけで、EditTextに入力した内容は返していない。
- 単純にEditTextの内容を、Stringで返すように変えてみた6
case BUTTONTAG_OK:
if( m_fnameSelect.getText().length() == 0 ) { // ファイル名が入力されていない
Toast.makeText( this, "No file selected.", Toast.LENGTH_LONG ).show();
}
else { // ファイル名が入力されている
// 呼出元へファイル名を戻す
Intent intent = new Intent();
Toast.makeText( this, m_fnameSelect.getText(), Toast.LENGTH_LONG ).show();
intent.putExtra( "file", m_fnameSelect.getText().toString());
setResult( Activity.RESULT_OK, intent );
// beginBatchEdit on inactive InputConnection Warning対策で、キーボード消す
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(arg0.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
finish();
}
break;
- beginBatchEdit on inactive InputConnection Warningがでるので、プログラムでキーボードを消してから返すようにしている
fname = extras.getString("file");
6Openダイアログも、そうした方がいいかも。
[top]
- アプリケーションが終了しても、保存しておきたい設定データはPreferenceファイルに保存する。
- ファイル名を指定して、Preferenceファイルを生成
SharedPreferences sp = getSharedPreferences("HelloWorld", MODE_PRIVATE);
- Activityのクラス名で、Preferenceファイルを生成する
SharedPreferences sp = getPreferences(MODE_PRIVATE);
アクセスモード | 説明 |
MODE_PRIVATE | 他のアプリからアクセス不可 |
MODE_WORLD_READABLE | 他のアプリから読込み可 |
MODE_WORLD_WRITEABLE | 他のアプリから書込み可 |
- MODE_WORLD_READABLEと、MODE_WORLD_WRITEABLEは、'|'で同時指定可能
- アプリケーション専用で、他からアクセスしないならば、
- Editorオブジェクトを取得して、キーと値をputして、
キーの型 | 書込みメソッド | 読出しメソッド |
boolean | putBoolean | getBoolean |
float | putfloat | getfloat |
long | putLong | getLong |
int | putInt | getInt |
String | putString | getString |
- getで読出し。keyが未定義の場合は、デフォルト値を返す
int value = sp.getInt("key", default);
- removeでkeyを指定して、commitすれば削除される
sp.edit().remove("key").commit();
sp.edit().clear().commit();
- DDMSのファイルエクスプローラで、/data/data/パッケージ名/shared_prefsを見れば良い筈なんだけど、
- パーミッションが無いので見れなかった。実機だからかな?
>adb shell
$ run-as パッケージ名
$ cd shared_pref
$ cat preferenceファイル名
- こんな感じで、XML形式でキーと値が設定されていることが分かる
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="key">value</string>
</map>
- onPauseでdataの内容をDATAというKeyで保存して、onCreateでdataを読込む例
String data;
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
:
:
try {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
data = sp.getString("DATA", null);
} catch (Exception e) {
data = null;
}
}
@Override
protected void onPause() {
super.onPause();
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
editor
.clear()
.putString("DATA", data)
.commit();
}
- PreferenceActivityで設定変更することを考えると、onPauseでPreferenceの設定をするのは良くない (151125追記)
[top]
- Preferenceの設定画面を作ろうと思ったけど、
- まぁでも、フラグメント使うとややこしくなるので、とりあえず無視。。
- まず、PreferenceActivityのレイアウト用のXMLを用意する
- 設定画面で設定した値で、Preferenceはちゃんと変更されるんだけど、設定画面上に変更内容の表示はされない。
- これぐらいXMLの記述だけでやってくれても良いと思う7んだけど、Summaryの更新は自分でやる必要がある。
- Preference変更時の処理は、onReume/onPauseで、登録/解除する
@Override
protected void onResume() {
super.onResume();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener((OnSharedPreferenceChangeListener) this);
}
@Override
protected void onPause() {
super.onPause();
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener((OnSharedPreferenceChangeListener) this);
SharedPreferences.Editor editor = getPreferenceScreen().getSharedPreferences().edit();
}
@Override
// Preference設定変更時の処理
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
resetSummary();
}
PreferenceActivityにOnSharedPreferenceChangeListenerをインプリメントすると、
- onSharedPreferenceChangedメソッドが追加されるので、ここで設定変更の処理を行う。
- Preferenceの項目の種類によって処理を変える必要がある
// 各項目のSummaryに現在値を表示する
public void resetSummary() {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
PreferenceScreen screen = this.getPreferenceScreen();
for (int i = 0; i < screen.getPreferenceCount(); i++) {
Preference pref = screen.getPreference(i);
if (pref instanceof CheckBoxPreference || pref instanceof SwitchPreference ) {
String key = pref.getKey();
boolean val = sharedPrefs.getBoolean(key, false);
pref.setSummary(val?"ON":"OFF");
}
if (pref instanceof EditTextPreference || pref instanceof ListPreference ) {
String key = pref.getKey();
String val = sharedPrefs.getString(key, "");
pref.setSummary(val);
}
if (pref instanceof MultiSelectListPreference) {
String val = multiSelectListSummary((MultiSelectListPreference) pref);
pref.setSummary(val);
}
}
}
- onCreate時にも、このresetSummaryで現在値の表示をすることが出来る。
- このぐらいの基本的な表示の更新は自動的にやってもらいたいんだけどなぁ。
- 設定画面作るのが面倒なので、PreferenceActivity使ってるわけだし。
- multiSelectListだけは、ちょっと面倒なので、カンマ区切りの文字列に変換する関数を別に用意する
// MultiSelectListPreferenceで選択された値をカンマ区切りの文字列で返す
private String multiSelectListSummary(MultiSelectListPreference pref) {
ArrayList summaryArray = new ArrayList();
CharSequence[] entries = pref.getEntries();
CharSequence[] entryValues = pref.getEntryValues();
Set values = pref.getValues();
for (CharSequence entryValue : entryValues) {
if (values.contains(entryValue)) {
int index = pref.findIndexOfValue(entryValue.toString());
if (index >= 0) {
summaryArray.add(entries[index]);
}
}
}
return TextUtils.join(", ", summaryArray);
}
- PreferenceActivityで設定すれば、自動的にPreferenceが更新される。。
- メインのAcitivityで更新された内容は、PreferenceActivityで表示されているので、
- メインのActifityの更新処理と同じ処理を、PreferenceActivityで実行しても、全く更新されない。
- これは、かなり嵌ってしまった。。ググっても同じことで悩んでいる人は居ないし。。。
- 結局、原因はメインのActivityのonPauseでPreferenceの更新をしていたため。
- Activity遷移の直前にアクセスしているので、PreferenceActivityの開始時にPreferenceをアクセス出来なかったと思われる。
- 対策として、メインのActivityのonPauseではPreferenceの更新をしないようにする。(重要)
- これで、問題なくPreferenceActivityでの設定が、Preferenceファイルに反映されるようになった。
- もちろん、onPause以外の場所であれば、メインでのActivityでPreferenceの設定もOK!
- もしかすると、フラグメントの場合は違うのかな。。まぁ、いずれにしても遷移時は注意が必要と思う。
- PreferenceをCategory分けしたら、Summaryの動的表示が出来なくなった。
- Categoryに設定した部分が読めなくなっているようだ。
- ちょっとググって見ると、 この情報 がまさにそれだった。
- PreferenceGroupでキャストすれば、さらにgetPreferenceで分解することが出来るようだ。
- ということで、resetSummaryを書き直してみた。
- とりあえずCategory設定したPreferenceでも動くようになった。感謝!
// Preferenceの各項目のSummaryに現在値を表示する
public void resetSummary() {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
PreferenceScreen screen = this.getPreferenceScreen();
screenSummary(sharedPrefs,screen);
}
// screenのpreferenceを順に処理
public void screenSummary(SharedPreferences sharedPrefs, PreferenceScreen screen) {
for (int i = 0; i < screen.getPreferenceCount(); i++) {
Preference pref = screen.getPreference(i);
if (pref instanceof PreferenceGroup) {
groupSummary(sharedPrefs,(PreferenceGroup)pref);
}
else if (pref instanceof PreferenceScreen) {
screenSummary(sharedPrefs,(PreferenceScreen)pref);
}
else {
prefSummary(sharedPrefs, pref);
}
}
}
// groupのpreferenceを順に処理
public void groupSummary(SharedPreferences sharedPrefs, PreferenceGroup group) {
for (int i = 0; i < group.getPreferenceCount(); i++) {
Preference pref = group.getPreference(i);
if (pref instanceof PreferenceGroup) {
groupSummary(sharedPrefs,(PreferenceScreen)pref);
}
else if (pref instanceof PreferenceScreen) {
screenSummary(sharedPrefs,(PreferenceScreen)pref);
}
else {
prefSummary(sharedPrefs, pref);
}
}
}
// preferenceの動的表示
public void prefSummary(SharedPreferences sharedPrefs, Preference pref) {
if (pref instanceof CheckBoxPreference || pref instanceof SwitchPreference ) {
String key = pref.getKey();
boolean val = sharedPrefs.getBoolean(key, false);
pref.setSummary(val?"ON":"OFF");
}
if (pref instanceof EditTextPreference || pref instanceof ListPreference ) {
String key = pref.getKey();
String val = sharedPrefs.getString(key, "");
pref.setSummary(val);
}
if (pref instanceof MultiSelectListPreference) {
String val = multiSelectListSummary((MultiSelectListPreference) pref);
pref.setSummary(val);
}
}
- PreferenceActivityでは、数値の設定をするGUIが無いみたいなので、
- この例では、XMLでEditTextの入力を数値に限定して、さらに桁数を2桁に制限している
- android:numericが integer の場合は、数値しか入力できないので、マイナス値や小数点以下の数値は入力できない
- マイナスを入力できるようにするには signed、小数点を入力するには decimal を指定する
- で、PreferenceActivityのThemeを設定する
- ListPreferenceの選択肢をXMLで設定するんじゃなくて、
- プログラムで変更したかったので、カスタマイズしてみた
public class MyPreference extends ListPreference {
private Context mContext;
private boolean showDialog = false;
private String[] entries = {"Red","Green","Blue"};
private String[] entryValues = {"1","2","3"};
public MyPreference(Context context) {
super(context);
this.mContext = context;
initPreference();
}
public MyPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initPreference();
}
private void initPreference() {
this.setEntries(entries);
this.setDefaultValue(entryValues[0]);
this.setSummary(this.getEntry());
this.setEntryValues(entryValues);
}
}
- Listのエントリを、上書きしてるだけ
7当然、やってくれるものと思っていたので、何が間違ってるか悩んじゃったよ。。
[top]
- adbコマンドで、作成したファイルを確認してみる。
- Windowsのコマンドプロンプトを起動して、adbコマンドを使う
>adb shell ls /sdcard/
- pullコマンドで、sdcardのファイルを現在のディレクトリに取込む
>adb pull /sdcard/xxxxx.txt
>adb rm /sdcard/xxxxx.txt
- Canvasに描画したものを、そのまま画像ファイルとして保存する
public static void png_out(String fname) {
try {
// 出力ファイルの準備
FileOutputStream fos = new FileOutputStream(new File(fname+".png"));
// PNG形式で出力
bitmap.compress(CompressFormat.PNG, 100, fos);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
- SDcardをアプリケーションからアクセスする場合、AndroidManifest.xmlに必要なおまじない、
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- 上にも書いてあるんだけど、大事な事なのでもう一度書いておく。
- SDカード等のストレージ上にファイルを保存して、USB接続したPCからアクセス出来るように8する。
- でも、ファイルを作成しても、そのままではPCから認識出来ない。
- USB接続しなおしてもダメ。Androidを再起動すればいいんだけど。。
- adbコマンドで確認る9すると、ちゃんと作成されているし、Androidを再起動すると見えんだけど。。
- どうやらPC側から参照されるAndroid側のインデックスが更新されていないらしい。
- よく紹介されている、MediaScannerConnection#scanFileで、作成したファイルをメディアスキャンする方法
- ファイル名と、MIMEタイプを指定して登録
MediaScannerConnection.scanFile(
context,
new String[] { fname },
new String[] { mtype },
new OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
Log.d("MediaScannerConnection", "Scanned " + path + ": uri="+uri);
}
});
- これで、インデックスは更新されるはずなんだけど、自分の場合(nexus7)はやはり認識されなかった。
- ちゃんとurlは戻ってくるので、更新自体は成功していると思う10んだけど。
- でも、sendBroadcastでACTION_MEDIA_SCANNER_SCAN_FILEのインテントを投げる方法だと、すぐに認識された。
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(fname))));
- とりあえず、この方法が簡便で良さそうだ。ただ成功したかどうか確認は出来ない。
- 以前は、ACTION_MEDIA_MOUNTEDを投げてメディア全体をスキャンし直すことも出来た11らしいんだけど、
- 現在は、この方法はシステムしか許されないので、Fatalエラーになる。
参考: MediaScannerに登録を依頼する方法
File newfolder = new File( foldername );
newfolder.mkdir();
- これでフォルダは出来るんだけど、MediaScanが効かないので、PCからフォルダとして認識されない。
- ただのファイルとして認識させることは出来るんだけど。。
- フォルダ内にファイルを作成して、それをMediaScanすれば、フォルダも認識されることが分かったので、
- とりあえずは、ダミーのファイルを作成すれば良いんだけど、消すのはScan後にしなければならない。
File newfile = new File(filepath);
newfile.createNewFile();
ファイル削除
file.delete();
ファイルの有無
file.exist();
ファイル名変更
File newfile = new File(foldername+File.separator+"newfilename");
oldfile.renameTo(newfile);
- ファイル作成が失敗した場合は例外が発生するので注意
- ファイル削除やファイル名変更の場合は例外は発生しないので、戻り値をチェックする
- ファイル読込み処理に長時間掛かる場合、しばらく無応答状態になってしまう。
- これは気持ち悪いので、AsyncTaskを継承してプログレス表示する例
public class FilelistOpenAsyncTask extends AsyncTask<String, Integer, Long> implements OnCancelListener{
final String TAG = "FilelistOpenAsyncTask";
ProgressDialog dialog;
Context context;
public FilelistOpenAsyncTask(Context context){
this.context = context;
}
@Override
protected void onPreExecute() {
Log.d(TAG, ">>>>onPreExecute");
dialog = new ProgressDialog(context);
dialog.setTitle("Please wait");
dialog.setMessage("Loading data...");
// dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); // スピナーを表示
dialog.setCancelable(true);
dialog.setOnCancelListener(this);
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
@Override
protected Long doInBackground(String... params) {
Log.d(TAG, ">>>>doInBackground - " + params[0]);
// 長時間掛かるファイル読込み処理等を記述する
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
Log.d(TAG, "onProgressUpdate - " + values[0]);
dialog.setProgress(values[0]);
}
@Override
protected void onCancelled() {
Log.d(TAG, "onCancelled");
dialog.dismiss();
}
@Override
protected void onPostExecute(Long result) {
Log.d(TAG, ">>>>onPostExecute - " + result);
dialog.dismiss();
}
@Override
public void onCancel(DialogInterface dialog) {
Log.d(TAG, "Dialog onCancell... calling cancel(true)");
this.cancel(true);
}
}
new FilelistOpenAsyncTask(this).execute(fname);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
カメラ画像フォルダ
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File[] files = new File(folderpath).listFiles();
for (int i=0; i<files.length; i++) {
if (files[i].isFile() && files[i].getName().endsWith(".txt")) {
Log.d(TAG,"File = "+files[i].getName());
}
}
- Assetsフォルダの場合、ファイル名のみの取得になる12ようだ
AssetManager as = getResources().getAssets();
String[] filelist = null;
try {
filelist = as.list("path");
} catch (IOException e) {
e.printStackTrace();
}
8予め、そのAndroid用デバイスドライバをインストールしておく必要がある。
9このためには、Androidをデベロッパーモードにして、さらにUSBデバッグを許可しておく必要があると思う。
10Androidの内部的には更新されているんだけど、USBストレージとして参照されるインデックスがすぐには更新されないのかな?
11やっぱり全体スキャンしてくれた方が楽で良いんだけどなぁ。。
12File形式で読めないのは、ちょっと面倒だ。
[top]
[プログラムの部屋に戻る]