下面的图片是用js,html要实现的瀑布流,图像为什么显示不出来,这些图片都是在dreamweaver目录中有的_百度知道传统界面的布局方式总是行列分明、坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳。这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面。
记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于&墙&上的每张图片大小都相同的情况,如果图片的大小参差不齐,在GridView中显示就会非常的难看。而使用瀑布流的布局方式就可以很好地解决这个问题,因此今天我们也来赶一下潮流,看看如何在Android上实现瀑布流照片墙的功能。
首先还是讲一下实现原理,瀑布流的布局方式虽然看起来好像排列的很随意,其实它是有很科学的排列规则的。整个界面会根据屏幕的宽度划分成等宽的若干列,由于手机的屏幕不是很大,这里我们就分成三列。每当需要添加一张图片时,会将这张图片的宽度压缩成和列一样宽,再按照同样的压缩比例对图片的高度进行压缩,然后在这三列中找出当前高度最小的一列,将图片添加到这一列中。之后每当需要添加一张新图片时,都去重复上面的操作,就会形成瀑布流格局的照片墙,示意图如下所示。
& & & & & & & & & & & & & & & & & & & &&
听我这么说完后,你可能会觉得瀑布流的布局非常简单嘛,只需要使用三个LinearLayout平分整个屏幕宽度,然后动态地addView()进去就好了。确实如此,如果只是为了实现功能的话,就是这么简单。可是别忘了,我们是在手机上进行开发,如果不停地往LinearLayout里添加图片,程序很快就会OOM。因此我们还需要一个合理的方案来对图片资源进行释放,这里仍然是准备使用LruCache算法,对这个算法不熟悉的朋友可以先参考&。
下面我们就来开始实现吧,新建一个Android项目,起名叫PhotoWallFallsDemo,并选择4.0的API。
第一个要考虑的问题是,我们到哪儿去收集这些大小参差不齐的图片呢?这里我事先在百度上搜索了很多张风景图片,并且为了保证它们访问的稳定性,我将这些图片都上传到了我的CSDN相册里,因此只要从这里下载图片就可以了。新建一个Images类,将所有相册中图片的网址都配置进去,代码如下所示:
public class Images {
public final static String[] imageUrls = new String[] {
"http://img.my.csdn.net/uploads//_3453.jpg",
"http://img.my.csdn.net/uploads//_7476.jpg",
"http://img.my.csdn.net/uploads//_9280.jpg",
"http://img.my.csdn.net/uploads//_3539.jpg",
"http://img.my.csdn.net/uploads//_6318.jpg",
"http://img.my.csdn.net/uploads//_2965.jpg",
"http://img.my.csdn.net/uploads//_1687.jpg",
"http://img.my.csdn.net/uploads//_1286.jpg",
"http://img.my.csdn.net/uploads//_8379.jpg",
"http://img.my.csdn.net/uploads//_9374.jpg",
"http://img.my.csdn.net/uploads//_1254.jpg",
"http://img.my.csdn.net/uploads//_6203.jpg",
"http://img.my.csdn.net/uploads//_6352.jpg",
"http://img.my.csdn.net/uploads//_9565.jpg",
"http://img.my.csdn.net/uploads//_7904.jpg",
"http://img.my.csdn.net/uploads//_7104.jpg",
"http://img.my.csdn.net/uploads//_8825.jpg",
"http://img.my.csdn.net/uploads//_5291.jpg",
"http://img.my.csdn.net/uploads//_3531.jpg",
"http://img.my.csdn.net/uploads//_1085.jpg",
"http://img.my.csdn.net/uploads//_7515.jpg",
"http://img.my.csdn.net/uploads//_8001.jpg",
"http://img.my.csdn.net/uploads//_7168.jpg",
"http://img.my.csdn.net/uploads//_4950.jpg",
"http://img.my.csdn.net/uploads//_6410.jpg",
"http://img.my.csdn.net/uploads//_6939.jpg",
"http://img.my.csdn.net/uploads//_4505.jpg",
"http://img.my.csdn.net/uploads//_4593.jpg",
"http://img.my.csdn.net/uploads//_7309.jpg",
"http://img.my.csdn.net/uploads//_8247.jpg",
"http://img.my.csdn.net/uploads//_1986.jpg",
"http://img.my.csdn.net/uploads//_8482.jpg",
"http://img.my.csdn.net/uploads//_3743.jpg",
"http://img.my.csdn.net/uploads//_4199.jpg",
"http://img.my.csdn.net/uploads//_3416.jpg",
"http://img.my.csdn.net/uploads//_5269.jpg",
"http://img.my.csdn.net/uploads//_7858.jpg",
"http://img.my.csdn.net/uploads//_9982.jpg",
"http://img.my.csdn.net/uploads//_2770.jpg",
"http://img.my.csdn.net/uploads//_8744.jpg",
"http://img.my.csdn.net/uploads//_5210.jpg",
"http://img.my.csdn.net/uploads//_1998.jpg",
"http://img.my.csdn.net/uploads//_8813.jpg",
"http://img.my.csdn.net/uploads//_6577.jpg",
"http://img.my.csdn.net/uploads//_4490.jpg",
"http://img.my.csdn.net/uploads//_6792.jpg",
"http://img.my.csdn.net/uploads//_6345.jpg",
"http://img.my.csdn.net/uploads//_4553.jpg",
"http://img.my.csdn.net/uploads//_8987.jpg",
"http://img.my.csdn.net/uploads//_5454.jpg",
"http://img.my.csdn.net/uploads//_6367.jpg",
"http://img.my.csdn.net/uploads//_4562.jpg" };
然后新建一个ImageLoader类,用于方便对图片进行管理,代码如下所示:
public class ImageLoader {
* 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
private static LruCache&String, Bitmap& mMemoryC
* ImageLoader的实例。
private static ImageLoader mImageL
private ImageLoader() {
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 设置图片缓存大小为程序最大可用内存的1/8
mMemoryCache = new LruCache&String, Bitmap&(cacheSize) {
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
* 获取ImageLoader的实例。
* @return ImageLoader的实例。
public static ImageLoader getInstance() {
if (mImageLoader == null) {
mImageLoader = new ImageLoader();
return mImageL
* 将一张图片存储到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);
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth) {
// 源图片的宽度
final int width = options.outW
int inSampleSize = 1;
if (width & reqWidth) {
// 计算出实际宽度和目标宽度的比率
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = widthR
return inSampleS
public static Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds =
BitmapFactory.decodeFile(pathName, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds =
return BitmapFactory.decodeFile(pathName, options);
这里我们将ImageLoader类设成单例,并在构造函数中初始化了LruCache类,把它的最大缓存容量设为最大可用内存的1/8。然后又提供了其它几个方法可以操作LruCache,以及对图片进行压缩和读取。
接下来新建MyScrollView继承自ScrollView,代码如下所示:
public class MyScrollView extends ScrollView implements OnTouchListener {
* 每页要加载的图片数量
public static final int PAGE_SIZE = 15;
* 记录当前已加载到第几页
* 每一列的宽度
private int columnW
* 当前第一列的高度
private int firstColumnH
* 当前第二列的高度
private int secondColumnH
* 当前第三列的高度
private int thirdColumnH
* 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
private boolean loadO
* 对图片进行管理的工具类
private ImageLoader imageL
* 第一列的布局
private LinearLayout firstC
* 第二列的布局
private LinearLayout secondC
* 第三列的布局
private LinearLayout thirdC
* 记录所有正在下载或等待下载的任务。
private static Set&LoadImageTask& taskC
* MyScrollView下的直接子布局。
private static View scrollL
* MyScrollView布局的高度。
private static int scrollViewH
* 记录上垂直方向的滚动距离。
private static int lastScrollY = -1;
* 记录所有界面上的图片,用以可以随时控制对图片的释放。
private List&ImageView& imageViewList = new ArrayList&ImageView&();
* 在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。
private static Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
MyScrollView myScrollView = (MyScrollView) msg.
int scrollY = myScrollView.getScrollY();
// 如果当前的滚动位置和上次相同,表示已停止滚动
if (scrollY == lastScrollY) {
// 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片
if (scrollViewHeight + scrollY &= scrollLayout.getHeight()
&& taskCollection.isEmpty()) {
myScrollView.loadMoreImages();
myScrollView.checkVisibility();
lastScrollY = scrollY;
Message message = new Message();
message.obj = myScrollV
// 5毫秒后再次对滚动位置进行判断
handler.sendMessageDelayed(message, 5);
* MyScrollView的构造函数。
* @param context
* @param attrs
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
imageLoader = ImageLoader.getInstance();
taskCollection = new HashSet&LoadImageTask&();
setOnTouchListener(this);
* 进行一些关键性的初始化操作,获取MyScrollView的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed && !loadOnce) {
scrollViewHeight = getHeight();
scrollLayout = getChildAt(0);
firstColumn = (LinearLayout) findViewById(R.id.first_column);
secondColumn = (LinearLayout) findViewById(R.id.second_column);
thirdColumn = (LinearLayout) findViewById(R.id.third_column);
columnWidth = firstColumn.getWidth();
loadOnce =
loadMoreImages();
* ***用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
Message message = new Message();
message.obj =
handler.sendMessageDelayed(message, 5);
* 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
public void loadMoreImages() {
if (hasSDCard()) {
int startIndex = page * PAGE_SIZE;
int endIndex = page * PAGE_SIZE + PAGE_SIZE;
if (startIndex & Images.imageUrls.length) {
Toast.makeText(getContext(), "正在加载...", Toast.LENGTH_SHORT)
if (endIndex & Images.imageUrls.length) {
endIndex = Images.imageUrls.
for (int i = startI i & endI i++) {
LoadImageTask task = new LoadImageTask();
taskCollection.add(task);
task.execute(Images.imageUrls[i]);
Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT)
Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT).show();
* 遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。
public void checkVisibility() {
for (int i = 0; i & imageViewList.size(); i++) {
ImageView imageView = imageViewList.get(i);
int borderTop = (Integer) imageView.getTag(R.string.border_top);
int borderBottom = (Integer) imageView
.getTag(R.string.border_bottom);
if (borderBottom & getScrollY()
&& borderTop & getScrollY() + scrollViewHeight) {
String imageUrl = (String) imageView.getTag(R.string.image_url);
Bitmap bitmap = imageLoader.getBitmapFromMemoryCache(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
LoadImageTask task = new LoadImageTask(imageView);
task.execute(imageUrl);
imageView.setImageResource(R.drawable.empty_photo);
* 判断手机是否有SD卡。
* @return 有SD卡返回true,没有返回false。
private boolean hasSDCard() {
return Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState());
* 异步下载图片的任务。
* @author guolin
class LoadImageTask extends AsyncTask&String, Void, Bitmap& {
* 图片的URL地址
private String mImageU
* 可重复使用的ImageView
private ImageView mImageV
public LoadImageTask() {
* 将可重复使用的ImageView传入
* @param imageView
public LoadImageTask(ImageView imageView) {
mImageView = imageV
protected Bitmap doInBackground(String... params) {
mImageUrl = params[0];
Bitmap imageBitmap = imageLoader
.getBitmapFromMemoryCache(mImageUrl);
if (imageBitmap == null) {
imageBitmap = loadImage(mImageUrl);
return imageB
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
double ratio = bitmap.getWidth() / (columnWidth * 1.0);
int scaledHeight = (int) (bitmap.getHeight() / ratio);
addImage(bitmap, columnWidth, scaledHeight);
taskCollection.remove(this);
* 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。
* @param imageUrl
图片的URL地址
* @return 加载到内存的图片。
private Bitmap loadImage(String imageUrl) {
File imageFile = new File(getImagePath(imageUrl));
if (!imageFile.exists()) {
downloadImage(imageUrl);
if (imageUrl != null) {
Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(
imageFile.getPath(), columnWidth);
if (bitmap != null) {
imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
* 向ImageView中添加一张图片
* @param bitmap
待添加的图片
* @param imageWidth
图片的宽度
* @param imageHeight
图片的高度
private void addImage(Bitmap bitmap, int imageWidth, int imageHeight) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
imageWidth, imageHeight);
if (mImageView != null) {
mImageView.setImageBitmap(bitmap);
ImageView imageView = new ImageView(getContext());
imageView.setLayoutParams(params);
imageView.setImageBitmap(bitmap);
imageView.setScaleType(ScaleType.FIT_XY);
imageView.setPadding(5, 5, 5, 5);
imageView.setTag(R.string.image_url, mImageUrl);
findColumnToAdd(imageView, imageHeight).addView(imageView);
imageViewList.add(imageView);
* 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。
* @param imageView
* @param imageHeight
* @return 应该添加图片的一列
private LinearLayout findColumnToAdd(ImageView imageView,
int imageHeight) {
if (firstColumnHeight &= secondColumnHeight) {
if (firstColumnHeight &= thirdColumnHeight) {
imageView.setTag(R.string.border_top, firstColumnHeight);
firstColumnHeight += imageH
imageView.setTag(R.string.border_bottom, firstColumnHeight);
return firstC
imageView.setTag(R.string.border_top, thirdColumnHeight);
thirdColumnHeight += imageH
imageView.setTag(R.string.border_bottom, thirdColumnHeight);
return thirdC
if (secondColumnHeight &= thirdColumnHeight) {
imageView.setTag(R.string.border_top, secondColumnHeight);
secondColumnHeight += imageH
.setTag(R.string.border_bottom, secondColumnHeight);
return secondC
imageView.setTag(R.string.border_top, thirdColumnHeight);
thirdColumnHeight += imageH
imageView.setTag(R.string.border_bottom, thirdColumnHeight);
return thirdC
* 将图片下载到SD卡缓存起来。
* @param imageUrl
图片的URL地址。
private void downloadImage(String imageUrl) {
HttpURLConnection con =
FileOutputStream fos =
BufferedOutputStream bos =
BufferedInputStream bis =
File imageFile =
URL url = new URL(imageUrl);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000);
con.setReadTimeout(15 * 1000);
con.setDoInput(true);
con.setDoOutput(true);
bis = new BufferedInputStream(con.getInputStream());
imageFile = new File(getImagePath(imageUrl));
fos = new FileOutputStream(imageFile);
bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024];
while ((length = bis.read(b)) != -1) {
bos.write(b, 0, length);
bos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bis != null) {
bis.close();
if (bos != null) {
bos.close();
if (con != null) {
con.disconnect();
} catch (IOException e) {
e.printStackTrace();
if (imageFile != null) {
Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(
imageFile.getPath(), columnWidth);
if (bitmap != null) {
imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
* 获取图片的本地存储路径。
* @param imageUrl
图片的URL地址。
* @return 图片的本地存储路径。
private String getImagePath(String imageUrl) {
int lastSlashIndex = imageUrl.lastIndexOf("/");
String imageName = imageUrl.substring(lastSlashIndex + 1);
String imageDir = Environment.getExternalStorageDirectory()
.getPath() + "/PhotoWallFalls/";
File file = new File(imageDir);
if (!file.exists()) {
file.mkdirs();
String imagePath = imageDir + imageN
return imageP
MyScrollView是实现瀑布流照片墙的核心类,这里我来重点给大家介绍一下。首先它是继承自ScrollView的,这样就允许用户可以通过滚动的方式来浏览更多的图片。这里提供了一个loadMoreImages()方法,是专门用于加载下一页的图片的,因此在onLayout()方法中我们要先调用一次这个方法,以初始化第一页的图片。然后在onTouch方法中每当***到手指离开屏幕的事件,就会通过一个handler来对当前ScrollView的滚动状态进行判断,如果发现已经滚动到了最底部,就会再次调用loadMoreImages()方法去加载下一页的图片。
那我们就要来看一看loadMoreImages()方法的内部细节了。在这个方法中,使用了一个循环来加载这一页中的每一张图片,每次都会开启一个LoadImageTask,用于对图片进行异步加载。然后在LoadImageTask中,首先会先检查一下这张图片是不是已经存在于SD卡中了,如果还没存在,就从网络上下载,然后把这张图片存放在LruCache中。接着将这张图按照一定的比例进行压缩,并找出当前高度最小的一列,把压缩后的图片添加进去就可以了。
另外,为了保证照片墙上的图片都能够合适地被回收,这里还加入了一个可见性检查的方法,即checkVisibility()方法。这个方法的核心思想就是检查目前照片墙上的所有图片,判断出哪些是可见的,哪些是不可见。然后将那些不可见的图片都替换成一张空图,这样就可以保证程序始终不会占用过高的内存。当这些图片又重新变为可见的时候,只需要再从LruCache中将这些图片重新取出即可。如果某张图片已经从LruCache中被移除了,就会开启一个LoadImageTask,将这张图片重新加载到内存中。
然后打开或新建activity_main.xml,在里面设置好瀑布流的布局方式,如下所示:
&com.example.photowallfallsdemo.MyScrollView xmlns:android="/apk/res/android"
android:id="@+id/my_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent" &
&LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" &
&LinearLayout
android:id="@+id/first_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" &
&/LinearLayout&
&LinearLayout
android:id="@+id/second_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" &
&/LinearLayout&
&LinearLayout
android:id="@+id/third_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" &
&/LinearLayout&
&/LinearLayout&
&/com.example.photowallfallsdemo.MyScrollView&
可以看到,这里我们使用了刚才编写好的MyScrollView作为根布局,然后在里面放入了一个直接子布局LinearLayout用于统计当前滑动布局的高度,然后在这个布局下又添加了三个等宽的LinearLayout分别作为第一列、第二列和第三列的布局,这样在MyScrollView中就可以动态地向这三个LinearLayout里添加图片了。
最后,由于我们使用到了网络和SD卡存储的功能,因此还需要在AndroidManifest.xml中添加以下权限:
&uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&
&uses-permission android:name="android.permission.INTERNET" /&
这样我们所有的编码工作就已经完成了,现在可以尝试运行一下,效果如下图所示:
& & & & & & & & & & & & & & & &&
瀑布流模式的照片墙果真非常美观吧,而且由于我们有非常完善的资源释放机制,不管你在照片墙上添加了多少图片,程序占用内存始终都会保持在一个合理的范围内。
好了,今天的讲解到此结束,有疑问的朋友请在下面留言。
阅读(...) 评论()9352人阅读
IOS开发(35)
在上一篇博客中,我们实现了异步加载图片的功能,由于上次的时间问题,讲的比较简单所以在这篇文章中,我对前面代码中涉及到得关键代码再做一次详细的解释。
先看一个函数,
* @brief 图片加载通用函数
* @parma imageName 图片名
- (void)imageStartLoading:(NSString *)imageName{
NSURL *url = [NSURL URLWithString:imageName];
if([_fileUtil hasCachedImage:url]){
UIImageView *imageView = [[UIImageView alloc] init];
NSString *path = [_fileUtil pathForUrl:url];
imageView = [_imageLoad compressImage:MY_WIDTH/3 imageView:nil imageName:path flag:NO];
[self addImage:imageView name:path];
[self adjustContentSize:NO];
UIImageView *imageView = [[UIImageView alloc] init];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:url, @&URL&,
imageView, @&imageView&, nil];
[NSThread detachNewThreadSelector:@selector(cacheImage:) toTarget:[ImageCacher shareInstance] withObject:dic];
这个函数的作用是为每一张网络图片开启一个下载线程,但是因为该程序用到了图片缓存的技术,所以在每次开线程下载图片的时候都会去本地缓存目录查找一下,
该图片是否已经存在,如果存在则直接加载在视图中。一般OC的线程函数有三个,NSThread, Cocoa Operations,和GCD,(想要了解三者的异同点可查看:),
这里我用了比较轻量级的NSThread,detachNewThreadSelector函数中所传的函数名:
cacheImage是类ImageCache中得函数,这里通过iOS开发中使用的比较多的单例模式,
得到了ImageCache的句柄,参数dic中主要存放了图片的网络地址以及imageView用来add图片进视图以及根据图片的大小压缩成合适的大小.
接下来是cacheImage函数:
- (void)cacheImage:(NSDictionary*)dic{
NSURL *url = [dic objectForKey:@&URL&];
NSFileManager *fileManage = [NSFileManager defaultManager];
NSData *data = [NSData dataWithContentsOfURL:url];
NSString *fileName = [_fileUtil pathForUrl:url];
[fileManage createFileAtPath:fileName contents:data attributes:nil];
UIImageView *imageView = [dic objectForKey:@&imageView&];
imageView.image = [UIImage imageWithData:data];
imageView = [_imageLoader compressImage:MY_WIDTH/3 imageView:imageView imageName:nil flag:YES];
[self.myDelegate addImage:imageView name:fileName];
[self.myDelegate adjustContentSize:NO];
该函数用来将下载下来的图片缓存进入文件沙盒中(缓存文件可以自己定义并指定),并且按照图片的大小进行等比例压缩,固定宽度是屏幕的三分之一大小,这样一来,
图片显示就不会出现不全或失真的现象。由于ImageCache和MyScrollView是两个独立的类,所以这里通过使用ios的delegate(代理)来进行图片在scrollView上的加载,
(什么是代理模式:).
下面我们来看如何在沙盒中建立缓存文件夹,其实缓存文件夹跟普通的文件夹一样,只是该文件夹是专门用来存放缓存文件的而已。类代码如下所示:
FileUtil.m
Created by silicon on 14-5-30.
Copyright (c) 2014年 silicon. All rights reserved.
#import &FileUtil.h&
@implementation FileUtil
+ (FileUtil *)shareInstance{
static FileUtil *
static dispatch_once_t onceT
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
@breif 创建缓存文件夹
- (void)createPathInDocumentDirectory{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *diskCachePath = [[[paths objectAtIndex:0] stringByAppendingPathComponent:@&ImageCache&] retain];
NSLog(@&%@&, diskCachePath);
if(![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath]){
NSError *error =
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:&error];
获取沙盒中文档目录
fileName:文件名字
- (NSString *)pathInDocumentDirectory:(NSString *)fileName{
NSArray *fileArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES);
NSString *cacheDirectory = [fileArray objectAtIndex:0];
return [cacheDirectory stringByAppendingPathComponent:fileName];
获取沙盒中缓存文件目录
fileName:文件名字
- (NSString *)pathInCacheDirectory:(NSString *)fileName{
NSArray *fileArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES);
NSString *cacheDirectory = [fileArray objectAtIndex:0];
return [cacheDirectory stringByAppendingPathComponent:fileName];
判断是否已经缓存
url:图片名称
- (BOOL)hasCachedImage:(NSURL *)url{
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:[self pathForUrl:url]]){
return YES;
return NO;
根据URL的?图片命名
url:图片url
- (NSString *)pathForUrl:(NSURL *)url{
return [self pathInCacheDirectory:[NSString stringWithFormat:@&qiaoqiao-%u&, [[url description] hash]]];
在这次的demo中,我新加入了用户可以点击图片放大 并可以左右滑动的功能,其实实现起来很简单,我一开始为每一个ScrollView 中得ImageView都设置了tag值,并且添加了
手势(UITapGestureRecognizer),当用户点击图片时,程序可以根据点击视图的tag值来获得相应的图片是哪一张,从而可以加载。支持左右滑动的功能在新的界面中增加了
一个ScrollView,然后将下载下来的图片添加到scrollView中。代码如下
PhotoViewController.m
Created by silicon on 14-5-22.
Copyright (c) 2014年 silicon. All rights reserved.
#import &PhotoViewController.h&
#import &ImageLoader.h&
@interface PhotoViewController ()
@implementation PhotoViewController
@synthesize scrollView = _scrollV
@synthesize imageArray = _imageA
@synthesize page = _
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view setFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
[self.view setBackgroundColor:[UIColor blackColor]];
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 20, MY_WIDTH, MY_HEIGHT)];
_scrollView.delegate =
_scrollView.contentSize = CGSizeMake(MY_WIDTH * [_imageArray count], MY_HEIGHT);
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.backgroundColor = [UIColor blackColor];
_scrollView.bounces = YES;
_scrollView.pagingEnabled = YES;
[self.view addSubview:_scrollView];
//图片添加事件响应
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closePhotoView)];
tapRecognizer.delegate =
_scrollView.userInteractionEnabled = YES;
[_scrollView addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
[self loadingImages];
- (void)viewWillAppear:(BOOL)animated{
[_scrollView setContentOffset:CGPointMake([_imageArray indexOfObject:_imageName] * MY_WIDTH, 0)];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
- (void)closePhotoView{
[self.view removeFromSuperview];
- (void)dealloc{
[_scrollView release];
[super dealloc];
- (void)loadingImages{
//加载图片
for(int i = 0; i & [_imageArray count]; i++){
NSString *picName = [_imageArray objectAtIndex:i];
UIImageView *imageV = [[ImageLoader shareInstance] compressImage:MY_WIDTH imageView:nil imageName:picName flag:NO];
float width = imageV.image.size.
float height = imageV.image.size.
float new_width = MY_WIDTH;
float new_height = (MY_WIDTH * height)/
imageV.frame = CGRectMake(MY_WIDTH * i, 0, new_width, new_height);
[_scrollView addSubview:imageV];
[imageV release];
- (void)scrollViewDidScroll:(UIScrollView *)_scrollView{
基本上这个程序的主要功能就这些啦,其他的一些有关瀑布流的实现请看我前面的博客吧!
如果有哪里写的或说的有问题,欢迎各位大神指出来。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:475397次
积分:2520
积分:2520
排名:第10590名
原创:39篇
评论:270条
(2)(6)(3)(2)(1)(2)(3)(1)(2)(4)(2)(4)(3)(1)(5)(5)