サムネイルの表示

このチュートリアルではサムネイルの表示方法について学びます。
thumbnail.cgicommand.cgi を使用します。 このチュートリアルは別項の Android Tutorial 3:コンテンツのダウンロード に基づいています。

サムネイルとファイル名をセットで表示し、インタラクティブなコンテンツリストにします。 アプリケーションの開始時は、ルートフォルダが表示されます。 リストは次のようになります:

This image shows a content list of the root directory

コンテンツリストのイメージファイルをクリック(タップ)すると、 画像はこのように表示されます:

This image shows image view

コンテンツリストのフォルダをクリック(タップ)すると、 その中身が表示されます:

This image shows a content list of a child directory

子のコンテンツリストのイメージファイルをクリック(タップ)すると、 画像はこのように表示されます:

This image shows image view

表示されているとおり、Android 端末に画像ファイルがダウンロードされます。

このチュートリアルでは、データの取得・表示のために、これまでに学んだコマンドを利用します。

アプリケーションを作成するために、次のファイルを作成します :

  • MainActivity.java
  • activity_main.xml
  • ImageViewActivity.java
  • activity_image_view.xml

重要: Android application は、デフォルトの状態ではインターネットにアクセスすることができません。 そのため、設定ファイル AndroidManifest.xml を変更し、権限を与える必要があります。
パス: [Project_Folder]/AndroidManifest.xml AndroidManifest.xml に以下のコードを追加してください:

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

リストのレイアウト作成

ではまず、 activity_main.xml を記述して、レイアウトを決定します。
このファイルは、layout フォルダに配置されています。
パス: [Project_Folder]/res/layout/activity_main.xml

activity_main.xmlAndroid Tutorial 3: コンテンツのダウンロードと同じになります。 実装の説明についてはそちらを参照してください。


イメージビューのレイアウト作成

次に、 activity_image_view.xml を記述して、レイアウトを決定します。
このファイルは、layout フォルダに配置されています。
パス: [Project_Folder]/res/layout/activity_image_view.xml

activity_image_view.xmlAndroid Tutorial 3: コンテンツのダウンロードと同じになります。 実装の説明についてはそちらを参照してください。


コンテンツリストの作成

さて、 MainActivity.java を修正します。
まずは FlashAir の任意のフォルダにあるファイル名の一覧を取得します(詳細は Android Tutorial 3: コンテンツのダウンロード を確認してください)。
次に、リスト内の各ファイルのサムネイルを取得し、 ListView にファイル名とサムネイルの情報を設定します。 リストはクリック可能にします。

初期化

コンテンツリストをクリックしたら、どの要素がクリックされたのかを判別し、画像を表示する必要があります。
onItemClick() のクラス宣言の変更から開始します。
アプリケーションの開始時に、コンテンツリストを表示したいので、 Activity クラスの onCreate(Bundle savedInstanceState) を修正します。

このコードは前回のチュートリアルの内容とよく似ていますが、いくつか異なっている点があります。
前回のチュートリアルのコードを再利用している場合は、変数の型や関数の呼び出しがこのチュートリアルのコードと一致していることを必ず確認してください。
SimpleAdapterを使用する理由については後述します。

MainActivity.java (1)
    public class MainActivity extends Activity implements AdapterView.OnItemClickListener {

        ListView listView;
        ImageView imageView;
        TextView currentDirText;
        TextView numFilesText;
        Button backButton;
        String rootDir = "DCIM";
        String directoryName = rootDir; // Initialize to rootDirectory
        SimpleAdapter listAdapter;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            try {
                // Set buttons
                getWindow().setTitleColor(Color.rgb(65, 183, 216));
                backButton = (Button)findViewById(R.id.button1);
                backButton.getBackground().setColorFilter(Color.rgb(65, 183, 216), PorterDuff.Mode.SRC_IN);
                backButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if(directoryName.equals(rootDir)) {
                            listRootDirectory();
                        }
                        else {
                            int index = directoryName.lastIndexOf("/");
                            directoryName = directoryName.substring(0, index);
                            listDirectory(directoryName);
                        }
                    }
                });
                backButton.setEnabled(false); // Disable in root directory
                listRootDirectory();
            }
            catch(Exception e) {
                Log.e("ERROR", "ERROR: " + e.toString());
                e.printStackTrace();
            }
        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }

ViewBinderの作成

ListView を使用してサムネイルとファイル名のペアを表示します。
しかし、画像はプロジェクトフォルダの Drawable では静的管理していません。実行時に処理する必要があります。
ファイル名と関連付けられたサムネイルを操作するために、 Adapter を使用します。 使用するのは上記で宣言している SimpleAdapter です。 サムネイルとファイル名を表示するのに、 SimpleAdapter が正確なサムネイルとファイル名を map をできるよう、カスタムの ViewBinder を作成する必要があります。

MainActivity.java (2)
        class CustomViewBinder implements ViewBinder {
            @Override
            public boolean setViewValue(View view, Object obj, String text) {
                if((view instanceof ImageView) && (obj instanceof Drawable)) {
                    ImageView imageView = (ImageView) view;
                    BitmapDrawable thumbnail = (BitmapDrawable) obj;
                    imageView.setImageDrawable((Drawable)thumbnail);
                    return true;
                }
                return false;
            }
        }

コンテンツリストの作成

ファイル数を取得するためには、以下のコマンドを使用します。

  • ファイル数は、command.cgiop=101 とフォルダパスを指定することで取得できます。
    • コマンド: http://flashair/command.cgi?op=101&DIR=/DCIM
    • コマンドが返す情報:<NumberofItems>

ファイルデータを取得するためには、以下のコマンドを使用します。

  • ファイルデータは、command.cgiop=100 とフォルダパスを指定することで取得できます。
    • コマンド:http://flashair/command.cgi?op=100&DIR=/DCIM
    • コマンドが返す情報: <Directory>,<Filename>,<Size>,<Attribute>,<Date>,<Time>

サムネイルを取得するためには、以下のコマンドを使用します:

  • thumbnail.cgi はバラメータにファイルパスを指定して使用します。
    • コマンド: http://flashair/thumbnail.cgi?directoryPath/fileName
    • コマンドが返す情報:<EXIF_thumbnail_image>

キー "thmb" と、ファイル名の "fname"Map オブジェクトに設定します。
この Map オブジェクトは 前述した SimpleAdapter で使用される ArrayListに追加されます。
ファイルは Bitmap オブジェクトで読み込みます。

コマンドの実行には前項 Android Tutorial 3: コンテンツのダウンロードまでに作成した、 FlashAirRequest.java を使用します。

listRootDirectory()listDirectory(directoryName)onCreate(Bundle savedInstanceState) で呼ばれ、コンテンツを取得し表示するものです。 詳細は Android Tutorial 3: コンテンツのダウンロード で説明しています。

MainActivity.java (3)
        public void listRootDirectory() {
            directoryName = rootDir;
            listDirectory(directoryName);
        }

        public void listDirectory(String dir) {
            // Prepare command directory path
            if(dir.equals(rootDir)) {
                backButton.setEnabled(false);
            }
            else {
                backButton.setEnabled(true);
            }
            currentDirText = (TextView)findViewById(R.id.textView1);
            currentDirText.setText(dir + "/");
            dir = "/" + dir;
            ArrayList <NameValuePair> httpParams = new  ArrayList <NameValuePair> ();
            httpParams.add(new BasicNameValuePair("DIR", dir));
            dir = URLEncodedUtils.format (httpParams, "UTF-8" );
            numFilesText = (TextView)findViewById(R.id.textView2);                
            // Fetch number of items in directory and display in a TextView
            new AsyncTask<String, Void, String>(){
                @Override
                protected String doInBackground(String... params) {
                    String dir = params[0];
                    String fileCount =  FlashAirRequest.getString("http://flashair/command.cgi?op=101&" + dir);                

                    return fileCount;
                }
                @Override
                protected void onPostExecute(String fileCount) {
                    numFilesText.setText("Items Found: " + fileCount);                
                }
            }.execute(dir);    
            // Fetch list of items in directory and display in a ListView
            new AsyncTask<String, Void, ListAdapter>(){
                @Override
                protected ListAdapter doInBackground(String... params) {
                    String dir = params[0];
                    ArrayList <String> fileNames = new ArrayList <String>();                
                    String files = FlashAirRequest.getString("http://flashair/command.cgi?op=100&" + dir);
                    String[] allFiles = files.split("([,\n])"); // split by newline or comma
                    for(int i = 2; i < allFiles.length; i= i + 6) {
                        if(allFiles[i].contains(".")) {
                            // File
                            fileNames.add(allFiles[i]);
                        }
                        else { // Directory, append "/"
                            fileNames.add(allFiles[i] + "/");
                        }
                    }                    

                    // Get thumbnails
                    ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
                    for(int i = 0; i < fileNames.size(); i++) {
                        String fileName = "";
                        fileName = "http://flashair/thumbnail.cgi?" + directoryName + "/" + fileNames.get(i);
                        Map<String, Object> entry = new HashMap<String, Object>();
                        BitmapDrawable drawnIcon = null;
                        if( (fileName.toLowerCase(Locale.getDefault()).endsWith(".jpg")) || (fileName.toLowerCase(Locale.getDefault()).endsWith(".jpeg")) ) {
                            Bitmap thumbnail = FlashAirRequest.getBitmap(fileName);
                            drawnIcon = new BitmapDrawable(getResources(), thumbnail);
                        }
                        if(drawnIcon == null) {
                            entry.put("thmb", R.drawable.ic_launcher);
                        }
                        else {
                            entry.put("thmb", drawnIcon);
                        }
                        entry.put("fname", fileNames.get(i)); // Put file name onto the map
                        data.add(entry);
                    }

                    // Set the file list to a widget
                    listAdapter = new SimpleAdapter(MainActivity.this,
                            data,
                            android.R.layout.activity_list_item,
                            new String[]{"thmb", "fname"},
                            new int[]{android.R.id.icon, android.R.id.text1});
                listAdapter.setViewBinder(new CustomViewBinder());

                    return listAdapter;
                }
                @Override
                protected void onPostExecute(ListAdapter listAdapter) {
                    listView = (ListView)findViewById(R.id.listView1);
                    ColorDrawable divcolor = new ColorDrawable(Color.rgb(17, 19, 58));
                    listView.setDivider(divcolor);
                    listView.setDividerHeight(1);
                    listView.setAdapter(listAdapter);
                    listView.setOnItemClickListener(MainActivity.this);                
                }
        }.execute(dir);    

    }

thumbnail.cgiは、EXIFデータを持ったJPEGファイルの場合にのみ、サムネイルを返すことに注意してください。

  • 60行目
    JPEG ファイルであるか判別しています。
    ファイルが JPEG であった場合は、EXIFデータを持っているかを判別しています(64行目)。EXIFデータを持っているかどうかは、BitmapDrawable を使用して試験的に保存し、サムネイルがnullであるかどうかで決定しています。

  • 64-66行目
    サムネイルを持っていない場合に、Androidのアイコンを設定しています。

  • 86-91行目
    新規のListViewを作成しています。
    サムネイルとファイル名がそれぞれ view にバインドされています。 作成したViewをレイアウトに設定しています。

  • 75-80行目
    新規のAdapterCustomViewBinderを生成しています。
    これらは、90行目で ListView にバインドされています。

クリックリスナーの設定

Android Tutorial 3: コンテンツのダウンロード と同様の設定を行います:

  • フォルダをクリックした場合は、選択したフォルダのコンテンツを表示した画面を表示します。
  • ファイルをクリックした場合は、ファイルをダウンロードし、ダウンロードした画像を表示しした画面を表示します。

リストの動作は同じですが、書式が異なっています。 ( ListViewAdapterMap オブジェクトとして構成されています。)

この変更に対応するために onItemClick() をオーバーライドします:

MainActivity.java (4)
        @Override
        public void onItemClick(AdapterView<?> l, View v, int position, long id) {
            Object item = l.getItemAtPosition(position); // Get item at clicked position in list of files
            if(item instanceof Map<?, ?>) {
                Map<String, Object> mapItem = (Map<String, Object>) item;
                Object downloadFile = mapItem.get("fname");
                if(downloadFile.toString().endsWith("/")) {
                    // Directory, remove "/" and show content list
                    String dirName = downloadFile.toString().substring(0, downloadFile.toString().length()-1); // All but the "/"
                    directoryName = directoryName + "/" + dirName;
                    listDirectory(directoryName);
                }
                else if( (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpg")) || (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpeg"))
                        || (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".jpe")) || (downloadFile.toString().toLowerCase(Locale.getDefault()).endsWith(".png")) ) {
                    // Image file, download using ImageViewActivity
                    Intent viewImageIntent = new Intent(this, ImageViewActivity.class);
                    viewImageIntent.putExtra("downloadFile", downloadFile.toString());
                    viewImageIntent.putExtra("directoryName", directoryName);
                    MainActivity.this.startActivity(viewImageIntent);
                }
            }
        }
    } // End MainActivity class
  • 4-6行目
    Map オブジェクトを使用して、ListView で選択されたファイル名を取得しています。

画像表示画面の作成

class ImageViewActivityAndroid Tutorial 3: コンテンツのダウンロードと同じです。 実装の詳細についてはこちらのチュートリアルを参照してください。


サンプルコード

android_tutorial_04.zip (533KB)

このサイトのサンプルコードは二条項BSDライセンスで提供されています。