SSブログ

Android・・・Volley【画像読み込み】 [Android]

先日の『Android・・・Volley』に今度は画像読み込み処理を追加した。

今回はリストビューのリストに画像を設定していく処理を備忘録として記載する。


---------------------------------------------------------------
■Volleyの実装サンプル
---------------------------------------------------------------
①Volleyインスタンス作成・停止クラス
※Volleyはシングルインスタンスで扱うようにすること。
※画像ローダーのインスタンス作成を追加

public class VolleyHelper {
    private static final Object sLock = new Object();
    private static RequestQueue sQueue;
    private static ImageLoaderPlus sImageLoader;

    public static void resetVolley() {
        if (sQueue != null) {
            cancelQueue();
            sQueue.stop();
        }
        sQueue = null;
    }

    public static void addRequest(final Context conetxt, JsonObjectRequest request) {
        if (sQueue == null) {
            sQueue = Volley.newRequestQueue(conetxt);
        }
        sQueue.add(request);
    }

    /**
    * Volleyインスタンス作成処理
    *
    * @param conetxt コンテキスト
    * @return Volleyインスタンス
    */
    public static RequestQueue getRequestQueue(final Context conetxt) {
        synchronized (sLock) {
            if (sQueue == null) {
                sQueue = Volley.newRequestQueue(conetxt);
            }
            return sQueue;
        }
    }

    public static void startQueue(final Context conetxt) {
        if (sQueue == null) {
            sQueue = Volley.newRequestQueue(conetxt);
        }
        sQueue.start();
    }

    public static void cancelQueue() {
        if (sQueue != null) {
            sQueue.cancelAll(new RequestFilter() {;
                @Override
                public boolean apply(Request request) {
                    return true;
                }
            });
        }
    }

    public static void stopQueue() {
        if (sQueue != null) {
            sQueue.stop();
            sQueue = null;
        }
    }

    /**
    * リストまたはグリッドなど複数画像取得時のローダーインスタンス作成処理
    *
    * @param conetxt コンテキスト
    * @param bitmapCache キャッシュ容量設定
    * @return ローダーインスタンス
    */
    public static ImageLoaderPlus getImageLoader(final Context conetxt, BitmapCache bitmapCache) {
        synchronized (sLock) {
            if (sImageLoader == null) {
                sImageLoader = new ImageLoaderPlus(getRequestQueue(conetxt), bitmapCache);
            }

            return sImageLoader;
        }
    }
}




②Bitmapキャッシュクラス
※キャッシュサイズクラスを作成し柔軟にサイズ調整できるようにする。

public class BitmapCache implements ImageLoader.ImageCache {
    public static final Object sLock = new Object();
    private static LruCache mCache;

    public BitmapCache() {
        synchronized (sLock) {
            if (mCache == null) {
                int maxSize = (int) (Runtime.getRuntime().maxMemory() / 1024);
                int cacheSize = maxSize / 8;

                mCache = new LruCache(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);
    }
}




③アダプタによる画像読み込み・設定クラス
※画像の読み込み時は必ずローダーのキャンセルチェックを行うこと。

public class MyAdapter extends BaseAdapter {
    /** コンテキスト */
    private Context mContext;
    private LayoutInflater mInflater;
    /** リスト数 */
    private int mListNum = 0;
    /** 画像キャッシュ */
    private BitmapCache mCache = new BitmapCache();

    public InfoListAdapter(Context context, int itemCount) {
        mContext = context;
        mListNum = itemCount;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return mListNum;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        // 本来は高速かなど考慮する為、下記のように都度viewを設定することはない(あくまでサンプル)
        view = (InfoListLayout) mInflater.inflate(R.layout.listview, null);

 ;       // リクエストのキャンセル処理
        ImageView image = (ImageView) view.findViewById(R.id.image);
        ImageLoaderPlus.ImageContainer imageContainer = (ImageLoaderPlus.ImageContainer) image.getTag();
        if (imageContainer != null) {
            imageContainer.cancelRequest();
        }

        String imageUrl = "xxxxx.png";
        ImageLoaderPlus.ImageListener listener =
            ImageLoaderPlus.getImageListener(
                image,
                R.drawable.waiting,
                R.drawable.error);
            image.setTag(VolleyHelper.getImageLoader(mContext, mCache).get(imageUrl, listener));
        }

        return view;
    }
}



後はActivityやFragment等でお決まりのリスト作成すればよい。
リスト作成時のgetView内でviewを作成する部分だが通常はレイアウトの使い回しなどを行い高速処理を施す。昔から行われているのはレイアウトをstaticで保持して使いまわす方法だがこれとは違う方法を私は実装する。次回はこの部分を備忘録として記載しる予定である。

Android・・・Volley [Android]

あるアプリ作成での通信処理部分を今回はVolleyを使用して作成した。

理由としてはJSONデータを送受信するのみの通信処理だった為。

そして、備忘録として処理を残す。
※今回はJSONデータを送受信するのみだったので最低限の処理のみだが今後画像なども扱う場合は処理内容を追加していく。


■Volleyの公式リポジトリ
https://android.googlesource.com/platform/frameworks/volley/

■Volleyのドキュメント
http://developer.android.com/intl/ja/training/volley/index.html


Volleyは以下の通信処理を効率的に支援してくれている。
・データ取得処理(JSON、XMLなど)
・データパース処理
・表示情報取得処理(画像、テキストなど)
・画面への設定処理(ImageView)


---------------------------------------------------------------
■開発環境
---------------------------------------------------------------
・Windows 7
・Android Studio 1.5


---------------------------------------------------------------
■Volleyの導入
---------------------------------------------------------------
Volleyをプロジェクトに導入するには以下の2つ方法がある。

①gitでVolleyプロジェクトを追加する
Volleyの公式リポジトリよりVolleyを取り組み、開発プロジェクトに追加する。
そして、build.gradle(Module:app)に以下を追加する。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':volley')
}

・メリットについて
プロジェクトごと取り込むので、Volleyの内容についてのカスタマイズ等が可能である。

・デメリットについて
ビルド時に依存関係の解決がされないので場合によって競合が起きる。


②外部リポジトリから取り込む
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.mcxiaoke.volley:library:1.0.+'
}

・メリットについて
バージョン指定が出来、ビルド時の依存関係も意識する必要がない。

・デメリットについて
公開停止になれば使えなくなるのと、Volleyの内容についてのカスタマイズ等が不可能である。


---------------------------------------------------------------
■Volleyの実装サンプル
---------------------------------------------------------------
①Volleyインスタンス作成・停止クラス
※Volleyはシングルインスタンスで扱うようにすること。

public class VolleyHelper {
    private static final Object sLock = new Object();
    private static RequestQueue sQueue;


    public static void resetVolley() {
        if (sQueue != null) {
            sQueue.stop();
        }
        sQueue = null;
    }

    public static void addRequest(final Context conetxt, JsonObjectRequest request) {
        if (sQueue == null) {
            sQueue = Volley.newRequestQueue(conetxt);
        }
        sQueue.add(request);
    }

    /**
    * Volleyインスタンス作成処理
    *
    * @param conetxt コンテキスト
    * @return Volleyインスタンス
    */
    public static RequestQueue getRequestQueue(final Context conetxt) {
        synchronized (sLock) {
            if (sQueue == null) {
                sQueue = Volley.newRequestQueue(conetxt);
            }
            return sQueue;
        }
    }

    public static void startQueue(final Context conetxt) {
        if (sQueue == null) {
            sQueue = Volley.newRequestQueue(conetxt);
        }
        sQueue.start();
    }

    public static void cancelQueue() {
        if (sQueue != null) {
            sQueue.cancelAll(new RequestFilter() {;
                @Override
                public boolean apply(Request request) {
                    return true;
                }
            });
        }
    }

    public static void stopQueue() {
        if (sQueue != null) {
            sQueue.stop();
            sQueue = null;
        }
    }
}




②Volley通信処理のタイムアウト及びリトライ回数設定クラス

public class VolleyRetryPolicy extends DefaultRetryPolicy {
    /** タイムアウト値 */
    private static final int CONNECTION_TIMEOUT = 10000;
    /** リトライ回数(初回リクエスト失敗後の回数) */
    private static final int CONNECTION_RETRY_NUM = 5;
    /** インターバル値 */
    private final long INTERVAL_TIME = 3000;
    private Request mRequest;

    public VolleyRetryPolicy(Request request) {
        super(CONNECTION_TIMEOUT, CONNECTION_RETRY_NUM, 1f);
        mRequest = request;
    }

    @Override
    public void retry(VolleyError error) throws VolleyError {
        NetworkResponse response = error.networkResponse;
        if (response != null && response.statusCode >= 500 && response.statusCode < 600) {
            throw error;
        }

        if (mRequest != null && mRequest.isCanceled()) {
            throw error;
        }

        if (INTERVAL_TIME > 0 ) {
            try {
                Thread.sleep(INTERVAL_TIME);
            }
            catch (InterruptedException e) {
            }
        }
        super.retry(error);
    }
}




③Activityクラス
今回はサンプルなのでActivity内でVolleyをコールして通信する。

public class MainActivity extends AppCompatActivity {
    /** 通信リクエスト */
    protected JsonObjectRequest mRequest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        VolleyHelper.getRequestQueue(getApplicationContext());
        startQueue();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        VolleyHelper.cancelQueue();
        VolleyHelper.stopQueue();
    }

    private void startQueue() {
        final String url = "http://xxxxx/xxxxx(通信先URL)";

        mRequest = new JsonObjectRequest(
            Request.Method.GET,
            url,
            null,
            // 通信成功時の処理
            new Response.Listener() {
                public void onResponse(JSONObject jsonRoot) {
                    //JSON解析処理記述
                    ・・・
                }
            }
        },
        // 通信失敗時の処理
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // ダイアログ等             }
        });
        // リクエストのリトライ及びタイムアウト設定
        mRequest.setRetryPolicy(new VolleyRetryPolicy(mRequest));
        VolleyHelper.addRequest(getApplicationContext(), mRequest);
    }
}




Android・・・NumberPickerの文字サイズ/文字色変更 [Android]

AndroidのNumberPickerによる数字のロール表示について文字サイズを変更した時の備忘録。

最初、NumberPickerからEditTextを取得しそれに対して文字サイズと文字色を設定したが中心部分の文字サイズ及び文字色は設定した通りになったが少しでも上下にスクロールすると元に戻ってしまった。

調べるとスクロールする度にレイアウトに対してViewが追加されていた。それによってレイアウトの設定情報がクリアされてしまうのでその追加される部分で文字サイズと文字色を設定するように変更。

下記はNumberPickerを継承したクラスを用意して文字サイズと文字色を変更した実装サンプルになる。
このクラスを表示したViewに追加すればロール型の数字スクロール表示が出来る。


------------------------------------------------------------------------
■実装
------------------------------------------------------------------------

public class MyNumberPicker extends NumberPicker {

    public MyNumberPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        super.addView(child, params);
        updateView(child);
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        updateView(child);
    }

    private void updateView(View child) {
        if (child instanceof EditText) {
            ((EditText) child).setTextSize(TypedValue.COMPLEX_UNIT_SP, 20.0f);
            ((EditText) child).setTextColor(Color.BLACK);
        }
    }
}


とりあえず、これで期待通り文字サイズも文字色も変更出来た。

Android・・・画像の切り抜き処理 [Android]

Androidで画像にマスク処理を施し、星形やハートなどに写真を加工するの作ってみた。

よくあるのは正方形、長方形、円などの画像表示。これ以外の形で表示してみたい。

考え方は円を作るときに行う切り抜きで作った。円を作るには他にも方法はあるが別の機会に説明ということで。

まず、以下の画像を用意する。

①元画像
②切り抜く型画像

1.については説明することはないので割愛。2.はイメージとしてはお弁当などに使用する型抜きの道具があるがそれを画像にしたイメージと思って欲しい。

上記の画像を用意したら後は元画像の上から型で切り抜けば出来上がり!

試してみると切り抜いた周りのざらつきもほとんどなく綺麗に出来上がった。

実装を備忘録として下記に示す。

-----------------------------------------------------------------------
1.元画像、切り抜き用画像の用意
-----------------------------------------------------------------------

Bitmap original = BitmapFactory.decodeFile(元画像ファイルPATH);
Bitmap mask1 = BitmapFactory.decodeFile(切り抜き画像ファイルPATH);

切り抜き画像を元画像のサイズにリサイズする。

Bitmap mask2 = Bitmap.createScaledBitmap(mask1, original.getWidth(), original.getHeight(), false);


-----------------------------------------------------------------------
2.切り抜いた画像の器を作成
-----------------------------------------------------------------------

Bitmap bitmap = Bitmap.createBitmap(original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888);

これで元画像と同じサイズの単なる器ができた。


-----------------------------------------------------------------------
3.元画像から切り抜き開始
-----------------------------------------------------------------------

1.と2.の処理で画像の用意と切り抜いた後の画像を設定する器ができたので最後に切り抜きを行なう。

Canvas tempCanvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
tempCanvas.drawBitmap(original, 0, 0, null);
tempCanvas.drawBitmap(mask2, 0, 0, paint);
paint.setXfermode(null);


以上でbitmapに切り抜かれた画像情報が設定されたので後は表示させたいImageViewなどに設定するばいい。

Android・・・RecyclerViewによる画像表示 [Android]

RecyclerViewでつまずいた事があったので内容と解決策を残す。

RecyclerViewクラスでレイアウト設定を行なうonBindViewHolderがスクロールの仕方によって呼ばれない場合がある。

まず、ListViewの横スクロール版をRecyclerViewで作成した。
そしてリストそれぞれに通信で取得した画像を設定していく。

まずは想定通りの見た目。
この後、右、左、右、左へとスワイプやスクロールなどでリストをずらしていく。

すると、所々画像が設定されない箇所が出てくる。
再現としてはリストのスクロールの動きをすごく小さくしてずらした場合、激しくスクロールした場合に決まっておきる。

ログなど入れて確認するとレイアウトの更新など行なうonBindViewHolderが呼ばれていない。。。
原因を突き止めたいが時間がない[ふらふら]

とりあえず、addOnScrollListenerを追加しスクロールやスワイプなどリストになんかしらのイベントが発生したらリストの更新を行なうようにした。
ただ、これだと更新処理が頻繁に行なわれるので時間を作ってきちんと原因を追究したいと思う。

もし、同じような現象で悩んでいる人がいたら何でもいいので情報共有お願いします。
nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:仕事

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。