キャッシュ領域にサムネイル画像ファイルを配置の場合。
大量の画像を見るとキャッシュディレクトリの容量が400MBを優に超える。
ので、作業ディレクトリを作って、閲覧ディレクトリごとに作業ディレクトリへサムネイル画像ファイルを作る。
流れは、
・作業ディレクトリを2個作る。
・ディレクトリのフォルダアイテム( dir と file )を取得する。
・ソースファイルから加工してテンプファイルを作業ディレクトリに作る。
・別の閲覧ディレクトリに移るとき、別の作業ディレクトリに場所を移す。
・前の作業ディレクトリのファイルを別スレッドで全て消去する。
・閲覧ディレクトリのフォルダアイテムを取得する。(繰り返し)
一つのディレクトリに50枚程度ならキャッシュファイルは必要ないかも。
一時的にリソースのアイコンを表示しておいて、別スレッドでサムネイル画像に変換して後から画像を更新させる。RecycleView の adapter.notifyItemChanged(no); で個別にアイテム更新できる。
recyclerView の adapter 設定の一部。
loadImage() でサムネイル画像を確認して bitmap を取得している。
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
File souFile = new File( myImageAbsPaths.get(position) );
Bitmap bitmap = loadImage( souFile );
if (bitmap != null)
holder.imageView.setImageBitmap(bitmap);
else
holder.imageView.setImageBitmap( BitmapFactory.decodeResource( mContext().getResources(), R.drawable.not_find) );
holder.textView.setText( String.valueOf( position ) );
holder.linearLayout.setId( holder.getAdapterPosition() );
}
作業ディレクトリ関連のメソッド。
private ArrayList< File> mTempDir = new ArrayList< File>();
private boolean mTempDirFlag = true;
public void setTempDir( File dir1, File dir2 ) { // 作業前に一度だけ初期化する
mTempDir.add(dir1);
mTempDir.add(dir2);
boolean b;
if ( !mTempDir.get(0).exists() ) b= mTempDir.get(0).mkdirs();
if ( !mTempDir.get(1).exists() ) b= mTempDir.get(1).mkdirs();
}
public File getTempDir() {
return (mTempDirFlag)? mTempDir.get(0): mTempDir.get(1);
}
public void switchTempDir() {
mTempDirFlag = !mTempDirFlag;
new Thread(new Runnable() {
@Override
public void run() {
allDeleteFiles();
}
}).start();
}
public void allDeleteFiles(){
int sw = (mTempDirFlag)? 1: 0;
File[] files = mTempDir.get(sw).listFiles();
if ( files == null || files.length == 0 ) return;
boolean b;
for (File file : files) {
b = file.delete();
}
}
/////// UI スレッド上に用意しておいて、別スレッドから呼び出す。
public void adapterNotifyItemChanged( int no ) {
mRecyclerView.post(new Runnable() {
@Override
public void run() {
RecyclerViewAdapter adapter = (RecyclerViewAdapter) mRecyclerView.getAdapter();
if (adapter != null) adapter.notifyItemChanged(no);
}
});
}
/////// スレッド内に直接配置でも動いたが表示に変な挙動が表れた。
Runnableの一部分。実際に動かすには、UIスレッドとの情報のやり取りが必要。引数に構造体を渡す工夫。
public class MyRunnable implements Runnable {
private List< String> mFileNameList = null;
private List< String> mFileAbsPathList = null;
private File mTargetDir = null;
MyRunnable( File dir ) {
mTargetDir = dir;
}
@Override
public void run() {
if ( mTargetDir == null || !mTargetDir.exists() || !mTargetDir.isDirectory() ) return;
mFileNameList = new ArrayList<>();
mFileNameList.addAll( getFileNameListForAdapter() );
mFileAbsPathList = new ArrayList<>();
mFileAbsPathList.addAll( getAbsolutePathListForAdapter() );
File tempDir = getTempDir();
File tempFile, souFile;
for ( int i = 0; i < mFileNameList.size(); i++ ) {
tempFile = new File( tempDir, mFileNameList.get(i) );
if ( tempFile.exists() ) continue;
souFile = new File( mFileAbsPathList.get(i) );
bitmapToThumbnaile( souFile, tempFile );
adapterNotifyItemChanged(i); // 外部のアダプタアイテム更新を呼ぶ
}
finish();
}
private void bitmapToThumbnaile( File souFile, File destfile ) {
Bitmap bitmap = thumbnaileImage( souFile );
if ( bitmap != null ) {
try {
FileOutputStream fos = new FileOutputStream( destfile );
Bitmap.CompressFormat bcf = getCompressFormat( souFile.getName() );
if ( bcf != null ) bitmap.compress( bcf, 100, fos );
fos.close();
} catch (IOException e) {
return;
}
if ( !bitmap.isRecycled() ) bitmap.recycle();
}
bitmap = null;
}
一部を抜粋。