ギャラリーやフォトアプリのように作るにはGridViewを使いますが、画像の扱いはうっかりするとOutOfMemoryにはまってしまいます。
そこでそういったところを簡単にしてくれるライブラリーPicassoを使ってGridViewを作ってみます。
2021.2.1
Picasso
Picassoはネット上の画像をもってくるのも簡単にできています。通常であれば非同期での http接続を考えなければいけないところ、コードがとても簡単になります。
PicassoをGridViewに導入
にサンプルコードがあります。ただ膨大な量なので美味しいところだけつまんでみたいということです。Picasso 大きい画像を扱ってみるで基本的な使い方がわかります。
GirdViewの基本的なところは GridViewの作り方使い方 を参照してください。
ざっくりした差分としてはViewHolderをやめて、Picassoの画像ダウンロードを記述するところです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = inflater.inflate(layoutId, parent, false); } else { view = convertView; } ImageView img = view.findViewById(R.id.image_view); img.setScaleType(ImageView.ScaleType.CENTER_CROP); Picasso.with(context) .load("https://hoge.hage.com/image.jpg") .resize(500, 500) .into(img); return view; } |
サンプルコード
例えば、下のような10個の画像をサーバーに上げて、それをダウンロードして表示させて見たいと思います。
画像サイズはバラバラですが、端末の画面が高解像度で横幅が1000ピクセルを超えているものに綺麗に表示させることを考えて、画面サイズの半分程度1000pix〜290pixあたりの大きさのものにしてみました。
ネットワークを使うのでマニフェストにpermissionを設定
AndroidManifest.xml
1 2 3 4 5 6 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <uses-permission android:name="android.permission.INTERNET" /> ... </manifest> |
Picassoを使うための設定
バージョンは Picasso
を確認して合わせてください。
(2.71828はAndroidXに未対応のようです2019/8)
1 2 3 4 5 |
... dependencies { ... implementation 'com.squareup.picasso:picasso:2.5.2' } |
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
//package your.package.name; /* Copyright 2013 Square, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.WindowMetrics; import android.widget.GridView; public class MainActivity extends AppCompatActivity{ private static final String[] photos = { "yuka1000x1000", "miki900x900", "mai290x290","yumiko790x790", "nagi380x380","saya808x808", "toko644x644", "yuyu410x410", "katakuriko495x508", "kurumi400x400", }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // GridViewのインスタンスを生成 GridView gridview = findViewById(R.id.gridview); // 画面の横幅の半分を計算 WindowMetrics windowMetrics = this.getWindowManager().getCurrentWindowMetrics(); int screenWHalf = windowMetrics.getBounds().height()/2; // BaseAdapter を継承したGridAdapterのインスタンスを生成 GridAdapter adapter = new GridAdapter( this.getApplicationContext(), screenWHalf, R.layout.grid_items, photos); // gridViewにadapterをセット gridview.setAdapter(adapter); } } |
アダプター
GridAdapter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
//package your.package.name; package com.example.testgridviewpicaso; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import com.squareup.picasso.Picasso; public class GridAdapter extends BaseAdapter { private final Context context; private final LayoutInflater inflater; private final int layoutId; private final List imageList = new ArrayList<>(); private final int ScreenWHalf; GridAdapter(Context context, int sWHalf, int layoutId, String[] iList) { super(); this.context = context; ScreenWHalf = sWHalf; this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.layoutId = layoutId; Collections.addAll(imageList, iList); } public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = inflater.inflate(layoutId, parent, false); } else { view = convertView; } ImageView img = view.findViewById(R.id.image_view); img.setScaleType(ImageView.ScaleType.CENTER_CROP); Picasso.with(context) .load(addUrl(position)) .resize(ScreenWHalf, ScreenWHalf) // .placeholder(R.drawable.placeholder) // .error(R.drawable.error) .into(img); return view; } // ネットワークアクセスするURLを設定する private String addUrl(int number){ return String.format(Locale.US, // 自分のサーバーに画像を上げます。 // URLは使えません、ダミーです "https://hoge.hage.com/images/%s.png", imageList.get(number)); } @Override public int getCount() { // 全要素数を返す return imageList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } } |
レイアウトです。
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#888" android:padding="4dp" tools:context=".MainActivity"> <GridView android:id="@+id/gridview" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:background="#888" android:horizontalSpacing="4dp" android:verticalSpacing="2dp" android:numColumns="2" android:stretchMode="columnWidth" /> </androidx.constraintlayout.widget.ConstraintLayout> |
grid_items.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="200dp" android:layout_height="200dp" android:background="#afc" android:orientation="vertical" android:gravity="center" android:padding="3dp" > <ImageView android:id="@+id/image_view" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/description" android:scaleType="fitCenter" /> </LinearLayout> |
strings.xml
1 2 3 4 |
<resources> <string name="app_name">Your App Name</string> <string name="description">picture</string> </resources> |
サンプル動画
画像をダウンロードするのですが、通信状況が悪くダウンロードに時間がかかる、あるいは接続先の画像がなかったりすることも考えられます。
ダウンロードに時間がかかって画像が表示されるまで、placeholderを表示させて
接続先の画像に問題があってダウンロードが失敗した場合に、エラー画像を表示させてみましょう。
placeholderとerror画像をdrawableに入れて実行してみます
1 2 3 4 5 6 |
Picasso.with(context) .load(addUrl(position)) .resize(ScreenWidthHalf, ScreenWidthHalf) .placeholder(R.drawable.placeholder) .error(R.drawable.error) .into(img); |
drawable, assets
Picassoなので設定はdrawable, assets, アプリ内の内部ストレージでもネットURLと同様になります。
.loadでdrawableのR.darowable.XXXを呼び出す
1 2 3 4 5 6 7 8 9 10 |
Picasso.with(context) .load(drawableImage(position)) ... private int drawableImage(int number){ return context.getResources().getIdentifier( imageList.get(number), "drawable", context.getPackageName()); } |
assetsでは
“file:///android_asset/yuka1000x1000”
というassets用のパスを指定すると画像を引っ張ってこられます。
また内部ストレージではファイルのパスは
/data/data/(package name)/files/…
となります
関連記事:
- GridView 作り方使い方
- 標準アイコンをGridViewで表示
- Picasso を使ってネット上の画像をGridViewで表示してみた
References:
Grid View | Android Developers
GridView | Android Developers
Picasso