編輯:關於Android編程
文章將在原有基礎之上做了一些擴展功能:
1.圖片的慣性滑動
2.圖片縮放小於正常比例時,松手會自動回彈成正常比例
3.圖片縮放大於最大比例時,松手會自動回彈成最大比例
實現圖片的縮放,平移,雙擊縮放等基本功能的代碼如下,每一行代碼我都做了詳細的注釋
public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener , ViewTreeObserver.OnGlobalLayoutListener{ /** * 縮放手勢的監測 */ private ScaleGestureDetector mScaleGestureDetector; /** * 監聽手勢 */ private GestureDetector mGestureDetector; /** * 對圖片進行縮放平移的Matrix */ private Matrix mScaleMatrix; /** * 第一次加載圖片時調整圖片縮放比例,使圖片的寬或者高充滿屏幕 */ private boolean mFirst; /** * 圖片的初始化比例 */ private float mInitScale; /** * 圖片的最大比例 */ private float mMaxScale; /** * 雙擊圖片放大的比例 */ private float mMidScale; /** * 是否正在自動放大或者縮小 */ private boolean isAutoScale; //----------------------------------------------- /** * 上一次觸控點的數量 */ private int mLastPointerCount; /** * 是否可以拖動 */ private boolean isCanDrag; /** * 上一次滑動的x和y坐標 */ private float mLastX; private float mLastY; /** * 可滑動的臨界值 */ private int mTouchSlop; /** * 是否用檢查左右邊界 */ private boolean isCheckLeftAndRight; /** * 是否用檢查上下邊界 */ private boolean isCheckTopAndBottom; public ZoomImageView(Context context) { this(context, null, 0); } public ZoomImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //一定要將圖片的ScaleType設置成Matrix類型的 setScaleType(ScaleType.MATRIX); //初始化縮放手勢監聽器 mScaleGestureDetector = new ScaleGestureDetector(context,this); //初始化矩陣 mScaleMatrix = new Matrix(); setOnTouchListener(this); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); //初始化手勢檢測器,監聽雙擊事件 mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onDoubleTap(MotionEvent e) { //如果是正在自動縮放,則直接返回,不進行處理 if (isAutoScale) return true; //得到點擊的坐標 float x = e.getX(); float y = e.getY(); //如果當前圖片的縮放值小於指定的雙擊縮放值 if (getScale() < mMidScale){ //進行自動放大 post(new AutoScaleRunnable(mMidScale,x,y)); }else{ //當前圖片的縮放值大於初試縮放值,則自動縮小 post(new AutoScaleRunnable(mInitScale,x,y)); } return true; } }); } /** * 當view添加到window時調用,早於onGlobalLayout,因此可以在這裡注冊監聽器 */ @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); } /** * 當view從window上移除時調用,因此可以在這裡移除監聽器 */ @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } /** * 當布局樹發生變化時會調用此方法,我們可以在此方法中獲得控件的寬和高 */ @Override public void onGlobalLayout() { //只有當第一次加載圖片的時候才會進行初始化,用一個變量mFirst控制 if (!mFirst){ mFirst = true; //得到控件的寬和高 int width = getWidth(); int height = getHeight(); //得到當前ImageView中加載的圖片 Drawable d = getDrawable(); if(d == null){//如果沒有圖片,則直接返回 return; } //得到當前圖片的寬和高,圖片的寬和高不一定等於控件的寬和高 //因此我們需要將圖片的寬和高與控件寬和高進行判斷 //將圖片完整的顯示在屏幕中 int dw = d.getIntrinsicWidth(); int dh = d.getIntrinsicHeight(); //我們定義一個臨時變量,根據圖片與控件的寬高比例,來確定這個最終縮放值 float scale = 1.0f; //如果圖片寬度大於控件寬度,圖片高度小於控件高度 if (dw>width && dh<height){ //我們需要將圖片寬度縮小,縮小至控件的寬度 //至於為什麼要這樣計算,我們可以這樣想 //我們調用matrix.postScale(scale,scale)時,寬和高都要乘以scale的 //當前我們的圖片寬度是dw,dw*scale=dw*(width/dw)=width,這樣就等於控件寬度了 //我們的高度同時也乘以scale,這樣能夠保證圖片的寬高比不改變,圖片不變形 scale = width * 1.0f / dw; } //如果圖片的寬度小於控件寬度,圖片高度大於控件高度 if (dw<width && dh>height){ //我們就應該將圖片的高度縮小,縮小至控件的高度,計算方法同上 scale = height * 1.0f / dh; } //如果圖片的寬度小於控件寬度,高度小於控件高度時,我們應該將圖片放大 //比如圖片寬度是控件寬度的1/2 ,圖片高度是控件高度的1/4 //如果我們將圖片放大4倍,則圖片的高度是和控件高度一樣了,但是圖片寬度就超出控件寬度了 //因此我們應該選擇一個最小值,那就是將圖片放大2倍,此時圖片寬度等於控件寬度 //同理,如果圖片寬度大於控件寬度,圖片高度大於控件高度,我們應該將圖片縮小 //縮小的倍數也應該為那個最小值 if ((dw < width && dh < height) || (dw > width && dh > height)){ scale = Math.min(width * 1.0f / dw , height * 1.0f / dh); } //我們還應該對圖片進行平移操作,將圖片移動到屏幕的居中位置 //控件寬度的一半減去圖片寬度的一半即為圖片需要水平移動的距離 //高度同理,大家可以畫個圖看一看 int dx = width/2 - dw/2; int dy = height/2 - dh/2; //對圖片進行平移,dx和dy分別表示水平和豎直移動的距離 mScaleMatrix.postTranslate(dx, dy); //對圖片進行縮放,scale為縮放的比例,後兩個參數為縮放的中心點 mScaleMatrix.postScale(scale, scale, width / 2, height / 2); //將矩陣作用於我們的圖片上,圖片真正得到了平移和縮放 setImageMatrix(mScaleMatrix); //初始化一下我們的幾個縮放的邊界值 mInitScale = scale; //最大比例為初始比例的4倍 mMaxScale = mInitScale * 4; //雙擊放大比例為初始化比例的2倍 mMidScale = mInitScale * 2; } } /** * 獲得圖片當前的縮放比例值 */ private float getScale(){ //Matrix為一個3*3的矩陣,一共9個值 float[] values = new float[9]; //將Matrix的9個值映射到values數組中 mScaleMatrix.getValues(values); //拿到Matrix中的MSCALE_X的值,這個值為圖片寬度的縮放比例,因為圖片高度 //的縮放比例和寬度的縮放比例一致,我們取一個就可以了 //我們還可以 return values[Matrix.MSCALE_Y]; return values[Matrix.MSCALE_X]; } /** * 獲得縮放後圖片的上下左右坐標以及寬高 */ private RectF getMatrixRectF(){ //獲得當錢圖片的矩陣 Matrix matrix = mScaleMatrix; //創建一個浮點類型的矩形 RectF rectF = new RectF(); //得到當前的圖片 Drawable d = getDrawable(); if (d != null){ //使這個矩形的寬和高同當前圖片一致 rectF.set(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight()); //將矩陣映射到矩形上面,之後我們可以通過獲取到矩陣的上下左右坐標以及寬高 //來得到縮放後圖片的上下左右坐標和寬高 matrix.mapRect(rectF); } return rectF; } /** * 當縮放時檢查邊界並且使圖片居中 */ private void checkBorderAndCenterWhenScale(){ if (getDrawable() == null){ return; } //初始化水平和豎直方向的偏移量 float deltaX = 0.0f; float deltaY = 0.0f; //得到控件的寬和高 int width = getWidth(); int height = getHeight(); //拿到當前圖片對應的矩陣 RectF rectF = getMatrixRectF(); //如果當前圖片的寬度大於控件寬度,當前圖片處於放大狀態 if (rectF.width() >= width){ //如果圖片左邊坐標是大於0的,說明圖片左邊離控件左邊有一定距離, //左邊會出現一個小白邊 if (rectF.left > 0){ //我們將圖片向左邊移動 deltaX = -rectF.left; } //如果圖片右邊坐標小於控件寬度,說明圖片右邊離控件右邊有一定距離, //右邊會出現一個小白邊 if (rectF.right <width){ //我們將圖片向右邊移動 deltaX = width - rectF.right; } } //上面是調整寬度,這是調整高度 if (rectF.height() >= height){ //如果上面出現小白邊,則向上移動 if (rectF.top > 0){ deltaY = -rectF.top; } //如果下面出現小白邊,則向下移動 if (rectF.bottom < height){ deltaY = height - rectF.bottom; } } //如果圖片的寬度小於控件的寬度,我們要對圖片做一個水平的居中 if (rectF.width() < width){ deltaX = width/2f - rectF.right + rectF.width()/2f; } //如果圖片的高度小於控件的高度,我們要對圖片做一個豎直方向的居中 if (rectF.height() < height){ deltaY = height/2f - rectF.bottom + rectF.height()/2f; } //將平移的偏移量作用到矩陣上 mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 平移時檢查上下左右邊界 */ private void checkBorderWhenTranslate() { //獲得縮放後圖片的相應矩形 RectF rectF = getMatrixRectF(); //初始化水平和豎直方向的偏移量 float deltaX = 0.0f; float deltaY = 0.0f; //得到控件的寬度 int width = getWidth(); //得到控件的高度 int height = getHeight(); //如果是需要檢查左和右邊界 if (isCheckLeftAndRight){ //如果左邊出現的白邊 if (rectF.left > 0){ //向左偏移 deltaX = -rectF.left; } //如果右邊出現的白邊 if (rectF.right < width){ //向右偏移 deltaX = width - rectF.right; } } //如果是需要檢查上和下邊界 if (isCheckTopAndBottom){ //如果上面出現白邊 if (rectF.top > 0){ //向上偏移 deltaY = -rectF.top; } //如果下面出現白邊 if (rectF.bottom < height){ //向下偏移 deltaY = height - rectF.bottom; } } mScaleMatrix.postTranslate(deltaX,deltaY); } /** * 自動放大縮小,自動縮放的原理是使用View.postDelay()方法,每隔16ms調用一次 * run方法,給人視覺上形成一種動畫的效果 */ private class AutoScaleRunnable implements Runnable{ //放大或者縮小的目標比例 private float mTargetScale; //可能是BIGGER,也可能是SMALLER private float tempScale; //放大縮小的中心點 private float x; private float y; //比1稍微大一點,用於放大 private final float BIGGER = 1.07f; //比1稍微小一點,用於縮小 private final float SMALLER = 0.93f; //構造方法,將目標比例,縮放中心點傳入,並且判斷是要放大還是縮小 public AutoScaleRunnable(float targetScale , float x , float y){ this.mTargetScale = targetScale; this.x = x; this.y = y; //如果當前縮放比例小於目標比例,說明要自動放大 if (getScale() < mTargetScale){ //設置為Bigger tempScale = BIGGER; } //如果當前縮放比例大於目標比例,說明要自動縮小 if (getScale() > mTargetScale){ //設置為Smaller tempScale = SMALLER; } } @Override public void run() { //這裡縮放的比例非常小,只是稍微比1大一點或者比1小一點的倍數 //但是當每16ms都放大或者縮小一點點的時候,動畫效果就出來了 mScaleMatrix.postScale(tempScale, tempScale, x, y); //每次將矩陣作用到圖片之前,都檢查一下邊界 checkBorderAndCenterWhenScale(); //將矩陣作用到圖片上 setImageMatrix(mScaleMatrix); //得到當前圖片的縮放值 float currentScale = getScale(); //如果當前想要放大,並且當前縮放值小於目標縮放值 //或者 當前想要縮小,並且當前縮放值大於目標縮放值 if ((tempScale > 1.0f) && currentScale < mTargetScale ||(tempScale < 1.0f) && currentScale > mTargetScale){ //每隔16ms就調用一次run方法 postDelayed(this,16); }else { //current*scale=current*(mTargetScale/currentScale)=mTargetScale //保證圖片最終的縮放值和目標縮放值一致 float scale = mTargetScale / currentScale; mScaleMatrix.postScale(scale, scale, x, y); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); //自動縮放結束,置為false isAutoScale = false; } } } /** * 這個是OnScaleGestureListener中的方法,在這個方法中我們可以對圖片進行放大縮小 */ @Override public boolean onScale(ScaleGestureDetector detector) { //當我們兩個手指進行分開操作時,說明我們想要放大,這個scaleFactor是一個稍微大於1的數值 //當我們兩個手指進行閉合操作時,說明我們想要縮小,這個scaleFactor是一個稍微小於1的數值 float scaleFactor = detector.getScaleFactor(); //獲得我們圖片當前的縮放值 float scale = getScale(); //如果當前沒有圖片,則直接返回 if (getDrawable() == null){ return true; } //如果scaleFactor大於1,說明想放大,當前的縮放比例乘以scaleFactor之後小於 //最大的縮放比例時,允許放大 //如果scaleFactor小於1,說明想縮小,當前的縮放比例乘以scaleFactor之後大於 //最小的縮放比例時,允許縮小 if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale) || scaleFactor < 1.0f && scale * scaleFactor > mInitScale){ //邊界控制,如果當前縮放比例乘以scaleFactor之後大於了最大的縮放比例 if (scale * scaleFactor > mMaxScale + 0.01f){ //則將scaleFactor設置成mMaxScale/scale //當再進行matrix.postScale時 //scale*scaleFactor=scale*(mMaxScale/scale)=mMaxScale //最後圖片就會放大至mMaxScale縮放比例的大小 scaleFactor = mMaxScale / scale; } //邊界控制,如果當前縮放比例乘以scaleFactor之後小於了最小的縮放比例 //我們不允許再縮小 if (scale * scaleFactor < mInitScale + 0.01f){ //計算方法同上 scaleFactor = mInitScale / scale; } //前兩個參數是縮放的比例,是一個稍微大於1或者稍微小於1的數,形成一個隨著手指放大 //或者縮小的效果 //detector.getFocusX()和detector.getFocusY()得到的是多點觸控的中點 //這樣就能實現我們在圖片的某一處局部放大的效果 mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); //因為圖片的縮放點不是圖片的中心點了,所以圖片會出現偏移的現象,所以進行一次邊界的檢查和居中操作 checkBorderAndCenterWhenScale(); //將矩陣作用到圖片上 setImageMatrix(mScaleMatrix); } return true; } /** * 一定要返回true */ @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } @Override public boolean onTouch(View v, MotionEvent event) { //當雙擊操作時,不允許移動圖片,直接返回true if (mGestureDetector.onTouchEvent(event)){ return true; } //將事件傳遞給ScaleGestureDetector mScaleGestureDetector.onTouchEvent(event); //用於存儲多點觸控產生的坐標 float x = 0.0f; float y = 0.0f; //得到多點觸控的個數 int pointerCount = event.getPointerCount(); //將所有觸控點的坐標累加起來 for(int i=0 ; i<pointerCount ; i++){ x += event.getX(i); y += event.getY(i); } //取平均值,得到的就是多點觸控後產生的那個點的坐標 x /= pointerCount; y /= pointerCount; //如果觸控點的數量變了,則置為不可滑動 if (mLastPointerCount != pointerCount){ isCanDrag = false; mLastX = x; mLastY = y; } mLastPointerCount = pointerCount; RectF rectF = getMatrixRectF(); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: isCanDrag = false; //當圖片處於放大狀態時,禁止ViewPager攔截事件,將事件傳遞給圖片,進行拖動 if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f){ if (getParent() instanceof ViewPager){ getParent().requestDisallowInterceptTouchEvent(true); } } break; case MotionEvent.ACTION_MOVE: //當圖片處於放大狀態時,禁止ViewPager攔截事件,將事件傳遞給圖片,進行拖動 if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f){ if (getParent() instanceof ViewPager){ getParent().requestDisallowInterceptTouchEvent(true); } } //得到水平和豎直方向的偏移量 float dx = x - mLastX; float dy = y - mLastY; //如果當前是不可滑動的狀態,判斷一下是否是滑動的操作 if (!isCanDrag){ isCanDrag = isMoveAction(dx,dy); } //如果可滑動 if (isCanDrag){ if (getDrawable() != null){ isCheckLeftAndRight = true; isCheckTopAndBottom = true; //如果圖片寬度小於控件寬度 if (rectF.width() < getWidth()){ //左右不可滑動 dx = 0; //左右不可滑動,也就不用檢查左右的邊界了 isCheckLeftAndRight = false; } //如果圖片的高度小於控件的高度 if (rectF.height() < getHeight()){ //上下不可滑動 dy = 0; //上下不可滑動,也就不用檢查上下邊界了 isCheckTopAndBottom = false; } } mScaleMatrix.postTranslate(dx,dy); //當平移時,檢查上下左右邊界 checkBorderWhenTranslate(); setImageMatrix(mScaleMatrix); } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: //當手指抬起時,將mLastPointerCount置0,停止滑動 mLastPointerCount = 0; break; case MotionEvent.ACTION_CANCEL: break; } return true; } /** * 判斷是否是移動的操作 */ private boolean isMoveAction(float dx , float dy){ //勾股定理,判斷斜邊是否大於可滑動的一個臨界值 return Math.sqrt(dx*dx + dy*dy) > mTouchSlop; } }
實現圖片縮小後,松手回彈的效果
實現這個功能很簡單,我們先添加一個mMinScale作為可縮小到的最小值,我們指定為初試比例的1/4
/** * 最小縮放比例 */ private float mMinScale; //在onGlobalLayout中進行初始化 @Override public void onGlobalLayout() { ... //最小縮放比例為初試比例的1/4倍 mMinScale = mInitScale / 4; ... } //在onScale中,修改如下代碼 @Override public boolean onScale(ScaleGestureDetector detector) { ... if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale) || scaleFactor < 1.0f && scale * scaleFactor > mMinScale){ //邊界控制,如果當前縮放比例乘以scaleFactor之後小於了最小的縮放比例 //我們不允許再縮小 if (scale * scaleFactor < mMinScale + 0.01f){ scaleFactor = mMinScale / scale; } ... }
這樣我們的圖片最小就可以縮放到初始化比例的1/4大小了,然後我們還需要添加一個松手後回彈至初試化大小的動畫效果,然後我們需要在onTouch的ACTION_UP中添加如下代碼
@Override public boolean onTouch(View v, MotionEvent event) { ... case MotionEvent.ACTION_UP: //當手指抬起時,將mLastPointerCount置0,停止滑動 mLastPointerCount = 0; //如果當前圖片大小小於初始化大小 if (getScale() < mInitScale){ //自動放大至初始化大小 post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2)); } break; ... }
現在我們看一下效果
實現圖片放大後,松手回彈效果
這個功能實現起來和上面那個功能基本一致,大家可以先試著自己寫一下。
同理,我們需要先定義一個mMaxOverScale作為放大到最大值後,還能繼續放大到的值。
/** * 最大溢出值 */ private float mMaxOverScale; //在onGlobalLayout中進行初始化 @Override public void onGlobalLayout() { ... //最大溢出值為最大值的5倍,可以隨意調 mMaxOverScale = mMaxScale * 5; ... } //在onScale中,修改如下代碼 @Override public boolean onScale(ScaleGestureDetector detector) { ... if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxOverScale) || scaleFactor < 1.0f && scale * scaleFactor > mMinScale){ if (scale * scaleFactor > mMaxOverScale + 0.01f){ scaleFactor = mMaxOverScale / scale; } ... }
這樣當我們圖片放大至最大比例後還可以繼續放大,然後我們同樣需要在onTouch中的ACTION_UP中添加自動縮小的功能
case MotionEvent.ACTION_UP: //當手指抬起時,將mLastPointerCount置0,停止滑動 mLastPointerCount = 0; //如果當前圖片大小小於初始化大小 if (getScale() < mInitScale){ //自動放大至初始化大小 post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2)); } //如果當前圖片大小大於最大值 if (getScale() > mMaxScale){ //自動縮小至最大值 post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2)); } break;
然後我們看一下效果
實現圖片的慣性滑動
要實現圖片的慣性滑動,我們需要借助VelocityTracker來幫我們檢測當我們手指離開圖片時的一個速度,然後根據這個速度以及圖片的位置來調用Scroller的fling方法來計算慣性滑動過程中的x和y的坐標
@Override public boolean onTouch(View v, MotionEvent event) { ... switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //初始化速度檢測器 mVelocityTracker = VelocityTracker.obtain(); if (mVelocityTracker != null){ //將當前的事件添加到檢測器中 mVelocityTracker.addMovement(event); } //當手指再次點擊到圖片時,停止圖片的慣性滑動 if (mFlingRunnable != null){ mFlingRunnable.cancelFling(); mFlingRunnable = null; } ... } ... case MotionEvent.ACTION_MOVE: ... //如果可滑動 if (isCanDrag){ if (getDrawable() != null){ if (mVelocityTracker != null){ //將當前事件添加到檢測器中 mVelocityTracker.addMovement(event); } ... } ... case MotionEvent.ACTION_UP: //當手指抬起時,將mLastPointerCount置0,停止滑動 mLastPointerCount = 0; //如果當前圖片大小小於初始化大小 if (getScale() < mInitScale){ //自動放大至初始化大小 post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2)); } //如果當前圖片大小大於最大值 if (getScale() > mMaxScale){ //自動縮小至最大值 post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2)); } if (isCanDrag){//如果當前可以滑動 if (mVelocityTracker != null){ //將當前事件添加到檢測器中 mVelocityTracker.addMovement(event); //計算當前的速度 mVelocityTracker.computeCurrentVelocity(1000); //得到當前x方向速度 final float vX = mVelocityTracker.getXVelocity(); //得到當前y方向的速度 final float vY = mVelocityTracker.getYVelocity(); mFlingRunnable = new FlingRunnable(getContext()); //調用fling方法,傳入控件寬高和當前x和y軸方向的速度 //這裡得到的vX和vY和scroller需要的velocityX和velocityY的負號正好相反 //所以傳入一個負值 mFlingRunnable.fling(getWidth(),getHeight(),(int)-vX,(int)-vY); //執行run方法 post(mFlingRunnable); } } break; case MotionEvent.ACTION_CANCEL: //釋放速度檢測器 if (mVelocityTracker != null){ mVelocityTracker.recycle(); mVelocityTracker = null; } break; /** * 慣性滑動 */ private class FlingRunnable implements Runnable{ private Scroller mScroller; private int mCurrentX , mCurrentY; public FlingRunnable(Context context){ mScroller = new Scroller(context); } public void cancelFling(){ mScroller.forceFinished(true); } /** * 這個方法主要是從onTouch中或得到當前滑動的水平和豎直方向的速度 * 調用scroller.fling方法,這個方法內部能夠自動計算慣性滑動 * 的x和y的變化率,根據這個變化率我們就可以對圖片進行平移了 */ public void fling(int viewWidth , int viewHeight , int velocityX , int velocityY){ RectF rectF = getMatrixRectF(); if (rectF == null){ return; } //startX為當前圖片左邊界的x坐標 final int startX = Math.round(-rectF.left); final int minX , maxX , minY , maxY; //如果圖片寬度大於控件寬度 if (rectF.width() > viewWidth){ //這是一個滑動范圍[minX,maxX],詳情見下圖 minX = 0; maxX = Math.round(rectF.width() - viewWidth); }else{ //如果圖片寬度小於控件寬度,則不允許滑動 minX = maxX = startX; } //如果圖片高度大於控件高度,同理 final int startY = Math.round(-rectF.top); if (rectF.height() > viewHeight){ minY = 0; maxY = Math.round(rectF.height() - viewHeight); }else{ minY = maxY = startY; } mCurrentX = startX; mCurrentY = startY; if (startX != maxX || startY != maxY){ //調用fling方法,然後我們可以通過調用getCurX和getCurY來獲得當前的x和y坐標 //這個坐標的計算是模擬一個慣性滑動來計算出來的,我們根據這個x和y的變化可以模擬 //出圖片的慣性滑動 mScroller.fling(startX,startY,velocityX,velocityY,minX,maxX,minY,maxY); } }
關於startX,minX,maxX做一個解釋
我們從圖中可以看出,當前圖片可滑動的一個區間就是左邊多出來的那塊區間,所以minX和maxX代表的是區間的最小值和最大值,startX就是屏幕左邊界的坐標值,我們可以想象成是startX在區間[minX,maxX]的移動。Y軸方向同理。
現在我們看一下效果
以上就是本文的全部內容,希望對大家學習Android軟件編程有所幫助。
集成地圖SDK國內常用的地圖SDK就是百度和高德了,二者的用法大同小異,可按照官網上的開發指南一步步來。下面是我在集成地圖SDK時遇到的問題說明:1、點擊基本地圖功能選項
本章簡單講述下android實現自動撥號的功能,該功能利用了系統啟動的rild的服務來實現,因為rild的服務是殺不死的,所以利用這一點,可以使撥號失敗或網絡斷掉後自動重
本文將告訴你如何讓你的應用程序支持各種不同屏幕大小,主要通過以下幾種辦法:讓你的布局能充分的自適應屏幕 根據屏幕的配置來加載合適的UI布局 確保正確的布局應用在正確的設備
android手機怎麼修改hosts。相信很多android手機用戶都會碰到過Google賬號無法登陸的問題。而無法登入安卓市場的情況。我們電腦可以修改電腦