Android瀑布流照片墙实现体验不规则排列的美感_第1页
Android瀑布流照片墙实现体验不规则排列的美感_第2页
Android瀑布流照片墙实现体验不规则排列的美感_第3页
Android瀑布流照片墙实现体验不规则排列的美感_第4页
Android瀑布流照片墙实现体验不规则排列的美感_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Android瀑布流照片墙实现,体验不规则排列的美感传统界面的布局方式总是行列分明、坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳。这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面。首先还是讲一下实现原理,瀑布流的布局方式虽然看起来好像排列的很随意,其实它是有很科学的排列规则的。整个界面会根据屏幕的宽度划分成等宽的若干列,由于手机的屏幕不是很大,这里我们就分成三列。每当需要添加一张图片时,会将这张图片的宽度压缩成和列一样宽,再按照同样的压缩比例对图片的高度进行压缩,然后在这三列中找出当前高度最小的一列,将图片添加到这一列中。之后每当需要添加一张新图片时,都去重复上面的操作,就会形成瀑布流格局的照片墙,示意图如下所示。听我这么说完后,你可能会觉得瀑布流的布局非常简单嘛,只需要使用三个LinearLayout平分整个屏幕宽度,然后动态地addView()进去就好了。确实如此,如果只是为了实现功能的话,就是这么简单。可是别忘了,我们是在手机上进行开发,如果不停地往LinearLayout里添加图片,程序很快就会OOM。因此我们还需要一个合理的方案来对图片资源进行释放,这里仍然是准备使用LruCache算法,对这个算法不熟悉的朋友可以先参考Android高效加载大图、多图方案,有效避免程序OOM。下面我们就来开始实现吧,新建一个Android项目,起名叫PhotoWallFallsDemo,并选择4.0的API。第一个要考虑的问题是,我们到哪儿去收集这些大小参差不齐的图片呢?这里我事先在百度上搜索了很多张风景图片,并且为了保证它们访问的稳定性,我将这些图片都上传到了我的CSDN相册里,因此只要从这里下载图片就可以了。新建一个Images类,将所有相册中图片的网址都配置进去,代码如下所示:[java]viewplaincopypublicclassImages{publicfinalstaticString[]imageUrls=newString[]{"/uploads/201309/01/1378037235_3453.jpg","/uploads/201309/01/1378037235_7476.jpg","/uploads/201309/01/1378037235_9280.jpg","/uploads/201309/01/1378037234_3539.jpg","/uploads/201309/01/1378037234_6318.jpg","/uploads/201309/01/1378037194_2965.jpg","/uploads/201309/01/1378037193_1687.jpg","/uploads/201309/01/1378037193_1286.jpg","/uploads/201309/01/1378037192_8379.jpg","/uploads/201309/01/1378037178_9374.jpg","/uploads/201309/01/1378037177_1254.jpg","/uploads/201309/01/1378037177_6203.jpg","/uploads/201309/01/1378037152_6352.jpg","/uploads/201309/01/1378037151_9565.jpg","/uploads/201309/01/1378037151_7904.jpg","/uploads/201309/01/1378037148_7104.jpg","/uploads/201309/01/1378037129_8825.jpg","/uploads/201309/01/1378037128_5291.jpg","/uploads/201309/01/1378037128_3531.jpg","/uploads/201309/01/1378037127_1085.jpg","/uploads/201309/01/1378037095_7515.jpg","/uploads/201309/01/1378037094_8001.jpg","/uploads/201309/01/1378037093_7168.jpg","/uploads/201309/01/1378037091_4950.jpg","/uploads/201308/31/1377949643_6410.jpg","/uploads/201308/31/1377949642_6939.jpg","/uploads/201308/31/1377949630_4505.jpg","/uploads/201308/31/1377949630_4593.jpg","/uploads/201308/31/1377949629_7309.jpg","/uploads/201308/31/1377949629_8247.jpg","/uploads/201308/31/1377949615_1986.jpg","/uploads/201308/31/1377949614_8482.jpg","/uploads/201308/31/1377949614_3743.jpg","/uploads/201308/31/1377949614_4199.jpg","/uploads/201308/31/1377949599_3416.jpg","/uploads/201308/31/1377949599_5269.jpg","/uploads/201308/31/1377949598_7858.jpg","/uploads/201308/31/1377949598_9982.jpg","/uploads/201308/31/1377949578_2770.jpg","/uploads/201308/31/1377949578_8744.jpg","/uploads/201308/31/1377949577_5210.jpg","/uploads/201308/31/1377949577_1998.jpg","/uploads/201308/31/1377949482_8813.jpg","/uploads/201308/31/1377949481_6577.jpg","/uploads/201308/31/1377949480_4490.jpg","/uploads/201308/31/1377949455_6792.jpg","/uploads/201308/31/1377949455_6345.jpg","/uploads/201308/31/1377949442_4553.jpg","/uploads/201308/31/1377949441_8987.jpg","/uploads/201308/31/1377949441_5454.jpg","/uploads/201308/31/1377949454_6367.jpg","/uploads/201308/31/1377949442_4562.jpg"};}然后新建一个ImageLoader类,用于方便对图片进行管理,代码如下所示:[java]viewplaincopypublicclassImageLoader{/***图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。*/privatestaticLruCache<String,Bitmap>mMemoryCache;/***ImageLoader的实例。*/privatestaticImageLoadermImageLoader;privateImageLoader(){//获取应用程序最大可用内存intmaxMemory=(int)Runtime.getRuntime().maxMemory();intcacheSize=maxMemory/8;//设置图片缓存大小为程序最大可用内存的1/8mMemoryCache=newLruCache<String,Bitmap>(cacheSize){@OverrideprotectedintsizeOf(Stringkey,Bitmapbitmap){returnbitmap.getByteCount();}};}/***获取ImageLoader的实例。**@returnImageLoader的实例。*/publicstaticImageLoadergetInstance(){if(mImageLoader==null){mImageLoader=newImageLoader();}returnmImageLoader;}/***将一张图片存储到LruCache中。**@paramkey*LruCache的键,这里传入图片的URL地址。*@parambitmap*LruCache的键,这里传入从网络上下载的Bitmap对象。*/publicvoidaddBitmapToMemoryCache(Stringkey,Bitmapbitmap){if(getBitmapFromMemoryCache(key)==null){mMemoryCache.put(key,bitmap);}}/***从LruCache中获取一张图片,如果不存在就返回null。**@paramkey*LruCache的键,这里传入图片的URL地址。*@return对应传入键的Bitmap对象,或者null。*/publicBitmapgetBitmapFromMemoryCache(Stringkey){returnmMemoryCache.get(key);}publicstaticintcalculateInSampleSize(BitmapFactory.Optionsoptions,intreqWidth){//源图片的宽度finalintwidth=options.outWidth;intinSampleSize=1;if(width>reqWidth){//计算出实际宽度和目标宽度的比率finalintwidthRatio=Math.round((float)width/(float)reqWidth);inSampleSize=widthRatio;}returninSampleSize;}publicstaticBitmapdecodeSampledBitmapFromResource(StringpathName,intreqWidth){//第一次解析将inJustDecodeBounds设置为true,来获取图片大小finalBitmapFactory.Optionsoptions=newBitmapFactory.Options();options.inJustDecodeBounds=true;BitmapFactory.decodeFile(pathName,options);//调用上面定义的方法计算inSampleSize值options.inSampleSize=calculateInSampleSize(options,reqWidth);//使用获取到的inSampleSize值再次解析图片options.inJustDecodeBounds=false;returnBitmapFactory.decodeFile(pathName,options);}}这里我们将ImageLoader类设成单例,并在构造函数中初始化了LruCache类,把它的最大缓存容量设为最大可用内存的1/8。然后又提供了其它几个方法可以操作LruCache,以及对图片进行压缩和读取。接下来新建MyScrollView继承自ScrollView,代码如下所示:[java]viewplaincopypublicclassMyScrollViewextendsScrollViewimplementsOnTouchListener{/***每页要加载的图片数量*/publicstaticfinalintPAGE_SIZE=15;/***记录当前已加载到第几页*/privateintpage;/***每一列的宽度*/privateintcolumnWidth;/***当前第一列的高度*/privateintfirstColumnHeight;/***当前第二列的高度*/privateintsecondColumnHeight;/***当前第三列的高度*/privateintthirdColumnHeight;/***是否已加载过一次layout,这里onLayout中的初始化只需加载一次*/privatebooleanloadOnce;/***对图片进行管理的工具类*/privateImageLoaderimageLoader;/***第一列的布局*/privateLinearLayoutfirstColumn;/***第二列的布局*/privateLinearLayoutsecondColumn;/***第三列的布局*/privateLinearLayoutthirdColumn;/***记录所有正在下载或等待下载的任务。*/privatestaticSet<LoadImageTask>taskCollection;/***MyScrollView下的直接子布局。*/privatestaticViewscrollLayout;/***MyScrollView布局的高度。*/privatestaticintscrollViewHeight;/***记录上垂直方向的滚动距离。*/privatestaticintlastScrollY=-1;/***记录所有界面上的图片,用以可以随时控制对图片的释放。*/privateList<ImageView>imageViewList=newArrayList<ImageView>();/***在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。*/privatestaticHandlerhandler=newHandler(){publicvoidhandleMessage(android.os.Messagemsg){MyScrollViewmyScrollView=(MyScrollView)msg.obj;intscrollY=myScrollView.getScrollY();//如果当前的滚动位置和上次相同,表示已停止滚动if(scrollY==lastScrollY){//当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片if(scrollViewHeight+scrollY>=scrollLayout.getHeight()&&taskCollection.isEmpty()){myScrollView.loadMoreImages();}myScrollView.checkVisibility();}else{lastScrollY=scrollY;Messagemessage=newMessage();message.obj=myScrollView;//5毫秒后再次对滚动位置进行判断handler.sendMessageDelayed(message,5);}};};/***MyScrollView的构造函数。**@paramcontext*@paramattrs*/publicMyScrollView(Contextcontext,AttributeSetattrs){super(context,attrs);imageLoader=ImageLoader.getInstance();taskCollection=newHashSet<LoadImageTask>();setOnTouchListener(this);}/***进行一些关键性的初始化操作,获取MyScrollView的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。*/@OverrideprotectedvoidonLayout(booleanchanged,intl,intt,intr,intb){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=true;loadMoreImages();}}/***监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。*/@OverridepublicbooleanonTouch(Viewv,MotionEventevent){if(event.getAction()==MotionEvent.ACTION_UP){Messagemessage=newMessage();message.obj=this;handler.sendMessageDelayed(message,5);}returnfalse;}/***开始加载下一页的图片,每张图片都会开启一个异步线程去下载。*/publicvoidloadMoreImages(){if(hasSDCard()){intstartIndex=page*PAGE_SIZE;intendIndex=page*PAGE_SIZE+PAGE_SIZE;if(startIndex<Images.imageUrls.length){Toast.makeText(getContext(),"正在加载...",Toast.LENGTH_SHORT).show();if(endIndex>Images.imageUrls.length){endIndex=Images.imageUrls.length;}for(inti=startIndex;i<endIndex;i++){LoadImageTasktask=newLoadImageTask();taskCollection.add(task);task.execute(Images.imageUrls[i]);}page++;}else{Toast.makeText(getContext(),"已没有更多图片",Toast.LENGTH_SHORT).show();}}else{Toast.makeText(getContext(),"未发现SD卡",Toast.LENGTH_SHORT).show();}}/***遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。*/publicvoidcheckVisibility(){for(inti=0;i<imageViewList.size();i++){ImageViewimageView=imageViewList.get(i);intborderTop=(Integer)imageView.getTag(R.string.border_top);intborderBottom=(Integer)imageView.getTag(R.string.border_bottom);if(borderBottom>getScrollY()&&borderTop<getScrollY()+scrollViewHeight){StringimageUrl=(String)imageView.getTag(R.string.image_url);Bitmapbitmap=imageLoader.getBitmapFromMemoryCache(imageUrl);if(bitmap!=null){imageView.setImageBitmap(bitmap);}else{LoadImageTasktask=newLoadImageTask(imageView);task.execute(imageUrl);}}else{imageView.setImageResource(R.drawable.empty_photo);}}}/***判断手机是否有SD卡。**@return有SD卡返回true,没有返回false。*/privatebooleanhasSDCard(){returnEnvironment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());}/***异步下载图片的任务。**@authorguolin*/classLoadImageTaskextendsAsyncTask<String,Void,Bitmap>{/***图片的URL地址*/privateStringmImageUrl;/***可重复使用的ImageView*/privateImageViewmImageView;publicLoadImageTask(){}/***将可重复使用的ImageView传入**@paramimageView*/publicLoadImageTask(ImageViewimageView){mImageView=imageView;}@OverrideprotectedBitmapdoInBackground(String...params){mImageUrl=params[0];BitmapimageBitmap=imageLoader.getBitmapFromMemoryCache(mImageUrl);if(imageBitmap==null){imageBitmap=loadImage(mImageUrl);}returnimageBitmap;}@OverrideprotectedvoidonPostExecute(Bitmapbitmap){if(bitmap!=null){doubleratio=bitmap.getWidth()/(columnWidth*1.0);intscaledHeight=(int)(bitmap.getHeight()/ratio);addImage(bitmap,columnWidth,scaledHeight);}taskCollection.remove(this);}/***根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。**@paramimageUrl*图片的URL地址*@return加载到内存的图片。*/privateBitmaploadImage(StringimageUrl){FileimageFile=newFile(getImagePath(imageUrl));if(!imageFile.exists()){downloadImage(imageUrl);}if(imageUrl!=null){Bitmapbitmap=ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(),columnWidth);if(bitmap!=null){imageLoader.addBitmapToMemoryCache(imageUrl,bitmap);returnbitmap;}}returnnull;}/***向ImageView中添加一张图片**@parambitmap*待添加的图片*@paramimageWidth*图片的宽度*@paramimageHeight*图片的高度*/privatevoidaddImage(Bitmapbitmap,intimageWidth,intimageHeight){LinearLayout.LayoutParamsparams=newLinearLayout.LayoutParams(imageWidth,imageHeight);if(mImageView!=null){mImageView.setImageBitmap(bitmap);}else{ImageViewimageView=newImageView(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);}}/***找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。**@paramimageView*@paramimageHeight*@return应该添加图片的一列*/privateLinearLayoutfindColumnToAdd(ImageViewimageView,intimageHeight){if(firstColumnHeight<=secondColumnHeight){if(firstColumnHeight<=thirdColumnHeight){imageView.setTag(R.string.border_top,firstColumnHeight);firstColumnHeight+=imageHeight;imageView.setTag(R.string.border_bottom,firstColumnHeight);returnfirstColumn;}imageView.setTag(R.string.border_top,thirdColumnHeight);thirdColumnHeight+=imageHeight;imageView.setTag(R.string.border_bottom,thirdColumnHeight);returnthirdColumn;}else{if(secondColumnHeight<=thirdColumnHeight){imageView.setTag(R.string.border_top,secondColumnHeight);secondColumnHeight+=imageHeight;imageView.setTag(R.string.border_bottom,secondColumnHeight);returnsecondColumn;}imageView.setTag(R.string.border_top,thirdColumnHeight);thirdColumnHeight+=imageHeight;imageView.setTag(R.string.border_bottom,thirdColumnHeight);returnthirdColumn;}}/***将图片下载到SD卡缓存起来。**@paramimageUrl*图片的URL地址。*/privatevoiddownloadImage(StringimageUrl){HttpURLConnectioncon=null;FileOutputStreamfos=null;BufferedOutputStreambos=null;BufferedInputStreambis=null;FileimageFile=null;try{URLurl=newURL(imageUrl);con=(HttpURLConnection)url.openConnection();con.setConnectTimeout(5*1000);con.setReadTimeout(15*1000);con.setDoInput(true);con.setDoOutput(true);bis=newBufferedInputStream(con.getInputStream());imageFile=newFile(getImagePath(imageUrl));fos=newFileOutputStream(imageFile);bos=newBufferedOutputStream(fos);byte[]b=newbyte[1024];intlength;while((length=bis.read(b))!=-1){bos.write(b,0,length);bos.flush();}}catch(Exceptione){e.printStackTrace();}finally{try{if(bis!=null){bis.close();}if(bos!=null){bos.close();}if(con!=null){con.disconnect();}}catch(IOExceptione){e.printStackTrace();}}if(imageFile!=null){Bitmapbitmap=ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(),umnWidth);if(bitmap!=null){imageLoader.addBitmapToMemoryCache(imageUrl,bitmap);}}}/***获取图片的本地存储路径。**@paramimageUrl*图片的URL地址。*@return图片的本地存储路径。*/privateStringgetImagePath(StringimageUrl){intlastSlashIndex=imageUrl.lastIndexOf("/");StringimageName=imageUrl.substring(lastSlashIndex+1);StringimageDir=Environment.getExternalStorageDirectory().getPath()+"/PhotoWallFalls/";Filefile=newFile(imageDir);if(!file.exists()){file.mkdirs();}StringimagePath=imageDir+imageName;returnimagePath;}}}MyScrollView是实现瀑布流照片墙的核心类,这里我来重点给大家介绍一下。首先它是继承自ScrollView的,这样就允许用户可以通过滚动的方式来浏览更多的图片。这里提供了一个loadMoreImages()方法,是专门用于加载下一页的图片的,因此在onLayout()方法中我们要先调用一次这个方法,以初始化第一页的图片。然后在onTouch方法中每当监听到手指离开屏幕的事件,就会通过一个handler来对当前ScrollView的滚动状态进行判断,如果发现已经滚动到了最底部,就会再次调用loadMoreImages()方法去加载下一页的图片。那我们就要来看一看loadMoreImages()方法的内部细节了。在这个方法中,使用了一个循环来加载这一页中的每一张图片,每次都会开启一个L

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论