4

私は長年StackOverflowにアクセスしてきましたが、問題を解決できる投稿が見つからないのは初めてです(少なくとも何も表示されませんでした)。

とがGridView作成したカスタムビューを返すためにオーバーライドしたカスタムアダプタを使用していImageViewますTextView

JSONを使用してURLから画像を解析し、AsyncTaskすべての情報をメソッドに格納しArrayListdoInBackground()メソッドを呼び出しnotifyDataSetChanged()た後、画像を読み込みますonPostExecute()。すべて順調。

私の問題は、アクティビティを起動すると、グリッドビューが作成され、エンティティ内のユーザーに表示されるまでに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()また、この遅延読み込みを実装した方法からわかるように、ListViewでの画像の遅延読み込みについてもお知らせする必要があります。正常に動作しますが、ビュー全体が再度読み込まれます。私がやりたいのは、アクティビティを起動し、最初にキャプションをロードしてから、ダウンロードが完了すると画像がロードされることです。このコードを使用すると、グリッドビューのすべてのセルに含まれるビュー全体を遅延ロードするだけです。以前のように一度にすべての画像をダウンロードするわけではないので、数秒稼ぎましたが、それでも私が探しているものではありません。

どうもありがとう。

4

3 に答える 3

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を設定します。そのため、キャプションはすぐに表示され、画像は遅延して読み込まれます。

于 2013-03-25T04:57:58.563 に答える
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);
        }

お役に立てば幸いです。

ありがとう

于 2013-03-25T04:57:01.403 に答える
0

私の意見では、あなたが言及した答えは良くありません。たとえば、50枚の画像がある場合、ユーザーがリスト全体を上下にスクロールすると、そのサンプルプロジェクトは50個のスレッドを生成します。これは、携帯電話などのモバイルデバイスには適していません。ちなみに、彼の「レイジーリスト」の概念は、AndroidSDKが定義しているものとは異なります。遅延読み込みリストビューのサンプルコードについては、以下をご覧ください。

[Android SDK]/samples/android-x/ApiDemos/src/com/example/android/apis/view/List13.java

ここxで、APIレベルです。コンパイルされたアプリは任意のエミュレーターでテストでき、アプリのAPIデモ>ビュー>リスト>13.低速アダプターを開きます。

あなたの現在のアプローチについて。AsyncTask画像のダウンロードには使用しないでください。ドキュメントには次のように書かれています。

AsyncTasksは、理想的には短い操作(最大で数秒)に使用する必要があります。

代わりに、次のことを行う必要があります。

  • サービスを使用して、バックグラウンドで画像をダウンロードします。NetworkOnMainThreadExceptionサービスはメインUIスレッドで実行されるため、回避するには、サービスのようなものが必要であることに注意してThreadください。
  • コンテンツプロバイダーを使用して、SDカードにダウンロードされた画像を管理します。たとえば、ダウンロードした対応するファイルへの元のURLのマップを保持します。
  • コンテンツプロバイダーとともに、グリッドビューにを使用しCursorAdapter、グリッドビューをホストするアクティビティ/フラグメントにローダーを使用します。

基本的に、ユーザーが初めてアクティビティを開いたときに、新しいアダプターを作成してグリッドビューに設定します。したがって、コンテンツプロバイダーとの接続があります。次に、サービスを開始して画像を確認およびダウンロードします。ダウンロードしたすべての画像について、コンテンツプロバイダーに挿入します。プロバイダーはオブザーバーに変更について通知します。アクティビティ/フラグメント(ローダー)は通知を受け取り、UIを更新します。

于 2013-03-25T05:04:39.543 に答える