我們?cè)陧?xiàng)目里經(jīng)常會(huì)用到需要使用ImageView的地方云石,這時(shí)候有些產(chǎn)品經(jīng)理可能會(huì)提出需要讓這個(gè)ImageView玩出花樣旋起來盔腔,原來的那個(gè)只能顯示圖片的就不好使了涤妒,需要考慮去實(shí)現(xiàn)一個(gè)功能更強(qiáng)大的ImageView了格二。
一歪沃、 準(zhǔn)備工作
由于我們要實(shí)現(xiàn)的是一個(gè)可自由變換的ImageView,那么首先要想到的是如何通過手勢(shì)的方式對(duì)特定的操作進(jìn)行區(qū)分,這里使用了Github中封裝手勢(shì)操作的組件庫Almeros/android-gesture-detectors达吞,其對(duì)移動(dòng)张弛、旋轉(zhuǎn)和縮放都進(jìn)行了很好的封裝,可以幫助我們減少很多麻煩酪劫。
repositories {
//添加jitpack倉庫
maven { url 'https://jitpack.io' }
}
implementation 'com.github.Almeros:android-gesture-detectors:v1.0.1'
二吞鸭、實(shí)現(xiàn)旋轉(zhuǎn)
實(shí)現(xiàn)思路
- 聲明RotateGestureDetector對(duì)象,初始化并設(shè)置監(jiān)聽器等覆糟;
- 為ImageView設(shè)置setOnTouchListener事件刻剥,在其回調(diào)中讓RotateGestureDetector實(shí)例對(duì)象接手觸摸事件;
- 在RotateGestureDetector的監(jiān)聽器回調(diào)中滩字,獲取對(duì)應(yīng)的變化值造虏,并設(shè)置給ImageView。
代碼實(shí)例
public class DemoRotateImageView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener{
//操作對(duì)象
private Matrix mMatrix;
//旋轉(zhuǎn)手勢(shì)監(jiān)聽器
private RotateGestureDetector mRotateGestureDetector ;
//當(dāng)前焦點(diǎn)
private float mFocusX, mFocusY;
public DemoRotateImageView(Context context) {
this(context, null);
}
public DemoRotateImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DemoRotateImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//記住麦箍,一定要把ScaleType設(shè)置成ScaleType.MATRIX漓藕,否則無法縮放
setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
//手勢(shì)旋轉(zhuǎn)
mRotateGestureDetector = new RotateGestureDetector(context, new RotateGestureDetector.OnRotateGestureListener() {
@Override
public boolean onRotate(RotateGestureDetector detector) {
float rotationDegrees = -detector.getRotationDegreesDelta();
mMatrix.postRotate(rotationDegrees,mFocusX,mFocusY);
setImageMatrix(mMatrix);
return true;
}
@Override
public boolean onRotateBegin(RotateGestureDetector detector) {
return true;
}
@Override
public void onRotateEnd(RotateGestureDetector detector) {
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mRotateGestureDetector.onTouchEvent(event);
return true ;
}
public boolean mIsOneLoad = true;
@Override
public void onGlobalLayout() {
if (mIsOneLoad) {
//獲取圖片,如果沒有圖片則直接退出
Drawable d = getDrawable();
if (d == null){
return;
}
//獲取圖片的寬和高
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
//將其中圖片中心點(diǎn)作為焦點(diǎn)
mFocusX = dw * 1.0f / 2 ;
mFocusY = dh * 1.0f / 2 ;
mIsOneLoad = false;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//修改窗口事件中對(duì)視圖變化進(jìn)行監(jiān)聽
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
三、實(shí)現(xiàn)縮放
實(shí)現(xiàn)思路
- 聲明ScaleGestureDetector對(duì)象挟裂,初始化并設(shè)置監(jiān)聽器等享钞;
- 為ImageView設(shè)置setOnTouchListener事件,在其回調(diào)中讓ScaleGestureDetector實(shí)例對(duì)象接手觸摸事件诀蓉;
- 在onGlobalLayout中設(shè)置初始栗竖、最大、最小縮放值渠啤;
- 在ScaleGestureDetector的監(jiān)聽器回調(diào)中狐肢,獲取焦點(diǎn)并對(duì)縮放進(jìn)行處理后設(shè)置ImageView;
- 當(dāng)縮放值大于最大和最小縮放值后不允許其繼續(xù)縮放沥曹。
代碼實(shí)例
public class DemoScaleImageView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener{
//操作對(duì)象
private Matrix mMatrix;
//縮放手勢(shì)監(jiān)聽器
private ScaleGestureDetector mScaleGestureDetector ;
//圖片寬高
private float mFocusX, mFocusY;
//初始化的比例,也就是最小比例
private float mInitScale;
//圖片最大比例
private float mMaxScale;
//圖片最小比例
private float mMinScale ;
public DemoScaleImageView(Context context) {
this(context, null);
}
public DemoScaleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DemoScaleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//記住份名,一定要把ScaleType設(shè)置成ScaleType.MATRIX碟联,否則無法縮放
setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
//手勢(shì)旋轉(zhuǎn)
mScaleGestureDetector = new ScaleGestureDetector(context,new ScaleGestureDetector.SimpleOnScaleGestureListener(){
@Override
public boolean onScale(ScaleGestureDetector detector) {
//開始縮放
float scaleFactor = detector.getScaleFactor();
Log.i("縮放操作值",scaleFactor+"");
float scale = getCurrentScale();
//縮放操作需要在mMinScale和mMaxScale中進(jìn)行 范圍外的話就攔截
if(scale < mMaxScale && scale > mMinScale){
mMatrix.postScale(scaleFactor, scaleFactor, mFocusX, mFocusY);
setImageMatrix(mMatrix);
return true;
}else{
return false;
}
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
//開始縮放并獲取焦點(diǎn)
mFocusX = detector.getFocusX();
mFocusY = detector.getFocusY();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
super.onScaleEnd(detector);
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleGestureDetector.onTouchEvent(event);
return true ;
}
public boolean mIsOneLoad = true;
@Override
public void onGlobalLayout() {
if (mIsOneLoad) {
//獲取圖片,如果沒有圖片則直接退出
Drawable d = getDrawable();
if (d == null){
return;
}
//獲取圖片的寬和高
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
//使其圖片中點(diǎn)作為焦點(diǎn)
mFocusX = dw * 1.0f / 2 ;
mFocusY = dh * 1.0f / 2 ;
//初始化縮放比例
mInitScale = getCurrentScale();
//手勢(shì)放大時(shí)最大比例
mMaxScale = mInitScale * 2;
//手勢(shì)縮小時(shí)的最小比例
mMinScale = mInitScale * 0.8f ;
mIsOneLoad = false;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
/**
* 獲取當(dāng)前圖片的縮放值
* @return 縮放值
*/
private float getCurrentScale() {
float[] values = new float[9];
mMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
}