Android 瀑布流照片牆(讓更多的人用到更好的框架)
阿新 • • 發佈:2019-01-06
今天想做個瀑布流,網上找了一圈,只找到這個可以點選看大圖的框架,就拿來用了一下,效果還不錯,推薦給大家,我們只是大自然的搬用工,讓更多的人用到更好的框架,節省程式猿們的時間,誰有好的也可以給我推薦推薦,記著,我們只是大自然的搬運工。
源文出處:http://blog.csdn.net/guolin_blog/article/details/10470797
效果圖:
點選圖片看大圖:
多點觸控,還可以放的更大:
效果非常不錯,如果大家想詳細瞭解怎麼實現的,就點選上面的連線瞭解一下,接下來我就說下怎麼用。
1、首先清單檔案要配置一下許可權
<uses-permission android:name ="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
主佈局檔案要用到自定義的ScrollView
<?xml version="1.0" encoding="utf-8"?>
<com.example.photoview.MyScrollView //自己的包名
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height ="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/first_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="@+id/second_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="@+id/third_column"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
</com.example.photoview.MyScrollView>
大圖詳情頁的佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<com.example.photoview.ZoomImageView //換成自己的包名
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/zoom_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000" >
</com.example.photoview.ZoomImageView>
strings裡面要用到這幾個檔案(還有一張圖片是載入真正圖片之前的佔位符,就不傳了):
<string name="border_top">Border Top</string>
<string name="border_bottom">Border Bottom</string>
<string name="image_url">Image Url</string>
首先宣告一個實體圖片實體類傳入圖片的地址:
public class Images {
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/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/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/1377949578_8744.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg" };
}
對圖片進行載入:
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);
}
}
自定義的ScrollView繼承ScrollView(注意一下:下文有個判斷手機是否有記憶體卡的,我的榮耀6p就會報空指標錯誤,可能原因就是手機是雙卡的,副卡可以放記憶體卡,也可以放電話卡,當你放了電話卡,卻沒有記憶體,就會報空指標);
/**
* 自定義的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);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), ImageDetailsActivity.class);
intent.putExtra("image_path", getImagePath(mImageUrl));
getContext().startActivity(intent);
}
});
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) {
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;
}
}
}
自定義的ImageView控制,可對圖片進行多點觸控縮放和拖動:
package com.example.photoview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 自定義的ImageView控制,可對圖片進行多點觸控縮放和拖動
*
* @author guolin
*/
public class ZoomImageView extends View {
/**
* 初始化狀態常量
*/
public static final int STATUS_INIT = 1;
/**
* 圖片放大狀態常量
*/
public static final int STATUS_ZOOM_OUT = 2;
/**
* 圖片縮小狀態常量
*/
public static final int STATUS_ZOOM_IN = 3;
/**
* 圖片拖動狀態常量
*/
public static final int STATUS_MOVE = 4;
/**
* 用於對圖片進行移動和縮放變換的矩陣
*/
private Matrix matrix = new Matrix();
/**
* 待展示的Bitmap物件
*/
private Bitmap sourceBitmap;
/**
* 記錄當前操作的狀態,可選值為STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE
*/
private int currentStatus;
/**
* ZoomImageView控制元件的寬度
*/
private int width;
/**
* ZoomImageView控制元件的高度
*/
private int height;
/**
* 記錄兩指同時放在螢幕上時,中心點的橫座標值
*/
private float centerPointX;
/**
* 記錄兩指同時放在螢幕上時,中心點的縱座標值
*/
private float centerPointY;
/**
* 記錄當前圖片的寬度,圖片被縮放時,這個值會一起變動
*/
private float currentBitmapWidth;
/**
* 記錄當前圖片的高度,圖片被縮放時,這個值會一起變動
*/
private float currentBitmapHeight;
/**
* 記錄上次手指移動時的橫座標
*/
private float lastXMove = -1;
/**
* 記錄上次手指移動時的縱座標
*/
private float lastYMove = -1;
/**
* 記錄手指在橫座標方向上的移動距離
*/
private float movedDistanceX;
/**
* 記錄手指在縱座標方向上的移動距離
*/
private float movedDistanceY;
/**
* 記錄圖片在矩陣上的橫向偏移值
*/
private float totalTranslateX;
/**
* 記錄圖片在矩陣上的縱向偏移值
*/
private float totalTranslateY;
/**
* 記錄圖片在矩陣上的總縮放比例
*/
private float totalRatio;
/**
* 記錄手指移動的距離所造成的縮放比例
*/
private float scaledRatio;
/**
* 記錄圖片初始化時的縮放比例
*/
private float initRatio;
/**
* 記錄上次兩指之間的距離
*/
private double lastFingerDis;
/**
* ZoomImageView建構函式,將當前操作狀態設為STATUS_INIT。
*
* @param context
* @param attrs
*/
public ZoomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
currentStatus = STATUS_INIT;
}
/**
* 將待展示的圖片設定進來。
*
* @param bitmap
* 待展示的Bitmap物件
*/
public void setImageBitmap(Bitmap bitmap) {
sourceBitmap = bitmap;
invalidate();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
// 分別獲取到ZoomImageView的寬度和高度
width = getWidth();
height = getHeight();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 2) {
// 當有兩個手指按在螢幕上時,計算兩指之間的距離
lastFingerDis = distanceBetweenFingers(event);
}
break;
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() == 1) {
// 只有單指按在螢幕上移動時,為拖動狀態
float xMove = event.getX();
float yMove = event.getY();
if (lastXMove == -1 && lastYMove == -1) {
lastXMove = xMove;
lastYMove = yMove;
}
currentStatus = STATUS_MOVE;
movedDistanceX = xMove - lastXMove;
movedDistanceY = yMove - lastYMove;
// 進行邊界檢查,不允許將圖片拖出邊界
if (totalTranslateX + movedDistanceX > 0) {
movedDistanceX = 0;
} else if (width - (totalTranslateX + movedDistanceX) > currentBitmapWidth) {
movedDistanceX = 0;
}
if (totalTranslateY + movedDistanceY > 0) {
movedDistanceY = 0;
} else if (height - (totalTranslateY + movedDistanceY) > currentBitmapHeight) {
movedDistanceY = 0;
}
// 呼叫onDraw()方法繪製圖片
invalidate();
lastXMove = xMove;
lastYMove = yMove;
} else if (event.getPointerCount() == 2) {
// 有兩個手指按在螢幕上移動時,為縮放狀態
centerPointBetweenFingers(event);
double fingerDis = distanceBetweenFingers(event);
if (fingerDis > lastFingerDis) {
currentStatus = STATUS_ZOOM_OUT;
} else {
currentStatus = STATUS_ZOOM_IN;
}
// 進行縮放倍數檢查,最大隻允許將圖片放大4倍,最小可以縮小到初始化比例
if ((currentStatus == STATUS_ZOOM_OUT && totalRatio < 4 * initRatio)
|| (currentStatus == STATUS_ZOOM_IN && totalRatio > initRatio)) {
scaledRatio = (float) (fingerDis / lastFingerDis);
totalRatio = totalRatio * scaledRatio;
if (totalRatio > 4 * initRatio) {
totalRatio = 4 * initRatio;
} else if (totalRatio < initRatio) {
totalRatio = initRatio;
}
// 呼叫onDraw()方法繪製圖片
invalidate();
lastFingerDis = fingerDis;
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerCount() == 2) {
// 手指離開螢幕時將臨時值還原
lastXMove = -1;
lastYMove = -1;
}
break;
case MotionEvent.ACTION_UP:
// 手指離開螢幕時將臨時值還原
lastXMove = -1;
lastYMove = -1;
break;
default:
break;
}
return true;
}
/**
* 根據currentStatus的值來決定對圖片進行什麼樣的繪製操作。
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (currentStatus) {
case STATUS_ZOOM_OUT:
case STATUS_ZOOM_IN:
zoom(canvas);
break;
case STATUS_MOVE:
move(canvas);
break;
case STATUS_INIT:
initBitmap(canvas);
default:
canvas.drawBitmap(sourceBitmap, matrix, null);
break;
}
}
/**
* 對圖片進行縮放處理。
*
* @param canvas
*/
private void zoom(Canvas canvas) {
matrix.reset();
// 將圖片按總縮放比例進行縮放
matrix.postScale(totalRatio, totalRatio);
float scaledWidth = sourceBitmap.getWidth() * totalRatio;
float scaledHeight = sourceBitmap.getHeight() * totalRatio;
float translateX = 0f;
float translateY = 0f;
// 如果當前圖片寬度小於螢幕寬度,則按螢幕中心的橫座標進行水平縮放。否則按兩指的中心點的橫座標進行水平縮放
if (currentBitmapWidth < width) {
translateX = (width - scaledWidth) / 2f;
} else {
translateX = totalTranslateX * scaledRatio + centerPointX * (1 - scaledRatio);
// 進行邊界檢查,保證圖片縮放後在水平方向上不會偏移出螢幕
if (translateX > 0) {
translateX = 0;
} else if (width - translateX > scaledWidth) {
translateX = width - scaledWidth;
}
}
// 如果當前圖片高度小於螢幕高度,則按螢幕中心的縱座標進行垂直縮放。否則按兩指的中心點的縱座標進行垂直縮放
if (currentBitmapHeight < height) {
translateY = (height - scaledHeight) / 2f;
} else {
translateY = totalTranslateY * scaledRatio + centerPointY * (1 - scaledRatio);
// 進行邊界檢查,保證圖片縮放後在垂直方向上不會偏移出螢幕
if (translateY > 0) {
translateY = 0;
} else if (height - translateY > scaledHeight) {
translateY = height - scaledHeight;
}
}
// 縮放後對圖片進行偏移,以保證縮放後中心點位置不變
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
currentBitmapWidth = scaledWidth;
currentBitmapHeight = scaledHeight;
canvas.drawBitmap(sourceBitmap, matrix, null);
}
/**
* 對圖片進行平移處理
*
* @param canvas
*/
private void move(Canvas canvas) {
matrix.reset();
// 根據手指移動的距離計算出總偏移值
float translateX = totalTranslateX + movedDistanceX;
float translateY = totalTranslateY + movedDistanceY;
// 先按照已有的縮放比例對圖片進行縮放
matrix.postScale(totalRatio, totalRatio);
// 再根據移動距離進行偏移
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
canvas.drawBitmap(sourceBitmap, matrix, null);
}
/**
* 對圖片進行初始化操作,包括讓圖片居中,以及當圖片大於螢幕寬高時對圖片進行壓縮。
*
* @param canvas
*/
private void initBitmap(Canvas canvas) {
if (sourceBitmap != null) {
matrix.reset();
int bitmapWidth = sourceBitmap.getWidth();
int bitmapHeight = sourceBitmap.getHeight();
if (bitmapWidth > width || bitmapHeight > height) {
if (bitmapWidth - width > bitmapHeight - height) {
// 當圖片寬度大於螢幕寬度時,將圖片等比例壓縮,使它可以完全顯示出來
float ratio = width / (bitmapWidth * 1.0f);
matrix.postScale(ratio, ratio);
float translateY = (height - (bitmapHeight * ratio)) / 2f;
// 在縱座標方向上進行偏移,以保證圖片居中顯示
matrix.postTranslate(0, translateY);
totalTranslateY = translateY;
totalRatio = initRatio = ratio;
} else {
// 當圖片高度大於螢幕高度時,將圖片等比例壓縮,使它可以完全顯示出來
float ratio = height / (bitmapHeight * 1.0f);
matrix.postScale(ratio, ratio);
float translateX = (width - (bitmapWidth * ratio)) / 2f;
// 在橫座標方向上進行偏移,以保證圖片居中顯示
matrix.postTranslate(translateX, 0);
totalTranslateX = translateX;
totalRatio = initRatio = ratio;
}
currentBitmapWidth = bitmapWidth * initRatio;
currentBitmapHeight = bitmapHeight * initRatio;
} else {
// 當圖片的寬高都小於螢幕寬高時,直接讓圖片居中顯示
float translateX = (width - sourceBitmap.getWidth()) / 2f;
float translateY = (height - sourceBitmap.getHeight()) / 2f;
matrix.postTranslate(translateX, translateY);
totalTranslateX = translateX;
totalTranslateY = translateY;
totalRatio = initRatio = 1f;
currentBitmapWidth = bitmapWidth;
currentBitmapHeight = bitmapHeight;
}
canvas.drawBitmap(sourceBitmap, matrix, null);
}
}
/**
* 計算兩個手指之間的距離。
*
* @param event
* @return 兩個手指之間的距離
*/
private double distanceBetweenFingers(MotionEvent event) {
float disX = Math.abs(event.getX(0) - event.getX(1));
float disY = Math.abs(event.getY(0) - event.getY(1));