33
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iTunes APIを使ったAndroidアプリハンズオン

Last updated at Posted at 2015-06-04

iTunes APIとは

登録不要で使えて、例えば、

https://itunes.apple.com/search?term=beatles&country=JP&lang=ja_jp&media=music

で、「beatles」を検索すると、

{
  "resultCount":50,
  "results": [
    {
      "wrapperType":"track", 
      "kind":"song", 
      "artistId":136975, 
      "collectionId":458032395, 
      "trackId":458032429, 
      "artistName":"ビートルズ", 
      "collectionName":"1", 
      "trackName":"Let It Be", 
      "collectionCensoredName":"1", 
      "trackCensoredName":"Let It Be", 
      "artistViewUrl":"https://itunes.apple.com/jp/artist/bitoruzu/id136975?uo=4", 
      "collectionViewUrl":"https://itunes.apple.com/jp/album/let-it-be/id458032395?i=458032429&uo=4", 
      "trackViewUrl":"https://itunes.apple.com/jp/album/let-it-be/id458032395?i=458032429&uo=4", 
      "previewUrl":"http://a1770.phobos.apple.com/us/r1000/059/Music4/v4/cf/b4/36/cfb43624-f3e0-269f-f81a-911f9876732c/mzaf_4722855266064184302.plus.aac.p.m4a", 
      "artworkUrl30":"http://is5.mzstatic.com/image/pf/us/r30/Features/85/fe/95/dj.kfrgxzbp.30x30-50.jpg", 
      "artworkUrl60":"http://is4.mzstatic.com/image/pf/us/r30/Features/85/fe/95/dj.kfrgxzbp.60x60-50.jpg", 
      "artworkUrl100":"http://is1.mzstatic.com/image/pf/us/r30/Features/85/fe/95/dj.kfrgxzbp.100x100-75.jpg", 
      "collectionPrice":2000.00, 
      "trackPrice":250.00, 
      "releaseDate":"2000-11-13T08:00:00Z", 
      "collectionExplicitness":"notExplicit", 
      "trackExplicitness":"notExplicit", 
      "discCount":2, 
      "discNumber":1, 
      "trackCount":27, 
      "trackNumber":26, 
      "trackTimeMillis":230773, 
      "country":"JPN", 
      "currency":"JPY", 
      "primaryGenreName":"ロック"
    }, {
      ...
    }, {
      ...
    }
  ]
}

が返ってきます。

今回は、

  • trackName(曲や動画のタイトル)
  • artistName(アーティスト名)
  • artworkUrl100(アートワーク画像URL)
  • previewUrl(曲や動画の試聴用データURL)

あたりを使って、Androidアプリを作ってみます。

プロジェクトを作成する

Android Studioを開きます。
スクリーンショット 2015-06-03 14.02.44.png

スクリーンショット 2015-06-03 14.05.33.png

スクリーンショット 2015-06-03 14.07.06.png

スクリーンショット 2015-06-03 14.09.20.png

自動importを設定する

Preferences > Editor > General > Auto Import >
スクリーンショット 2015-06-04 21.30.21.png

Volleyをインストールする

build.gradleに設定を書く

build.gradle (Module: app)を開いて、dependenciesの中に以下を追加します。

compile 'com.mcxiaoke.volley:library:1.0.16'

スクリーンショット 2015-06-03 14.20.48.png

インストールする

スクリーンショット 2015-06-03 14.21.05.png

iTunes APIと通信して、結果を一覧画面に表示する

Volleyで使う画像キャッシュクラスを作る

ルートパッケージ上で右クリックして、New > Java Class。
スクリーンショット 2015-06-03 15.43.32.png

スクリーンショット 2015-06-03 15.45.20.png

以下のように書きます。先頭行のpackageの行は書いてませんので、コピペするときはpackageの行を消さないように気をつけてください。

import android.graphics.Bitmap;
import android.util.LruCache;

import com.android.volley.toolbox.ImageLoader;


class LruImageCache implements ImageLoader.ImageCache {

    private LruCache<String, Bitmap> mCache;

    public LruImageCache() {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;

        mCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    @Override
    public Bitmap getBitmap(String url) {
        return mCache.get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        mCache.put(url, bitmap);
    }
}

これは、メモリ内に、使用可能なメモリ内の一定の量まで画像をキャッシュし、いっぱいになったら使用頻度が低い画像から破棄されるようになっています。

一覧画面のClassを作る

ルートパッケージ上で右クリックして、New > Activity > Blank Activity。
71158ac0-9fbc-de15-ae8e-34972db40650.png

Launcher ActivityをONにすると、アプリ起動時に表示される画面になります。
スクリーンショット 2015-06-03 15.57.37.png

3つのファイルが作成されます。ListActivity.javaは、Java Classで処理を書くところです。activity_list.xmlは、画面のレイアウトを書くところです。menu_list.xmlは、メニューを書くところです。
スクリーンショット 2015-06-03 16.03.57.png

加えて、AndroidManifest.xmlにも、activity要素が追加されます。Java ClassからActivityを作成した場合は、自分でAndroidManifest.xmlに追加する必要があります。AndroidManifest.xmlに追加しないで呼び出そうとすると実行時エラーになります。activity要素の下のintent-filter要素は、アプリ起動時に表示される画面であることが書かれています(Launcher ActivityをONで作成した場合)。
スクリーンショット 2015-06-03 16.11.42.png

今回はメニューを使わないので、ListActivityのonCreateOptionsMenu(Menu)とonOptionsItemSelected(MenuItem)は削除していいです。menu_list.xmlも削除していいです。
スクリーンショット 2015-06-03 16.17.08.png

インターネットを使う権限を設定する

AndroidManifest.xmlを開いて、manifest要素の中に以下を追加します。これを書かないと、このアプリからインターネットを使うことができません。

    <uses-permission android:name="android.permission.INTERNET" />

一覧画面のActionBarのタイトルをアプリ名にする

AndroidManifest.xmlを開いて、activity要素のandroid:label属性を以下のように変更します。

        <activity
            android:name=".ListActivity"
            android:label="@string/app_name" >

一覧画面のLayout XMLを書く

activity_list.xmlを開いて、以下のように書きます。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.example.kentoriumi.itunesmusicsearch.ListActivity">

    <EditText android:id="@+id/edit_text"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_alignParentTop="true"
              android:hint="Keyword"
              android:inputType="text"/>
    <ListView android:id="@+id/list_view"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_alignParentBottom="true"
              android:layout_below="@id/edit_text"/>

</RelativeLayout>

スクリーンショット 2015-06-03 16.29.50.png

一覧画面の行のLayout XMLを書く

app/res/layout上で右クリックして、New > Layout resource file。
スクリーンショット 2015-06-03 16.33.17.png

スクリーンショット 2015-06-03 16.35.32.png

list_item.xmlを開いて、以下のように書きます。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginRight="10dp"/>

    <TextView android:id="@+id/track_text_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_alignParentTop="true"
              android:layout_toRightOf="@id/image_view"
              android:ellipsize="end"
              android:lines="1"
              android:textSize="20sp"
              tools:text="track"/>

    <TextView android:id="@+id/artist_text_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_alignParentBottom="true"
              android:layout_toRightOf="@id/image_view"
              android:ellipsize="end"
              android:lines="1"
              tools:text="artist"/>

</RelativeLayout>

スクリーンショット 2015-06-03 16.41.02.png

再度、activity_list.xmlを開いて、ListView要素の中に以下を追記すると、プレビューの一覧画面に先ほど作った行が表示されるようになります。

    <ListView android:id="@+id/list_view"
              ...
              tools:listitem="@layout/list_item"/>

スクリーンショット 2015-06-03 16.45.29.png

Volleyを準備する

ListActivity.javaを開いて、ListActivityの中に

    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

を、onCreate(Bundle)の中に

        mRequestQueue = Volley.newRequestQueue(this);
        mImageLoader = new ImageLoader(mRequestQueue, new LruImageCache());

を追加します。importの追加を聞かれたらOKしてください。

行を表示する処理を書く

ListActivity.javaを開いて、ListActivityの中に以下を追加します。importの追加を聞かれたらOKしてください。Adapterは、ListViewのように、保持している大量のデータの中から一部だけを表示するViewを使うときに、Viewとデータの橋渡しをします。

private class ListAdapter extends ArrayAdapter<JSONObject> {

        public ListAdapter(Context context, int resource) {
            super(context, resource);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                // 再利用可能なViewがない場合は作る
                convertView = getLayoutInflater().inflate(R.layout.list_item, null);
            }

            ImageView imageView = (ImageView) convertView.findViewById(R.id.image_view);
            TextView trackTextView = (TextView) convertView.findViewById(R.id.track_text_view);
            TextView artistTextView = (TextView) convertView.findViewById(R.id.artist_text_view);

            ImageLoader.ImageContainer imageContainer = (ImageLoader.ImageContainer) imageView.getTag();
            if (imageContainer != null) {
                imageContainer.cancelRequest(); // 画像取得中のリクエストをキャンセルする(再利用された時)
            }
            imageView.setImageBitmap(null); // 残ってる画像を消す(再利用された時)

            // 表示する行番号のデータを取り出す
            JSONObject result = getItem(position);

            ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, 0, 0);
            imageView.setTag(mImageLoader.get(result.optString("artworkUrl100"), listener));

            trackTextView.setText(result.optString("trackName"));
            artistTextView.setText(result.optString("artistName"));

            return convertView;
        }
    }

さらに、ListActivityの中に

    private ListAdapter mAdapter;

を、onCreate(Bundle)の中に

        mAdapter = new ListAdapter(this, R.layout.list_item);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(mAdapter);

を追加します。先ほど作ったAdapterをListViewに設定してます。

検索の処理を書く

ListActivity.javaを開いて、ListActivityの中に以下を追加します。importの追加を聞かれたらOKしてください。View.OnKeyListenerは、キーボードのキーを押されたりしたときに呼び出されるところです。Enterキーが上がったときに検索処理を実行し、実行結果をAdapterに詰める(詰めたら画面は随時更新される)処理を書いています。

private class OnKeyListener implements View.OnKeyListener {

        @Override
        public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
            if (keyEvent.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) {
                EditText editText = (EditText) view;

                // キーボードを閉じる
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);

                String text = editText.getText().toString();
                try {
                    // url encode 例. スピッツ > %83X%83s%83b%83c
                    text = URLEncoder.encode(text, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    Log.e("", e.toString(), e);
                    return true;
                }
                if (!TextUtils.isEmpty(text)) {
                    String url =
                            "https://itunes.apple.com/search?term=" + text + "&country=JP&media=music&lang=ja_jp";
                    mRequestQueue.add(new JsonObjectRequest(Request.Method.GET, url,
                            new Response.Listener<JSONObject>() {
                                @Override
                                public void onResponse(JSONObject response) {
                                    Log.d("", response.toString());

                                    mAdapter.clear();

                                    JSONArray results = response.optJSONArray("results");
                                    if (results != null) {
                                        for (int i = 0; i < results.length(); i++) {
                                            mAdapter.add(results.optJSONObject(i));
                                        }
                                    }
                                }
                            },
                            null));
                }
                return true;
            }
            return false;
        }
    }

さらに、onCreate(Bundle)の中に

        final EditText editText = (EditText) findViewById(R.id.edit_text);
        editText.setOnKeyListener(new OnKeyListener());

を追加します。先ほど作ったOnKeyListenerをEditTextに設定してます。

ここまでで、ListActivity.javaは以下のようになってるはずです。先頭行のpackageの行は書いてませんので、コピペするときはpackageの行を消さないように気をつけてください。

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


public class ListActivity extends Activity {

    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

    private ListAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);

        mRequestQueue = Volley.newRequestQueue(this);
        mImageLoader = new ImageLoader(mRequestQueue, new LruImageCache());

        mAdapter = new ListAdapter(this, R.layout.list_item);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(mAdapter);
    }

    private class ListAdapter extends ArrayAdapter<JSONObject> {

        public ListAdapter(Context context, int resource) {
            super(context, resource);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                // 再利用可能なViewがない場合は作る
                convertView = getLayoutInflater().inflate(R.layout.list_item, null);
            }

            ImageView imageView = (ImageView) convertView.findViewById(R.id.image_view);
            TextView trackTextView = (TextView) convertView.findViewById(R.id.track_text_view);
            TextView artistTextView = (TextView) convertView.findViewById(R.id.artist_text_view);

            ImageLoader.ImageContainer imageContainer = (ImageLoader.ImageContainer) imageView.getTag();
            if (imageContainer != null) {
                imageContainer.cancelRequest(); // 画像取得中のリクエストをキャンセルする(再利用された時)
            }
            imageView.setImageBitmap(null); // 残ってる画像を消す(再利用された時)

            // 表示する行番号のデータを取り出す
            JSONObject result = getItem(position);

            ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, 0, 0);
            imageView.setTag(mImageLoader.get(result.optString("artworkUrl100"), listener));

            trackTextView.setText(result.optString("trackName"));
            artistTextView.setText(result.optString("artistName"));

            return convertView;
        }
    }

    private class OnKeyListener implements View.OnKeyListener {

        @Override
        public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
            if (keyEvent.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) {
                EditText editText = (EditText) view;

                // キーボードを閉じる
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);

                String text = editText.getText().toString();
                try {
                    // url encode 例. スピッツ > %83X%83s%83b%83c
                    text = URLEncoder.encode(text, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    Log.e("", e.toString(), e);
                    return true;
                }
                if (!TextUtils.isEmpty(text)) {
                    String url =
                            "https://itunes.apple.com/search?term=" + text + "&country=JP&media=music&lang=ja_jp";
                    mRequestQueue.add(new JsonObjectRequest(Request.Method.GET, url,
                            new Response.Listener<JSONObject>() {
                                @Override
                                public void onResponse(JSONObject response) {
                                    Log.d("", response.toString());

                                    mAdapter.clear();

                                    JSONArray results = response.optJSONArray("results");
                                    if (results != null) {
                                        for (int i = 0; i < results.length(); i++) {
                                            mAdapter.add(results.optJSONObject(i));
                                        }
                                    }
                                }
                            },
                            null));
                }
                return true;
            }
            return false;
        }
    }
}

実行してみましょう

詳細画面に遷移して、試聴する

詳細画面のClassを作る

ルートパッケージ上で右クリックして、New > Activity > Blank Activity。
スクリーンショット 2015-06-03 18.16.19.png

スクリーンショット 2015-06-03 18.16.32.png

今回はメニューを使わないので、DetailActivityのonCreateOptionsMenu(Menu)とonOptionsItemSelected(MenuItem)は削除していいです。menu_detail.xmlも削除していいです。

詳細画面のLayout XMLを書く

activity_detail.xmlを開いて、以下のように書きます。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.example.kentoriumi.itunesmusicsearch.DetailActivity">

    <VideoView android:id="@+id/video_view"
               android:layout_width="match_parent"
               android:layout_height="match_parent"/>

</RelativeLayout>

スクリーンショット 2015-06-03 18.22.23.png

一覧画面から詳細画面に遷移する処理を書く

ListActivity.javaを開いて、ListActivityの中に以下を追加します。importの追加を聞かれたらOKしてください。AdapterView.OnItemClickListenerは、ListViewの行がタップされたときに呼び出されるところです。タップされた行番号のデータを取り出し、必要なデータを詳細画面に渡しています。

    private class OnItemClickListener implements AdapterView.OnItemClickListener {

        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
            Intent intent = new Intent(ListActivity.this, DetailActivity.class);

            // タップされた行番号のデータを取り出す
            JSONObject result = mAdapter.getItem(position);
            intent.putExtra("track_name", result.optString("trackName"));
            intent.putExtra("preview_url", result.optString("previewUrl"));

            startActivity(intent);
        }
    }

さらに、onCreate(Bundle)の中に

        listView.setOnItemClickListener(new OnItemClickListener());

を追加します。先ほど作ったOnItemClickListenerをListViewに設定してます。

詳細画面で試聴する処理を書く

DetailActivity.javaを開いて、以下のように書きます。先頭行のpackageの行は書いてませんので、コピペするときはpackageの行を消さないように気をつけてください。

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.MediaController;
import android.widget.VideoView;


public class DetailActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        // ActionBarのタイトルを設定する
        String trackName = getIntent().getExtras().getString("track_name");
        getActionBar().setTitle(trackName);

        String previewUrl = getIntent().getExtras().getString("preview_url");
        if (!TextUtils.isEmpty(previewUrl)) {
            VideoView videoView = (VideoView) findViewById(R.id.video_view);
            videoView.setMediaController(new MediaController(this)); // 再生ボタンとかをつける
            videoView.setVideoURI(Uri.parse(previewUrl)); // URLを設定する
            videoView.start(); // 再生する
        }
    }
}

実行してみましょう

おわり。お疲れさまでした。

完成品はここにあります

AndroidStudioのプロジェクト構成になっているので、Eclipseの場合はごにょごにょしてください。
https://codebreak.com/git/kenchan1837/iTunesMusicSearchAndroid/

33
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?