解决自定义图库加载图片时,缩略图显示错乱的问题。
问题描述:
自定义图库里的图片缩略图显示重复,且有些位置显示空白。
出问题的代码如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String path = mImageInfos.get(position).getPath();
Holder holder = null;
if(convertView == null){
holder = new Holder();
convertView = mInflater.inflate(R.layout.picture_item, parent, false);
holder.imageView = (ImageView) convertView.findViewById(R.id.iv_picture_item);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.cb_picture_item);
//convertView.setLayoutParams(new AbsListView.LayoutParams(mGridWidth, mGridHeight));
convertView.setTag(holder);
***holder.imageView.setTag(path);***
}else{
holder = (Holder) convertView.getTag();
}
setImageView(path, holder.imageView);
holder.checkBox.setChecked(false);
if (mIsSingleChoice) {
if (position == mChoiceIndex) {
holder.checkBox.setChecked(true);
}
} else {
if (mChecked.contains(position)) {
holder.checkBox.setChecked(true);
}
}
return convertView;
}
其中出问题代码在 holder.imageView.setTag(path); 因为在 convertView == null 时才会执行到该行代码,而当convertView 不为Null 时,执行不到此行代码,会复用上次设置的TAG,这样就会照成图片显示一样的问题。正确的做法是将 holder.imageView.setTag(path); 放到if else 之外执行,为每个View都设置一个不同的TAG标识,在使用的时候每个View取到的TAG都不一样,图片显示才不会重复。
正确的代码应该如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String path = mImageInfos.get(position).getPath();
Holder holder = null;
if(convertView == null){
holder = new Holder();
convertView = mInflater.inflate(R.layout.picture_item, parent, false);
holder.imageView = (ImageView) convertView.findViewById(R.id.iv_picture_item);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.cb_picture_item);
//convertView.setLayoutParams(new AbsListView.LayoutParams(mGridWidth, mGridHeight));
convertView.setTag(holder);
}else{
holder = (Holder) convertView.getTag();
}
//解决图片缩略图错乱的问题
Log.d(TAG, "--setTag--path:"+path+"--position:"+position);
holder.imageView.setTag(path);
setImageView(path, holder.imageView);
holder.checkBox.setChecked(false);
if (mIsSingleChoice) {
if (position == mChoiceIndex) {
holder.checkBox.setChecked(true);
}
} else {
if (mChecked.contains(position)) {
holder.checkBox.setChecked(true);
}
}
return convertView;
}
附上相关代码供参考:
public class FragmentImagesGrid extends Fragment implements View.OnClickListener{
public static final String TAG = "ImagesGrid";
public static final boolean DEBUG = true;
public static final String IMAGES_PATH = "images_path";
public static final String IMAGES = "images";
private ArrayList<ImageInfo> mImageInfos;
private Context mContext;
private ImagesAdapter mAdapter;
private GridView mGridView;
private ImageButton mIbtSend;
private TextView mTvNumber;
private LayoutInflater mInflater;
//we save the checked info to the list
// private SparseBooleanArray mChecked;
private List<Integer> mChecked;
private int mGridWidth;
private int mGridHeight;
private ArrayList<ImageInfo> mResultInfos;
private boolean mIsSingleChoice = true;
private int mChoiceIndex = -1;
private ViewGroup mDialogView;
private ImageView mIvDialogPre;
private Button mBtDialogOk;
private Button mBtDialogCancel;
//private SoftReference<Bitmap> mDrawable = null;
private Bitmap mBitmap;
private Dialog mImageDialog;
private final int MAX_SUM = 9; //单次最多可发送图片的数量
@SuppressWarnings("unchecked")
@Override
public void onCreate(Bundle savedInstanceState) {
//
super.onCreate(savedInstanceState);
if(getArguments() != null){
mImageInfos = (ArrayList<ImageInfo>) getArguments().get(FragmentImagesList.IMAGES_LIST);
}else{
mImageInfos = new ArrayList<ImageInfo>();
}
mResultInfos = new ArrayList<ImageInfo>();
mContext = getActivity();
mInflater = LayoutInflater.from(mContext);
// mChecked = new SparseBooleanArray();
mChecked = new ArrayList<Integer>();
mGridWidth = getActivity().getWindow().getDecorView().getWidth()/3;
mGridHeight = mGridWidth;
//createDialog();
}
private void createDialog() {
mImageDialog = new Dialog(getActivity(), R.style.AletDialogFullScreen);
mDialogView = (ViewGroup) mInflater.inflate(R.layout.image_dialog, null, false);
mIvDialogPre = (ImageView) mDialogView.findViewById(R.id.iv_image_dialog);
mBtDialogOk = (Button) mDialogView.findViewById(R.id.bt_select_ok);
mBtDialogCancel = (Button) mDialogView.findViewById(R.id.bt_select_cancel);
mImageDialog.setContentView(mDialogView);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.images_grid_fragment, container, false);
mGridView = (GridView) layout.findViewById(R.id.gv_images);
mIbtSend = (ImageButton) layout.findViewById(R.id.ibt_pictrue_send);
mIbtSend.setOnClickListener(this);
mTvNumber = (TextView) layout.findViewById(R.id.tv_images_send_number);
mAdapter = new ImagesAdapter(mGridView);
mGridView.setAdapter(mAdapter);
mGridView.setOnScrollListener(mAdapter);
mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if (DEBUG)
Log.d(TAG, "item click xxxxxxxxxxx");
previewImage(position);
}
});
if(DEBUG)
Log.d(TAG, "grid view width and height"+mGridView.getMeasuredHeight()+"//"+mGridView.getMeasuredWidth());
return layout;
}
/**preview show a image
* @param position the index what image have clicked.
* */
private void previewImage(int position) {
if(mDialogView == null){
createDialog();
}
final ImageInfo info = mImageInfos.get(position);
final String path = info.getPath();
File file = new File(path);
if (file != null && file.isFile()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
// intent.setType("image/*");
intent.setDataAndType(Uri.fromFile(file), "image/*");
// intent.setData(Uri.fromFile(file));
// intent.setType("image/*");
try {
startActivity(intent);
} catch(Exception e) {
Log.d(TAG, "Failed to open image in photo. " + e.getMessage());
}
}
// imageLoader.displayImage("file://" + path, mIvDialogPre, options, null, null);
// final int index = position;
// BitmapFactory.Options options = new BitmapFactory.Options();
// options.inSampleSize = 8;
// mBitmap = BitmapFactory.decodeFile(path, options);
// //mDrawable = new SoftReference<Bitmap>(bitmap);
// mIvDialogPre.setImageBitmap(mBitmap);
//
// mImageDialog.show();
// mBtDialogOk.setOnClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View v) {
// if(mIsSingleChoice){
// mChoiceIndex = index;
// }else{
// mResultInfos.add(info);
// mChecked.put(index, true);
// mAdapter.updateCheckedCount();
// mAdapter.notifyDataSetChanged();
// }
// mImageDialog.dismiss();
// //dialog.getWindow().
// }
// });
// mBtDialogCancel.setOnClickListener(new OnClickListener() {
//
// @Override
// public void onClick(View v) {
// if(mIsSingleChoice){
// mChoiceIndex = -1;
// }else{
// mResultInfos.remove(info);
// mChecked.delete(index);
// mAdapter.updateCheckedCount();
// }
// mImageDialog.dismiss();
// }
// });
// mImageDialog.setOnDismissListener(new OnDismissListener() {
//
// @Override
// public void onDismiss(DialogInterface dialog) {
// //mDrawable = null;
// recycler();
// }
// });
}
protected void recycler() {
// first we need set the source null that make mBitmap no uesed. then
// recycle it.
// Log.d(TAG, "recycle");
// mIvDialogPre.setImageBitmap(null);
// mBitmap.recycle();
// //mBitmap = null;
// Log.d(TAG, "the image Source "+ mBitmap +" df"+mIvDialogPre.getDrawable()+" is recycle"+mBitmap.isRecycled());
}
/**
* the adapter for gird view
* */
private class ImagesAdapter extends BaseAdapter implements OnScrollListener {
/**
* 记录所有正在下载或等待下载的任务。
*/
private Set<BitmapWorkerTask> taskCollection;
/**
* 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
*/
private LruCache<String, Bitmap> mMemoryCache;
/**
* 第一张可见图片的下标
*/
private int mFirstVisibleItem;
/**
* 一屏有多少张图片可见
*/
private int mVisibleItemCount;
/**
* 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
*/
private boolean isFirstEnter = true;
private GridView mImagesGrid;
public ImagesAdapter(GridView gridView) {
mImagesGrid = gridView;
taskCollection = new HashSet<BitmapWorkerTask>();
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory /6;
// 设置图片缓存大小为程序最大可用内存的1/6
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
}
@Override
public int getCount() {
//
return mImageInfos.size();
}
@Override
public Object getItem(int position) {
//
return mImageInfos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
String path = mImageInfos.get(position).getPath();
Holder holder = null;
if(convertView == null){
holder = new Holder();
convertView = mInflater.inflate(R.layout.picture_item, parent, false);
holder.imageView = (ImageView) convertView.findViewById(R.id.iv_picture_item);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.cb_picture_item);
//convertView.setLayoutParams(new AbsListView.LayoutParams(mGridWidth, mGridHeight));
convertView.setTag(holder);
}else{
holder = (Holder) convertView.getTag();
}
//解决图片缩略图错乱的问题
Log.d(TAG, "--setTag--path:"+path+"--position:"+position);
holder.imageView.setTag(path);
setImageView(path, holder.imageView);
holder.checkBox.setChecked(false);
if (mIsSingleChoice) {
if (position == mChoiceIndex) {
holder.checkBox.setChecked(true);
}
} else {
if (mChecked.contains(position)) {
holder.checkBox.setChecked(true);
}
}
final int index = position;
final CheckBox box = holder.checkBox;
holder.checkBox.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (checkPictureIsOutLimitSize(index)) {
if(mIsSingleChoice){
if(box.isChecked()){
mChoiceIndex = index;
}else{
mChoiceIndex = -1;
}
notifyDataSetChanged();
}else{
if(box.isChecked()){
saveCheckedToArray(index);
// mChecked.put(index, true);
}else{
// mChecked.delete(index);
if(mChecked.contains(index)){
mChecked.remove(mChecked.indexOf(index));
}
}
updateCheckedCount();
}
}else {
box.setChecked(false);
notifyDataSetChanged();
Toast.makeText(getActivity(), R.string.file_size_too_large,Toast.LENGTH_SHORT).show();
}
}
});
return convertView;
}
private class Holder{
ImageView imageView;
CheckBox checkBox;
}
/**
* check whether current picture is more than 20M
* @param position
*/
private boolean checkPictureIsOutLimitSize(int position){
String path = mImageInfos.get(position).getPath();
File imageFile= new File(path);
long imageFileSize=imageFile.getAbsoluteFile().length();
if (imageFileSize<20*1024*1024L) {
return true;
}else{
return false;
}
}
public void setImageView(String path,ImageView imageView){
Bitmap bitmap = getBitmapFromMemoryCache(path);
if(bitmap != null){
imageView.setImageBitmap(bitmap);
}else{
imageView.setImageResource(R.drawable.message_picture);
}
}
public Bitmap getBitmapFromMemoryCache(String key){
return mMemoryCache.get(key);
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemoryCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
/**
* 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
* 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
*
* @param firstVisibleItem
* 第一个可见的ImageView的下标
* @param visibleItemCount
* 屏幕中总共可见的元素数
*/
private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
//标准
try {
for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
String path = mImageInfos.get(i).getPath();
Bitmap bitmap = getBitmapFromMemoryCache(path);
if (bitmap == null) {
BitmapWorkerTask task = new BitmapWorkerTask();
taskCollection.add(task);
task.execute(path);
}else{
ImageView imageView = (ImageView) mImagesGrid.findViewWithTag(path);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
/**
* 图片的URL地址
*/
private String path;
@Override
protected Bitmap doInBackground(String... params) {
path = params[0];
// 在后台开始下载图片
Bitmap bitmap = createBitmapFromFile(path);
if (bitmap != null) {
// 图片下载完成后缓存到LrcCache中
addBitmapToMemoryCache(path, bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
// 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
ImageView imageView = (ImageView) mGridView.findViewWithTag(path);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
taskCollection.remove(this);
}
/**
* get bitmap from sdcardfile
* */
private Bitmap createBitmapFromFile(String path){
Options opts = new Options();
opts.inJustDecodeBounds = true;
opts.inInputShareable = true;
opts.inPurgeable = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeFile(path, opts);
int imageHeight = opts.outHeight;
int imageWidth = opts.outWidth;
int sampleSize = getSampleSize(imageWidth,imageHeight);
opts.inJustDecodeBounds = false;
opts.inSampleSize = sampleSize;
return BitmapFactory.decodeFile(path, opts);
}
private int getSampleSize(int width,int height){
int inSampleSize = 1;
if(width > mGridWidth || height > mGridHeight){
int widthRatio = Math.round((float)width/mGridWidth);
int heightRatio = Math.round(height/mGridHeight);
inSampleSize =(widthRatio > heightRatio )? widthRatio : heightRatio;
}
return inSampleSize;
}
}
/**
* 取消所有正在下载或等待下载的任务。
*/
public void cancelAllTasks() {
if (taskCollection != null) {
for (BitmapWorkerTask task : taskCollection) {
task.cancel(false);
}
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
if (scrollState == SCROLL_STATE_IDLE) {
loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
} else {
cancelAllTasks();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
Log.d("missyou", "on scroll view the firstVisibleItem "+firstVisibleItem+"visible Item"+visibleItemCount);
mFirstVisibleItem = firstVisibleItem;
mVisibleItemCount = visibleItemCount;
// 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,
// 因此在这里为首次进入程序开启下载任务。
if (isFirstEnter && visibleItemCount > 0) {
loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
isFirstEnter = false;
}
}
}
protected void updateCheckedCount() {
if(mChecked.size() == 0){
mTvNumber.setText("");
mIbtSend.setClickable(false);
}else{
mIbtSend.setClickable(true);
mTvNumber.setText("("+String.valueOf(mChecked.size())+")");
}
}
@SuppressWarnings("unchecked")
public void changedData(Bundle peekData) {
mImageInfos = (ArrayList<ImageInfo>) peekData.get(FragmentImagesList.IMAGES_LIST);
if(mAdapter != null)
mAdapter.notifyDataSetChanged();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ibt_pictrue_send:
finishActivityOnresult();
break;
default:
break;
}
}
private void finishActivityOnresult() {
if(mIsSingleChoice){
if(mChoiceIndex == -1){
return;
}
mResultInfos.add(mImageInfos.get(mChoiceIndex));
}else{
if(mChecked.size() == 0){
return;
}
for(int i =0 ;i<mChecked.size();i++){
int positon = mChecked.get(i);
if(DEBUG)
Log.d(TAG, "we check item position"+positon);
mResultInfos.add(mImageInfos.get(positon));
}
}
if(DEBUG){
if(mIsSingleChoice){
Log.d(TAG, "the result image path is"+mImageInfos.get(mChoiceIndex).getPath());
}
}
Intent data = new Intent();
//Bundle bundle = new Bundle();
//bundle.putSerializable(IMAGES_PATH, mResultInfos);
//data.putSerializable(IMAGES_PATH, mResultInfos);
data.putExtra(IMAGES_PATH, mResultInfos);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
/**
* set whether we just choice single image.
* */
public void setSingleChoice(boolean isSingle){
this.mIsSingleChoice = isSingle;
}
/**
* check whether current checked Picture's Number is more than MAX_SUM
* @return true ,more than MAX_SUM; false otherwise
*/
private boolean checkPictureNumberIsMoreThanMaxSum(){
if(mChecked.size() >= MAX_SUM){
return true;
}
return false;
}
/**
* save current index to Array in order
* @param index ,current index to save
*/
private void saveCheckedToArray(int index){
if(checkPictureNumberIsMoreThanMaxSum()){
MDSVideoCallUtils.showMessage(R.string.image_exceed_max_sum);
Log.e(TAG, "--saveCheckedToArray--removeindex:"+mChecked.get(0));
mChecked.remove(0);
if(mAdapter != null)
mAdapter.notifyDataSetChanged();
}
Log.e(TAG, "--saveCheckedToArray--index:"+index);
mChecked.add(index);
updateCheckedCount();
}
}