ギャラリーやフォトアプリのように作るにはGridViewを使いますが、画像の扱いはうっかりするとOutOfMemoryにはまってしまいます。
そこでそういったところを簡単にしてくれるライブラリーPicassoを使ってGridViewを作ってみます。
API 29
AndroidX
Picasso
Picassoはネット上の画像をもってくるのも簡単にできています。通常であれば非同期での http接続を考えなければいけないところ、コードがとても簡単になります。
PicassoをGridViewに導入
Picassoにサンプルコードがあります。ただ膨大な量なので美味しいところだけつまんでみたいということです。Picasso 大きい画像を扱ってみるで基本的な使い方がわかります。ほぼ1行です。
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 |
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. */ //AndroidX import androidx.appcompat.app.AppCompatActivity; //import android.support.v7.app.AppCompatActivity; import android.os.Bundle; 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); // BaseAdapter を継承したGridAdapterのインスタンスを生成 GridAdapter adapter = new GridAdapter( this.getApplicationContext(), 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
import android.content.Context; import android.graphics.Point; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.BaseAdapter; import android.widget.ImageView; import com.squareup.picasso.Picasso; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import static android.content.Context.WINDOW_SERVICE; public class GridAdapter extends BaseAdapter { private Context context; private LayoutInflater inflater; private int layoutId; private List<String> imageList = new ArrayList<>(); private int ScreenWHalf = 0; GridAdapter(Context context, int layoutId, String[] iList) { super(); this.context = context; this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.layoutId = layoutId; Collections.addAll(imageList, iList); // 画面の横幅の半分を計算 WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE); if(wm != null){ Display disp = wm.getDefaultDisplay(); Point size = new Point(); disp.getSize(size); int screenWidth = size.x; ScreenWHalf = screenWidth/2; Log.d("debug","ScreenWidthHalf="+ScreenWHalf); } } 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, "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 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" 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="2dp" android:verticalSpacing="2dp" android:numColumns="2" android:stretchMode="columnWidth" /> </LinearLayout> |
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">YourAppName</string> <string name="description">picture</string> </resources> |
サンプル動画
画像をダウンロードするのですが、通信状況が悪くダウンロードに時間がかかる、あるいは接続先の画像がなかったりすることも考えられます。
ダウンロードに時間がかかって画像が表示されるまで、placeholderを表示させて
接続先の画像に問題があってダウンロードが失敗した場合に、エラー画像を表示させてみましょう。
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