瀑布流照片牆實現
/**
* 原始碼片段
* 自定義的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;
}
}
}</string,></loadimagetask></imageview></imageview></loadimagetask>