編輯:關於Android編程
因為項目中需要用到所以實現的一個橫向的照片浏覽器,使用橫向SrollView實現。
實現效果如下:

實現思路:
在開始做之前呢,本著有輪子就用輪子的精神,也還是去百度找了很久,諸如"Android橫向照片牆"、"Android橫向照片流"、"Android橫向照片選擇器"…完全沒有好咩。。查來查去都是那幾篇豎向照片瀑布流的,橫向的反正我是沒找到。
然後,在將郭神的《ndroid瀑布流照片牆實現,體驗不規則排列的美感》一文拜讀之後(http://blog.csdn.net/guolin_blog/article/details/10470797),靈雞一動,想著既然豎向的能實現,那麼橫向的豈不是轉個方向就好了?
雖然最終實現起來還是花了一點時間,但是基本原理好像就是轉了個方向。。。但是修改後的代碼和郭神的源碼雖說圖片加載部分我們沒有改動,但在很多地方都還是很很大區別的。下面特別說明一下。
需要注意的點有以下三個方面:
1、不再繼承自普通的ScrollView而是繼承自HorizontalScrollView 。
2、在onLayout的初始化中不再初始化三列照片(郭神寫的是瀑布流嘛~),而是改為橫向的一列,並使每張照片的寬高相等,使之保持正方形。
3、在可見性判斷中不再根據高度而是根據寬度進行判斷:
/**
* 遍歷imageViewList中的每張圖片,對圖片的可見性進行檢查,如果圖片已經離開屏幕可見范圍,則將圖片替換成一張空圖。
*/
public void checkVisibility() {
for (int i = 0; i < imageViewList.size(); i++) {
ImageView imageView = imageViewList.get(i);
int borderLeft = (Integer) imageView.getTag(R.string.border_top);
int borderRight = (Integer) imageView
.getTag(R.string.border_bottom);
if (borderLeft > getScrollX()
&& borderRight < getScrollX() + ColumnWith) {
String imageUrl = (String) imageView.getTag(R.string.image_url);
//從內存中獲取圖片
Bitmap bitmap = NGImageLoader.getBitmapFromMemoryCache(imageUrl);
if (bitmap!=null) {
imageView.setImageBitmap(bitmap);
}else {
//TODO 顯示空圖
}
} else {
}
}
}
源碼實現如下:
package com.whale.nangua.toquan.view;
import android.content.Context;
import android.content.res.Resources;
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.util.TypedValue;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.whale.nangua.toquan.R;
import com.whale.nangua.toquan.UserInfoActivity;
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;
/**
* Created by nangua on 2016/8/3.
*/
public class NGOthersInfoAtyHorizotalScrollView extends HorizontalScrollView implements View.OnTouchListener{
/**
* 是否已加載過一次layout,這裡onLayout中的初始化只需加載一次
*/
private boolean loadOnce;
/**
* 列的布局
*/
private LinearLayout linearlayout_userinfo_personal_column;
/**
* 總的寬度
*/
private static int ColumnWith;
/**
* 每張照片的寬度
*/
private int perphotoWidth;
/**
* 記錄上垂直方向的滾動距離。
*/
private static int lastScrollX = -1;
/**
* MyScrollView下的直接子布局。
*/
private static View scrollLayout;
/**
* 進行一些關鍵性的初始化操作,NGOthersInfoAtyHorizotalScrollView,以及得到高度值。並在這裡開始加載第一頁的圖片。
*/
@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();
linearlayout_userinfo_personal_column = new LinearLayout(getContext());
linearlayout_userinfo_personal_column.setLayoutParams(
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
, ViewGroup.LayoutParams.MATCH_PARENT)
);
linearlayout_userinfo_personal_column.setOrientation(LinearLayout.HORIZONTAL);
this.addView(linearlayout_userinfo_personal_column);
perphotoWidth = scrollViewHeight; //每張照片的寬度等於列高
loadOnce = true;
//加載下一頁圖片
loadMoreImages();
scrollLayout = getChildAt(0);
}
}
/**
* 記錄當前已加載到第幾頁
* @param context
*/
private int page = 0;
/**
* 每頁要加載的圖片數量
* 默認為5張
* @param context
*/
public static final int PAGE_SIZE = 5;
/**
* 記錄所有正在下載或等待下載的任務。
*/
private static Set taskCollection;
/**
* 對圖片進行管理的工具類
*/
private com.whale.nangua.toquan.utils.NGImageLoader NGImageLoader;
/**
* MyScrollView布局的高度。
*/
private static int scrollViewHeight;
/**
* MyScrollView的構造函數。
* @param context
* @param attrs
*/
public NGOthersInfoAtyHorizotalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
NGImageLoader = NGImageLoader.getInstance();
taskCollection = new HashSet();
setOnTouchListener(this);
}
/**
* 在Handler中進行圖片可見性檢查的判斷,以及加載更多圖片的操作。
*/
private static Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
NGOthersInfoAtyHorizotalScrollView myScrollView = (NGOthersInfoAtyHorizotalScrollView) msg.obj;
int scrollX = myScrollView.getScrollX();
// 如果當前的滾動位置和上次相同,表示已停止滾動
if (scrollX == lastScrollX) {
// 當滾動的最右邊,並且當前沒有正在下載的任務時,開始加載下一頁的圖片
if ( ColumnWith + scrollX >= (scrollLayout.getWidth() )
&& taskCollection.isEmpty()) {
myScrollView.loadMoreImages();
}
myScrollView.checkVisibility();
} else {
lastScrollX = scrollX;
Message message = new Message();
message.obj = myScrollView;
// 5毫秒後再次對滾動位置進行判斷
handler.sendMessageDelayed(message, 5);
}
}
};
/**
* 監聽用戶的觸屏事件,如果用戶手指離開屏幕則開始進行滾動檢測。
*/
@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;
}
/**
* 遍歷imageViewList中的每張圖片,對圖片的可見性進行檢查,如果圖片已經離開屏幕可見范圍,則將圖片替換成一張空圖。
*/
public void checkVisibility() {
for (int i = 0; i < imageViewList.size(); i++) {
ImageView imageView = imageViewList.get(i);
int borderLeft = (Integer) imageView.getTag(R.string.border_top);
int borderRight = (Integer) imageView
.getTag(R.string.border_bottom);
if (borderLeft > getScrollX()
&& borderRight < getScrollX() + ColumnWith) {
String imageUrl = (String) imageView.getTag(R.string.image_url);
//從內存中獲取圖片
Bitmap bitmap = NGImageLoader.getBitmapFromMemoryCache(imageUrl);
if (bitmap!=null) {
imageView.setImageBitmap(bitmap);
}else {
//TODO 顯示空圖
}
} else {
}
}
}
static boolean hasOver = false;
private String[] imageUrls;
public void setImageUrls(String[] imageUrls) {
this.imageUrls = imageUrls;
}
/**
* 開始加載下一頁的圖片,每張圖片都會開啟一個異步線程去下載。
*/
public void loadMoreImages() {
if (hasSDCard()&& imageUrls!=null) { //判斷是否有SD卡
int startIndex = page * PAGE_SIZE; //起始位置
int endIndex = page * PAGE_SIZE + PAGE_SIZE; //結束位置
if (startIndex < imageUrls.length) {
//Toast.makeText(getContext(), "正在加載...", Toast.LENGTH_SHORT).show();
if (endIndex > imageUrls.length) {
endIndex = imageUrls.length;
}
for (int i = startIndex; i < endIndex; i++) {
//加載圖片
LoadImageTask task = new LoadImageTask();
taskCollection.add(task);
task.execute(imageUrls[i]);
}
page++;
} else {
if (hasOver == false) {
Toast.makeText(getContext(), "已沒有更多圖片", Toast.LENGTH_SHORT).show();
hasOver = true;
}
}
} else {
//Toast.makeText(getContext(), "未發現SD卡", Toast.LENGTH_SHORT).show();
}
}
/**
* 判斷手機是否有SD卡。
* @return 有SD卡返回true,沒有返回false。
*/
private boolean hasSDCard() {
return Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState());
}
public int dpToPx(Resources res, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
}
/**
* 記錄所有界面上的圖片,用以可以隨時控制對圖片的釋放。
*/
private List imageViewList = new ArrayList();
/**
* 異步下載圖片的任務。
*
* @author guolin
*/
class LoadImageTask extends AsyncTask {
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode();
}
/**
* 圖片的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 = NGImageLoader
.getBitmapFromMemoryCache(mImageUrl);
if (imageBitmap == null) {
imageBitmap = loadImage(mImageUrl);
}
return imageBitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
addImage(bitmap, perphotoWidth, perphotoWidth);
}
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 = NGImageLoader.decodeSampledBitmapFromResource(
imageFile.getPath(), perphotoWidth);
if (bitmap != null) {
NGImageLoader.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(ImageView.ScaleType.FIT_XY);
//imageView.setPadding(5, 5, 5, 5);
imageView.setTag(R.string.image_url, mImageUrl);
//這裡應該計算圖片當前的位置
// imageView.setTag(R.string.border_top, firstColumnHeight);
imageView.setTag(R.string.border_top, ColumnWith);
ColumnWith += perphotoWidth;//增加總寬
imageView.setTag(R.string.border_bottom, ColumnWith);
linearlayout_userinfo_personal_column.addView(imageView);
imageViewList.add(imageView);
}
}
/**
* 將圖片下載到SD卡緩存起來。
*
* @param imageUrl
* 圖片的URL地址。
*/
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 (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 = NGImageLoader.decodeSampledBitmapFromResource(
imageFile.getPath(), perphotoWidth);
if (bitmap != null) {
NGImageLoader.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;
}
}
}
Android編程基於自定義View實現絢麗的圓形進度條功能示例
本文實例講述了Android編程基於自定義View實現絢麗的圓形進度條功能。分享給大家供大家參考,具體如下:本文包含兩個組件,首先上效果圖:1.ProgressBarVi
Android優化查詢加載大數量的本地相冊圖片
一、概述講解優化查詢相冊圖片之前,我們先來看下PM提出的需求,PM的需求很簡單,就是要做一個類似微信的本地相冊圖片查詢控件,主要包含兩個兩部分: 進入圖片選擇頁面就要顯
Android UI實現廣告Banner輪播效果
本篇博客要分享的一個效果是實現廣告Banner輪播效果,這個效果也比較常見,一些視頻類應用就經常有,就拿360影視大全來舉例吧: 用紅框框住的那個效果就是小巫今
Android編程實現長按Button按鈕連續響應功能示例
本文實例講述了Android編程實現長按Button按鈕連續響應功能。分享給大家供大家參考,具體如下:在電商類app的購物車頁面,經常會有這樣的需求:長按“+”按鈕或者“