Android用(ちょっと)本格的なミュージックプレーヤの開発 part4

ちょっと間が開いてしまいましたが、ミュージックプレーヤの開発 part4です。

前回までにトラックやアルバムのリストが表示できるようになったので、
今回はそれらをタップ/ロングタップした時にそれぞれ適切なアクションが
発生するようにしていこうと思います。

今回は基本となるアルバムについて扱います。

Fragmentの積み上げ

まずはMainアクティビティにfragmentの積み上げを管理する仕組みを
作っておきます。

最初に、Mainクラスに FrgmTypeという名前で、管理するフラグメントの
タイプを列挙しておきます。 また、現在積み上げられているフラグメントの内、
一番上(つまり実際に表示されている)のフラグメントのタイプを保持するようにします。
現在は RootMenuのライブラリの初期メニューしかありませんが、
今回はアルバムごとの個別メニューを作る予定なので先取りして

enum FrgmType { fRoot, fAlbum }
private FrgmType fTop;

と記述しておきます。
今後、アーティストの個別画面や楽曲検索画面、再生画面などを作ったら
ここにタイプを登録していきましょう。

次に、指定されたタイプのフラグメントを、現在の画面の上に乗せる

setNewFragment(flgmType CallFragment) というメソッドをMainアクティビティに
追加します。

public void setNewFragment(FrgmType CallFragment){

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        fTop = CallFragment;
        switch(CallFragment)
        {
         case fRoot   : ft.replace(R.id.root, new RootMenu(),     "Root"); break;
         case fAlbum  : ft.replace(R.id.root, new RootMenu()/*ダミー*/,   "album"); break;
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        ft.addToBackStack(null);
        ft.commit();
}

現在、アルバム個別メニュー用のコードができていないのでとりあえず
new RootMenu() と書いておきましょう。
次に、現在つまれているFragmentを何らかの理由で1段戻したい時のために
popBackFragment() という簡単なメソッドも追加します。

public void popBackFragment()
{
        FragmentManager fm = getSupportFragmentManager();
        fm.popBackStack();
}

アルバム個別画面の作成

続いて、早速アルバム用画面の作成を開始します。
まずはデザインです。
part_album.xmlという名前で↓のようなデザインを組みました

アルバムタイトルとアート、 アーティスト名、保持しているトラックの数
最後にアルバムに含まれているトラックの一覧 というシンプルな
表示内容です。

続いて、新しく AlbumMenuというクラスを作るのですがその前に。。。
このアルバムメニューは指定されたアルバムの内容を表示させるので
AlbumMenuに開いてほしいアルバムの情報を伝える必要があるのですが、
Fragmentへの引数の受け渡しは若干面倒なので、今回はとりあえず 
ということで Mainクラスに focusedAlbum という名前の変数を作っておいて
それを確かめる方式にしてしまいます。

Mainクラスに

  private Album     focusedAlbum;
 public void      focusAlbum(Album item)  {if(item != null) focusedAlbum  = item;}
 public Album  getFocusedAlbum()       {return focusedAlbum ;}

この3行を追記しておきます。

もう一つ準備が必要です。

上記3行の追加でFragmentから現在選択されているアルバムの情報が
コピーできるようになりました。
次にそのアルバムの情報から、そのアルバムに含まれるトラックの一覧
を取得できるようにしなければいけません。

TrackクラスにList<Track> getItems() というメソッドがありますが、
これは全トラックしか探せないので新たに

List<Track> getItemsByAlbum(Context activity, long albumID)

というアルバムIDを指定するとそのアルバムに登録された
トラックのみのリストを返してくれるメソッドを追加します。

public static List getItemsByAlbum(Context activity, long albumID) {

        List tracks = new ArrayList();
        ContentResolver resolver = activity.getContentResolver();       
        String[] SELECTION_ARG = {""};
        SELECTION_ARG[0] = String.valueOf(albumID);
        Cursor cursor = resolver.query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, 
                Track.COLUMNS, 
                MediaStore.Audio.Media.ALBUM_ID + "= ?",
                SELECTION_ARG,
                null
                );
        while( cursor.moveToNext() ){
                if( cursor.getLong(cursor.getColumnIndex( MediaStore.Audio.Media.DURATION)) < 3000 ){continue;}
                tracks.add(new Track(cursor));
        }
        cursor.close();
        return tracks;
}

これで準備は全て完了です。
AlbumMenuという名前のクラスを作り以下のように実装します。

public class AlbumMenu extends Fragment{

        private static Album album_item; 

        @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
                View partView =inflater.inflate(R.layout.part_album, container, false);  

                Main activity = (Main)getActivity();
                album_item = activity.getFocusedAlbum();

                TextView album_title =  (TextView) partView.findViewById(R.id.title);
                TextView album_artist = (TextView) partView.findViewById(R.id.artist);
                TextView album_tracks = (TextView) partView.findViewById(R.id.tracks);
                ImageView album_art =  (ImageView) partView.findViewById(R.id.albumart);

                album_title.setText(album_item.album);
                album_artist.setText(album_item.artist);
                album_tracks.setText(String.valueOf(album_item.tracks)+"tracks");

                String path = album_item.albumArt;
                album_art.setImageResource(R.drawable.dummy_album_art_slim);
                if(path!=null){
                        album_art.setTag(path);
                        ImageGetTask task = new ImageGetTask(album_art);
                        task.execute(path);
                }

                partView.findViewById(R.id.album_info).setOnClickListener(new View.OnClickListener()
                                                                                                        {@Override public void onClick(View v) {}});
                partView.findViewById(R.id.tracktitle).setOnClickListener(new View.OnClickListener()
                                                                                                        {@Override public void onClick(View v) {}});

                List tracks  = Track.getItemsByAlbum(getActivity(), album_item.albumId);

        ListView trackList = (ListView) partView.findViewById(R.id.list);
        ListTrackAdapter adapter = new ListTrackAdapter(activity, tracks);
        trackList.setAdapter(adapter);

                return partView;
        } 
}

処理内容はあまり説明はいらないかと思います。
getFocusedAlbum() でMainアクティビティでフォーカスされたアルバムアイテムを
もらってきて。その内容をもとに タイトルやトラック数、アルバムアートを表示した後、
albumIDを使って、アルバムに含まれるトラックのみのリストを入手して、
表示しているだけです。

これで、アルバム個別画面が出来ましたので、
最初に作成した setNewFragment() のダミー行を正式なものに直します。

case fAlbum : ft.replace(R.id.root, new RootMenu()/*ダミー*/, "album"); break;

case fAlbum : ft.replace(R.id.root, new AlbumMenu(), "album"); break;

ClickListenerの設定

ここまでの操作で、
 アクティビティがフォーカスしたアルバムの個別メニューの表示
が可能になりましたが、
肝心の
 アルバムのリストから特定のアルバムをフォーカスして、
 AlbumMenuのFragmentを呼び出す
部分ができていないのでささっと作ります。

まずはMainクラスに以下を追加します。

public AdapterView.OnItemClickListener  AlbumClickListener
         = new AdapterView.OnItemClickListener(){
         @Override
         public void onItemClick(AdapterView parent, View view,
                int position, long id) {
                        ListView lv = (ListView)parent;
                        focusAlbum((Album)lv.getItemAtPosition(position));
                        setNewFragment(FrgmType.fAlbum); 
                } 
        };

public  AdapterView.OnItemLongClickListener AlbumLongClickListener
          = new AdapterView.OnItemLongClickListener(){
          @Override
          public boolean onItemLongClick(AdapterView parent, View view,
                 int position, long id){
                        ListView lv = (ListView)parent;
                        Album item = (Album)lv.getItemAtPosition(position);
                        Toast.makeText(Main.this, "LongClick:"+item.album, Toast.LENGTH_LONG).show();
                        return true;    
                }
        };

これはアルバムリストから、あるアルバムをタップ/ロングタップした時に呼ばれる
処理です。タップ時の処理は、(Album)lv.getItemAtPosition(position) にて、
タップされたリストに対応するアルバムアイテムを取得してそれをフォーカスさせて
setNewFragment(flgmType.fAlbum); でアルバムメニューを開いています。

ロングタップ時の処理はまだ作成していないので Toastでとりあえず選択内容を
返しています。今後、再生部分やプレイリスト管理ができるようになれば
このロングタップで サブメニューをポップさせて プレイリストに追加~~ などといった
項目を追加できます。

作成したリスナが実際に呼ばれるようにします。
使い方は簡単です。
RootMenuの、AlbumSectionFragmentクラスにて
 albumList.setAdapter(adapter);
が呼ばれた直後に

albumList.setOnItemClickListener(activity.AlbumClickListener);
albumList.setOnItemLongClickListener(activity.AlbumLongClickListener);

この二行を追加すればOKです。

以上で今回の作業は完了です。
エミュレータで起動してみます。

まずはアルバムメニューで適当にロングタップしてみると、
タップしたアイテムがToastで返され、正しく認識しているのが
確認できます。

そして、アイテムをタップしてみると

この通り、アルバムの個別メニューが直ちに開き、
アルバムに含まれているトラックのみが表示されているのが分かります。

お疲れ様でした。
次回はアーティストの個別メニューについて扱う予定です。

アルバムと違って アーティストは
アーティストが 複数のアルバムを保持して、
それぞれのアルバムが更にトラックを保持する形になっており、
また複数のアーティストが登録されているアルバムの扱いなど
いくつか決めなければいけないルールが発生するので
ちょっと面倒です。

アーティストメニューが出来れば、あとは簡易検索機能を実装して
大体のライブラリ機能は完成です。

それからは遂に実際に音がなるようにしていきます。

コメントを追加する