My Footprint -5ページ目

My Footprint

どんな靴を履いてても、歩けば僕の足跡。
目標に向かって歩き続ける日々を書いていくブログです。

昨日SDカードからファイルを読み込む方法を書いたんですが、別の方法がありました。




ContentResolverとMediaStoreというクラスを使ったやり方。




ContentResolverは簡単にいうとAndroid端末のDBにアクセスするためのクラス(たぶん)。
SDカードや本体にアクセスして連絡先や画像、音楽ファイルなんかを検索できる。

Androidでは定期的にこういったファイルを検索しているらしいので、これにアクセスします。



んで、画像や音楽ファイル、動画なんかにアクセスするときに使うのがMediaStore。



とりあえず詳しい仕様はまだ調べてないので使い方だけ。




public class MusicList extends Activity {

private List&ltFile> musics;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.answer_list);

ArrayAdapter&ltString> adapter
= new ArrayAdapter&ltString>(this, android.R.layout.simple_list_item_1);

musics = new ArrayList&ltFile>();


ContentResolver cr = getApplicationContext().getContentResolver();
Cursor cursor = cr.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI , //データの種類
new String[]{
MediaStore.Audio.Media.ALBUM ,
MediaStore.Audio.Media.ARTIST ,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media._ID
} ,//取得する
null , //フィルター条件 nullはフィルタリング無し
null , //フィルター用のパラメータ
null //並べ替え
);
cursor.moveToFirst();
do{
adapter.add(cursor.getString(cursor.getColumnIndex( MediaStore.Audio.Media.TITLE)));


}while(cursor.moveToNext());

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);

}
}


http://d.hatena.ne.jp/umezo/20100608/1276014215
ここを参考にさせてもらいました。


MediaStoreで指定すればアルバム情報やタイトルなどがもってこれるみたいです。
(何故かアルバムのジャケットはそんなコラムないってエラーになりました)



で、URI(上のMediaStore.Audio.Media.EXTERNAL_CONTENT_URI) + ID でファイルのURIを示すことに
なるそうなので、


MediaPlayer m = new MediaPlayer();
mp.setDataSource(getApplicationContext(),
         Uri.withAppendedPath(
           MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
           cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media._ID))
 )
    );
mp.prepare();
mp.start();
※例外処理は省く


のようにすれば再生もできます。


昨日は拡張子でフィルタリングしましたが、クエリの引数で指定すればフィルタもかけられるようですね。

MediaStoreを使うと音楽ファイルの詳細ももってこれるので楽曲リストつくったりするのに必須になりそうです。



MediaPlayerについてはそのうち書こうかな。
今日はAndroidのSDカードからファイルを読み込む方法。



ちょっと前に知り合いがAndroidはどっから音楽ファイルを持ってくるかわからない!とつぶやいていたので、


SDカードから音楽ファイルを全部引っこ抜いてくる方法を考えました。



まずはSDカードへのアクセス。

File file = Environment.getExternalStorageDirectory();

これでSDカードのルートディレクトリに入れるので、後は下層のディレクトリとファイルをしらみつぶしに検索していきます。




public class SDCS extends Activity {
/** Called when the activity is first created. */

private List&ltFile> musicFiles;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

musicFiles = new ArrayList&ltFile>();

File file = Environment.getExternalStorageDirectory();//SDカードにアクセス

searchMusicFiles(file);

ArrayAdapter&ltString> adapter
= new ArrayAdapter&ltString>(this, android.R.layout.simple_list_item_1);

for(File f : musicFiles){
adapter.add(f.getName());
}

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
}

//再帰的にディレクトリ内を調べるメソッド

private void searchMusicFiles(File f){
if(f.isDirectory()){ // ディレクトリならそれ以下のディレクトリ、ファイルを検査
File[] files = f.listFiles();
for(File file : files){
searchMusicFiles(file); // 再帰
}
}else{ // ファイルの場合は音楽ファイルかどうかの判断
// Androidで扱えるのはmp3とwavファイルなので、
        // これらのファイルの場合リストに保存
if(f.getName().endsWith("mp3") || f.getName().endsWith("MP3")
|| f.getName().endsWith("wav") || f.getName().endsWith("WAV")){
musicFiles.add(f);
}
}
}
}



$My Footprint-sdcardaccess



これでSDカードに入っている音楽ファイルを無差別に引っこ抜いてこれます。


ファイルの拡張子さえ変えれば任意の種類のファイルも持ってこれます(画像とか)


このままだと拡張子が一致するもの全て持ってくるのでなにかフィルターをかけたい場合は
別に処理が必要です。




まぁその辺のサイトにも載ってるんだけど、最近忘れっぽいので備忘録的な感じで。


Androidでリスト表示する時に、一緒に画像やチェックボックスなんかを入れるときは
オリジナルのアダプタを作ってやるのが定石みたいな感じだけど、意外とめんどくさいし変にハマったりすることがある。


今回はチェックボックスを入れるときの注意点的なもの。



基本的にはオリジナルのアダプタを作る時はArrayAdapterを継承して作ることが多いけど、データベースにアクセスして検索結果をリストアップするなんて時は、Cursorを使うことになるのでSimpleCursorAdapterが便利です。


で、アダプタを作る時にgetViewをオーバーライドして要求されたViewを返すわけだけど

このgetViewでちょっと注意が必要で、



public View getView(int position,
View convertView,
ViewGroup parent)


上の2番目の引数がnullじゃないときは、どこかの行で使われたviewで、必ずしもpositionの場所で
使われているViewとは限らないって事があります。


なのでconvertViewがnullの場合は新しくViewを作って追加、nullじゃなければ一回どこかで使われたViewを使いまわすっていうやり方をする必要があるんですが、


これはつまりCheckBoxを入れた場合も一緒で、positionが一緒でもgetViewで帰ってくるCheckBoxが同じオブジェクトとは限らないので、チェックしたところと違うところもチェックがついてるなんて事がおきます。


たいてい画面に映りきっていない部分が一緒にチェックされたりします。


コレを防ぐためにどこの行のCheckBoxがチェックされていたのかって言う情報を保持しておく必要があります。




public class CursorAdapter extends SimpleCursorAdapter {

private List checked;

@Override
public View getView(final int position,View v,ViewGroup parent){

View returnedView = super.getView(position, v, parent);
LinearLayout l = (LinearLayout)returnedView;
CheckBox cb = (CheckBox)l.findViewById(R.id.checkbox);
cb.setOnCheckedChangeListener(
 new CompoundButton.OnCheckedChangeListener(){
@Override
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
// TODO Auto-generated method stub
checked.set(position, arg1); //チェックが変わるたびに
                   //位置と状態を保存
listener.onCheckedChanged(arg0, arg1);
}
});
cb.setChecked(checked.get(position));//positionからチェック
//の状態を取り出す
cb.setFocusable(false);
return returnedView;
}
}
}



※他の部分は省略してます。

getViewにはいる度にリスナーをセットし、Boolean用のリストを作っておき、チェックの状態に変化があった時に位置とチェックの状態を保存しています。

で、Viewを返す前にCheckBoxの状態をpositionにあう状態に変更してます。



これで変なところにチェックがつくとかはなくなります。

今回はSimpleCursorAdapterを継承しているのでCheckBox以外の処理は親クラスのgetViewに任せています。


listener.onCheckedChanged(arg0, arg1) は別クラスのOnCheckedChangeListenerで、チェックの状態が変わった時に他に処理をしたい場合はコンストラクタとかの引数としてイベントハンドラのクラスを渡して、むりやりここで呼び出せば使えます。



あと、cb.setFocusable(false)でフォーカスをfalseにしないとリストのほうがクリックできなくなるので、リストのアイテムをクリックできるようにしたい場合はコレをする必要があります。





最近自分で書いたプログラムもわかんなくなっちゃったりするからコメントとかメモとか
残さなくちゃなぁ・・・