溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android怎么實(shí)現(xiàn)圖片在屏幕內(nèi)縮放和移動(dòng)效果

發(fā)布時(shí)間:2021-04-16 10:13:33 來(lái)源:億速云 閱讀:744 作者:小新 欄目:移動(dòng)開(kāi)發(fā)

小編給大家分享一下Android怎么實(shí)現(xiàn)圖片在屏幕內(nèi)縮放和移動(dòng)效果,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

通常我們遇到的圖片縮放需求,都是圖片基于屏幕自適應(yīng)后,進(jìn)行縮放和移動(dòng),且圖片最小只能是自適應(yīng)的大小。最近遇到一個(gè)需求,要求圖片只能在屏幕內(nèi)縮放和移動(dòng),不能超出屏幕。

一、需求

在屏幕中加載一張圖片,圖片可以手勢(shì)縮放移動(dòng)。但是圖片最大只能縮放到屏幕大小,也只允許在屏幕內(nèi)移動(dòng)??梢詮南到y(tǒng)中讀取圖片(通過(guò)絕對(duì)路徑),也可以從資源文件中讀取圖片。

Android怎么實(shí)現(xiàn)圖片在屏幕內(nèi)縮放和移動(dòng)效果

二、自定義ZoomImageView

屏幕內(nèi)手勢(shì)縮放圖片與普通的圖片縮放相比,比較麻煩的是,需要計(jì)算圖片的精確位置。不同于普通縮放的圖片充滿屏幕,屏內(nèi)縮放的圖片只占據(jù)屏幕的一部分,我們需要判斷手指是否點(diǎn)在圖片內(nèi),才能進(jìn)行各種操作。

/**
 * 判斷手指是否點(diǎn)在圖片內(nèi)(單指)
 */
 private void isClickInImage(){
 if (translationX <= mFirstX && mFirstX <= (translationX + currentW)
  && translationY <= mFirstY && mFirstY <= (translationY + currentH)){
  isClickInImage = true;
 }else {
  isClickInImage = false;
 }
 }
 
 /**
 * 判斷手指是否點(diǎn)在圖片內(nèi)(雙指)
 * 只要有一只手指在圖片內(nèi)就為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;
 }
 }

其他的各種操作,之于縮放,移動(dòng),邊界檢查等,和普通的圖片縮放沒(méi)有太多區(qū)別。完整代碼如下:

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;//屏幕高度
 
 //單指按下的坐標(biāo)
 private float mFirstX = 0.0f;
 private float mFirstY = 0.0f;
 
 //單指離開(kāi)的坐標(biāo)
 private float lastMoveX =-1f;
 private float lastMoveY =-1f;
 
 //兩指的中點(diǎn)坐標(biāo)
 private float centPointX;
 private float centPointY;
 
 //圖片的繪制坐標(biāo)
 private float translationX = 0.0f;
 private float translationY = 0.0f;
 
 //圖片的原始寬高
 private float primaryW;
 private float primaryH;
 
 //圖片當(dāng)前寬高
 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();
 }
 
 /**
 * 根據(jù)路徑添加圖片
 * @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;
 }
 
 
 /**
 * 移動(dòng)操作
 * @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();
 }
 }
 
 /**
 * 圖片恢復(fù)到指定大小
 */
 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();
 }
 
 
 /**
 * 判斷手指是否點(diǎn)在圖片內(nèi)(單指)
 */
 private void isClickInImage(){
 if (translationX <= mFirstX && mFirstX <= (translationX + currentW)
  && translationY <= mFirstY && mFirstY <= (translationY + currentH)){
  isClickInImage = true;
 }else {
  isClickInImage = false;
 }
 }
 
 /**
 * 判斷手指是否點(diǎn)在圖片內(nèi)(雙指)
 * 只要有一只手指在圖片內(nèi)就為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;
 }
 
 
 /**
 * 獲取兩指間的中點(diǎn)坐標(biāo)
 * @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);//中點(diǎn)坐標(biāo)移動(dòng)
 canvas.drawBitmap(bitmap, matrix, null);
 }
 
 /**
 * 圖片邊界檢查
 * (只在屏幕內(nèi))
 */
 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;
 }
 }
 
}

實(shí)際上,用Bitmap繪制圖片時(shí),可以通過(guò)Paint設(shè)置圖片透明度。

Paint paint = new Paint();
paint.setStyle( Paint.Style.STROKE);
paint.setAlpha(150);

在setAlpha()中傳入一個(gè)0~255的整數(shù)。數(shù)字越大,透明度越低。

然后在繪制圖片時(shí)

canvas.drawBitmap(bitmap, matrix, paint);

三、ImageLoadUtils圖片加載類

這個(gè)類是對(duì)傳入的圖片進(jìn)行壓縮處理的類,在應(yīng)用從系統(tǒng)中讀取圖片時(shí)用到。在寫這個(gè)類時(shí),發(fā)現(xiàn)一些和網(wǎng)上說(shuō)法不一樣的地方。

options.inSampleSize這個(gè)屬性,網(wǎng)上的說(shuō)法是必須是2的冪次方,但實(shí)際上,我的驗(yàn)證結(jié)果是所有的整數(shù)都可以。

這里采用的壓縮方法是,獲取系統(tǒng)剩余內(nèi)存和圖片大小,然后將圖片壓縮到合適的大小。

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 {
 
 /**
 * 原圖加載,根據(jù)傳入的指定圖片大小。
 * @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;
 }
 
 
 /**
 * 獲取手機(jī)運(yùn)行內(nèi)存
 * @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對(duì)象里面包含了內(nèi)存相關(guān)的信息
 activityManager.getMemoryInfo(outInfo);//把內(nèi)存相關(guān)的信息傳遞到outInfo里面C++思想
 //size = outInfo.totalMem; //總內(nèi)存
 size = outInfo.availMem; //剩余內(nèi)存
 return (size/1024/1024);
 }
 
}

四、調(diào)用

使用方法和通常的控件差不多,只是多了一個(gè)設(shè)置圖片的方法。

1.在布局文件中添加布局。

<com.uni.myapplication.ZoomImageView
 android:id="@+id/zoom_image_view"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

2.在代碼中調(diào)用

zoomImageView = (ZoomImageView) findViewById(R.id.zoom_image_view);
zoomImageView.setImagePathBitmap(MainActivity.this, imagePath, 1.0f);
zoomImageView.setResourceBitmap(MainActivity.this, R.mipmap.ic_launcher);

其中setImagePathBitmap()是從系統(tǒng)中讀取圖片加載的方法,setResourceBitmap()是從資源文件中讀取圖片的方法。
當(dāng)然,從系統(tǒng)讀取圖片需要添加讀寫權(quán)限,這個(gè)不能忘了。而且6.0以上的系統(tǒng)需要?jiǎng)討B(tài)獲取權(quán)限。動(dòng)態(tài)獲取權(quán)限的方法這里就不介紹了,網(wǎng)上有很詳細(xì)的說(shuō)明。

五、最終效果

Android怎么實(shí)現(xiàn)圖片在屏幕內(nèi)縮放和移動(dòng)效果

以上是“Android怎么實(shí)現(xiàn)圖片在屏幕內(nèi)縮放和移動(dòng)效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI