3

Я думаю, что я понимаю механизм повторного использования представлений для ListView, но для GridView то, как он перерабатывает представления, не в ожидании.Как именно GridView перерабатывает/повторно использует вид в Android?

в моем примере, я просто просто перерабатываю представление, если convertView не равен нулю в getView() от адаптера.

Также я регистрирую позицию вида.

enter image description here

но странная вещь после того, как я проверяю LogCat, я обнаружил, даже если я не прокручивать все, вид с позиции 0 (первая VI! ЭВ) не равно нулю (на в первый раз нет переработанного представления, поэтому convertView for positon 0 должен иметь нулевое значение?).

enter image description here

даже посторонний предмет есть, после того, как он регистрирует все 54 элементов (как вы можете увидеть весь экран может держать 54 просмотров), он регистрирует Serveral позиции 0 «ы переработаны вид

enter image description here

, так что это полностью смущает меня, как GridView перерабатывает виды. По-моему, это должно быть что-то вроде ListView, если я прокручиваю вниз, верхние виды исчезают с экрана, затем они возвращаются к нижней части списка один за другим, но, похоже, в GridView его нет.

Я даже использовать HashMap, чтобы рассчитать время рециркуляции каждого просмотра (строки в Integer Entry, Key является toString() значения зрения), я нашел время рециркуляции не распределяется равномерно, в то время как некоторые зрения имеют 30 раз рецикла в то время как некоторые из них имеют только один раз .....


Редактировать (добавить коды рециркуляции, а также скриншот за исключением)

enter image description here

enter image description here

(Edit) even though I tried using `setImageDrawable(null)` it still throws exception, as noticed you can see the exception will be thrown no matter I called it before or after the recycle 

enter image description here enter image description here

Я думаю, что дело в этом вопросе Android не обрабатывает переработанный вид, как я ожидал, так что я думаю, даже если convertView не равно нулю, это делает не докажите, что он уходит с экрана, как вы уже ответили ранее, андроид будет использовать позицию позиции 0 для первоначального измерения, поэтому это может быть основной причиной.

**Edit Again** 

код адаптера:

package com.example.photoswalldemo; 

    import android.content.Context; 
    import android.graphics.Bitmap; 
    import android.graphics.BitmapFactory; 
    import android.os.AsyncTask; 
    import android.util.LruCache; 
    import android.view.LayoutInflater; 
    import android.view.View; 
    import android.view.ViewGroup; 
    import android.widget.AbsListView; 
    import android.widget.AbsListView.OnScrollListener; 
    import android.widget.ArrayAdapter; 
    import android.widget.GridView; 
    import android.widget.ImageView; 

    import com.android.volley.RequestQueue; 
    import com.android.volley.Response; 
    import com.android.volley.VolleyError; 
    import com.android.volley.toolbox.ImageRequest; 
    import com.android.volley.toolbox.Volley; 

    import java.net.HttpURLConnection; 
    import java.net.URL; 
    import java.util.HashSet; 
    import java.util.Set; 

    /** 
    * GridView的适配器,负责异步从网络上下载图片展示在照片墙上。 
    * 
    * @author guolin 
    */ 
    public class PhotoWallAdapter extends ArrayAdapter<String> implements OnScrollListener { 

     /** 
     * 记录所有正在下载或等待下载的任务。 
     */ 
     private Set<BitmapWorkerTask> taskCollection; 

     /** 
     * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。 
     */ 
     private LruCache<String, Bitmap> mMemoryCache; 

     /** 
     * GridView的实例 
     */ 
     private GridView mPhotoWall; 

     /** 
     * 第一张可见图片的下标 
     */ 
     private int mFirstVisibleItem; 

     /** 
     * 一屏有多少张图片可见 
     */ 
     private int mVisibleItemCount; 

     /** 
     * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。 
     */ 
     private boolean isFirstEnter = true; 


     private RequestQueue queue= Volley.newRequestQueue(getContext()); 



     public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects, 
       GridView photoWall) { 
      super(context, textViewResourceId, objects); 
      mPhotoWall = photoWall; 
      taskCollection = new HashSet<BitmapWorkerTask>(); 
      // 获取应用程序最大可用内存 
      int maxMemory = (int) Runtime.getRuntime().maxMemory(); 
      int cacheSize = maxMemory/8; 
      // 设置图片缓存大小为程序最大可用内存的1/8 
      mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { 
       @Override 
       protected int sizeOf(String key, Bitmap bitmap) { 
        return bitmap.getByteCount(); 
       } 
      }; 
      mPhotoWall.setOnScrollListener(this); 
     } 

     @Override 
     public View getView(int position, View convertView, ViewGroup parent) { 
      // Log.e("Monitor View","This View is on position "+position+" and its "+convertView); 
      final String url = getItem(position); 
      View view; 
      if (convertView == null) { 
       view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null); 
      } else { 

       View tmp=convertView; 

    //   ImageView tmp1=(ImageView)tmp.findViewById(R.id.photo); 
    //   if(tmp1.getDrawable()!=null){ 
    //    //Toast.makeText(getContext(),"it has Drawable!,",Toast.LENGTH_LONG).show(); 
    //    Log.e("Monitor View", "This View is on position " + position + " and its view " + convertView+" and this drawable is "+tmp1.getDrawable()); 
    // 
    //    Bitmap bitmap=((BitmapDrawable)tmp1.getDrawable()).getBitmap(); 
    //    tmp1.setImageDrawable(null); 
    //    tmp1.setImageBitmap(null); 
    //    bitmap.recycle(); 
    //    bitmap=null; 
    //    //tmp1.setImageDrawable(null); 


      // } 


       view = convertView; 


      } 
      final ImageView photo = (ImageView) view.findViewById(R.id.photo); 
      // 给ImageView设置一个Tag,保证异步加载图片时不会乱序 
      photo.setTag(url); 
      setImageView(url, photo); 
      return view; 
     } 

     /** 
     * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。如果LruCache中没有该图片的缓存, 
     * 就给ImageView设置一张默认图片。 
     * 
     * @param imageUrl 
     *   图片的URL地址,用于作为LruCache的键。 
     * @param imageView 
     *   用于显示图片的控件。 
     */ 
     private void setImageView(String imageUrl, ImageView imageView) { 
      Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); 
      if (bitmap != null) { 
       imageView.setImageBitmap(bitmap); 
      } else { 
       imageView.setImageResource(R.drawable.empty_photo); 
      } 
     } 

     /** 
     * 将一张图片存储到LruCache中。 
     * 
     * @param key 
     *   LruCache的键,这里传入图片的URL地址。 
     * @param bitmap 
     *   LruCache的键,这里传入从网络上下载的Bitmap对象。 
     */ 
     public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 
      if (getBitmapFromMemoryCache(key) == null) { 
       mMemoryCache.put(key, bitmap); 
      } 
     } 

     /** 
     * 从LruCache中获取一张图片,如果不存在就返回null。 
     * 
     * @param key 
     *   LruCache的键,这里传入图片的URL地址。 
     * @return 对应传入键的Bitmap对象,或者null。 
     */ 
     public Bitmap getBitmapFromMemoryCache(String key) { 
      return mMemoryCache.get(key); 
     } 

     @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) { 
      mFirstVisibleItem = firstVisibleItem; 
      mVisibleItemCount = visibleItemCount; 
      // 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用, 
      // 因此在这里为首次进入程序开启下载任务。 
      if (isFirstEnter && visibleItemCount > 0) { 
       loadBitmaps(firstVisibleItem, visibleItemCount); 
       isFirstEnter = false; 
      } 
     } 

     /** 
     * 加载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 imageUrl = Images.imageThumbUrls[i]; 
        Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); 
        if (bitmap == null) { 
         //BitmapWorkerTask task = new BitmapWorkerTask(); 
         //taskCollection.add(task); 
         //task.execute(imageUrl); 

         //new method , use volley 

         useVolley(imageUrl); 
        } else { 
         ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl); 
         if (imageView != null && bitmap != null) { 
          imageView.setImageBitmap(bitmap); 
         } 
        } 
       } 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 

     /** 
     * 取消所有正在下载或等待下载的任务。 
     */ 
     public void cancelAllTasks() { 
      if (taskCollection != null) { 
       for (BitmapWorkerTask task : taskCollection) { 
        task.cancel(false); 
       } 
      } 
     } 

     /** 
     * 异步下载图片的任务。 
     * 
     * @author guolin 
     */ 
     class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> { 

      /** 
      * 图片的URL地址 
      */ 
      private String imageUrl; 

      @Override 
      protected Bitmap doInBackground(String... params) { 
       imageUrl = params[0]; 
       // 在后台开始下载图片 
       Bitmap bitmap = downloadBitmap(params[0]); 
       if (bitmap != null) { 
        // 图片下载完成后缓存到LrcCache中 
        addBitmapToMemoryCache(params[0], bitmap); 
       } 
       return bitmap; 
      } 

      @Override 
      protected void onPostExecute(Bitmap bitmap) { 
       super.onPostExecute(bitmap); 
       // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。 
       ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl); 
       if (imageView != null && bitmap != null) { 
        imageView.setImageBitmap(bitmap); 
       } 
       taskCollection.remove(this); 
      } 

      /** 
      * 建立HTTP请求,并获取Bitmap对象。 
      * 
      * @param imageUrl 
      *   图片的URL地址 
      * @return 解析后的Bitmap对象 
      */ 
      private Bitmap downloadBitmap(String imageUrl) { 
       Bitmap bitmap = null; 
       HttpURLConnection con = null; 
       try { 
        URL url = new URL(imageUrl); 
        con = (HttpURLConnection) url.openConnection(); 
        con.setConnectTimeout(5 * 1000); 
        con.setReadTimeout(10 * 1000); 
        con.setDoInput(true); 
        con.setDoOutput(true); 
        bitmap = BitmapFactory.decodeStream(con.getInputStream()); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } finally { 
        if (con != null) { 
         con.disconnect(); 
        } 
       } 
       return bitmap; 
      } 

     } 



     private void useVolley(String url){ 

      final String tmpurl=url; 

      // ImageView tmpImageView=(ImageView)mPhotoWall.findViewWithTag(tmpurl); 
      ImageRequest imageRequest=new ImageRequest(url,new Response.Listener<Bitmap>() { 
       @Override 
       public void onResponse(Bitmap response) { 

        ImageView tmpImageView=(ImageView)mPhotoWall.findViewWithTag(tmpurl); 

        tmpImageView.setImageBitmap(response); 

        mMemoryCache.put(tmpurl,response); 
       } 
      },0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() { 
       @Override 
       public void onErrorResponse(VolleyError error) { 

        ImageView tmpImageView=(ImageView)mPhotoWall.findViewWithTag(tmpurl); 
        //imageView.setImageResource(R.drawable.default_image); 
        tmpImageView.setImageResource(R.drawable.empty_photo); 
       } 
      }); 



      this.queue.add(imageRequest); 

     } 

    } 

Основная деятельность Код:

public class MainActivity extends Activity { 

    /** 
    * 用于展示照片墙的GridView 
    */ 
    private GridView mPhotoWall; 

    /** 
    * GridView的适配器 
    */ 
    private PhotoWallAdapter adapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     mPhotoWall = (GridView) findViewById(R.id.photo_wall); 
     adapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls, mPhotoWall); 
     mPhotoWall.setAdapter(adapter); 
    } 

    @Override 
    protected void onDestroy() { 
     super.onDestroy(); 
     // 退出程序时结束所有的下载任务 
     adapter.cancelAllTasks(); 
    } 

} 
+0

Не могли бы вы предоставить код для вашего адаптера и как установить этот адаптер на вид? – dkarmazi

+0

hi Karmazi Ive обновил сообщение для моей 1.MainActivity и 2.Adapter, в основном у меня есть gridview в макете, и я установил адаптер в своей mainactivity, спасибо человеку – Qing

+0

Получил модифицированную версию этого, чтобы работать, но нагрузка на свиток медленная , Вероятно, что размер кеша/размер изображения обрабатывается – Rarw

ответ

2

Путь вы раздувания вид, если это нулевой и многократно использовать его, если это не нуль является точно правильно.Это способ, которым подкласс AdapterView позволяет повторно использовать его виды.

Первоначально я был озадачен, так как вы были о элементе списка 0, имеющем ненулевой вид; но затем я вспомнил, что AdapterView вызывает адаптер, чтобы получить представление списка 0 несколько раз во время фазы измерения и выкладки, прежде чем даже отобразить элемент списка. Это звучит излишне, но, очевидно, у разработчика Google были причины для этого. Это также объясняет, почему вы видите несколько журналов для элемента списка 0.

Что касается неравномерной переработки, я не могу вспомнить, являются ли виды переработанными FIFO или LIFO, но дело в том, что это не имеет значения. Если вы указали (через getItemViewType(), что макеты одинаковы, то не имеет значения, какой из них AdapterView подает вам на переработку. Если все, что вам нужно, это алюминиевая банка, вам все равно, если это кока-кола или пепси?

Считайте также, что взгляды на самом верху AdapterView обычно уходят на экран и на экране гораздо чаще, чем взгляды дальше вниз, и я думаю, что это то, что объясняет однобокое распределение просмотра, которое вы наблюдаете.


EDIT: на вашем исключение: Вы вызываете bitmap.recycle() то установка bitmap = null Но имейте в ми. nd, даже если вы установили bitmap = null, Drawable все еще содержит ссылку на растровое изображение, которое теперь отмечено как переработанное! Поэтому, когда ImageView пытается сделать это, он замечает, что растровое изображение было помечено как переработанное, поэтому генерируется исключение. Если вы звоните tmp1.setImageDrawable(null) после работы с растровым изображением, исключение должно исчезнуть.

+0

, вопрос, который я задал этому вопросу, заключается в том, что вы видите, что convertView содержит ImageView, образное изображение указывает на растровое изображение, и я хочу его переработать(). – Qing

+0

что я сделал, если convertView не является нулевым, то он использовался раньше, и этот вид отключается от экрана, поэтому я больше не нуждаюсь в нем, поэтому я могу освободить память для растрового изображения этого представления, но когда я это сделал, он выбрасывает исключение, такое как битмап мусора, который все еще используется или что-то еще, что не имеет смысла, поскольку convertView уходит с экрана и будет перерабатываться ... – Qing

+0

, поэтому поэтому мне нужно выяснить, как GridView обрабатывает переработанные виды, например его последовательность манипуляций или прочее, если нет, это не может объяснить, почему я получаю фатальные исключения, даже если я переработал растровое изображение из convertView, которое выходит из экрана. – Qing