画像ダウンロード中にローディングっぽい表示を出しておきたい

という需要が良くあるのだけど、標準のSDKのライブラリにはピッタリのものがないので、それっぽいものを書いてみた。

まず、画面のレイアウト。今回はListActivityを使うのでそのように。今回の本筋とは関係ない。

  • layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ListView
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:id="@android:id/list"
    />
</LinearLayout>


こちらは、ProgressBarのくるくる回るやつ用のlayout. codingではこのstyleが指定できないので仕方なくlayoutで。

  • layout/loading
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    style="?android:attr/progressBarStyle"
/>

で、Javaのコード。キモはImageBox class。

package com.komamitsu.hogedoroid;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.app.ListActivity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ViewFlipper;

class ImageBox extends ViewFlipper {
  private final ImageView image;
  private final String imageUrl;
  private Status status = Status.INIT;
  
  private enum Status { INIT, LOADING, LOADED };
  
  public ImageBox(Context context, String imageUrl) {
    super(context);
    this.imageUrl = imageUrl;
    
    LayoutInflater inflater = 
      (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.loading, this);
    
    image = new ImageView(context);
    addView(image);
  }
  
  public void draw() throws IOException {
    if (imageUrl == null) return;
    
    switch (status) {
    case INIT:
      if (getDisplayedChild() == 1) showPrevious();
      LodingTask lt = new LodingTask(imageUrl);
      lt.execute();
      status = Status.LOADING;
      break;
    case LOADING:
      break;
    case LOADED:
      break;
    }
  }
  
  private class LodingTask extends AsyncTask<Void, Drawable, Void> {
    private final String url;
    
    LodingTask(String url) {
      super();
      this.url = url;
    }

    @Override
    protected Void doInBackground(Void... params) {
      URL url;
      try {
        url = new URL(this.url);
        Drawable drawable = Drawable.createFromStream((InputStream)url.getContent(), "src");
        publishProgress(drawable);
      } catch (MalformedURLException e) {
        Log.e(getClass().getName(), "MalformedURLException " + e == null ? "" : e.getMessage());
      } catch (IOException e) {
        Log.e(getClass().getName(), "IOException " + e == null ? "" : e.getMessage());
      }
      return null;
    }

    @Override
    protected void onProgressUpdate(Drawable... values) {
      image.setImageDrawable(values[0]);
      if (getDisplayedChild() == 0) {
        showNext();
      }
      status = ImageBox.Status.LOADED;
    }
  }
}

class HogeListAdapter extends ArrayAdapter<ImageBox> {
  public HogeListAdapter(Context context, int textViewResourceId, List<ImageBox> objects) {
    super(context, textViewResourceId, objects);
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    ImageBox imageBox = getItem(position);
    try {
      imageBox.draw();
    } catch (IOException e) {
      Log.e(getClass().getName(), "IOException " + e == null ? "" : e.getMessage());
    }
    return imageBox;
  }
}

public class Main extends ListActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        List<String> urls = Arrays.asList(
            "http://www.bestfreeicons.com/smimages/walking-icon.png",
            "http://www.bestfreeicons.com/smimages/seniors-icon.png",
            "http://www.bestfreeicons.com/smimages/reading-icon.png",
            "http://www.bestfreeicons.com/smimages/glasses-icon.png",
            "http://www.bestfreeicons.com/smimages/family-icon.png",
            "http://www.bestfreeicons.com/smimages/walking-icon.png",
            "http://www.bestfreeicons.com/smimages/seniors-icon.png",
            "http://www.bestfreeicons.com/smimages/reading-icon.png",
            "http://www.bestfreeicons.com/smimages/glasses-icon.png",
            "http://www.bestfreeicons.com/smimages/family-icon.png"
        );
        List<ImageBox> imageBoxes = new ArrayList<ImageBox>();
        for (String url : urls) {
          imageBoxes.add(new ImageBox(this, url));
        }
        setListAdapter(new HogeListAdapter(this, 0, imageBoxes));
    }

これを起動させると、List上で各画像をダウンロードしようとするのだけど、最初はクルクルしていて、ダウンロード後に切り替わる。読み込みに失敗したら失敗画像でもセットしておけば、より良さげ。

画像を貼り付けようかと思ったけど、ローディング中の間にキャプチャするのに二回失敗したので、風呂に入ってフテ寝することに。