もう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();
ここなのでこの前後だけ書き換えるといくらでも使いまわせるはずです。