1.1
package com.example.photowallfallsdemo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
/**
* 自定义的ScrollView,在其中动态地对图片进行添加。
*
* @author guolin
*/
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,这里onLayout中的初始化只需加载一次
*/
private boolean loadOnce;
/**
* 对图片进行管理的工具类
*/
private ImageLoader imageLoader;
/**
* 第一列的布局
*/
private LinearLayout firstColumn;
/**
* 第二列的布局
*/
private LinearLayout secondColumn;
/**
* 第三列的布局
*/
private LinearLayout thirdColumn;
/**
* 记录所有正在下载或等待下载的任务。
*/
private static Set<LoadImageTask> taskCollection;
/**
* MyScrollView下的直接子布局。
*/
private static View scrollLayout;
/**
* MyScrollView布局的高度。
*/
private static int scrollViewHeight;
/**
* 记录上垂直方向的滚动距离。
*/
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.obj;
int scrollY = myScrollView.getScrollY();
// 如果当前的滚动位置和上次相同,表示已停止滚动
if (scrollY == lastScrollY) {
// 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片
if (scrollViewHeight + scrollY >= scrollLayout.getHeight()
&& taskCollection.isEmpty()) {
myScrollView.loadMoreImages();
}
myScrollView.checkVisibility();
} else {
lastScrollY = scrollY;
Message message = new Message();
message.obj = myScrollView;
// 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的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。
*/
@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;
loadMoreImages();
}
}
/**
* 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
Message message = new Message();
message.obj = this;
handler.sendMessageDelayed(message, 5);
}
return false;
}
/**
* 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
*/
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)
.show();
if (endIndex > Images.imageUrls.length) {
endIndex = Images.imageUrls.length;
}
for (int i = startIndex; i < endIndex; i++) {
LoadImageTask task = new LoadImageTask();
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中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。
*/
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);
} else {
LoadImageTask task = new LoadImageTask(imageView);
task.execute(imageUrl);
}
} else {
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 mImageUrl;
/**
* 可重复使用的ImageView
*/
private ImageView mImageView;
public LoadImageTask() {
}
/**
* 将可重复使用的ImageView传入
*
* @param imageView
*/
public LoadImageTask(ImageView imageView) {
mImageView = imageView;
}
@Override
protected Bitmap doInBackground(String... params) {
mImageUrl = params[0];
Bitmap imageBitmap = imageLoader
.getBitmapFromMemoryCache(mImageUrl);
if (imageBitmap == null) {
imageBitmap = loadImage(mImageUrl);
}
return imageBitmap;
}
@Override
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);
return bitmap;
}
}
return null;
}
/**
* 向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);
} 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, 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 += imageHeight;
imageView.setTag(R.string.border_bottom, firstColumnHeight);
return firstColumn;
}
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;
}
imageView.setTag(R.string.border_top, thirdColumnHeight);
thirdColumnHeight += imageHeight;
imageView.setTag(R.string.border_bottom, thirdColumnHeight);
return thirdColumn;
}
}
/**
* 将图片下载到SD卡缓存起来。
*
* @param imageUrl
* 图片的URL地址。
*/
private void downloadImage(String imageUrl) {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
Log.d("TAG", "monted sdcard");
} else {
Log.d("TAG", "has no sdcard");
}
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 (Exception 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) {
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 + imageName;
return imagePath;
}
}
}
1.2
package com.example.photowallfallsdemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
/**
* 对图片进行管理的工具类。
*
* @author Tony
*/
public class ImageLoader {
/**
* 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
*/
private static LruCache<String, Bitmap> mMemoryCache;
/**
* ImageLoader的实例。
*/
private static ImageLoader mImageLoader;
private ImageLoader() {
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 设置图片缓存大小为程序最大可用内存的1/8
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
}
/**
* 获取ImageLoader的实例。
*
* @return ImageLoader的实例。
*/
public static ImageLoader getInstance() {
if (mImageLoader == null) {
mImageLoader = new ImageLoader();
}
return mImageLoader;
}
/**
* 将一张图片存储到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.outWidth;
int inSampleSize = 1;
if (width > reqWidth) {
// 计算出实际宽度和目标宽度的比率
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, options);
}
}
2.1
package com.example.demo_zhy_18_networkimageloader;
import android.content.Context;
import android.os.Bundle;
import android.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import com.zhy.utils.ImageLoader;
import com.zhy.utils.ImageLoader.Type;
import com.zhy.utils.Images;
public class ListImgsFragment extends Fragment
{
private GridView mGridView;
private String[] mUrlStrs = Images.imageThumbUrls;
private ImageLoader mImageLoader;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mImageLoader = ImageLoader.getInstance(3, Type.LIFO);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_list_imgs, container,
false);
mGridView = (GridView) view.findViewById(R.id.id_gridview);
setUpAdapter();
return view;
}
private void setUpAdapter()
{
if (getActivity() == null || mGridView == null)
return;
if (mUrlStrs != null)
{
mGridView.setAdapter(new ListImgItemAdaper(getActivity(), 0,
mUrlStrs));
} else
{
mGridView.setAdapter(null);
}
}
private class ListImgItemAdaper extends ArrayAdapter<String>
{
public ListImgItemAdaper(Context context, int resource, String[] datas)
{
super(getActivity(), 0, datas);
Log.e("TAG", "ListImgItemAdaper");
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = getActivity().getLayoutInflater().inflate(
R.layout.item_fragment_list_imgs, parent, false);
}
ImageView imageview = (ImageView) convertView
.findViewById(R.id.id_img);
imageview.setImageResource(R.drawable.pictures_no);
mImageLoader.loadImage(getItem(position), imageview, true);
return convertView;
}
}
}
2.2
package com.zhy.utils;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.LruCache;
import android.util.Log;
import android.widget.ImageView;
import com.zhy.utils.ImageSizeUtil.ImageSize;
public class ImageLoader
{
private static ImageLoader mInstance;
private LruCache<String, Bitmap> mLruCache;
private ExecutorService mThreadPool;
private static final int DEAFULT_THREAD_COUNT = 1;
private Type mType = Type.LIFO;
private LinkedList<Runnable> mTaskQueue;
private Thread mPoolThread;
private Handler mPoolThreadHandler;
private Handler mUIHandler;
private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0);
private Semaphore mSemaphoreThreadPool;
private boolean isDiskCacheEnable = true;
private static final String TAG = "ImageLoader";
public enum Type
{
FIFO, LIFO;
}
private ImageLoader(int threadCount, Type type)
{
init(threadCount, type);
}
private void init(int threadCount, Type type)
{
initBackThread();
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheMemory = maxMemory / 8;
mLruCache = new LruCache<String, Bitmap>(cacheMemory)
{
@Override
protected int sizeOf(String key, Bitmap value)
{
return value.getRowBytes() * value.getHeight();
}
};
mThreadPool = Executors.newFixedThreadPool(threadCount);
mTaskQueue = new LinkedList<Runnable>();
mType = type;
mSemaphoreThreadPool = new Semaphore(threadCount);
}
private void initBackThread()
{
mPoolThread = new Thread()
{
@Override
public void run()
{
Looper.prepare();
mPoolThreadHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
mThreadPool.execute(getTask());
try
{
mSemaphoreThreadPool.acquire();
} catch (InterruptedException e)
{
}
}
};
mSemaphorePoolThreadHandler.release();
Looper.loop();
};
};
mPoolThread.start();
}
public static ImageLoader getInstance()
{
if (mInstance == null)
{
synchronized (ImageLoader.class)
{
if (mInstance == null)
{
mInstance = new ImageLoader(DEAFULT_THREAD_COUNT, Type.LIFO);
}
}
}
return mInstance;
}
public static ImageLoader getInstance(int threadCount, Type type)
{
if (mInstance == null)
{
synchronized (ImageLoader.class)
{
if (mInstance == null)
{
mInstance = new ImageLoader(threadCount, type);
}
}
}
return mInstance;
}
public void loadImage(final String path, final ImageView imageView,
final boolean isFromNet)
{
imageView.setTag(path);
if (mUIHandler == null)
{
mUIHandler = new Handler()
{
public void handleMessage(Message msg)
{
ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
Bitmap bm = holder.bitmap;
ImageView imageview = holder.imageView;
String path = holder.path;
if (imageview.getTag().toString().equals(path))
{
imageview.setImageBitmap(bm);
}
};
};
}
Bitmap bm = getBitmapFromLruCache(path);
if (bm != null)
{
refreashBitmap(path, imageView, bm);
} else
{
addTask(buildTask(path, imageView, isFromNet));
}
}
private Runnable buildTask(final String path, final ImageView imageView,
final boolean isFromNet)
{
return new Runnable()
{
@Override
public void run()
{
Bitmap bm = null;
if (isFromNet)
{
File file = getDiskCacheDir(imageView.getContext(),
md5(path));
if (file.exists())
{
Log.e(TAG, "find image :" + path + " in disk cache .");
bm = loadImageFromLocal(file.getAbsolutePath(),
imageView);
} else
{
if (isDiskCacheEnable)
{
boolean downloadState = DownloadImgUtils
.downloadImgByUrl(path, file);
if (downloadState)
{
Log.e(TAG,
"download image :" + path
+ " to disk cache . path is "
+ file.getAbsolutePath());
bm = loadImageFromLocal(file.getAbsolutePath(),
imageView);
}
} else
{
Log.e(TAG, "load image :" + path + " to memory.");
bm = DownloadImgUtils.downloadImgByUrl(path,
imageView);
}
}
} else
{
bm = loadImageFromLocal(path, imageView);
}
addBitmapToLruCache(path, bm);
refreashBitmap(path, imageView, bm);
mSemaphoreThreadPool.release();
}
};
}
private Bitmap loadImageFromLocal(final String path,
final ImageView imageView)
{
Bitmap bm;
ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
bm = decodeSampledBitmapFromPath(path, imageSize.width,
imageSize.height);
return bm;
}
private Runnable getTask()
{
if (mType == Type.FIFO)
{
return mTaskQueue.removeFirst();
} else if (mType == Type.LIFO)
{
return mTaskQueue.removeLast();
}
return null;
}
public String md5(String str)
{
byte[] digest = null;
try
{
MessageDigest md = MessageDigest.getInstance("md5");
digest = md.digest(str.getBytes());
return bytes2hex02(digest);
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return null;
}
public String bytes2hex02(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
String tmp = null;
for (byte b : bytes)
{
tmp = Integer.toHexString(0xFF & b);
if (tmp.length() == 1)
{
tmp = "0" + tmp;
}
sb.append(tmp);
}
return sb.toString();
}
private void refreashBitmap(final String path, final ImageView imageView,
Bitmap bm)
{
Message message = Message.obtain();
ImgBeanHolder holder = new ImgBeanHolder();
holder.bitmap = bm;
holder.path = path;
holder.imageView = imageView;
message.obj = holder;
mUIHandler.sendMessage(message);
}
protected void addBitmapToLruCache(String path, Bitmap bm)
{
if (getBitmapFromLruCache(path) == null)
{
if (bm != null)
mLruCache.put(path, bm);
}
}
protected Bitmap decodeSampledBitmapFromPath(String path, int width,
int height)
{
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options,
width, height);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return bitmap;
}
private synchronized void addTask(Runnable runnable)
{
mTaskQueue.add(runnable);
try
{
if (mPoolThreadHandler == null)
mSemaphorePoolThreadHandler.acquire();
} catch (InterruptedException e)
{
}
mPoolThreadHandler.sendEmptyMessage(0x110);
}
public File getDiskCacheDir(Context context, String uniqueName)
{
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState()))
{
cachePath = context.getExternalCacheDir().getPath();
} else
{
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
private Bitmap getBitmapFromLruCache(String key)
{
return mLruCache.get(key);
}
private class ImgBeanHolder
{
Bitmap bitmap;
ImageView imageView;
String path;
}
}
2.3
package com.zhy.utils;
import java.lang.reflect.Field;
import android.graphics.BitmapFactory.Options;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
public class ImageSizeUtil
{
public static int caculateInSampleSize(Options options, int reqWidth,
int reqHeight)
{
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if (width > reqWidth || height > reqHeight)
{
int widthRadio = Math.round(width * 1.0f / reqWidth);
int heightRadio = Math.round(height * 1.0f / reqHeight);
inSampleSize = Math.max(widthRadio, heightRadio);
}
return inSampleSize;
}
public static ImageSize getImageViewSize(ImageView imageView)
{
ImageSize imageSize = new ImageSize();
DisplayMetrics displayMetrics = imageView.getContext().getResources()
.getDisplayMetrics();
LayoutParams lp = imageView.getLayoutParams();
int width = imageView.getWidth();
if (width <= 0)
{
width = lp.width;
}
if (width <= 0)
{
width = getImageViewFieldValue(imageView, "mMaxWidth");
}
if (width <= 0)
{
width = displayMetrics.widthPixels;
}
int height = imageView.getHeight();
if (height <= 0)
{
height = lp.height;
}
if (height <= 0)
{
height = getImageViewFieldValue(imageView, "mMaxHeight");
}
if (height <= 0)
{
height = displayMetrics.heightPixels;
}
imageSize.width = width;
imageSize.height = height;
return imageSize;
}
public static class ImageSize
{
int width;
int height;
}
private static int getImageViewFieldValue(Object object, String fieldName)
{
int value = 0;
try
{
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = field.getInt(object);
if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE)
{
value = fieldValue;
}
} catch (Exception e)
{
}
return value;
}
}
2.4
package com.zhy.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.util.Log;
import android.widget.ImageView;
import com.zhy.utils.ImageSizeUtil.ImageSize;
public class DownloadImgUtils
{
public static boolean downloadImgByUrl(String urlStr, File file)
{
FileOutputStream fos = null;
InputStream is = null;
try
{
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = conn.getInputStream();
fos = new FileOutputStream(file);
byte[] buf = new byte[512];
int len = 0;
while ((len = is.read(buf)) != -1)
{
fos.write(buf, 0, len);
}
fos.flush();
return true;
} catch (Exception e)
{
e.printStackTrace();
} finally
{
try
{
if (is != null)
is.close();
} catch (IOException e)
{
}
try
{
if (fos != null)
fos.close();
} catch (IOException e)
{
}
}
return false;
}
public static Bitmap downloadImgByUrl(String urlStr, ImageView imageview)
{
FileOutputStream fos = null;
InputStream is = null;
try
{
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(conn.getInputStream());
is.mark(is.available());
Options opts = new Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
ImageSize imageViewSize = ImageSizeUtil.getImageViewSize(imageview);
opts.inSampleSize = ImageSizeUtil.caculateInSampleSize(opts,
imageViewSize.width, imageViewSize.height);
opts.inJustDecodeBounds = false;
is.reset();
bitmap = BitmapFactory.decodeStream(is, null, opts);
conn.disconnect();
return bitmap;
} catch (Exception e)
{
e.printStackTrace();
} finally
{
try
{
if (is != null)
is.close();
} catch (IOException e)
{
}
try
{
if (fos != null)
fos.close();
} catch (IOException e)
{
}
}
return null;
}
}
3.1
package com.cascade;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ScrollView;
import com.cascade.DrawableManager.ImageCallback;
public class TestCascadeActivity extends Activity {
private static final String TAG = "TestCascadeActivity";
private LinearLayout llCcasecade;
private LinearLayout lvCasecade1;
private LinearLayout lvCasecade2;
private LinearLayout lvCasecade3;
private Display display;
private AssetManager assetManager;
private List<String> iamgePaths;
private static final String imgspath = "imgs";
private int casecadeWidth;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_temp);
display = this.getWindowManager().getDefaultDisplay();
casecadeWidth = display.getWidth()/3;
assetManager = this.getAssets();
findView();
}
private void findView(){
llCcasecade = (LinearLayout)this.findViewById(R.id.llCcasecade);
lvCasecade1 = (LinearLayout)this.findViewById(R.id.casecade1);
lvCasecade2 = (LinearLayout)this.findViewById(R.id.casecade2);
lvCasecade3 = (LinearLayout)this.findViewById(R.id.casecade3);
LayoutParams lp1 = lvCasecade1.getLayoutParams();
lp1.width = casecadeWidth;
lvCasecade1.setLayoutParams(lp1);
LayoutParams lp2 = lvCasecade2.getLayoutParams();
lp2.width = casecadeWidth;
lvCasecade2.setLayoutParams(lp2);
LayoutParams lp3 = lvCasecade3.getLayoutParams();
lp3.width = casecadeWidth;
lvCasecade3.setLayoutParams(lp3);
try {
iamgePaths = Arrays.asList(assetManager.list("imgs"));
} catch (IOException e) {
e.printStackTrace();
}
int j = 0;
for (int i = 0; i < iamgePaths.size(); i++) {
addImgToCasecade(iamgePaths.get(i), j);
j++;
if(j>=3) j = 0;
}
}
private void addImgToCasecade(String filename,int j){
ImageView iv = (ImageView)LayoutInflater.from(this).inflate(R.layout.item_temp, null);
if(j==0){
lvCasecade1.addView(iv);
}else if(j==1){
lvCasecade2.addView(iv);
}else{
lvCasecade3.addView(iv);
}
String imgPath = imgspath+"/"+filename;
iv.setTag(imgPath);
Drawable drawable = DrawableManager.getInstance().fetchDrawableOnThread(imgPath, assetManager, new ImageCallback() {
@Override
public void imageLoaded(Drawable imageDrawable, String imageUrl) {
ImageView iv = (ImageView)llCcasecade.findViewWithTag(imageUrl);
if(iv!=null && imageDrawable!=null){
int oldwidth = imageDrawable.getIntrinsicWidth();
int oldheight = imageDrawable.getIntrinsicHeight();
LayoutParams lp = iv.getLayoutParams();
lp.height = (oldheight * casecadeWidth)/oldwidth;
iv.setPadding(0, 2, 0, 0);
iv.setLayoutParams(lp);
iv.setImageDrawable(imageDrawable);
}
}
});
if(drawable!=null){
int oldwidth = drawable.getIntrinsicWidth();
int oldheight = drawable.getIntrinsicHeight();
LayoutParams lp = iv.getLayoutParams();
lp.height = (oldheight * casecadeWidth)/oldwidth;
iv.setPadding(0, 2, 0, 0);
iv.setLayoutParams(lp);
iv.setImageDrawable(drawable);
}
}
}
3.2
package com.cascade;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class DrawableManager {
private static Map<String, Drawable> drawableMap = new LinkedHashMap<String, Drawable>();
private static DrawableManager instance;
private DrawableManager() {
}
public static DrawableManager getInstance() {
if (instance == null) {
instance = new DrawableManager();
}
return instance;
}
public void clear() {
if (drawableMap != null) {
drawableMap.clear();
drawableMap = null;
}
Log.d(this.getClass().getSimpleName(), "clear image hashmap");
}
public Drawable fetchDrawableOnThread(final String urlString,final AssetManager assetManager,
final ImageCallback imageCallback) {
if (drawableMap == null) {
drawableMap = new HashMap<String, Drawable>();
}
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
imageCallback.imageLoaded((Drawable) message.obj, urlString);
}
};
Thread thread = new Thread() {
@Override
public void run() {
Drawable drawable = fetchDrawable(urlString,assetManager);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
return null;
}
private Drawable fetchDrawable(String urlString,AssetManager assetManager) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}
Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
InputStream is = null;
try {
is = assetManager.open(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
if (drawableMap == null) {
drawableMap = new HashMap<String, Drawable>();
}
drawableMap.put(urlString, drawable);
return drawable;
} catch (Exception e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
} finally{//关闭流
try {
if(is!=null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public Drawable getDrawable(String urlkey) {
return drawableMap.get(urlkey);
}
public interface ImageCallback {
public void imageLoaded(Drawable imageDrawable, String imageUrl);
}
public void remove(String key){
if(drawableMap.containsKey(key))
drawableMap.remove(key);
}
}
4.1
package com.dodowaterfall;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.dodowaterfall.LazyScrollView.OnScrollListener;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
private LazyScrollView waterfall_scroll;
private LinearLayout waterfall_container;
private ArrayList<LinearLayout> waterfall_items;
private Display display;
private AssetManager assetManager;
private List<String> image_filenames;
private final String image_path = "images";
private int itemWidth;
private int column_count = 3;// 显示列数
private int page_count = 15;// 每次加载15张图片
private int current_page = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
display = this.getWindowManager().getDefaultDisplay();
itemWidth = display.getWidth() / column_count;// 根据屏幕大小计算每列大小
assetManager = this.getAssets();
InitLayout();
}
private void InitLayout() {
waterfall_scroll = (LazyScrollView) findViewById(R.id.waterfall_scroll);
waterfall_scroll.getView();
waterfall_scroll.setOnScrollListener(new OnScrollListener() {
@Override
public void onTop() {
// 滚动到最顶端
Log.d("LazyScroll", "Scroll to top");
}
@Override
public void onScroll() {
// 滚动中
Log.d("LazyScroll", "Scroll");
}
@Override
public void onBottom() {
// 滚动到最低端
AddItemToContainer(++current_page, page_count);
}
});
waterfall_container = (LinearLayout) this
.findViewById(R.id.waterfall_container);
waterfall_items = new ArrayList<LinearLayout>();
for (int i = 0; i < column_count; i++) {
LinearLayout itemLayout = new LinearLayout(this);
LinearLayout.LayoutParams itemParam = new LinearLayout.LayoutParams(
itemWidth, LayoutParams.WRAP_CONTENT);
// itemParam.width = itemWidth;
// itemParam.height = LayoutParams.WRAP_CONTENT;
itemLayout.setPadding(2, 2, 2, 2);
itemLayout.setOrientation(LinearLayout.VERTICAL);
itemLayout.setLayoutParams(itemParam);
waterfall_items.add(itemLayout);
waterfall_container.addView(itemLayout);
}
// 加载所有图片路径
try {
image_filenames = Arrays.asList(assetManager.list(image_path));
} catch (IOException e) {
e.printStackTrace();
}
// 第一次加载
AddItemToContainer(current_page, page_count);
}
private void AddItemToContainer(int pageindex, int pagecount) {
int j = 0;
int imagecount = image_filenames.size();
for (int i = pageindex * pagecount; i < pagecount * (pageindex + 1)
&& i < imagecount; i++) {
j = j >= column_count ? j = 0 : j;
AddImage(image_filenames.get(i), j++);
}
}
private void AddImage(String filename, int columnIndex) {
ImageView item = (ImageView) LayoutInflater.from(this).inflate(
R.layout.waterfallitem, null);
waterfall_items.get(columnIndex).addView(item);
TaskParam param = new TaskParam();
param.setAssetManager(assetManager);
param.setFilename(image_path + "/" + filename);
param.setItemWidth(itemWidth);
ImageLoaderTask task = new ImageLoaderTask(item);
task.execute(param);
}
}
4.2
package com.dodowaterfall;
//来自:http://blog.csdn.net/listening_music/article/details/7192629
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;
public class LazyScrollView extends ScrollView{
private static final String tag="LazyScrollView";
private Handler handler;
private View view;
public LazyScrollView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public LazyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public LazyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
//这个获得总的高度
public int computeVerticalScrollRange(){
return super.computeHorizontalScrollRange();
}
public int computeVerticalScrollOffset(){
return super.computeVerticalScrollOffset();
}
private void init(){
this.setOnTouchListener(onTouchListener);
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
// process incoming messages here
super.handleMessage(msg);
switch(msg.what){
case 1:
if(view.getMeasuredHeight() <= getScrollY() + getHeight()) {
if(onScrollListener!=null){
onScrollListener.onBottom();
}
}else if(getScrollY()==0){
if(onScrollListener!=null){
onScrollListener.onTop();
}
}
else{
if(onScrollListener!=null){
onScrollListener.onScroll();
}
}
break;
default:
break;
}
}
};
}
OnTouchListener onTouchListener=new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
if(view!=null&&onScrollListener!=null){
handler.sendMessageDelayed(handler.obtainMessage(1), 200);
}
break;
default:
break;
}
return false;
}
};
/**
* 获得参考的View,主要是为了获得它的MeasuredHeight,然后和滚动条的ScrollY+getHeight作比较。
*/
public void getView(){
this.view=getChildAt(0);
if(view!=null){
init();
}
}
/**
* 定义接口
* @author admin
*
*/
public interface OnScrollListener{
void onBottom();
void onTop();
void onScroll();
}
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener){
this.onScrollListener=onScrollListener;
}
}
4.3
package com.dodowaterfall;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
public class ImageLoaderTask extends AsyncTask<TaskParam, Void, Bitmap> {
private TaskParam param;
private final WeakReference<ImageView> imageViewReference; // 防止内存溢出
public ImageLoaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(TaskParam... params) {
param = params[0];
return loadImageFile(param.getFilename(), param.getAssetManager());
}
private Bitmap loadImageFile(final String filename,
final AssetManager manager) {
InputStream is = null;
try {
Bitmap bmp = BitmapCache.getInstance().getBitmap(filename,
param.getAssetManager());
return bmp;
} catch (Exception e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
if (bitmap != null) {
int width = bitmap.getWidth();// 获取真实宽高
int height = bitmap.getHeight();
LayoutParams lp = imageView.getLayoutParams();
lp.height = (height * param.getItemWidth()) / width;// 调整高度
imageView.setLayoutParams(lp);
imageView.setImageBitmap(bitmap);
}
}
}
}
}
4.4
package com.dodowaterfall;
import android.content.res.AssetManager;
public class TaskParam {
private String filename;
private AssetManager assetManager;
private int ItemWidth;
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public AssetManager getAssetManager() {
return assetManager;
}
public void setAssetManager(AssetManager assetManager) {
this.assetManager = assetManager;
}
public int getItemWidth() {
return ItemWidth;
}
public void setItemWidth(int itemWidth) {
ItemWidth = itemWidth;
}
}
4.5
package com.dodowaterfall;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
* 防止溢出
*
*/
public class BitmapCache {
static private BitmapCache cache;
/** 用于Chche内容的存储 */
private Hashtable<String, BtimapRef> bitmapRefs;
/** 垃圾Reference的队列(所引用的对象已经被回收,则将该引用存入队列中) */
private ReferenceQueue<Bitmap> q;
/**
* 继承SoftReference,使得每一个实例都具有可识别的标识。
*/
private class BtimapRef extends SoftReference<Bitmap> {
private String _key = "";
public BtimapRef(Bitmap bmp, ReferenceQueue<Bitmap> q, String key) {
super(bmp, q);
_key = key;
}
}
private BitmapCache() {
bitmapRefs = new Hashtable<String, BtimapRef>();
q = new ReferenceQueue<Bitmap>();
}
/**
* 取得缓存器实例
*/
public static BitmapCache getInstance() {
if (cache == null) {
cache = new BitmapCache();
}
return cache;
}
/**
* 以软引用的方式对一个Bitmap对象的实例进行引用并保存该引用
*/
private void addCacheBitmap(Bitmap bmp, String key) {
cleanCache();// 清除垃圾引用
BtimapRef ref = new BtimapRef(bmp, q, key);
bitmapRefs.put(key, ref);
}
/**
* 依据所指定的文件名获取图片
*/
public Bitmap getBitmap(String filename, AssetManager assetManager) {
Bitmap bitmapImage = null;
// 缓存中是否有该Bitmap实例的软引用,如果有,从软引用中取得。
if (bitmapRefs.containsKey(filename)) {
BtimapRef ref = (BtimapRef) bitmapRefs.get(filename);
bitmapImage = (Bitmap) ref.get();
}
// 如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,
// 并保存对这个新建实例的软引用
if (bitmapImage == null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16 * 1024];
// bitmapImage = BitmapFactory.decodeFile(filename, options);
BufferedInputStream buf;
try {
buf = new BufferedInputStream(assetManager.open(filename));
bitmapImage = BitmapFactory.decodeStream(buf);
this.addCacheBitmap(bitmapImage, filename);
} catch (IOException e) {
e.printStackTrace();
}
}
return bitmapImage;
}
private void cleanCache() {
BtimapRef ref = null;
while ((ref = (BtimapRef) q.poll()) != null) {
bitmapRefs.remove(ref._key);
}
}
// 清除Cache内的全部内容
public void clearCache() {
cleanCache();
bitmapRefs.clear();
System.gc();
System.runFinalization();
}
}