小編給大家分享一下Android怎么實現圖片在屏幕內縮放和移動效果,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
通常我們遇到的圖片縮放需求,都是圖片基于屏幕自適應后,進行縮放和移動,且圖片最小只能是自適應的大小。最近遇到一個需求,要求圖片只能在屏幕內縮放和移動,不能超出屏幕。
一、需求
在屏幕中加載一張圖片,圖片可以手勢縮放移動。但是圖片最大只能縮放到屏幕大小,也只允許在屏幕內移動??梢詮南到y中讀取圖片(通過絕對路徑),也可以從資源文件中讀取圖片。

二、自定義ZoomImageView
屏幕內手勢縮放圖片與普通的圖片縮放相比,比較麻煩的是,需要計算圖片的精確位置。不同于普通縮放的圖片充滿屏幕,屏內縮放的圖片只占據屏幕的一部分,我們需要判斷手指是否點在圖片內,才能進行各種操作。
/**
* 判斷手指是否點在圖片內(單指)
*/
private void isClickInImage(){
if (translationX <= mFirstX && mFirstX <= (translationX + currentW)
&& translationY <= mFirstY && mFirstY <= (translationY + currentH)){
isClickInImage = true;
}else {
isClickInImage = false;
}
}
/**
* 判斷手指是否點在圖片內(雙指)
* 只要有一只手指在圖片內就為true
* @param event
*/
private void isClickInImage(MotionEvent event){
if (translationX <= event.getX(0) && event.getX(0) <= (translationX + currentW)
&& translationY <= event.getY(0) && event.getY(0) <= (translationY + currentH)){
isClickInImage = true;
}else if (translationX <= event.getX(1) && event.getX(1) <= (translationX + currentW)
&& translationY <= event.getY(1) && event.getY(1) <= (translationY + currentH)){
isClickInImage = true;
}else {
isClickInImage = false;
}
}其他的各種操作,之于縮放,移動,邊界檢查等,和普通的圖片縮放沒有太多區別。完整代碼如下:
package com.uni.myapplication;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.io.File;
/**
* Created by newcboy on 2018/3/9.
*/
public class ZoomImageView extends View {
public static final int IMAGE_MAX_SIZE = 1000;//加載圖片允許的最大size,單位kb
private float minimal = 100.0f;
private float screenW;//屏幕寬度
private float screenH;//屏幕高度
//單指按下的坐標
private float mFirstX = 0.0f;
private float mFirstY = 0.0f;
//單指離開的坐標
private float lastMoveX =-1f;
private float lastMoveY =-1f;
//兩指的中點坐標
private float centPointX;
private float centPointY;
//圖片的繪制坐標
private float translationX = 0.0f;
private float translationY = 0.0f;
//圖片的原始寬高
private float primaryW;
private float primaryH;
//圖片當前寬高
private float currentW;
private float currentH;
private float scale = 1.0f;
private float maxScale, minScale;
private Bitmap bitmap;
private Matrix matrix;
private int mLocker = 0;
private float fingerDistance = 0.0f;
private boolean isLoaded = false;
private boolean isClickInImage = false;
public ZoomImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
/**
* 從資源文件中讀取圖片
* @param context
* @param imageId
*/
public void setResourceBitmap(Context context, int imageId){
bitmap = BitmapFactory.decodeResource(context.getResources(), imageId);
isLoaded = true;
primaryW = bitmap.getWidth();
primaryH = bitmap.getHeight();
matrix = new Matrix();
}
/**
* 根據路徑添加圖片
* @param path
* @param scale
*/
public void setImagePathBitmap(String path, float scale){
this.scale = scale;
setImageBitmap(path);
}
private void setImageBitmap(String path){
File file = new File(path);
if (file.exists()){
isLoaded = true;
bitmap = ImageLoadUtils.getImageLoadBitmap(path, IMAGE_MAX_SIZE);
primaryW = bitmap.getWidth();
primaryH = bitmap.getHeight();
matrix = new Matrix();
}else {
isLoaded = false;
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed){
screenW = getWidth();
screenH = getHeight();
translationX = (screenW - bitmap.getWidth() * scale)/ 2;
translationY = (screenH - bitmap.getHeight() * scale) / 2;
setMaxMinScale();
}
}
/**
*
*/
private void setMaxMinScale(){
float xScale, yScale;
xScale = minimal / primaryW;
yScale = minimal / primaryH;
minScale = xScale > yScale ? xScale : yScale;
xScale = primaryW / screenW;
yScale = primaryH / screenH;
if (xScale > 1 || yScale > 1 ) {
if (xScale > yScale) {
maxScale = 1/xScale;
}else {
maxScale = 1/yScale;
}
}else {
if (xScale > yScale) {
maxScale = 1/xScale;
}else {
maxScale = 1/yScale;
}
}
if (isScaleError()){
restoreAction();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isLoaded){
return true;
}
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
mFirstX = event.getX();
mFirstY = event.getY();
isClickInImage();
break;
case MotionEvent.ACTION_POINTER_DOWN:
fingerDistance = getFingerDistance(event);
isClickInImage(event);
break;
case MotionEvent.ACTION_MOVE:
float fingerNum = event.getPointerCount();
if (fingerNum == 1 && mLocker == 0 && isClickInImage){
movingAction(event);
}else if (fingerNum == 2 && isClickInImage){
zoomAction(event);
}
break;
case MotionEvent.ACTION_POINTER_UP:
mLocker = 1;
if (isScaleError()){
translationX = (event.getX(1) + event.getX(0)) / 2;
translationY = (event.getY(1) + event.getY(0)) / 2;
}
break;
case MotionEvent.ACTION_UP:
lastMoveX = -1;
lastMoveY = -1;
mLocker = 0;
if (isScaleError()){
restoreAction();
}
break;
}
return true;
}
/**
* 移動操作
* @param event
*/
private void movingAction(MotionEvent event){
float moveX = event.getX();
float moveY = event.getY();
if (lastMoveX == -1 || lastMoveY == -1) {
lastMoveX = moveX;
lastMoveY = moveY;
}
float moveDistanceX = moveX - lastMoveX;
float moveDistanceY = moveY - lastMoveY;
translationX = translationX + moveDistanceX;
translationY = translationY + moveDistanceY;
lastMoveX = moveX;
lastMoveY = moveY;
invalidate();
}
/**
* 縮放操作
* @param event
*/
private void zoomAction(MotionEvent event){
midPoint(event);
float currentDistance = getFingerDistance(event);
if (Math.abs(currentDistance - fingerDistance) > 1f) {
float moveScale = currentDistance / fingerDistance;
scale = scale * moveScale;
translationX = translationX * moveScale + centPointX * (1-moveScale);
translationY = translationY * moveScale + centPointY * (1-moveScale);
fingerDistance = currentDistance;
invalidate();
}
}
/**
* 圖片恢復到指定大小
*/
private void restoreAction(){
if (scale < minScale){
scale = minScale;
}else if (scale > maxScale){
scale = maxScale;
}
translationX = translationX - bitmap.getWidth()*scale / 2;
translationY = translationY - bitmap.getHeight()*scale / 2;
invalidate();
}
/**
* 判斷手指是否點在圖片內(單指)
*/
private void isClickInImage(){
if (translationX <= mFirstX && mFirstX <= (translationX + currentW)
&& translationY <= mFirstY && mFirstY <= (translationY + currentH)){
isClickInImage = true;
}else {
isClickInImage = false;
}
}
/**
* 判斷手指是否點在圖片內(雙指)
* 只要有一只手指在圖片內就為true
* @param event
*/
private void isClickInImage(MotionEvent event){
if (translationX <= event.getX(0) && event.getX(0) <= (translationX + currentW)
&& translationY <= event.getY(0) && event.getY(0) <= (translationY + currentH)){
isClickInImage = true;
}else if (translationX <= event.getX(1) && event.getX(1) <= (translationX + currentW)
&& translationY <= event.getY(1) && event.getY(1) <= (translationY + currentH)){
isClickInImage = true;
}else {
isClickInImage = false;
}
}
/**
* 獲取兩指間的距離
* @param event
* @return
*/
private float getFingerDistance(MotionEvent event){
float x = event.getX(1) - event.getX(0);
float y = event.getY(1) - event.getY(0);
return (float) Math.sqrt(x * x + y * y);
}
/**
* 判斷圖片大小是否符合要求
* @return
*/
private boolean isScaleError(){
if (scale > maxScale
|| scale < minScale){
return true;
}
return false;
}
/**
* 獲取兩指間的中點坐標
* @param event
*/
private void midPoint(MotionEvent event){
centPointX = (event.getX(1) + event.getX(0))/2;
centPointY = (event.getY(1) + event.getY(0))/2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isLoaded){
imageZoomView(canvas);
}
}
private void imageZoomView(Canvas canvas){
currentW = primaryW * scale;
currentH = primaryH * scale;
matrix.reset();
matrix.postScale(scale, scale);//x軸y軸縮放
peripheryJudge();
matrix.postTranslate(translationX, translationY);//中點坐標移動
canvas.drawBitmap(bitmap, matrix, null);
}
/**
* 圖片邊界檢查
* (只在屏幕內)
*/
private void peripheryJudge(){
if (translationX < 0){
translationX = 0;
}
if (translationY < 0){
translationY = 0;
}
if ((translationX + currentW) > screenW){
translationX = screenW - currentW;
}
if ((translationY + currentH) > screenH){
translationY = screenH - currentH;
}
}
}實際上,用Bitmap繪制圖片時,可以通過Paint設置圖片透明度。
Paint paint = new Paint(); paint.setStyle( Paint.Style.STROKE); paint.setAlpha(150);
在setAlpha()中傳入一個0~255的整數。數字越大,透明度越低。
然后在繪制圖片時
canvas.drawBitmap(bitmap, matrix, paint);
三、ImageLoadUtils圖片加載類
這個類是對傳入的圖片進行壓縮處理的類,在應用從系統中讀取圖片時用到。在寫這個類時,發現一些和網上說法不一樣的地方。
options.inSampleSize這個屬性,網上的說法是必須是2的冪次方,但實際上,我的驗證結果是所有的整數都可以。
這里采用的壓縮方法是,獲取系統剩余內存和圖片大小,然后將圖片壓縮到合適的大小。
package com.uni.myapplication;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.net.Uri;
import java.io.File;
import java.io.FileInputStream;
/**
* 圖片加載工具類
*
* Created by newcboy on 2018/1/25.
*/
public class ImageLoadUtils {
/**
* 原圖加載,根據傳入的指定圖片大小。
* @param imagePath
* @param maxSize
* @return
*/
public static Bitmap getImageLoadBitmap(String imagePath, int maxSize){
int fileSize = 1;
Bitmap bitmap = null;
int simpleSize = 1;
File file = new File(imagePath);
if (file.exists()) {
Uri imageUri = Uri.parse(imagePath);
try {
fileSize = (int) (getFileSize(file) / 1024);
} catch (Exception e) {
e.printStackTrace();
}
Options options = new Options();
if (fileSize > maxSize){
for (simpleSize = 2; fileSize>= maxSize; simpleSize++){
fileSize = fileSize / simpleSize;
}
}
options.inSampleSize = simpleSize;
bitmap = BitmapFactory.decodeFile(imageUri.getPath(), options);
}
return bitmap;
}
/**
* 獲取指定文件的大小
* @param file
* @return
* @throws Exception
*/
public static long getFileSize(File file) throws Exception{
if(file == null) {
return 0;
}
long size = 0;
if(file.exists()) {
FileInputStream mInputStream = new FileInputStream(file);
size = mInputStream.available();
}
return size;
}
/**
* 獲取手機運行內存
* @param context
* @return
*/
public static long getTotalMemorySize(Context context){
long size = 0;
ActivityManager activityManager = (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();//outInfo對象里面包含了內存相關的信息
activityManager.getMemoryInfo(outInfo);//把內存相關的信息傳遞到outInfo里面C++思想
//size = outInfo.totalMem; //總內存
size = outInfo.availMem; //剩余內存
return (size/1024/1024);
}
}四、調用
使用方法和通常的控件差不多,只是多了一個設置圖片的方法。
1.在布局文件中添加布局。
<com.uni.myapplication.ZoomImageView android:id="@+id/zoom_image_view" android:layout_width="wrap_content" android:layout_height="wrap_content" />
2.在代碼中調用
zoomImageView = (ZoomImageView) findViewById(R.id.zoom_image_view); zoomImageView.setImagePathBitmap(MainActivity.this, imagePath, 1.0f); zoomImageView.setResourceBitmap(MainActivity.this, R.mipmap.ic_launcher);
其中setImagePathBitmap()是從系統中讀取圖片加載的方法,setResourceBitmap()是從資源文件中讀取圖片的方法。
當然,從系統讀取圖片需要添加讀寫權限,這個不能忘了。而且6.0以上的系統需要動態獲取權限。動態獲取權限的方法這里就不介紹了,網上有很詳細的說明。
五、最終效果

以上是“Android怎么實現圖片在屏幕內縮放和移動效果”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。