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

もう2年以上前に書いた
Androidの(ちょっと)本格的なミュージックプレーヤの開発
シリーズはおかげさまで未だにじわじわとアクセスカウンタを
回していますが、当時はまだAndroid 2.Xの時代、
流石にちょっと古くなってしまい、サンプルのままでは
上手く動かないという連絡を多く頂くようになりました。

というわけで Android 4.X 〜 でも動くように(もうちょっとだけ)
2時間ほどのやっつけ近代化改修を行ったので補足記事として公開します。

今回作成したアプリの apkファイル
今回作成したアプリの プロジェクトファイル

作業手順概要

今回の近代化にあたっては
まず、Android Studio で
Minimum SDK API 15 Android 4.0.3 (97.4% cover)
Empty Activity
でアプリを作成し、
part1〜6で作った Main以外のクラスをすべてコピー、
layoutなどもまるまるコピーしたところから開始しています。

MainActivityは

public class Main extends AppCompatActivity  implements View.OnClickListener , NavigationView.OnNavigationItemSelectedListener{

こう書き換えて
中身は旧版をコピーしておきます。

ホームの今風化

まずは、完全放置していたホーム画面を今めかしい感じ
にしておきます。
app_barと FloatingActionButton を
menu_home.xml に投げてしまえばそれっぽい
ハリボテの完成です。

サイドナビ実装

次にサイドナビも装備して、最近のアプリ感を
醸し出します。 アイコンはサンプルからコピーしたもの
なのでおかしなことになっていますが
面倒なのでこのままにしてます。

まず、
res > values > styles.xml

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

これを追記、
次に
AndroidManifest.xmlの Mainアクティビティを

android:theme="@style/AppTheme.NoActionBar"

書き換えます。

ここで
drawableにGIMPで作成した
金属のヘアライン処理っぽい画像を背景素材用などをちゃちゃっと
用意して

サイドナビ用のレイアウトを用意します。
基本的にはサンプルをまるまる移植可能です。

res > menu > activity_main_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:title="@string/nav_libraries">
        <menu>
            <group android:checkableBehavior="single">
            <item
                android:id="@+id/nav_tracks"
                android:icon="@drawable/ic_menu_camera"
                android:title="@string/tracks" />
            <item
                android:id="@+id/nav_albums"
                android:icon="@drawable/ic_menu_gallery"
                android:title="@string/albums" />
            <item
                android:id="@+id/nav_artists"
                android:icon="@drawable/ic_menu_slideshow"
                android:title="@string/artists" />
            <item
                android:id="@+id/nav_playlists"
                android:icon="@drawable/ic_menu_manage"
                android:title="@string/playlists" />
            </group>
        </menu>
    </item>>

    <item android:title="@string/nav_application">
    <menu>
        <item
            android:id="@+id/nav_search"
            android:icon="@drawable/ic_menu_share"
            android:title="@string/search" />
        <item
            android:id="@+id/nav_tools"
            android:icon="@drawable/ic_menu_share"
            android:title="@string/tools" />
        <item
            android:id="@+id/nav_settings"
            android:icon="@drawable/ic_menu_send"
            android:title="@string/settings" />
    </menu>
    </item>

</menu>

お次に
res > layout > header_nav.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="@dimen/nav_header_height"
    android:background="@drawable/background"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:weightSum="1">

    <ImageView
        android:id="@+id/imageView"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        app:srcCompat="@drawable/logo"
        android:layout_weight="0.30"
        android:layout_width="70dp"
        android:layout_height="60dp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        android:text="MusicPlayerDH"
        android:textAppearance="@style/TextAppearance.AppCompat.Body2" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hiroumauma" />

</LinearLayout>

これで準備完了です。
旧ソースから持ってきた
linearLayoutを

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout 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:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

        ~~ここに旧コードをそのまま移動~~

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/header_nav"
        app:menu="@menu/activity_main_drawer"
        android:nestedScrollingEnabled="false" />

</android.support.v4.widget.DrawerLayout>

これでサイドナビが付きます。
昔は有志の方が公開した外部ライブラリなどを用意していましたが
楽になりましたね。

Mainクラスの onCreateに

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

を追加

サイドナビでの選択イベントを拾ってくるための以下のメソッドを追加します。

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        int id = item.getItemId();

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        RootMenu fg = (RootMenu)fm.findFragmentByTag("Root");

        switch(id) {
            case R.id.nav_tracks:
                fm.popBackStack("BASE", 0);
                fg.moveTo(1);
                break;
            case R.id.nav_albums:
                fm.popBackStack("BASE", 0);
                fg.moveTo(2);
                break;
            case R.id.nav_artists:
                fm.popBackStack("BASE", 0);
                fg.moveTo(3);
                break;
            case R.id.nav_search:
                searchPopup();
                break;
            case R.id.nav_tools:
                break;
            case R.id.nav_settings:
                break;
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

このメソッドでは

RootMenu fg = (RootMenu)fm.findFragmentByTag("Root");

Mainアクティビティから RootMenuフラグメント へ外からアクセス
しています。アクティビティからフラグメントのメソッドアクセスは
混乱する人が多いみたいです。

フラグメント側の moveTo メソッドは

    public void moveTo(int position)
    {
        mViewPager.setCurrentItem(position, true);
    }

手抜きして追記してあるだけです。
以上で大枠を変更せずにサイドナビがつくと思います。
手順が多いようですが実体は
Android Studio のサイドナビアプリのテンプレートを
移植してだけともいいます。

検索ボタンの話

part6では検索画面を作りましたが、
この呼び出し方法に関してほとんど書かずにおわっていたので補足します。
せっかくなのでサイドナビから呼び出すようにしました。

こんなかんじのダイアログが開いて検索できるようにします。
検索すると
DialogFragment を使って作るように書いてありますが、
これは面倒ですなので、手をぬいた方法でいきます。

まずは
popup_search.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:ems="10"
        android:id="@+id/searchText" />

</LinearLayout>

でEditTextを用意して

Mainアクティビティに

    private  void searchPopup()
    {
        LayoutInflater inflater
                = LayoutInflater.from(this);
        View view = inflater.inflate(R.layout.popup_search, null);
        final EditText editText
                = (EditText)view.findViewById(R.id.searchText);

        new AlertDialog.Builder(this)
                .setTitle(R.string.search_desc)
                .setView(view)
                .setPositiveButton(
                        R.string.search,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                searchWord = editText.getText().toString();
                                setNewFragment(FrgmType.fSearch);
                            }
                        })
                .show();

    }

これだけでできます。
入力されたテキストを取得しているのは

 searchWord = editText.getText().toString();

ここなのでこの前後だけ書き換えるといくらでも使いまわせるはずです。

コメントを追加する