Android 带有复选框的自定义listview在滚动时崩溃
我正在使用带有复选框、imageview和2文本框的listview。 列出使用ArayAdapter生成的列表,但我正在缓慢地滚动listview,但当我以某种速度滚动listview时,它崩溃了。 下面是我的代码 导入java.io.File; 导入java.util.ArrayList; 导入java.util.ListAndroid 带有复选框的自定义listview在滚动时崩溃,android,android-listview,android-arrayadapter,Android,Android Listview,Android Arrayadapter,我正在使用带有复选框、imageview和2文本框的listview。 列出使用ArayAdapter生成的列表,但我正在缓慢地滚动listview,但当我以某种速度滚动listview时,它崩溃了。 下面是我的代码 导入java.io.File; 导入java.util.ArrayList; 导入java.util.List import android.annotation.SuppressLint; import android.annotation.TargetApi; import a
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
@SuppressLint("SdCardPath")
public class AddOptionItemAdapter extends ArrayAdapter<String>
{
String MenuId="";
Context context;
String tag;
String tag1;
String ogFilePath="";
String imgpath="";
private List<NameBean> list;
ArrayList< String>priceList=new ArrayList<String>();
ArrayList< String>imagepathList=new ArrayList<String>();
public AddOptionItemAdapter(Context context, List<NameBean> list,ArrayList<String> priceList,ArrayList<String> imagePathList) {
super(context, R.layout.lvoptncat,priceList);
this.list=list;
this.priceList=priceList;
this.imagepathList=imagePathList;
this.context=context;
}
@TargetApi(16)
public View getView (int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if (convertView == null)
{
holder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(R.layout.lvoptncat, null);
holder.tvOptnCatName=(com.example.hotelmenu.CustomTextView)convertView.findViewById(R.id.tvSubMenu);
holder.tvPrice=(com.example.hotelmenu.CustomTextView)convertView.findViewById(R.id.tvPrice);
holder.checkbox = (CheckBox) convertView.findViewById(R.id.checkBox1);
holder.imgPath = (ImageView) convertView.findViewById(R.id.imgMenu);
holder.checkbox
.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton view,
boolean isChecked) {
int getPosition = (Integer) view.getTag();
addOptionActivity.optnCatNameList.get(getPosition).setSelected(view.isChecked());
}
});
}
else
holder = (ViewHolder) convertView.getTag();
String filepath = Environment.getExternalStorageDirectory().getPath();
File file = new File(filepath,"HotelMenuImages");
if(!file.exists())
{
file.mkdirs();
}
ogFilePath=file.getAbsolutePath();
//String str=getItem(position);
//String []OptnCatDetails=str.split(",");
//DataSource datasource=new DataSource(context);
try
{
//Log.d("ImagePath111",MenuDetails[3].trim());
//String photo2 =OptnCatDetails[2];
//Log.d("ImagePath1222",photo2);
String photo2=imagepathList.get(position);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize = 4;
Bitmap myBitmap = BitmapFactory.decodeFile(ogFilePath+"/"+photo2, options);
holder.imgPath.setImageBitmap(myBitmap);
}
catch(OutOfMemoryError e)
{
Log.d("ImageError",""+e);
}
holder.tvOptnCatName.setText(list.get(position).getName());
holder.tvPrice.setText(priceList.get(position));
holder.checkbox.setTag(position);
holder.checkbox.setChecked(addOptionActivity.optnCatNameList.get(position).isSelected());
return convertView;
}
class ViewHolder {
protected com.example.hotelmenu.CustomTextView tvOptnCatName,tvPrice;
protected CheckBox checkbox;
protected ImageView imgPath;
}
}
请从getView()中删除文件操作和重位图操作 使用下面的类来处理列表视图中的图像,而不是使用此类
public class ImageStorage {
private String mBaseDir;
private static final String TAG = "Image Storage";
private static boolean DEBUG = false;
public static final int DEFAULT_CACHE_SIZE = 64;
private BasicBitmapCache mCache;
private Handler mHandler = new Handler();
private ExecutorService mExecutor = Executors.newCachedThreadPool();
private Set<LoadRequest> mActiveRequests = new HashSet<LoadRequest>();
public static class LoadRequest {
public LoadRequest(String key, ImageView v) {
if (key == null)
throw new NullPointerException("key must not be null");
mKey = key;
mImageView = v;
}
public ImageView getImageView() {
return mImageView;
}
public String getKey() {
return mKey;
}
@Override
public boolean equals(Object b) {
if (b instanceof LoadRequest)
return mKey.equals(((LoadRequest) b).getKey());
return false;
}
private String mKey;
private ImageView mImageView;
}
public ImageStorage(String baseDir) {
this.mBaseDir = baseDir;
this.mCache = new BasicBitmapCache(DEFAULT_CACHE_SIZE);
}
public boolean exists(String key) {
File file = new File(new File(mBaseDir), key);
return file.exists();
}
public Bitmap loadData(String key) {
if (!exists(key)) {
return null;
}
// load the bitmap as-is (no scaling, no crop)
Bitmap bitmap = null;
File file = new File(new File(mBaseDir), key);
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
bitmap = BitmapFactory.decodeStream(fis);
if (bitmap == null) {
// something wrong with the persistent data, can't be decoded to
// bitmap.
// removeDir(file); // Let's remove this file will gets
// downloaded again from server
throw new RuntimeException(
"data from db can't be decoded to bitmap");
}
return bitmap;
} catch (IOException e) {
if (DEBUG)
Log.e(TAG, "error loading bitmap", e);
return null;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
}
}
}
}
public void storeData(String key, Bitmap data) {
if (data == null || data.isRecycled())
return;
//Scaling image before storing
data = scaleBitmap(data);
OutputStream outputStream = null;
try {
File file = new File(new File(mBaseDir), key);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
outputStream = new FileOutputStream(file);
if (!data.compress(Bitmap.CompressFormat.PNG, 100, outputStream)) {
throw new RuntimeException("failed to compress bitmap");
}
} catch (IOException e) {
if (DEBUG)
Log.e(TAG, "error storing bitmap", e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
}
}
}
}
public void clear() {
try {
this.removeDir(new File(mBaseDir));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public int deleteFile(String key) {
if (!exists(key)) {
return -1;
}
File file = new File(new File(mBaseDir), key);
boolean deleted = file.delete();
if (!deleted)
return 0;
return 1;
}
/**
* Delete a directory
*
* @param d
* the directory to delete
*
*/
private void removeDir(File d) throws IOException {
// to see if this directory is actually a symbolic link to a directory,
// we want to get its canonical path - that is, we follow the link to
// the file it's actually linked to
File candir = d.getCanonicalFile();
// a symbolic link has a different canonical path than its actual path,
// unless it's a link to itself
if (!candir.equals(d.getAbsoluteFile())) {
// this file is a symbolic link, and there's no reason for us to
// follow it, because then we might be deleting something outside of
// the directory we were told to delete
return;
}
// now we go through all of the files and subdirectories in the
// directory and delete them one by one
File[] files = candir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File file = files[i];
// in case this directory is actually a symbolic link, or it's
// empty, we want to try to delete the link before we try
// anything
boolean deleted = file.delete();
if (!deleted) {
// deleting the file failed, so maybe it's a non-empty
// directory
if (file.isDirectory())
removeDir(file);
// otherwise, there's nothing else we can do
}
}
}
// now that we tried to clear the directory out, we can try to delete it
// again
d.delete();
}
public Bitmap loadImage(LoadRequest r) {
if (r == null || r.getKey() == null) {
// throw new IllegalArgumentException( "null or empty request");
return null;
}
ImageView v = r.getImageView();
if (v != null) {
synchronized (v) {
v.setTag(r.getKey()); // bind URI to the ImageView, to prevent
// image write-back of earlier requests.
}
}
mExecutor.submit(newRequestCall(r));
return null;
}
private Bitmap scaleBitmap(Bitmap image) {
int width = image.getWidth();
int height = image.getHeight();
float scale = (float)480/(float)width;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(image,
(int) (width * scale), (int) (height * scale), true);
// Bitmap scaledBitmap = Bitmap.createBitmap(images.get(i), 0, 0, width,
// height, matrix, true);
return scaledBitmap;
}
private Callable<LoadRequest> newRequestCall(final LoadRequest request) {
return new Callable<LoadRequest>() {
public LoadRequest call() {
synchronized (mActiveRequests) {
while (mActiveRequests.contains(request)) {
try {
mActiveRequests.wait();
} catch (InterruptedException e) {
}
}
mActiveRequests.add(request);
}
Bitmap data = null;
try {
String key = request.getKey();
data = mCache.loadData(key);
if (data == null) {
if (DEBUG)
Log.d(TAG, "cache missing " + request.getKey());
// then check the persistent storage
data = loadData(key);
if (data != null) {
if (DEBUG)
Log.d(TAG,
"found in persistent: "
+ request.getKey());
mCache.storeData(key, data);
}
}
if (data != null && !data.isRecycled()
&& request.getImageView() != null) {
final Bitmap theData = data;
final ImageView iv = request.getImageView();
synchronized (iv) {
if (iv != null && iv.getTag() == request.getKey()) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (iv.getTag() == request.getKey()
&& !theData.isRecycled()) {
iv.setAnimation(null);
iv.setImageBitmap(theData);
}
}
});
}
}
}
} catch (Throwable e) {
if (DEBUG)
Log.e(TAG,
"error handling request " + request.getKey(), e);
} finally {
synchronized (mActiveRequests) {
mActiveRequests.remove(request);
mActiveRequests.notifyAll(); // wake up pending requests
// who's querying the
// same URL.
}
if (DEBUG)
Log.d(TAG, "finished request for: " + request.getKey());
}
return request;
}
};
}
}
基本位图缓存在这里
/**
* Basic implementation of BitmapCache
*
* @author Chirag Jain
*/
public class BasicBitmapCache {
private static class CacheEntry {
public Bitmap data;
public int nUsed;
public long timestamp;
}
private static final String TAG = "BasicBitmapCache";
private static final boolean DEBUG = false;
private int mMaxSize;
private HashMap<String, CacheEntry> mMap = new HashMap<String, CacheEntry>();
/**
* max number of resource this cache contains
*
* @param size
*/
public BasicBitmapCache(int size) {
this.mMaxSize = size;
}
public synchronized boolean exists(String key) {
return mMap.get(key) != null;
}
public synchronized void invalidate(String key) {
CacheEntry e = mMap.get(key);
Bitmap data = e.data;
data.recycle();
mMap.remove(key);
if (DEBUG)
Log.d(TAG, key + " is invalidated from the cache");
}
public synchronized void clear() {
for (String key : mMap.keySet()) {
invalidate(key);
}
}
/**
* If the cache storage is full, return an item to be removed.
*
* Default strategy: the least and oldest out: O(n)
*
* @return item key
*/
protected synchronized String findItemToInvalidate() {
Map.Entry<String, CacheEntry> out = null;
for (Map.Entry<String, CacheEntry> e : mMap.entrySet()) {
if (out == null || e.getValue().nUsed < out.getValue().nUsed
|| e.getValue().nUsed == out.getValue().nUsed
&& e.getValue().timestamp < out.getValue().timestamp) {
out = e;
}
}
return out.getKey();
}
public synchronized Bitmap loadData(String key) {
if (!exists(key)) {
return null;
}
CacheEntry res = mMap.get(key);
res.nUsed++;
res.timestamp = System.currentTimeMillis();
return res.data;
}
public synchronized void storeData(String key, Bitmap data) {
if (this.exists(key) || data == null || data.isRecycled()) {
return;
}
CacheEntry res = new CacheEntry();
res.nUsed = 1;
res.timestamp = System.currentTimeMillis();
res.data = data;
// if the number exceeds, move an item out
// to prevent the storage from increasing indefinitely.
if (mMap.size() >= mMaxSize) {
String outkey = this.findItemToInvalidate();
this.invalidate(outkey);
}
mMap.put(key, res);
}
}
/**
*BitmapCache的基本实现
*
*@作者Chirag Jain
*/
公共类BasicBitmapCache{
私有静态类CacheEntry{
公共位图数据;
公众参与;
公共长时间戳;
}
私有静态最终字符串标记=“BasicBitmapCache”;
私有静态最终布尔调试=false;
私有int-mMaxSize;
私有HashMap mMap=新HashMap();
/**
*此缓存包含的最大资源数
*
*@param大小
*/
公共BasicBitmapCache(整数大小){
this.mMaxSize=大小;
}
存在公共同步布尔值(字符串键){
返回mMap.get(key)!=null;
}
公共同步无效失效(字符串键){
CacheEntry e=mMap.get(键);
位图数据=e.data;
data.recycle();
mMap.移除(键);
如果(调试)
Log.d(标记,key+“从缓存中无效”);
}
公共同步无效清除(){
for(字符串键:mMap.keySet()){
使(密钥)无效;
}
}
/**
*如果缓存已满,请返回要删除的项。
*
*默认策略:最小和最早的输出:O(n)
*
*@返回项目密钥
*/
受保护的同步字符串findItemToInvalidate(){
Map.Entry out=null;
对于(Map.Entry e:mMap.entrySet()){
如果(out==null | | e.getValue().nUsed=mMaxSize){
String outkey=this.findItemToInvalidate();
此。无效(outkey);
}
mMap.put(键,res);
}
}
第95行是什么addOptionineMadapter.java
?-Raghunandan holder.imgPath.setImageBitmap(myBitmap);但每当我在适配器类中为视图设置值时,就会发生错误。如果我删除这一行,错误将更改为下一个视图,我将在其中设置文本视图的值。-错误仅在我以一定速度滚动列表视图时出现。我应该首先说,我不确定,但请尝试添加if(holder!=null){holder.imgPath.setImageBitmap(myBitmap);}
.Hemant这将中断ui线程,从外部内存加载listview中的图像,并且从内存加载适配器的getView中的图像与从网络加载图像(并且您希望在运行时加载图像)类似.–Chirag Jain那么我们应该做些什么来克服这样的问题呢?只要在活动中或适配器的构造函数中给出基本目录的路径,创建ImageStorage类的实例即可。然后调用您的\u instance.loadImage(您的\u文件\u名称,您的\u图像\u视图);在第一条评论中有一个拼写错误。。你是说“不打扰”吗。感谢分享你的课程,期待在我的画廊项目中使用它…谢谢Deepak。是的,它是“不打断”。很抱歉,键入错误:(
getImageStorage().loadImage(
new LoadRequest(//name of image,
//your image view));
/**
* Basic implementation of BitmapCache
*
* @author Chirag Jain
*/
public class BasicBitmapCache {
private static class CacheEntry {
public Bitmap data;
public int nUsed;
public long timestamp;
}
private static final String TAG = "BasicBitmapCache";
private static final boolean DEBUG = false;
private int mMaxSize;
private HashMap<String, CacheEntry> mMap = new HashMap<String, CacheEntry>();
/**
* max number of resource this cache contains
*
* @param size
*/
public BasicBitmapCache(int size) {
this.mMaxSize = size;
}
public synchronized boolean exists(String key) {
return mMap.get(key) != null;
}
public synchronized void invalidate(String key) {
CacheEntry e = mMap.get(key);
Bitmap data = e.data;
data.recycle();
mMap.remove(key);
if (DEBUG)
Log.d(TAG, key + " is invalidated from the cache");
}
public synchronized void clear() {
for (String key : mMap.keySet()) {
invalidate(key);
}
}
/**
* If the cache storage is full, return an item to be removed.
*
* Default strategy: the least and oldest out: O(n)
*
* @return item key
*/
protected synchronized String findItemToInvalidate() {
Map.Entry<String, CacheEntry> out = null;
for (Map.Entry<String, CacheEntry> e : mMap.entrySet()) {
if (out == null || e.getValue().nUsed < out.getValue().nUsed
|| e.getValue().nUsed == out.getValue().nUsed
&& e.getValue().timestamp < out.getValue().timestamp) {
out = e;
}
}
return out.getKey();
}
public synchronized Bitmap loadData(String key) {
if (!exists(key)) {
return null;
}
CacheEntry res = mMap.get(key);
res.nUsed++;
res.timestamp = System.currentTimeMillis();
return res.data;
}
public synchronized void storeData(String key, Bitmap data) {
if (this.exists(key) || data == null || data.isRecycled()) {
return;
}
CacheEntry res = new CacheEntry();
res.nUsed = 1;
res.timestamp = System.currentTimeMillis();
res.data = data;
// if the number exceeds, move an item out
// to prevent the storage from increasing indefinitely.
if (mMap.size() >= mMaxSize) {
String outkey = this.findItemToInvalidate();
this.invalidate(outkey);
}
mMap.put(key, res);
}
}