android瀑布流照片墙实现代码详解

照片墙的实现,是需要往手机里面添加很多图片的,如果没有对资源进行合理的释放,程序很快就会出现OOM.所以需要用到LruCache算法来缓存图片.

1,首先是图片资源类,这个类中包含了很多图片链接.

public class AllImages {

public final static String[] imageUrls = new String[] {

"http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037194_2965.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037193_1687.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037193_1286.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037192_8379.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037178_9374.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037177_1254.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037177_6203.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037152_6352.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037151_9565.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037148_7104.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037129_8825.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037128_5291.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037128_3531.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037127_1085.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037095_7515.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037094_8001.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037093_7168.jpg",

"http://img.my.csdn.net/uploads/201309/01/1378037091_4950.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949643_6410.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949642_6939.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949630_4505.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949630_4593.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949629_7309.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949629_8247.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949615_1986.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949614_8482.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949614_3743.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949614_4199.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949599_3416.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949599_5269.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949598_7858.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949598_9982.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949578_2770.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949578_8744.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949577_5210.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949577_1998.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949482_8813.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949481_6577.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949480_4490.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949455_6792.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949455_6345.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949442_4553.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949441_8987.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949441_5454.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949454_6367.jpg",

"http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg" };

}

2.有了图片,还需要一个图片的工具类.新建一个ImageTools.

//图片处理的工具类

public class ImageTools {

//图片缓存,用于缓存所有下载好的图片

private static LruCache<String, Bitmap> mMemoryCache;

//ImageTools实例

private static ImageTools imageTools;

public ImageTools() {

//获取应用程序的最大可用内存

int maxMemory=(int)Runtime.getRuntime().maxMemory();

int cacheMemory=maxMemory/8;

mMemoryCache=new LruCache<String, Bitmap>(cacheMemory){

@Override

protected int sizeOf(String key, Bitmap bitmap) {

return bitmap.getByteCount();

}

};

}

//获取ImageTools的实例

public static ImageTools getInstance(){

if(imageTools==null){

imageTools=new ImageTools();

}

return imageTools;

}

//将图片放入LruCache中

public void addBitmapToMemoryCache(String key,Bitmap bitmap){

if(getBitmapFromMemoryCache(key)==null){

mMemoryCache.put(key, bitmap);

}

}

//从LruCache中获取一张图片,如果不存在就返回null

public Bitmap getBitmapFromMemoryCache(String key){

return mMemoryCache.get(key);

}

//求出图片需要压缩的比例

public static int calculateInSampleSize(BitmapFactory.Options option,int reqWidth){

//得到原图片的宽度

final int width=option.outWidth;

int inSampleSize=1;

if(width>reqWidth){

//计算出实际宽度与目标宽度的比例

final int widthRadio=Math.round((float)width/(float)reqWidth);

inSampleSize=widthRadio;

}

return inSampleSize;

}

//压缩图片

public static Bitmap decodeSampedBitmapFromResource(String pathName,int reqWidth){

// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小

final BitmapFactory.Options options=new Options();

options.inJustDecodeBounds=true;

BitmapFactory.decodeFile(pathName, options);

options.inSampleSize = calculateInSampleSize(options, reqWidth);

// 使用获取到的inSampleSize值再次解析图片

options.inJustDecodeBounds = false;

return BitmapFactory.decodeFile(pathName, options);

}

}

这里将ImageTools设为单例,并在构造函数中初始化了LruCache类,提供了操作LruCache的读取和增加方法  以及两个对图片进行处理的方法.

3.接下来就是最重要的MyScrollView类.核心类.

public class MyScrollView extends ScrollView  implements OnTouchListener {

//每页要加载的图片数量

public static final int PAGE_SIZE=15;

//记录当前加载到了第几页

private int page;

//每一列的宽度

private int columnWidth;

//第一列的高度

private int firstColumnHeight;

//第二列的高度

private int secondColumnHeight;

//第三列的高度

private int thirdColumnHeight;

//是否已经加载过Layout

private boolean loadOnce;

//对图片进行管理的工具类

private ImageTools imageTools;

//第一列的布局

private LinearLayout firstColumn;

//第二列布局

private LinearLayout secondColumn;

//第三列布局

private LinearLayout thirdColumn;

//MyScrollView下的子布局

private static View scrollLayout;

//MyScrollView的高度

private static int scrollViewHeight;

//记录上次垂直滚动的距离

private static int lastScrollY=-1;

//记录界面上所有的图片

private List<ImageView> imageList=new ArrayList<ImageView>();

//记录所有正在下载的任务

private static Set<LoadImageTask> imageTaskList;

private static Handler mh=new Handler(){

@Override

public void handleMessage(Message msg) {

MyScrollView mScrollView=(MyScrollView) msg.obj;

int scrollY=mScrollView.getScrollY();

if(scrollY==lastScrollY){  //如果当前滚动位置与上次滚动位置相同,则说明滚动停止

// 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片

if(scrollViewHeight+scrollY>=scrollLayout.getHeight()&&imageTaskList.isEmpty()){

mScrollView.loadMoreImage();

}

mScrollView.checkVisiblility();

}

else{

lastScrollY=scrollY;

Message message = new Message();

message.obj = mScrollView;

// 5毫秒后再次对滚动位置进行判断

mh.sendMessageDelayed(message, 5);

}

}

};

public MyScrollView(Context context, AttributeSet attrs) {

super(context, attrs);

imageTools=ImageTools.getInstance();

imageTaskList=new HashSet<MyScrollView.LoadImageTask>();

setOnTouchListener(this);

}

@Override

public boolean onTouch(View v, MotionEvent event) {

if(event.getAction()==MotionEvent.ACTION_UP){

Message msg=new Message();

msg.obj=this;

mh.sendMessageDelayed(msg,5);

}

return false;

}

@Override

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=true;

loadMoreImage();

}

}

//加载更多图片

public void loadMoreImage(){

if(hasSDCard()){

int startIndex=PAGE_SIZE*page;

int endIndex=PAGE_SIZE*page+PAGE_SIZE;

if(startIndex<AllImages.imageUrls.length){  //判断图片是否已经加载完毕

Toast.makeText(getContext(), "正在加载",Toast.LENGTH_SHORT).show();

if(endIndex>AllImages.imageUrls.length){

endIndex=AllImages.imageUrls.length;

}

for (int i = startIndex; i < endIndex; i++) {

LoadImageTask task=new LoadImageTask();

imageTaskList.add(task);

task.execute(AllImages.imageUrls[i]);

}

page++;

}

else{

Toast.makeText(getContext(), "加载完毕",Toast.LENGTH_SHORT).show();

}

}

else{

Toast.makeText(getContext(), "没有内存卡",Toast.LENGTH_SHORT).show();

}

}

//对图片的可见性进行检查,如果已经移出视线外,则将图片设为一张空白图片

public void checkVisiblility(){

for (int i = 0; i < imageList.size(); i++) {

ImageView imageView=imageList.get(i);

int border_top=(Integer) imageView.getTag(R.string.border_top);

int border_bottom=(Integer) imageView.getTag(R.string.border_bottom);

if(border_bottom>getScrollY()&&border_top<getScrollY()+scrollViewHeight){   //检查是不是在可见视图之内

String imageUrl=(String) imageView.getTag(R.string.image_url);

Bitmap imageBitmap=imageTools.getBitmapFromMemoryCache(imageUrl);

if(imageBitmap==null){

LoadImageTask task=new LoadImageTask(imageView);

task.execute(imageUrl);

}

else{

imageView.setImageBitmap(imageBitmap);

}

}

else{  //如果已经不再屏幕内

imageView.setImageResource(R.drawable.ic_launcher);

}

}

}

//判断手机是否有SD卡

private boolean hasSDCard(){

return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());

}

//异步加载图片的任务

class LoadImageTask extends AsyncTask<String, Void, Bitmap>{

//图片地址Url

private String imageUrl;

//可重复使用的ImageView

private ImageView mImageView;

public LoadImageTask() {

}

//将可重复使用的ImageView传入

public LoadImageTask(ImageView imageView) {

this.mImageView=imageView;

}

@Override

protected Bitmap doInBackground(String... params) { //根据imageUrl作为key值查找Bitmap,如果没有,调用loadImage去加载

imageUrl=params[0];

Bitmap imageBitmap=imageTools.getBitmapFromMemoryCache(imageUrl);

if(imageBitmap==null){

imageBitmap=loadImage(imageUrl);

}

return imageBitmap;

}

@Override

protected void onPostExecute(Bitmap imageBitmap) {

if(imageBitmap!=null){

double ratio=imageBitmap.getWidth()/(columnWidth*1.0);   //得到宽度的压缩比

int scaleHeight=(int)(imageBitmap.getHeight()/ratio);    //根据宽度的压缩比  得到这张图片的高度

addImage(imageBitmap, columnWidth, scaleHeight);

}

imageTaskList.remove(this);

}

//根据图片的Url去加载图片   首先判断该图片是否在内存卡里面,如果没有,则去下载,如果有,则经过压缩处理后返回

private Bitmap loadImage(String imageUrl){

File imageFile=new File(getImagePath(imageUrl));

if(!imageFile.exists()){

downloadImage(imageUrl);

}

if(imageUrl!=null){

Bitmap bitmap=ImageTools.decodeSampedBitmapFromResource(imageFile.getPath(), columnWidth);

if(bitmap!=null){

imageTools.addBitmapToMemoryCache(imageUrl, bitmap);

return bitmap;

}

}

return null;

}

//向ImageView中添加一张图片

private void addImage(Bitmap bitmap,int imageWidth,int imageHeight){

LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(imageWidth, imageHeight);

if(mImageView!=null){

mImageView.setImageBitmap(bitmap);

}

else{

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,imageUrl);

findColumnToAdd(imageView, imageHeight).addView(imageView);

imageList.add(imageView);

}

}

//找到应该添加图片的一列,选取三列中高度最小的一列返回.

private LinearLayout findColumnToAdd(ImageView imageView,int imageHeight){

if(firstColumnHeight<=secondColumnHeight){

if(firstColumnHeight<=thirdColumnHeight){

imageView.setTag(R.string.border_top,firstColumnHeight);

firstColumnHeight+=imageHeight;

imageView.setTag(R.string.border_bottom,firstColumnHeight);

return firstColumn;

}

else{

imageView.setTag(R.string.border_top,thirdColumnHeight);

thirdColumnHeight+=imageHeight;

imageView.setTag(R.string.border_bottom,thirdColumnHeight);

return thirdColumn;

}

}

else{

if(secondColumnHeight<=thirdColumnHeight){

imageView.setTag(R.string.border_top,secondColumnHeight);

secondColumnHeight+=imageHeight;

imageView.setTag(R.string.border_bottom, secondColumnHeight);

return secondColumn;

}

else{

imageView.setTag(R.string.border_top,thirdColumnHeight);

thirdColumnHeight+=imageHeight;

imageView.setTag(R.string.border_bottom,thirdColumnHeight);

return thirdColumn;

}

}

}

//将图片下载到SD卡缓存

private void downloadImage(String imageUrl){

HttpURLConnection con=null;

FileOutputStream fos=null;

BufferedOutputStream bos=null;

BufferedInputStream bis=null;

File imageFile=null;

try {

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];

int length;

while((length=bis.read(b))!=-1){

bos.write(b, 0, length);

bos.flush();

}

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

finally{

try {

if(bis!=null){

bis.close();

}

if(bos!=null){

bos.close();

}

if(con!=null){

con.disconnect();

}

} catch (IOException e) {

e.printStackTrace();

}

}

if(imageFile!=null){

//裁剪图片

Log.e("info", "path---"+imageFile.getPath());

Bitmap bitmap=ImageTools.decodeSampedBitmapFromResource(imageFile.getPath(), columnWidth);

if(bitmap!=null){

imageTools.addBitmapToMemoryCache(imageUrl, bitmap);

}

}

}

}

//获取图片的本地缓存路径

private String getImagePath(String imageUrl){

int lastIndex=imageUrl.lastIndexOf("/");

String imageName=imageUrl.substring(lastIndex+1);

String imageDir=Environment.getExternalStorageDirectory().getPath()+"/PhotoWallFall/";

File file=new File(imageDir);

if(!file.exists()){

file.mkdirs();

}

String imagePath=imageDir+imageName;

return imagePath;

}

}

这个自定义控件是继承自ScrollView类的,这样就允许用户可以通过滚动的方式浏览更多的图片,loadMoreImage()是专门用来浏览下一页图片的.

看一看loadMoreImages()方法的内部细节了。在这个方法中,使用了一个循环来加载这一页中的每一张图片,每次都会开启一

个LoadImageTask,用于对图片进行异步加载。然后在LoadImageTask中,首先会先检查一下这张图片是不是已经存在于SD卡中了,如果还没

存在,就从网络上下载,然后把这张图片存放在LruCache中。接着将这张图按照一定的比例进行压缩,并找出当前高度最小的一列,把压缩

后的图片添加进去就可以了。

另外,为了保证照片墙上的图片都能够合适地被回收,这里还加入了一个可见性检查的方法,即checkVisibility()方法。这个方法的核心思

想就是检查目前照片墙上的所有图片,判断出哪些是可见的,哪些是不可见。然后将那些不可见的图片都替换成一张空图,这样就可以保证

程序始终不会占用过高的内存。当这些图片又重新变为可见的时候,只需要再从LruCache中将这些图片重新取出即可。如果某张图片已经从

LruCache中被移除了,就会开启一个LoadImageTask,将这张图片重新加载到内存中.

4.打开activity_main.xml

<com.example.photodemo.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/scroll_view"

tools:context=".MainActivity" >

<LinearLayout

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="horizontal"

>

<LinearLayout

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:id="@+id/first_column"

android:orientation="vertical"

></LinearLayout>

<LinearLayout

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:id="@+id/second_column"

android:orientation="vertical"

></LinearLayout>

<LinearLayout

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:id="@+id/third_column"

android:orientation="vertical"

></LinearLayout>

</LinearLayout>

</com.example.photodemo.MyScrollView>

5.当然,因为是获取网络图片,所以必须加上联网权限 ,以及将图片缓存到SD卡,所以需要写权限.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.INTERNET" />

来自为知笔记(Wiz)

时间: 2024-10-13 06:24:24

android瀑布流照片墙实现代码详解的相关文章

Android瀑布流照片墙实现,体验不规则排列的美感Demo

Android瀑布流照片墙实现,体验不规则排列的美感Demo.略有逼格~ 下载地址:http://www.devstore.cn/code/info/637.html

Android瀑布流照片墙实现,体验不规则排列的美感

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10470797 传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳.这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面. 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个

转载—— android 瀑布流的实现详解,附源码

介绍 参考自:https://github.com/dodola/android_waterfall,因为原来的代码封装不好,所以,我根据源码的思路,重新写了一遍,所以有了现在这个项目:https://github.com/youxilua/waterfall4android 原作者表示: 试过在1万张可以流畅的滑动,不出现内存溢出情况 设计思路 之前的作者的自定义view 只有主滑动一层,其他的设置要在相应的活动设置,个人觉得,重用起来比较麻烦,所以决定封装一层.现在定义一个默认的瀑布流只需5

Android 壁纸设置代码 详解

做一个游戏图片列表,想着增加设置壁纸功能,就差了一些资料. 1 别忘记在ApplicationManifest.xml 中加上权限的设置. <uses-permission android:name = "android.permission.SET_WALLPAPER"/> 2.设置壁纸的方法总结. 壁纸设置方法有三种 第一 通过WallpaperManager方法中的 setBitmap() 第二 通过WallpaperManager方法中的 setResource()

Android AndroidManifest 清单文件以及权限详解

每个Android应用都需要一个名为AndroidManifest.xml的程序清单文件,这个清单文件名是固定的并且放在每个Android应用的根目录下.它定义了该应用对于Android系统来说一些非常重要的信息.Android系统需要这些信息才能正常运行该应用.Android程序清单文件主要具有下面作用: ·        它给应用程序Java包命名,这个包名作为应用程序唯一标识符. ·        它描述了应用程序中的每个程序组件-Activity,Service,Broadcast Re

瀑布流照片墙与图片缓存的完美结合

传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳.这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面. 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于“墙”上的每张图片大小都相同的情况,如果图片的大小参差不齐,在Grid

Android ViewGroup触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android View触摸屏事件派发机制详解与源码分析>,阅读本篇之前建议先阅读. 1 背景 还记得前一篇<Android View触摸屏事件派发机制详解与源码分析>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事

Android 4.4 KitKat NotificationManagerService使用详解与原理分析(二)__原理分析

前置文章: <Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解> 转载请务必注明出处:http://blog.csdn.net/yihongyuelan 概况 在上一篇文章<Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解>中详细介绍了NotificationListenerService的使用方法,以及在使用过程中遇到的问题和

Android瀑布流照片

http://blog.csdn.net/guolin_blog/article/details/10470797 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于“墙”上的每张图片大小都相同的情况,如果图片的大小参差不齐,在GridView中显示就会非常的难看.而使用瀑布流的布局方式就可以很好地解决这个问题,因此今天我们也来赶一下潮流,看看如何在Android上实现瀑布流照片墙的功能. 首先还是讲一下