IT虾米网

Android图片墙实现详解

admin 2018年06月09日 手机开发 442 0

在学习了郭霖大神的Android高效加载大图、多图解决方案,有效避免程序OOM  之后,又跟着郭大神学习了照片墙的实现

Android照片墙应用实现,再多的图片也不怕崩溃  使用缓存技术使得加载图片避免了OOM(Out Of Memory),下面是做此次照片墙的思

路和记录

第一步:定义图片数据源

第二步:编写主界面布局,使用GridView

第三步:编写每一个item的布局,使用ImageView

第四步:有数据和GridView,因此也要编写一个适配器类

public class PhotoWallApdater extends ArrayAdapter<String> implements OnScrollListener { 
	 
	//记录所有正在下载或等待下载的任务,便于一次性中断任务 
	private Set<BitMapWorkTask> taskCollection;  
	 
	//图片缓存 
	private LruCache<String, Bitmap> mMemoryCache; 
	 
	//获得布局实例 
	private GridView mPhotoWall; 
	 
	//第一个可见的图片下标 
	private int mFirstVisibleItem; 
 
	//一屏可见多少图片 
	private int mVisibleItemCount; 
	 
	//记录是否第一次进入程序 
	private boolean mIsFirstEnter = true; 
	 
	public PhotoWallApdater(Context context, int textViewResourceId, String[] objects, GridView photoWall) { 
		super(context, textViewResourceId, objects); 
		mPhotoWall = photoWall; 
		taskCollection = new HashSet<BitMapWorkTask>(); 
		//设置缓冲区大小 
		int maxMemory = (int) Runtime.getRuntime().maxMemory(); 
		int maxCache = maxMemory / 10; 
		 
		mMemoryCache = new LruCache<String, Bitmap>(maxCache){ 
 
			@Override 
			protected int sizeOf(String key, Bitmap bitmap) { 
				 
				return bitmap.getByteCount();//返回图片个数 
			} 
			 
		}; 
		 
		mPhotoWall.setOnScrollListener(this); 
	} 
	 
 
	@Override 
	public View getView(int position, View convertView, ViewGroup parent) { 
		String imageUrl = getItem(position); 
		View view; 
		 
		if(convertView == null){ 
			view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null); 
		}else { 
			view = convertView; 
		} 
		 
		 ImageView photo = (ImageView) view.findViewById(R.id.photo); 
		 // 给ImageView设置一个Tag,保证异步加载图片时不会乱序   
		 photo.setTag(imageUrl); 
		 setImageView(imageUrl, photo); 
		 
		return view; 
	} 
 
 
	/**  
     * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。如果LruCache中没有该图片的缓存,  
     * 就给ImageView设置一张默认图片。  
     *   
     * @param imageUrl  
     *            图片的URL地址,用于作为LruCache的键。  
     * @param imageView  
     *            用于显示图片的控件。  
     */   
 
	private void setImageView(String imageUrl, ImageView photo) { 
		Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); 
		if(bitmap != null){ 
			photo.setImageBitmap(bitmap); 
		}else{ 
		} 
	} 
	 
	/**  
     * 将一张图片存储到LruCache中。  
     *   
     * @param key  
     *            LruCache的键,这里传入图片的URL地址。  
     * @param bitmap  
     *            LruCache的键,这里传入从网络上下载的Bitmap对象。  
     */  
 
	private void addBitmapFromMemoryCache(String key, Bitmap bitmap) { 
		 
		if(getBitmapFromMemoryCache(key) == null){ 
			mMemoryCache.put(key, bitmap); 
		} 
	} 
 
	/**  
     * 从LruCache中获取一张图片,如果不存在就返回null。  
     *   
     * @param key  
     *            LruCache的键,这里传入图片的URL地址。  
     * @return 对应传入键的Bitmap对象,或者null。  
     */  
	 
	private Bitmap getBitmapFromMemoryCache(String key) { 
		Bitmap bitmap = mMemoryCache.get(key); 
		return bitmap; 
	} 
 
 
	@Override   
    public void onScrollStateChanged(AbsListView view, int scrollState) {   
        // 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务   
        if (scrollState == SCROLL_STATE_IDLE) {   
            loadBitmaps(mFirstVisibleItem, mVisibleItemCount);   
        } else {   
            cancleAllTasks();   
        }   
    }   
   
    @Override   
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,   
            int totalItemCount) {   
    	mFirstVisibleItem = firstVisibleItem; 
    	mVisibleItemCount = visibleItemCount;   
        // 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,   
        // 因此在这里为首次进入程序开启下载任务。   
        if (mIsFirstEnter && (visibleItemCount > 0)) {   
            loadBitmaps(firstVisibleItem, visibleItemCount);   
            mIsFirstEnter = 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.imageUrls[i];   
                Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);   
                if (bitmap == null) {   
                    BitMapWorkTask task = new BitMapWorkTask();   
                    taskCollection.add(task);   
                    task.execute(imageUrl);   
                } else {   
                    ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);   
                    if (imageView != null && bitmap != null) {   
                        imageView.setImageBitmap(bitmap);   
                    }   
                }   
            }   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
    }   
	 
	public void cancleAllTasks() { 
		if(taskCollection != null){ 
			for(BitMapWorkTask task : taskCollection){ 
				task.cancel(false); 
			} 
		} 
		 
	} 
	 
	 
	class BitMapWorkTask extends AsyncTask<String, Void, Bitmap>{ 
		 
		private String imageUrl; 
 
		@Override 
		protected Bitmap doInBackground(String... params) { 
			imageUrl = params[0]; 
			Bitmap bitmap = downloadBitmap(imageUrl); 
			 
			if(bitmap != null){ 
				//如果下载图片成功,就把它放入缓冲区 
				addBitmapFromMemoryCache(imageUrl, bitmap); 
			} 
			//把值返回到onPostExecute,更新主线程UI 
			return bitmap; 
		} 
		 
 
		@Override 
		protected void onPostExecute(Bitmap result) { 
			super.onPostExecute(result); 
			 
			ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl); 
			 
			if(imageView != null && result != null){ 
				imageView.setImageBitmap(result); 
			} 
			 
			taskCollection.remove(this); 
		} 
 
 
 
		private Bitmap downloadBitmap(String imageUrl) { 
			 
			Bitmap bitmap = null; 
			HttpURLConnection conn = null;//如何判断哪些需要初始化哪些不需要初始化 
			InputStream is = null; 
			ByteArrayOutputStream baos = null; 
			try { 
				URL url = new URL(imageUrl); 
				conn = (HttpURLConnection) url.openConnection(); 
				conn.setReadTimeout(5000); 
				conn.setConnectTimeout(5000); 
				is = conn.getInputStream(); 
				 
				 
				/* 
				 * byte方式 
				 */ 
//				int len = -1; 
//				byte[] buf = new byte[1024]; 
//				baos = new ByteArrayOutputStream(); 
//				 
//				while((len = is.read(buf)) != -1){//is.read字节读取,读入缓冲区数组buf中 
//					baos.write(buf, 0, len);//把数组写入字节数组buf中 
//				} 
//				baos.flush(); 
//				 
//				byte[] data = baos.toByteArray(); 
//				 
//				bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 
				 
				 
				/* 
				 * stream方式 
				 */ 
				 
				bitmap = BitmapFactory.decodeStream(is); 
				 
				Log.d("TAG", "downloadBitmap调用了"); 
			 
			} catch (Exception e) { 
				e.printStackTrace();		 
			} finally{ 
				if(conn != null){ 
					conn.disconnect(); 
				} 
			} 
			return bitmap; 
		} 
		 
		 
 
	} 
	 
 
}

适配器的思路是这样的:

一:GridView要滑动,所以我们让它实现了OnScrollListener监听器,并实现了监听器的两个方法

1.public void onScrollStateChanged(AbsListView view, int scrollState)

这个方法用于处理屏幕滑动时的事件,在这里我们限定了,当屏幕静止时才开启加载图片的任务,滑动时则停止任务

2.public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,  int totalItemCount)

这个方法用于处理刚进入程序时的事件,在这里我们限定了第一次进入程序时要开启加载图片任务,不然会不显示图片

二:进入加图片的任务后,首先从缓存区通过键查找缓存区是否有该键值对应的图片,如果有的话,直接把该Bitmap对象赋给imageView的实例,如果缓存区没有,则把url传入,开启下载任务。

三:下载任务中使用异步任务,在doinBackgroud中去下载图片,如果下载成功,则首先把图片加入缓存区,然后把Bitmap对象返回到onPostExecute()中去更新UI,设置imageView。


具体每一步的逻辑在代码中。

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Android Volley网络通信框架的使用(二):Volley加载网络图片详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。