├── .gitignore
├── res
├── drawable
│ └── icon.png
├── values
│ └── strings.xml
└── layout
│ └── main.xml
├── .classpath
├── AndroidManifest.xml
├── .project
├── proguard.cfg
├── src
└── jp
│ └── sharakova
│ └── android
│ └── urlimageview
│ ├── Request.java
│ ├── sample
│ └── UrlImageViewSampleActivity.java
│ ├── Channel.java
│ ├── WorkerThread.java
│ ├── UrlImageView.java
│ └── ImageCache.java
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | gen/
3 | project.properties
4 |
--------------------------------------------------------------------------------
/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharakova/UrlImageView/master/res/drawable/icon.png
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello World, UrlImageViewSample!
4 | UrlImageView
5 |
6 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | UrlImageView
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/proguard.cfg:
--------------------------------------------------------------------------------
1 | -optimizationpasses 5
2 | -dontusemixedcaseclassnames
3 | -dontskipnonpubliclibraryclasses
4 | -dontpreverify
5 | -verbose
6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7 |
8 | -keep public class * extends android.app.Activity
9 | -keep public class * extends android.app.Application
10 | -keep public class * extends android.app.Service
11 | -keep public class * extends android.content.BroadcastReceiver
12 | -keep public class * extends android.content.ContentProvider
13 | -keep public class * extends android.app.backup.BackupAgentHelper
14 | -keep public class * extends android.preference.Preference
15 | -keep public class com.android.vending.licensing.ILicensingService
16 |
17 | -keepclasseswithmembernames class * {
18 | native ;
19 | }
20 |
21 | -keepclasseswithmembernames class * {
22 | public (android.content.Context, android.util.AttributeSet);
23 | }
24 |
25 | -keepclasseswithmembernames class * {
26 | public (android.content.Context, android.util.AttributeSet, int);
27 | }
28 |
29 | -keepclassmembers enum * {
30 | public static **[] values();
31 | public static ** valueOf(java.lang.String);
32 | }
33 |
34 | -keep class * implements android.os.Parcelable {
35 | public static final android.os.Parcelable$Creator *;
36 | }
37 |
--------------------------------------------------------------------------------
/src/jp/sharakova/android/urlimageview/Request.java:
--------------------------------------------------------------------------------
1 | package jp.sharakova.android.urlimageview;
2 |
3 | import java.io.File;
4 |
5 | public final class Request {
6 | private final String url;
7 | private final File cacheDir;
8 | private final Runnable runnable;
9 | private Status status = Status.WAIT;
10 |
11 | public enum Status {
12 | WAIT, LOADING, LOADED
13 | }
14 |
15 | public Request(String url, File cacheDir) {
16 | this.url = url;
17 | this.cacheDir = cacheDir;
18 | this.runnable = getDefaultRunnable();
19 | }
20 |
21 | public Request(String url, File cacheDir, Runnable runnable) {
22 | this.url = url;
23 | this.cacheDir = cacheDir;
24 | this.runnable = runnable;
25 | }
26 |
27 | public synchronized void setStatus(Status status) {
28 | this.status = status;
29 | }
30 |
31 | public synchronized Status getStatus() {
32 | return status;
33 | }
34 |
35 | public String getUrl() {
36 | return url;
37 | }
38 |
39 | public File getCacheDir() {
40 | return cacheDir;
41 | }
42 |
43 | public Runnable getRunnable() {
44 | return (runnable != null) ? runnable : getDefaultRunnable();
45 | }
46 |
47 | private Runnable getDefaultRunnable() {
48 | return new Runnable() {
49 | public void run() {
50 | }
51 | };
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/jp/sharakova/android/urlimageview/sample/UrlImageViewSampleActivity.java:
--------------------------------------------------------------------------------
1 | package jp.sharakova.android.urlimageview.sample;
2 |
3 | import jp.sharakova.android.urlimageview.ImageCache;
4 | import jp.sharakova.android.urlimageview.R;
5 | import jp.sharakova.android.urlimageview.UrlImageView;
6 | import jp.sharakova.android.urlimageview.UrlImageView.OnImageLoadListener;
7 | import android.app.Activity;
8 | import android.os.Bundle;
9 | import android.widget.Toast;
10 |
11 | public class UrlImageViewSampleActivity extends Activity {
12 |
13 | UrlImageView mImageView;
14 |
15 | /** Called when the activity is first created. */
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.main);
20 |
21 | mImageView = (UrlImageView) findViewById(R.id.imageView);
22 | mImageView.setImageUrl("http://k.yimg.jp/images/top/sp/logo.gif",
23 | imageLoadListener);
24 | }
25 |
26 | @Override
27 | public void onDestroy() {
28 | ImageCache.deleteAll(getCacheDir());
29 | super.onDestroy();
30 | }
31 |
32 | final private OnImageLoadListener imageLoadListener = new OnImageLoadListener() {
33 | @Override
34 | public void onStart(String url) {
35 | Toast.makeText(getApplicationContext(), "start", Toast.LENGTH_SHORT)
36 | .show();
37 | }
38 |
39 | @Override
40 | public void onComplete(String url) {
41 | Toast.makeText(getApplicationContext(), "end", Toast.LENGTH_SHORT)
42 | .show();
43 | }
44 | };
45 | }
--------------------------------------------------------------------------------
/src/jp/sharakova/android/urlimageview/Channel.java:
--------------------------------------------------------------------------------
1 | package jp.sharakova.android.urlimageview;
2 |
3 | import java.util.LinkedList;
4 |
5 | public final class Channel {
6 |
7 | public enum Priority {
8 | HIGH, LOW
9 | }
10 |
11 | private final static Channel instance = new Channel();
12 |
13 | static {
14 | instance.startWorkers();
15 | }
16 |
17 | private static final int MAX_THREAD = 5;
18 | private final LinkedList requestQueue = new LinkedList();
19 | private final WorkerThread[] threadPool;
20 |
21 | private Channel() {
22 | threadPool = new WorkerThread[MAX_THREAD];
23 | for (int i = 0; i < threadPool.length; i++) {
24 | threadPool[i] = new WorkerThread("Worker-" + i, this);
25 | }
26 | }
27 |
28 | public static Channel getInstance() {
29 | return instance;
30 | }
31 |
32 | public synchronized void removeQueueAll() {
33 | requestQueue.clear();
34 | }
35 |
36 | public void startWorkers() {
37 | for (WorkerThread thread : threadPool) {
38 | thread.start();
39 | }
40 | }
41 |
42 | public void stopWorkers() {
43 | for (WorkerThread thread : threadPool) {
44 | thread.interrupt();
45 | }
46 | }
47 |
48 | public synchronized void putRequest(Request request, Priority priority) {
49 | if (priority == Priority.HIGH) {
50 | requestQueue.addFirst(request);
51 | } else {
52 | requestQueue.addLast(request);
53 | }
54 | notifyAll();
55 | }
56 |
57 | public synchronized Request takeRequest() {
58 | while (requestQueue.size() <= 0) {
59 | try {
60 | wait();
61 | } catch (InterruptedException e) {
62 | }
63 | }
64 | return requestQueue.poll();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/jp/sharakova/android/urlimageview/WorkerThread.java:
--------------------------------------------------------------------------------
1 | package jp.sharakova.android.urlimageview;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.lang.ref.SoftReference;
6 | import java.net.HttpURLConnection;
7 | import java.net.URL;
8 |
9 | import android.graphics.Bitmap;
10 | import android.graphics.BitmapFactory;
11 |
12 | public final class WorkerThread extends Thread {
13 | private final Channel channel;
14 |
15 | public WorkerThread(String name, Channel channel) {
16 | super(name);
17 | this.channel = channel;
18 | }
19 |
20 | @Override
21 | public void run() {
22 | while (true) {
23 | Request request = channel.takeRequest();
24 | request.setStatus(Request.Status.LOADING);
25 | SoftReference image = ImageCache.getImage(
26 | request.getCacheDir(), request.getUrl());
27 | if (image == null || image.get() == null) {
28 | image = getImage(request.getUrl());
29 | if (image != null && image.get() != null) {
30 | ImageCache.saveBitmap(request.getCacheDir(),
31 | request.getUrl(), image.get());
32 | }
33 | }
34 | request.setStatus(Request.Status.LOADED);
35 | request.getRunnable().run();
36 | }
37 | }
38 |
39 | private SoftReference getImage(String url) {
40 | try {
41 | return new SoftReference(getBitmapFromURL(url));
42 | } catch (Exception e) {
43 | e.printStackTrace();
44 | return null;
45 | } catch (OutOfMemoryError e) {
46 | e.printStackTrace();
47 | return null;
48 | }
49 | }
50 |
51 | private Bitmap getBitmapFromURL(String strUrl) throws IOException {
52 | HttpURLConnection con = null;
53 | InputStream in = null;
54 |
55 | try {
56 | URL url = new URL(strUrl);
57 | con = (HttpURLConnection) url.openConnection();
58 | con.setUseCaches(true);
59 | con.setRequestMethod("GET");
60 | con.setReadTimeout(500000);
61 | con.setConnectTimeout(50000);
62 | con.connect();
63 | in = con.getInputStream();
64 | return BitmapFactory.decodeStream(in);
65 | } finally {
66 | try {
67 | if (con != null)
68 | con.disconnect();
69 | if (in != null)
70 | in.close();
71 | } catch (Exception e) {
72 | e.printStackTrace();
73 | }
74 | }
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/src/jp/sharakova/android/urlimageview/UrlImageView.java:
--------------------------------------------------------------------------------
1 | package jp.sharakova.android.urlimageview;
2 |
3 | import java.lang.ref.SoftReference;
4 |
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.os.Handler;
8 | import android.util.AttributeSet;
9 | import android.widget.ImageView;
10 |
11 | public final class UrlImageView extends ImageView {
12 | private final Context context;
13 |
14 | private Request request;
15 | private String url;
16 | private boolean isLoading = false;
17 | private final Handler handler = new Handler();
18 | private OnImageLoadListener listener = new OnImageLoadListener() {
19 | public void onStart(String url) {
20 | }
21 |
22 | public void onComplete(String url) {
23 | }
24 | };
25 |
26 | public static interface OnImageLoadListener {
27 | public void onStart(String url);
28 |
29 | public void onComplete(String url);
30 | }
31 |
32 | private final Runnable threadRunnable = new Runnable() {
33 | public void run() {
34 | handler.post(imageLoadRunnable);
35 | }
36 | };
37 |
38 | private final Runnable imageLoadRunnable = new Runnable() {
39 | public void run() {
40 | setImageLocalCache();
41 | }
42 | };
43 |
44 | private boolean setImageLocalCache() {
45 | SoftReference image = ImageCache.getImage(
46 | context.getCacheDir(), url);
47 | if (image != null && image.get() != null) {
48 | setImageBitmap(image.get());
49 | listener.onComplete(url);
50 | isLoading = false;
51 | return true;
52 | }
53 | return false;
54 | }
55 |
56 | public UrlImageView(Context context) {
57 | super(context);
58 | this.context = context;
59 | }
60 |
61 | public UrlImageView(Context context, AttributeSet attrs) {
62 | super(context, attrs);
63 | this.context = context;
64 | }
65 |
66 | public UrlImageView(Context context, AttributeSet attrs, int defStyle) {
67 | super(context, attrs, defStyle);
68 | this.context = context;
69 | }
70 |
71 | public void setOnImageLoadListener(OnImageLoadListener listener) {
72 | this.listener = listener;
73 | }
74 |
75 | public void setImageUrl(String url, OnImageLoadListener listener) {
76 | setOnImageLoadListener(listener);
77 | setImageUrl(url);
78 | }
79 |
80 | public void setImageUrl(String url) {
81 | this.url = url;
82 | isLoading = true;
83 | request = new Request(url, context.getCacheDir(), threadRunnable);
84 | if (setImageLocalCache()) {
85 | return;
86 | }
87 | listener.onStart(url);
88 | Channel.getInstance().putRequest(request, Channel.Priority.HIGH);
89 | }
90 |
91 | public Boolean isLoading() {
92 | return isLoading;
93 | }
94 |
95 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # UrlImageView
2 |
3 | AndroidのImageViewで、インターネット上にある画像を表示する独自ImageView。
4 | MIT Licenseで、自由にお使いください。
5 |
6 | 変更点:
7 | * 複数のUrlImageViewを利用されると、画像の読み込み順番がLIFOの方がなにかと良かったので、画像を読み込む優先順位を変更。
8 | * WorkerThreadを実装しました。スレッド数は5つ。
9 | * これにより、GridViewなどで利用した場合、ユーザ操作で見たい画像がある程度先に読まれるようになった。
10 | * 画像のキャッシュの圧縮率は90です。また、アニメーションGIFなどには対応してません。
11 | * 画像のキャッシュデータをファイルに書き出す際に若干もたつくので、今後の課題です。
12 |
13 | ## 以下、注意点とサンプルActivity
14 |
15 | # Android マニフェスト
16 | * マニフェストファイルに android.permission.INTERNET のパーミッションをつける
17 |
18 |
19 |
20 | # layoutファイル
21 | * レイアウトファイルでは、ImageViewと同様の設定が可能。設定例。
22 |
23 |
28 |
29 | # Activity サンプル
30 | package jp.sharakova.android.urlimageview.sample;
31 | import jp.sharakova.android.urlimageview.ImageCache;
32 | import jp.sharakova.android.urlimageview.R;
33 | import jp.sharakova.android.urlimageview.UrlImageView;
34 | import jp.sharakova.android.urlimageview.UrlImageView.OnImageLoadListener;
35 | import android.app.Activity;
36 | import android.os.Bundle;
37 | import android.widget.Toast;
38 |
39 | public class UrlImageViewSampleActivity extends Activity {
40 |
41 | private UrlImageView mImageView;
42 |
43 | @Override
44 | public void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | setContentView(R.layout.main);
47 | mImageView = (UrlImageView)findViewById(R.id.imageView);
48 | mImageView.setImageUrl("http://k.yimg.jp/images/top/sp/logo.gif", imageLoadListener);
49 | }
50 |
51 | @Override
52 | public void onDestroy() {
53 | ImageCache.deleteAll(getCacheDir());
54 | super.onDestroy();
55 | }
56 |
57 | final private OnImageLoadListener imageLoadListener = new OnImageLoadListener() {
58 | @Override
59 | public void onStart(String url) {
60 | Toast.makeText(getApplicationContext(), "start", Toast.LENGTH_SHORT).show();
61 | }
62 |
63 | @Override
64 | public void onComplete(String url) {
65 | Toast.makeText(getApplicationContext(), "end", Toast.LENGTH_SHORT).show();
66 | }
67 | };
68 | }
69 |
70 |
71 | * OnImageLoadListener の onStart で、画像を読み込む、直前に処理を入れる事ができます。
72 | * OnImageLoadListener の onComplete で、画像を読み込んだ後の処理を実行できます。
73 | * setImageUrl で、画像をインターネットから読み込みを開始して、読み込み終わると画像を表示いたします。読み込んだ画像は、一時的にAndroid内にキャッシュし、2度目の表示では高速に読み込む事ができます。
74 | * onDestroyで実行している。ImageCache.deleteAll(getCacheDir());で、Android内に保存したキャッシュを削除いたします。
--------------------------------------------------------------------------------
/src/jp/sharakova/android/urlimageview/ImageCache.java:
--------------------------------------------------------------------------------
1 | package jp.sharakova.android.urlimageview;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.lang.ref.SoftReference;
7 | import java.util.HashMap;
8 |
9 | import android.graphics.Bitmap;
10 | import android.graphics.BitmapFactory;
11 | import android.util.Log;
12 |
13 | public class ImageCache {
14 |
15 | private ImageCache() {
16 | }
17 |
18 | private static HashMap> cache = new HashMap>();
19 |
20 | private static String getFileName(String url) {
21 | int hash = url.hashCode();
22 | return String.valueOf(hash);
23 | }
24 |
25 | public static void saveBitmap(File cacheDir, String url, Bitmap bitmap) {
26 | cache.put(url, new SoftReference(bitmap));
27 |
28 | String fileName = getFileName(url);
29 | File localFile = new File(cacheDir, fileName);
30 |
31 | FileOutputStream fos = null;
32 | try {
33 | fos = new FileOutputStream(localFile);
34 | bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
35 | } catch (Exception e) {
36 | e.printStackTrace();
37 | } catch (OutOfMemoryError e) {
38 | e.printStackTrace();
39 | } finally {
40 | if (fos != null) {
41 | try {
42 | fos.close();
43 | } catch (IOException e1) {
44 | Log.w("save", "finally");
45 | }
46 | }
47 | }
48 | }
49 |
50 | public static SoftReference getImage(File cacheDir, String url) {
51 |
52 | SoftReference ref = cache.get(url);
53 | if (ref != null && ref.get() != null) {
54 | return ref;
55 | }
56 |
57 | String fileName = getFileName(url);
58 | File localFile = new File(cacheDir, fileName);
59 | SoftReference bitmap = null;
60 | try {
61 | bitmap = new SoftReference(
62 | BitmapFactory.decodeFile(localFile.getPath()));
63 | } catch (Exception e) {
64 | e.printStackTrace();
65 | } catch (OutOfMemoryError e) {
66 | e.printStackTrace();
67 | cache.clear();
68 | }
69 | return bitmap;
70 | }
71 |
72 | public static void memoryCacheClear() {
73 | cache.clear();
74 | }
75 |
76 | public static void deleteAll(File cacheDir) {
77 | if (!cacheDir.isDirectory()) {
78 | return;
79 | }
80 | File[] files = cacheDir.listFiles();
81 | for (File file : files) {
82 | if (file.isFile()) {
83 | if (!file.delete()) {
84 | Log.v("file", "file delete false");
85 | }
86 | }
87 | }
88 | }
89 |
90 | public static long dirSize(File cacheDir) {
91 | long size = 0L;
92 | if (cacheDir == null) {
93 | return size;
94 | }
95 | if (cacheDir.isDirectory()) {
96 | for (File file : cacheDir.listFiles()) {
97 | size += file.length();
98 | }
99 | } else {
100 | size = cacheDir.length();
101 | }
102 | return size;
103 | }
104 | }
--------------------------------------------------------------------------------