Android仅加载ListView的可见元素
我在我的应用程序中使用延迟加载列表,我只需要加载 ListView 的可见元素。我在互联网上搜索,尝试了很多东西,但仍然无法让这些东西工作。你能给我一个建议吗怎么办呢?
这是 ImageLoader 类:
package com.stampii.stampii.tableview;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Stack;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.stampii.stampii.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.ImageView;
public class ImageLoader {
//the simplest in-memory cache implementation. This should be replaced with something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>();
private File cacheDir;
private AssetManager mAssetManager;
public ImageLoader(Context context){
//Make the background thead low priority. This way it will not affect the UI performance
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
mAssetManager = context.getAssets();
//Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"Stampii");
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
final int stub_id=R.drawable.default_img;
public void DisplayImage(String url, Activity activity, ImageView imageView)
{
if(cache.containsKey(url))
imageView.setImageBitmap(cache.get(url));
else
{
queuePhoto(url, activity, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, Activity activity, ImageView imageView) //opashka
{
//This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
photosQueue.Clean(imageView);
PhotoToLoad p=new PhotoToLoad(url, imageView);
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.push(p);
photosQueue.photosToLoad.notifyAll();
}
//start thread if it's not started yet
if(photoLoaderThread.getState()==Thread.State.NEW)
photoLoaderThread.start();
}
private Bitmap getBitmap(String src) {
Bitmap myBitmap = null;
//Decryption
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
InputStream input = mAssetManager.open(src);
CipherInputStream cis = new CipherInputStream(input, cipher);
myBitmap = BitmapFactory.decodeStream(cis);
}
catch(Exception e){
e.printStackTrace();
Log.v("ERROR","Error : "+e);
}
return myBitmap;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i){
url=u;
imageView=i;
}
}
PhotosQueue photosQueue=new PhotosQueue();
public void stopThread()
{
photoLoaderThread.interrupt();
}
//stores list of photos to download
class PhotosQueue
{
private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();
//removes all instances of this ImageView
public void Clean(ImageView image)
{
for(int j=0 ;j<photosToLoad.size();){
if(photosToLoad.get(j).imageView==image)
photosToLoad.remove(j);
else
++j;
}
}
}
class PhotosLoader extends Thread {
public void run() {
try {
while(true)
{
//thread waits until there are any images to load in the queue
if(photosQueue.photosToLoad.size()==0)
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.wait();
}
if(photosQueue.photosToLoad.size()!=0)
{
PhotoToLoad photoToLoad;
synchronized(photosQueue.photosToLoad){
photoToLoad=photosQueue.photosToLoad.pop();
}
Bitmap bmp=getBitmap(photoToLoad.url);
cache.put(photoToLoad.url, bmp);
Object tag=photoToLoad.imageView.getTag();
if(tag!=null && ((String)tag).equals(photoToLoad.url)){
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
if(Thread.interrupted())
break;
}
} catch (InterruptedException e) {
//allow thread to exit
}
}
}
PhotosLoader photoLoaderThread=new PhotosLoader();
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
ImageView imageView;
public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
public void run()
{
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
imageView.setImageResource(stub_id);
}
}
public void clearCache() {
//clear memory cache
long size=0;
cache.clear();
//clear SD cache
File[] files=cacheDir.listFiles();
for(File f:files){
size=size+f.length();
f.delete();
}
}
}
EDIT1:
我的 LazyAdapter 类中的 getView 方法:
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
ViewHolder holder;
if(convertView==null){
vi = inflater.inflate(R.layout.item, null);
holder=new ViewHolder();
holder.name=(TextView)vi.findViewById(R.id.name);
holder.info=(TextView)vi.findViewById(R.id.info);
holder.image=(ImageView)vi.findViewById(R.id.image);
vi.setTag(holder);
}
else
holder=(ViewHolder)vi.getTag();
holder.name.setText("MotoGP Rider");
holder.info.setText("Category");
holder.image.setTag(data[position]);
imageLoader.DisplayImage(data[position], activity, holder.image);
return vi;
}
有什么想法吗?
I'm using Lazy Loading List in my application and I need to load only the visible elements of ListView.I've search over the internet,tried a lot of things but still cannot get the things to work.Can you give me a suggestion how to do it?
Here is the ImageLoader class :
package com.stampii.stampii.tableview;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Stack;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.stampii.stampii.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.ImageView;
public class ImageLoader {
//the simplest in-memory cache implementation. This should be replaced with something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>();
private File cacheDir;
private AssetManager mAssetManager;
public ImageLoader(Context context){
//Make the background thead low priority. This way it will not affect the UI performance
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
mAssetManager = context.getAssets();
//Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"Stampii");
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
final int stub_id=R.drawable.default_img;
public void DisplayImage(String url, Activity activity, ImageView imageView)
{
if(cache.containsKey(url))
imageView.setImageBitmap(cache.get(url));
else
{
queuePhoto(url, activity, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, Activity activity, ImageView imageView) //opashka
{
//This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
photosQueue.Clean(imageView);
PhotoToLoad p=new PhotoToLoad(url, imageView);
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.push(p);
photosQueue.photosToLoad.notifyAll();
}
//start thread if it's not started yet
if(photoLoaderThread.getState()==Thread.State.NEW)
photoLoaderThread.start();
}
private Bitmap getBitmap(String src) {
Bitmap myBitmap = null;
//Decryption
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
InputStream input = mAssetManager.open(src);
CipherInputStream cis = new CipherInputStream(input, cipher);
myBitmap = BitmapFactory.decodeStream(cis);
}
catch(Exception e){
e.printStackTrace();
Log.v("ERROR","Error : "+e);
}
return myBitmap;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i){
url=u;
imageView=i;
}
}
PhotosQueue photosQueue=new PhotosQueue();
public void stopThread()
{
photoLoaderThread.interrupt();
}
//stores list of photos to download
class PhotosQueue
{
private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();
//removes all instances of this ImageView
public void Clean(ImageView image)
{
for(int j=0 ;j<photosToLoad.size();){
if(photosToLoad.get(j).imageView==image)
photosToLoad.remove(j);
else
++j;
}
}
}
class PhotosLoader extends Thread {
public void run() {
try {
while(true)
{
//thread waits until there are any images to load in the queue
if(photosQueue.photosToLoad.size()==0)
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.wait();
}
if(photosQueue.photosToLoad.size()!=0)
{
PhotoToLoad photoToLoad;
synchronized(photosQueue.photosToLoad){
photoToLoad=photosQueue.photosToLoad.pop();
}
Bitmap bmp=getBitmap(photoToLoad.url);
cache.put(photoToLoad.url, bmp);
Object tag=photoToLoad.imageView.getTag();
if(tag!=null && ((String)tag).equals(photoToLoad.url)){
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
if(Thread.interrupted())
break;
}
} catch (InterruptedException e) {
//allow thread to exit
}
}
}
PhotosLoader photoLoaderThread=new PhotosLoader();
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
ImageView imageView;
public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
public void run()
{
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
imageView.setImageResource(stub_id);
}
}
public void clearCache() {
//clear memory cache
long size=0;
cache.clear();
//clear SD cache
File[] files=cacheDir.listFiles();
for(File f:files){
size=size+f.length();
f.delete();
}
}
}
EDIT1:
getView method in my LazyAdapter class :
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
ViewHolder holder;
if(convertView==null){
vi = inflater.inflate(R.layout.item, null);
holder=new ViewHolder();
holder.name=(TextView)vi.findViewById(R.id.name);
holder.info=(TextView)vi.findViewById(R.id.info);
holder.image=(ImageView)vi.findViewById(R.id.image);
vi.setTag(holder);
}
else
holder=(ViewHolder)vi.getTag();
holder.name.setText("MotoGP Rider");
holder.info.setText("Category");
holder.image.setTag(data[position]);
imageLoader.DisplayImage(data[position], activity, holder.image);
return vi;
}
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
检查代码后:我确信只有可见的图像正在加载!
但是因为下面的一些图像具有相同的网址!您会认为它们是以不同的顺序加载的。但是,它们是根据可见图像加载的,但具有相同 url 的任何其他列表项都会显示图像。
ListView 上可见的每个位置都会调用
getView
需要加载的图片必须从 Adapter 的
getView
添加到队列中,因此您应该重写
注意:小心
假设您将照片 1 添加到队列中:这并不意味着不会再次调用
getView
位置 1。换句话说,考虑以下事实:可以使用
getView
多次调用position。After checking the code: I am sure that ONLY the images that are visible are being loaded!
BUT because some of the images down have the same url! You will think that they are loading in a different order. However they are loaded based on the images visible but any other listitem with the same url will show the image.
getView
is called for each position that is visible on the ListViewThe pictures you need to load must be added to the queue from the
getView
of the AdapterSo you should Override
Note: Be careful
Say you added photo 1 to the queue: This does not mean that
getView
will not be called again with position 1.In other words, account for the fact that a position may be called many times using
getView
.要仅加载可见元素,您需要实现 AbsListView.OnScrollListener 接口。从这个接口中你可以从它的方法中找到“scroll event”和“scrolleventstate”的变化。
有关于此的更多解释。您可以按照本教程进行操作。 智能加载列表视图 , 动态数据listview,检测结束滚动,获取可见数据索引
To load only visible elements you need to implements a interface of AbsListView.OnScrollListener. From this interface you can find out the "scroll event" and "scrolleventstate" change from its methods.
For more explanation about this. you can follow this tutorials. smart loading listview, dynamic data listview, detect end scroll, get visible data index