日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

PhotoView图片缩放控件源码浅析(一)

發(fā)布時(shí)間:2025/3/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PhotoView图片缩放控件源码浅析(一) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文參考自http://www.tuicool.com/articles/ea2ANjm

簡(jiǎn)介

PhotoView屬性:?
可以用于查看圖片,并對(duì)圖片進(jìn)行拖動(dòng)縮放,拖動(dòng)過(guò)程中不會(huì)出現(xiàn)邊緣空白;?
雙擊縮小放大,Fling移動(dòng),并支持上述過(guò)程的漸變;?
在放大情況下也支持viewpager等的拖動(dòng)切換;?
支持多擊事件檢測(cè),單機(jī),雙擊事件;?
支持各種回調(diào)給調(diào)用者;

具體的調(diào)用方法如下

//將imageview和PhotoViewAttacher 這個(gè)控制器關(guān)聯(lián)起來(lái) mAttacher = new PhotoViewAttacher(mImageView); 可以看出來(lái) 主要的工作都是在這個(gè)PhotoViewAttacher里做的。

public PhotoViewAttacher(ImageView imageView) {//使用軟引用,防止內(nèi)存泄露mImageView = new WeakReference<ImageView>(imageView); //這個(gè)draswingcache 我們做截屏的時(shí)候會(huì)經(jīng)常用到,只需要理解成我們可以通過(guò)getDrawingCache拿到view里的內(nèi)容(這個(gè)內(nèi)容被轉(zhuǎn)成了bitmap) imageView.setDrawingCacheEnabled(true); imageView.setOnTouchListener(this); ?//這里就是監(jiān)聽(tīng)imageview的 layout變化用的 imageview發(fā)生變化就會(huì)調(diào)用這個(gè)回調(diào)接口 ViewTreeObserver observer = imageView.getViewTreeObserver(); if (null != observer)observer.addOnGlobalLayoutListener(this); ?// 設(shè)置繪制時(shí)這個(gè)imageview 可以隨著matrix矩陣進(jìn)行變換 setImageViewScaleTypeMatrix(imageView); ? //這個(gè)是讓你在可視化界面能看到預(yù)覽效果的,大家自定義控件時(shí) 也可以使用這個(gè)技巧 if (imageView.isInEditMode()) {return; } //根據(jù)版本不同 取得需要的mScaleDragDetector 主要就是監(jiān)聽(tīng)pinch手勢(shì)的 mScaleDragDetector = VersionedGestureDetector.newInstance(imageView.getContext(), this); //這個(gè)dectecor 就是用來(lái)監(jiān)聽(tīng)雙擊和長(zhǎng)按事件的? mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() {// forward long click listener @Override public void onLongPress(MotionEvent e) {if (null != mLongClickListener) {mLongClickListener.onLongClick(getImageView()); }}}); //監(jiān)聽(tīng)雙擊事件 mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this)); // Finally, update the UI so that we're zoomable setZoomable(true); }

接下來(lái)我們來(lái)看看?addOnGlobalLayoutListener這個(gè)接聽(tīng)里做了什么,接口中有onGlobalLayout方法

public void onGlobalLayout() {ImageView imageView = getImageView(); if (null != imageView) {if (mZoomEnabled) {?//這個(gè)地方要注意imageview的 四個(gè)坐標(biāo)點(diǎn)是永遠(yuǎn)不會(huì)變化的。final int top = imageView.getTop(); final int right = imageView.getRight(); final int bottom = imageView.getBottom(); final int left = imageView.getLeft(); /** * We need to check whether the ImageView's bounds have changed. * This would be easier if we targeted API 11+ as we could just use * View.OnLayoutChangeListener. Instead we have to replicate the * work, keeping track of the ImageView's bounds and then checking * if the values change. */ if (top != mIvTop || bottom != mIvBottom || left != mIvLeft || right != mIvRight) {// Update our base matrix, as the bounds have changed updateBaseMatrix(imageView.getDrawable()); // Update values as something has changed mIvTop = top; mIvRight = right; mIvBottom = bottom; mIvLeft = left; }} else {updateBaseMatrix(imageView.getDrawable()); }} } 然后我們跟到updateBaseMatrix()里邊

private void updateBaseMatrix(Drawable d) {ImageView imageView = getImageView(); if (null == imageView || null == d) {return; }final float viewWidth = getImageViewWidth(imageView); final float viewHeight = getImageViewHeight(imageView); final int drawableWidth = d.getIntrinsicWidth();//這個(gè)是取原始圖片大小的 永遠(yuǎn)不會(huì)變化的 final int drawableHeight = d.getIntrinsicHeight(); mBaseMatrix.reset(); final float widthScale = viewWidth / drawableWidth; final float heightScale = viewHeight / drawableHeight; if (mScaleType == ScaleType.CENTER) {? ?//根據(jù)傳進(jìn)去的scaletype的值來(lái)確定 基礎(chǔ)的matrix大小mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F, (viewHeight - drawableHeight) / 2F); } else if (mScaleType == ScaleType.CENTER_CROP) {float scale = Math.max(widthScale, heightScale); mBaseMatrix.postScale(scale, scale); mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, (viewHeight - drawableHeight * scale) / 2F); } else if (mScaleType == ScaleType.CENTER_INSIDE) {float scale = Math.min(1.0f, Math.min(widthScale, heightScale)); mBaseMatrix.postScale(scale, scale); mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, (viewHeight - drawableHeight * scale) / 2F); } else {RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight); RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight); switch (mScaleType) {case FIT_CENTER:mBaseMatrix .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER); break; case FIT_START:mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START); break; case FIT_END:mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END); break; case FIT_XY:mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL); break; default:break; }}resetMatrix(); } 然后我們跟到resetMatrix()中看看

/** * Resets the Matrix back to FIT_CENTER, and then displays it.s */ private void resetMatrix() {mSuppMatrix.reset(); setImageViewMatrix(getDrawMatrix()); checkMatrixBounds(); } 進(jìn)入checkMatrixBounds()中看看

private boolean checkMatrixBounds() {//檢查當(dāng)前顯示范圍是否在邊界上 ?然後對(duì)圖片進(jìn)行平移(垂直或水平方向) 防止出現(xiàn)留白的現(xiàn)象final ImageView imageView = getImageView(); if (null == imageView) {return false; }final RectF rect = getDisplayRect(getDrawMatrix()); if (null == rect) {return false; }final float height = rect.height(), width = rect.width(); float deltaX = 0, deltaY = 0; final int viewHeight = getImageViewHeight(imageView); if (height <= viewHeight) {switch (mScaleType) {case FIT_START:deltaY = -rect.top; break; case FIT_END:deltaY = viewHeight - height - rect.top; break; default:deltaY = (viewHeight - height) / 2 - rect.top; break; }} else if (rect.top > 0) {deltaY = -rect.top; } else if (rect.bottom < viewHeight) {deltaY = viewHeight - rect.bottom; }final int viewWidth = getImageViewWidth(imageView); if (width <= viewWidth) {switch (mScaleType) {case FIT_START:deltaX = -rect.left; break; case FIT_END:deltaX = viewWidth - width - rect.left; break; default:deltaX = (viewWidth - width) / 2 - rect.left; break; }mScrollEdge = EDGE_BOTH; } else if (rect.left > 0) {mScrollEdge = EDGE_LEFT; deltaX = -rect.left; } else if (rect.right < viewWidth) {deltaX = viewWidth - rect.right; mScrollEdge = EDGE_RIGHT; } else {mScrollEdge = EDGE_NONE; }// Finally actually translate the matrix mSuppMatrix.postTranslate(deltaX, deltaY); return true; }

這個(gè)地方有的人可能會(huì)對(duì)最后那個(gè)檢測(cè)是否在邊界的那個(gè)函數(shù)不太明白,其實(shí)還是挺好理解的,對(duì)于容器imageview來(lái)說(shuō) 他的范圍是固定的。里面的drawable是不斷的變化的,

但是這個(gè)drawable 可以和 RectF來(lái)關(guān)聯(lián)起來(lái),這個(gè)rectF 就是描述出一個(gè)矩形,這個(gè)矩形就恰好是drawable的大小范圍。他有四個(gè)值 分別是top left right和bottom。

其中2個(gè)值表示矩形的左上面ed點(diǎn)的坐標(biāo) 另外2個(gè)表示右下角的坐標(biāo)。一個(gè)矩形由這2個(gè)點(diǎn)即可確定位置以及大小。我用下圖來(lái)表示:


所以那個(gè)函數(shù)你要想理解的話(huà) 就是自己去畫(huà)個(gè)圖。就能知道如何判斷是否到邊緣了!實(shí)際上就是drawbl---matrix---rectF的一個(gè)轉(zhuǎn)換。 接著回到PhotoViewAttacher的構(gòu)造方法,我們看到mScaleDragDetector

public final class VersionedGestureDetector {public static GestureDetector newInstance(Context context, OnGestureListener listener) {final int sdkVersion = Build.VERSION.SDK_INT; GestureDetector detector; if (sdkVersion < Build.VERSION_CODES.ECLAIR) {detector = new CupcakeGestureDetector(context); } else if (sdkVersion < Build.VERSION_CODES.FROYO) {detector = new EclairGestureDetector(context); } else {detector = new FroyoGestureDetector(context); }detector.setOnGestureListener(listener); return detector; } 我們發(fā)現(xiàn)這個(gè)地方是一個(gè)單例,實(shí)際上這邊代碼就是根據(jù)sdk的版本號(hào)不同 提供不一樣的功能,接著我們看FroyoGestureDetector()

@TargetApi(8) public class FroyoGestureDetector extends EclairGestureDetector {//用于檢測(cè)縮放的手勢(shì)protected final ScaleGestureDetector mDetector; public FroyoGestureDetector(Context context) {super(context); ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {@Override public boolean onScale(ScaleGestureDetector detector) {float scaleFactor = detector.getScaleFactor(); if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))return false; mListener.onScale(scaleFactor, detector.getFocusX(), detector.getFocusY()); return true; }//這個(gè)函數(shù)返回true,onScale函數(shù)才會(huì)被真正調(diào)用@Override public boolean onScaleBegin(ScaleGestureDetector detector) {return true; }@Override public void onScaleEnd(ScaleGestureDetector detector) {// NO-OP }}; mDetector = new ScaleGestureDetector(context, mScaleListener); }@Override public boolean isScaling() {return mDetector.isInProgress(); }@Override public boolean onTouchEvent(MotionEvent ev) {mDetector.onTouchEvent(ev); return super.onTouchEvent(ev); }}

總結(jié)

以上是生活随笔為你收集整理的PhotoView图片缩放控件源码浅析(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。