Android实现照片墙
一、背景描述
照片墙是一种在移动应用中常见的展示形式,用于以网格的形式整齐排列图片,这种布局不仅美观,而且能够高效利用屏幕空间,使用户能够一目了然地看到更多的图片,实现照片墙功能需要考虑图片的加载、缓存和内存管理等多方面的问题,本文将详细介绍如何在Android平台上实现一个高效的照片墙,并重点讲述如何通过LruCache来防止由于图片过多导致的程序崩溃。
二、需求分析
功能需求
GridView控件:作为照片墙的容器,按网格形式排列图片。
图片加载:支持从网络下载图片和本地存储的图片。
图片缓存:使用LruCache缓存已下载的图片,避免重复下载和内存溢出。
滚动加载:在用户滚动GridView时动态加载更多图片。
非功能需求
性能要求:图片滑动流畅,无明显卡顿。
内存管理:合理释放不再使用的图片资源,防止OOM(Out of Memory)。
用户体验:界面简洁美观,操作顺畅。
三、方案设计
技术选型
GridView:作为主要的图片容器。
ImageView:用于显示单张图片。
LruCache:用于缓存图片,减少内存消耗。
AsyncTask或Glide/Picasso等图片加载库:用于异步加载图片。
系统架构
UI层:包含GridView和自定义的适配器。
数据层:包含图片URL或本地路径。
缓存层:使用LruCache进行图片缓存。
网络层:负责从网络下载图片。
四、实现步骤
创建项目和布局文件
1.1 新建Android项目PhotoWallDemo
创建一个名为PhotoWallDemo的Android项目,并在res/layout
目录下创建两个XML布局文件:activity_main.xml
和photo_layout.xml
。
1.2 activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <GridView android:id="@+id/photo_wall" android:layout_width="match_parent" android:layout_height="match_parent" android:columnWidth="90dip" android:stretchMode="columnWidth" android:numColumns="auto_fit" android:verticalSpacing="10dip" android:horizontalSpacing="10dip" android:gravity="center" /> </LinearLayout>
1.3 photo_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:id="@+id/photo" android:layout_width="90dip" android:layout_height="90dip" android:src="@drawable/logo" android:layout_centerInParent="true"/> </RelativeLayout>
2.编写适配器类PhotoWallAdapter
创建一个名为PhotoWallAdapter
的类,继承自BaseAdapter
,用于绑定数据到GridView。
public class PhotoWallAdapter extends BaseAdapter { private Context mContext; private String[] imageUrls; private LruCache<String, Bitmap> mMemoryCache; private final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); private final int cacheSize = maxMemory / 8; // Use 1/8th of available memory for cache private Set<BitmapWorkerTask> taskCollection; private GridView mPhotoWall; private int mFirstVisibleItem; private int mVisibleItemCount; private boolean isFirstEnter = true; public PhotoWallAdapter(Context context, String[] imageUrls, GridView photoWall) { mContext = context; this.imageUrls = imageUrls; mPhotoWall = photoWall; taskCollection = new HashSet<>(); final int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); // Use 1/8th of the available memory for this memory cache. int cacheSize = 1024 * memClass / 8; mMemoryCache = new LruCache<>(cacheSize); mPhotoWall.setOnScrollListener(new AbsListView.OnScrollListener() { public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleItem = firstVisibleItem; mVisibleItemCount = visibleItemCount; } public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) { isFirstEnter = false; loadMoreImages(); } } }); } @Override public int getCount() { return imageUrls.length; } @Override public Object getItem(int position) { return imageUrls[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.photo_layout, null); } ImageView photo = (ImageView) convertView.findViewById(R.id.photo); String url = getItem(position).toString(); Bitmap bitmap = mMemoryCache.get(url); if (bitmap != null) { photo.setImageBitmap(bitmap); } else { photo.setImageResource(R.drawable.logo); // Default placeholder. taskCollection.add(new BitmapWorkerTask(url, photo)); } return convertView; } private void loadMoreImages() { int toIndex = Math.min(mFirstVisibleItem + mVisibleItemCount, 2 * mVisibleItemCount); for (int i = mFirstVisibleItem; i < toIndex; i++) { loadBitmap(i, imageUrls[i]); } } private void loadBitmap(final int position, final String url) { if (cancelPotentialWork(url, position)) { BitmapWorkerTask task = new BitmapWorkerTask(url, imageUrls[position]); taskCollection.add(task); task.execute(url); } } private static boolean cancelPotentialWork(String url, int position) { final BitmapWorkerTask task = getBitmapWorkerTask(url); if (task != null) { final int currentPosition = task.position; if (currentPosition == position) { return false; } task.cancel(true); } return true; } private static BitmapWorkerTask getBitmapWorkerTask(String url) { // Return the existing task if it's still being executed or has been cancelled but not yet removed. for (BitmapWorkerTask task : taskCollection) { if (task.url.equals(url)) { return task; } } return null; } class BitmapWorkerTask extends AsyncTask<String, String> { private String url; private final ImageView imageView; private int position; public BitmapWorkerTask(String u, ImageView i) { url = u; imageView = i; } @Override protected String doInBackground(String... params) { return downloadUrl(params[0]); } @Override protected void onPostExecute(String result) { if (isCancelled) { taskCollection.remove(this); return; } if (!isCancelled()) { Bitmap bitmap = decodeSampledBitmapFromResource(result, 90, 90); if (bitmap != null) { mMemoryCache.put(url, bitmap); imageView.setImageBitmap(bitmap); } } } private String downloadUrl(String u) { InputStream inputStream = null; HttpURLConnection connection = null; try { URL url = new URL(u); connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); inputStream = connection.getInputStream(); return BitmapFactory.decodeStream(inputStream); } catch (IOException e) { e.printStackTrace(); return null; } finally { try { if (inputStream != null) inputStream.close(); if (connection != null) connection.disconnect(); } catch (IOException ignored) {} } } } }
小伙伴们,上文介绍了“android实现照片墙”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。