您好,登錄后才能下訂單哦!
小編給大家分享一下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ì)路徑),也可以從資源文件中讀取圖片。
二、自定義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)效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(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)容。