少しトリッキーな問題を解決しようとしています。カスタム BaseAdapter を介してリモート サーバーから画像を取得するグリッドビューがあります。以下の関連コード。
//The gridview
pictureAdapter = new PictureAdapter(cont, document, thumbWidth);
GridView gridview = (GridView) findViewById(R.id.pictures_gridview);
gridview.setColumnWidth(thumbWidth);
gridview.setAdapter(pictureAdapter);
このため、グリッドビューの定義は非常に簡単です...
//The adapter
public class PictureAdapter extends BaseAdapter {
private Context mContext;
private JSONArray mPics;
private List<String> mThumbs;
private List<String> mViews;
private List<Integer> mIds;
private int thumbWidth;
private SparseArray<Bitmap> imageData;
private boolean isFlinging;
public PictureAdapter(Context c, JSONArray pics, int thumbWidth) {
mContext = c;
mPics = pics;
this.thumbWidth = thumbWidth;
setPicThumbs();
imageData = new SparseArray<Bitmap>();
}
public void setFlinging(boolean isFlinging) {
this.isFlinging=isFlinging;
}
public boolean getFlinging() {
return this.isFlinging;
}
private void setPicThumbs() {
mThumbs = new Vector<String>();
mViews = new Vector<String>();
mIds = new Vector<Integer>();
for(int i=0; i<mPics.length(); i++) {
JSONObject row;
try {
row = mPics.getJSONObject(i);
mThumbs.add(row.getString("thumb"));
mViews.add(row.getString("view"));
mIds.add(row.getInt("id"));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public int getCount() {
return mThumbs.size();
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public void setThumbWidth(int width) {
thumbWidth = width;
}
public List<String> getViews() {
return mViews;
}
public View getView(final int position, final View convertView, ViewGroup parent) {
final PycsellImageView imageView;
String imageUrlDirty = mThumbs.get(position);
String imageUrlClean = imageUrlDirty.split("\\?")[0];
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new PycsellImageView(mContext, mIds.get(position));
imageView.setLayoutParams(new GridView.LayoutParams(thumbWidth+3, thumbWidth+3));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(0, 0, 0, 0);
//imageView.setVisibility(View.INVISIBLE);
} else {
imageView = (PycsellImageView) convertView;
imageView.setPicId(mIds.get(position));
if(imageView.getCurrentDrawable() != mThumbs.get(position) || isFlinging == true) {
imageView.setImageDrawable(null);
imageView.setCurrentDrawable("");
}
// imageView.setVisibility(View.INVISIBLE);
}
if(imageData.get(position) != null) {
if (imageView.getDrawable() == null) {
imageView.startFade();
}
imageView.setImageBitmap(imageData.get(position));
imageView.setCurrentDrawable(mThumbs.get(position));
}
else if (isFlinging == false) {
//Log.d("Picture Adapter", "Getting album thumb: "+imageUrlClean);
DownloadHelper downloadHelper = new DownloadHelper() {
public void OnFailure(String response) {
Log.e("Picture Adapter", response);
}
public void OnSucess(Bitmap bitmap) {
if (imageView.getDrawable() == null) {
imageView.setImageBitmap(bitmap);
imageView.setCurrentDrawable(mThumbs.get(position));
imageView.startFade();
}
imageData.put(position, bitmap);
}
};
new ImageTask(mContext, downloadHelper, imageUrlClean).execute();
}
return imageView;
}
}
アダプター内のコードのほとんどはこれには関係ありませんが、全体を示します。リクエスト イメージが DownloadHelper AsyncTask を介してダウンロードされ、ローカルの SparseArray に配置されていることがわかります。画像を再度表示する必要がある場合は、再ダウンロードするのではなく、この配列からフェッチされます。
多数の画像が存在する可能性があるため、明らかにこれは非常に悪いことです。これは単なるプレースホルダー ソリューションです。これらの画像は、ダウンロード後にオフラインでも利用できるようにする必要があるため、明らかにより堅牢な画像キャッシュを実装したいと考えています。問題は... 方法がわかりません。
データベースを実装しました...
public class PycsellDatabase extends SQLiteOpenHelper {
private static final int DB_VERSION = 1;
private static final String DB_NAME = "pycsell_data";
public static final String TABLE_ALBUMS_IMAGES = "albums_and_images";
public static final String ID="_id";
public static final String COL_TYPE="type";
public static final String COL_PYCID="pycsell_id";
public static final String COL_THUMB="thumb";
public static final String COL_VIEW="view";
public static final String COL_ALBUM="album";
public static final String COL_TITLE="title";
public static final String COL_PICNUM="picnum";
private static final String CREATE_TABLE_ALBUMS_IMAGES = "create table" + TABLE_ALBUMS_IMAGES + "("
+ ID + "integer primary key autoincrement"
+ COL_TYPE + "integer not null"
+ COL_PYCID + "integer not null"
+ COL_THUMB + "text not null"
+ COL_VIEW + "text"
+ COL_ALBUM +"integer"
+ COL_TITLE + "text"
+ COL_PICNUM + "integer);";
private static final String DB_SCHEMA = CREATE_TABLE_ALBUMS_IMAGES;
public PycsellDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_SCHEMA);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_ALBUMS_IMAGES);
onCreate(db);
}
}
...そして (部分的に) ContentProvider...
public class BitmapProvider extends ContentProvider {
private PycsellDatabase pDB;
private static final String AUTHORITY = "com.atlantbh.pycsell.db.BitmapProvider";
public static final int ALBUMS=100;
public static final int IMAGES=110;
public static final int SINGLE_IMAGE=120;
private static final String ALBUMS_IMAGES_BASE_PATH = "albums_images";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + ALBUMS_IMAGES_BASE_PATH);
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE+"/album_image";
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE+"/album_image";
private static final UriMatcher sURIMatcher = new UriMatcher(
UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, ALBUMS_IMAGES_BASE_PATH, ALBUMS);
sURIMatcher.addURI(AUTHORITY, ALBUMS_IMAGES_BASE_PATH + "/#", IMAGES);
sURIMatcher.addURI(AUTHORITY, ALBUMS_IMAGES_BASE_PATH + "/#/#", SINGLE_IMAGE);
}
@Override
public boolean onCreate() {
pDB = new PycsellDatabase(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(PycsellDatabase.TABLE_ALBUMS_IMAGES);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
//1 = albums, 2 = images
case ALBUMS:
queryBuilder.appendWhere(PycsellDatabase.COL_TYPE+"= 1");
break;
case IMAGES:
queryBuilder.appendWhere(PycsellDatabase.COL_TYPE+"= 2");
queryBuilder.appendWhere(PycsellDatabase.COL_ALBUM+"="+uri.getLastPathSegment());
break;
case SINGLE_IMAGE:
List<String> segments = uri.getPathSegments();
queryBuilder.appendWhere(PycsellDatabase.COL_TYPE+"= 2");
queryBuilder.appendWhere(PycsellDatabase.COL_ALBUM+"="+segments.get(0));
queryBuilder.appendWhere(PycsellDatabase.COL_PYCID+"="+segments.get(1));
break;
}
Cursor cursor = queryBuilder.query(pDB.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
}
そして、私はこれらを自分で処理できるとかなり確信しています...私が問題を抱えているのは実際のアダプターです。カーソルアダプターの実装方法は知っていますが、「最初にダウンロードし、後でDBからフェッチする」を実行するのではなく、DBに対してのみチェックできます。ロジックは次のようになると思います。
- プロバイダー経由で画像をクエリする
- 戻りカーソルをアダプターに渡します
- カーソルが空の場合 (つまり、DB にその画像のエントリがない場合)、ダウンロードして DB に配置します。
ただし、これがその方法であると100%確信しているわけではありません。どんな助けでも大歓迎です。
敬具、ダミール H.