サムネイルの表示

最終更新: 2013/7

このチュートリアルではサムネイルの表示方法について学びます。
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ライセンスで提供されています。