我一直在访问Stack Overflow多年,这是我第一次找不到任何可以解决我问题的帖子(至少我没有看到任何帖子)。
我有GridView
个自定义适配器,我已覆盖该适配器以返回由ImageView
和TextView
制作的自定义视图。
我在JSON使用AsyncTask
的网址解析后加载图片,将所有信息存储到ArrayList
方法中的doInBackground()
并在notifyDataSetChanged()
中调用onPostExecute()
{1}}方法。一切都很好。
现在我的问题是,当我启动活动时,网格视图将创建并向实体中的用户呈现自己需要5-10秒的时间。我想知道是否有办法首先显示文本信息的网格视图,然后每个图像将加载。这是否可能,因为它们都是用同一种方法创建的?
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
View v = null;
if (arg1 == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.custom_product_view, null);
} else {
v = arg1;
}
iv = (ImageView) v.findViewById(R.id.product_image);
imageLoader.DisplayImage(products.get(arg0).getImage(), iv);
TextView tv = (TextView) v.findViewById(R.id.product_price);
tv.setText(products.get(arg0).getPrice());
return v;
}
我也必须通知你,你可以从DisplayImage()
方法看到我实现了这个延迟加载:Lazy load of images in ListView。它工作正常,但问题是它再次加载整个视图。我想要做的是启动活动,首先加载标题,然后在完成下载后加载图像。使用此代码,它只是延迟加载网格视图的每个单元格包含的整个视图。我赚了几秒钟,因为我没有像以前那样一次下载所有图像,但仍然不是我正在寻找的。 p>
非常感谢。
答案 0 :(得分:4)
遵循这种方法。
首先,按如下方式创建自定义 WebImageView 类。
public class WebImageView extends ImageView {
private Drawable placeholder, image;
public WebImageView(Context context) {
super(context);
}
public WebImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WebImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setPlaceholderImage(Drawable drawable) {
placeholder = drawable;
if (image == null) {
setImageDrawable(placeholder);
}
}
public void setPlaceholderImage(int resid) {
placeholder = getResources().getDrawable(resid);
if (image == null) {
setImageDrawable(placeholder);
}
}
public void setImageUrl(String url) {
DownloadTask task = new DownloadTask();
task.execute(url);
}
private class DownloadTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
try {
URLConnection conn = (new URL(url)).openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current=bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
return BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
} catch (Exception e) {
return null;
}
}
@Override
protected void onPostExecute(Bitmap result) {
image = new BitmapDrawable(result);
if (image != null) {
setImageDrawable(image);
}
}
}
}
下一步,在Activity中使用上面的自定义ImageView,如下所示:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebImageView imageView = (WebImageView) findViewById(R.id.webimage);
imageView.setPlaceholderImage(R.drawable.ic_launcher);
imageView.setImageUrl("http://www.google.co.in/images/srpr/logo3w.png");
}
简而言之,您正在为ImageView设置占位符图像,当下载完成时,该图像将被实际图像替换。因此,GridView将立即呈现。
实施细则: 因此,在您的自定义视图(使用图像+文本)而不是使用简单的ImageView时,请使用WebImageView,如上所示。当您获得JSON响应时,使用标题设置TextView,使用图像URL设置WebImageView。 因此,标题将立即显示,图像将延迟加载。
答案 1 :(得分:1)
我已经使用下面的类实现了对我来说很棒的图像的延迟加载。你也试试。
<强> ImageLoader的强>
/** * This is class for display image in lazy-loading way. */ public class ImageLoader { private static final String TAG = ImageLoader.class.getSimpleName(); private InputStream m_is = null; private OutputStream m_os = null; private Bitmap m_bitmap = null; private String m_imagePath; private File m_cacheDir; private WeakHashMap<String, Bitmap> m_cache = new WeakHashMap<String, Bitmap>(); /** * Makes the background thread low priority. This way it will not affect the * UI performance.<br> * Checks the Device SD card exits or not and assign path according this * condition. * * @param p_context * activity context */ public ImageLoader(Context p_context) { /** * Make the background thread low priority. This way it will not affect * the UI performance */ m_imageLoaderThread.setPriority(Thread.NORM_PRIORITY - 1); /** * Check the Device SD card exits or not and assign path according this * condition. */ if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { m_imagePath = Environment.getExternalStorageDirectory() + "/Android/data/" + p_context.getPackageName(); m_cacheDir = new File(m_imagePath); } else { m_cacheDir = new File(p_context.getDir("Cache", Context.MODE_PRIVATE), "Cache"); } if (!m_cacheDir.exists()) m_cacheDir.mkdirs(); } /** * Check Image exits on HashMap or not.If exist then set image to ImageView * else send request in the queue. * * @param p_url * image Url * @param p_imageView * image container * @param p_prgBar * progressbar that is displayed till image is not download from * server. */ public void DisplayImage(String p_url, ImageView p_imageView, ProgressBar p_prgBar) throws CustomException { if (m_cache.containsKey(p_url)) { p_prgBar.setVisibility(View.GONE); p_imageView.setVisibility(View.VISIBLE); p_imageView.setImageBitmap(m_cache.get(p_url)); } else { queueImage(p_url, p_imageView, p_prgBar); } } /** * Clear old task from the queue and add new image downloading in the queue. * * @param p_url * image Url * @param p_imageView * image container * @param p_prgBar * progressbar that is displayed till image is not download from * server. */ private void queueImage(String p_url, ImageView p_imageView, ProgressBar p_prgBar) throws CustomException { try { m_imagesQueue.Clean(p_imageView); ImageToLoad m_photoObj = new ImageToLoad(p_url, p_imageView, p_prgBar); synchronized (m_imagesQueue.m_imagesToLoad) { m_imagesQueue.m_imagesToLoad.push(m_photoObj); m_imagesQueue.m_imagesToLoad.notifyAll(); } /** * start thread if it's not started yet */ if (m_imageLoaderThread.getState() == Thread.State.NEW) m_imageLoaderThread.start(); } catch (CustomException c) { throw c; } catch (Throwable t) { CustomLogHandler.printErrorlog(t); throw new CustomException(TAG + " Error in queueImage(String p_url, ImageView p_imageView, ProgressBar p_prgBar) of ImageLoader", t); } } /** * Checks in SD card for cached file.If bitmap is not available then will * download it from Url. * * @param p_url * imgae Url * @return bitmap from Cache or from server. */ private Bitmap getBitmap(String p_url) throws CustomException { System.gc(); String m_fileName = String.valueOf(p_url.hashCode()); File m_file = new File(m_cacheDir, m_fileName); // from SD cache m_bitmap = decodeFile(m_file); if (m_bitmap != null) return m_bitmap; // from web try { Bitmap m_bitmap = null; int m_connectionCode = 0; m_connectionCode = HttpConnection.getHttpUrlConnection(p_url).getResponseCode(); if (m_connectionCode == HttpURLConnection.HTTP_OK) { m_is = new URL(p_url).openStream(); m_os = new FileOutputStream(m_file); FileIO.copyStream(m_is, m_os); m_os.close(); m_os = null; m_bitmap = decodeFile(m_file); m_is.close(); m_is = null; HttpConnection.getHttpUrlConnection(p_url).disconnect(); } return m_bitmap; } catch (CustomException c) { throw c; } catch (Throwable t) { CustomLogHandler.printErrorlog(t); throw new CustomException(TAG + " Error in getBitmap(String p_url) of ImageLoader", t); } } /** * Decodes the Image file to bitmap. * * @param p_file * Image file object * @return decoded bitmap */ private Bitmap decodeFile(File p_file) throws CustomException { try { // decode image size Bitmap m_retBmp = null; System.gc(); int m_scale = 1; if (p_file.length() > 400000) { m_scale = 4; } else if (p_file.length() > 100000 && p_file.length() < 400000) { m_scale = 3; } // decode with inSampleSize if (p_file.exists()) { BitmapFactory.Options m_o2 = new BitmapFactory.Options(); m_o2.inSampleSize = m_scale; m_retBmp = BitmapFactory.decodeFile(p_file.getPath(), m_o2); } return m_retBmp; } catch (Throwable t) { CustomLogHandler.printErrorlog(t); throw new CustomException(TAG + " Error in decodeFile(File p_file) of ImageLoader", t); } } /** * Stores image information */ private class ImageToLoad { public String m_url; public ImageView m_imageView; public ProgressBar m_prgBar; public ImageToLoad(String p_str, ImageView p_img, ProgressBar p_prgBar) { m_url = p_str; m_imageView = p_img; m_imageView.setTag(p_str); m_prgBar = p_prgBar; } } ImagesQueue m_imagesQueue = new ImagesQueue(); /** * This is method to stop current running thread. */ public void stopThread() { m_imageLoaderThread.interrupt(); } /** * Stores list of image to be downloaded in stack. */ class ImagesQueue { private Stack<ImageToLoad> m_imagesToLoad = new Stack<ImageToLoad>(); /** * Removes all instances of this ImageView * * @param p_ivImage * imageView */ public void Clean(ImageView p_ivImage) throws CustomException { try { for (int m_i = 0; m_i < m_imagesToLoad.size();) { if (m_imagesToLoad.get(m_i).m_imageView == p_ivImage) m_imagesToLoad.remove(m_i); else m_i++; } } catch (Throwable t) { CustomLogHandler.printErrorlog(t); throw new CustomException(TAG + " Error in Clean(ImageView p_image) of ImageLoader", t); } } } /** * * This is class waits until there are any images to load in the queue. */ class ImagesLoader extends Thread { public void run() { try { while (true) { if (m_imagesQueue.m_imagesToLoad.size() == 0) synchronized (m_imagesQueue.m_imagesToLoad) { m_imagesQueue.m_imagesToLoad.wait(); } if (m_imagesQueue.m_imagesToLoad.size() != 0) { ImageToLoad m_imageToLoadObj; synchronized (m_imagesQueue.m_imagesToLoad) { m_imageToLoadObj = m_imagesQueue.m_imagesToLoad.pop(); } Bitmap m_bmp = getBitmap(m_imageToLoadObj.m_url); m_cache.put(m_imageToLoadObj.m_url, m_bmp); if (((String) m_imageToLoadObj.m_imageView.getTag()).equals(m_imageToLoadObj.m_url)) { BitmapDisplayer m_bmpdisplayer = new BitmapDisplayer(m_bmp, m_imageToLoadObj.m_imageView, m_imageToLoadObj.m_prgBar); Activity m_activity = (Activity) m_imageToLoadObj.m_imageView.getContext(); m_activity.runOnUiThread(m_bmpdisplayer); } } if (Thread.interrupted()) break; } } catch (InterruptedException e) { /* * allow thread to exit */ } catch (Throwable t) { CustomLogHandler.printErrorlog(t); } } } ImagesLoader m_imageLoaderThread = new ImagesLoader(); /** * This class Used to display bitmap in the UI thread */ class BitmapDisplayer implements Runnable { Bitmap m_bmp; ImageView m_imageView; ProgressBar m_prgBar; public BitmapDisplayer(Bitmap p_bmp, ImageView p_imgview, ProgressBar p_prgBar) { m_bmp = p_bmp; m_imageView = p_imgview; m_prgBar = p_prgBar; } public void run() { if (m_bmp != null) { m_imageView.setImageBitmap(m_bmp); m_prgBar.setVisibility(View.GONE); m_imageView.setVisibility(View.VISIBLE); } } } }
使用以下课程:
首先,您需要将ProgressBar
放在自定义布局中,如下所示ImageView
:
<RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/RelativeImagelayout"> <ProgressBar android:id="@+id/Progress" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginTop="10dp"/> <ImageView android:id="@+id/ivImage" android:layout_width="80dp" android:layout_height="90dp" android:layout_marginTop="10dp" android:clickable="false"/> </RelativeLayout>
在适配器类中创建ImageLoader
类的实例,并在getView
方法中使用它,如下所示:
ImageView m_ibImage = (ImageView) v.findViewById(R.id.ivImage); ProgressBar m_pbProgress = (ProgressBar) v.findViewById(R.id.Progress); if (products.get(arg0).getImage().toString().equals(null) || products.get(arg0).getImage().toString().equals("")) { m_pbProgress.setVisibility(View.INVISIBLE); m_ibImage.setVisibility(View.VISIBLE); } else if (!products.get(arg0).getImage().toString().equals(null)) { m_imgLoader.DisplayImage(products.get(arg0).getImage(), m_ibImage, m_pbProgress); }
我希望它会对你有所帮助。
由于
答案 2 :(得分:0)
在我看来,你提到的答案 not 不错。例如,如果您有50个图像,当用户向上/向下滚动整个列表时,该示例项目将生成50个线程。这对手机等移动设备来说很糟糕。旁注,他的概念“懒惰列表”与Android SDK定义的概念不同。有关延迟加载列表视图的示例代码,请查看:
[Android SDK]/samples/android-x/ApiDemos/src/com/example/android/apis/view/List13.java
其中x
是API级别。您可以在任何模拟器中测试已编译的应用程序,打开应用程序 API Demos&gt;意见&gt;列表&gt; 13.慢速适配器。
关于您当前的方法。您不应使用AsyncTask
下载图像。 documentation说:
理想情况下,AsyncTasks应该用于短操作(最多几秒钟。)
你应该改为:
NetworkOnMainThreadException
,您需要在服务中使用Thread
之类的内容。CursorAdapter
作为网格视图,loaders作为托管网格视图的活动/片段。基本上,在用户第一次打开您的活动时,您需要创建新适配器并将其设置为网格视图。因此它与内容提供商有联系。然后启动服务以检查并下载图像。对于下载的每个图像,您将其插入内容提供程序。提供者通知任何观察者有关更改 - 您的活动/片段(加载器)接收通知并更新UI。