Android中自定義點選縮放ImageView
我們平時在開發中,有時候需要在點選控制元件後有一個縮放效果,當然,有些朋友會講這很簡單啊,在資原始檔中給兩個不同的背景,用selecter作為切換,可以達到需要的效果,或者用動畫,這也很好的實現需求。但是,今天我們要做的這個點選縮放控制元件不單單是縮放那麼簡單,請看效果圖!
首先,要實現以上效果,我們要自定義View繼承Imageview,通過Imageview的Matrix來設定圖片的縮放,旋轉,平移等等。開始初始化引數:
/**
* 是否執行中
*/
private boolean onAnimation = true;
/**
* 旋轉角度
*/
private int rotateDegree = 10;
private boolean isFirst = true;
/**
* 比例
*/
private float minScale = 0.95f;
/**
* 控制元件寬度
*/
private int vWidth;
/**
* 控制元件高度
*/
private int vHeight;
private boolean isFinish = true, isActionMove = false, isScale = false ;
/**
* 相機api
*/
private Camera camera;
boolean XbigY = false;
float RolateX = 0;
float RolateY = 0;
這裡要說明一下,Camera類在這裡,主要用於,圖片的處理。好 了,接下來初始化構造方法,同時Camera也創建出來。
public MyImageView(Context context) {
super(context);
camera = new Camera();
}
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
camera = new Camera();
}
這樣我們就可以開始在ondraw()裡面操作各種資料了,下面重寫ondraw()並在裡面初始化,這裡有一個判斷是否是第一次展示,用了isFirst來做標誌位,如果第一次就初始化引數,如果不是就 不再初始化引數了。當然這個直接寫在構造方法裡面也可以。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isFirst) {
isFirst = false;
//初始化
init();
}
//為畫布設定抗鋸齒
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG));
}
/**
* 初始化
*/
public void init() {
vWidth = getWidth() - getPaddingLeft() - getPaddingRight();
vHeight = getHeight() - getPaddingTop() - getPaddingBottom();
Drawable drawable = getDrawable();
BitmapDrawable bd = (BitmapDrawable) drawable;
/**畫筆設定抗鋸齒
*/
bd.setAntiAlias(true);
}
上面的init()方法中getDrawable()方法作用就是得到Imageview的顯示圖片。這下我們就應該開始處理點選事件了。重寫OnTuchEvent()在裡面分別處理Down,Move,Up事件。
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (!onAnimation)
return true;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
float X = event.getX();
float Y = event.getY();
RolateX = vWidth / 2 - X;
RolateY = vHeight / 2 - Y;
XbigY = Math.abs(RolateX) > Math.abs(RolateY) ? true : false;
//這裡判斷縮放區域
isScale = X > vWidth / 3 && X < vWidth * 2 / 3 && Y > vHeight / 3 && Y < vHeight * 2 / 3;
isScale = true;
isActionMove = false;
if (isScale) {
handler.sendEmptyMessage(1);
} else {
rolateHandler.sendEmptyMessage(1);
}
break;
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
if (x > vWidth || y > vHeight || x < 0 || y < 0) {
isActionMove = true;
} else {
isActionMove = false;
}
break;
case MotionEvent.ACTION_UP:
if (isScale) {
handler.sendEmptyMessage(6);
} else {
rolateHandler.sendEmptyMessage(6);
}
break;
}
return true;
}
當檢測到點選區域位於View的位置後,就通過handler傳送訊息,在handler裡面處理,縮放操作。在這裡我們分為兩個效果,1.點選View邊緣,所在邊緣縮放效果。2.點選View中心四周縮放。這樣rolateHandler用於邊緣縮放,handler用於四周縮放。
private Handler handler = new Handler() {
private Matrix matrix = new Matrix();
private float s;
int count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
matrix.set(getImageMatrix());
switch (msg.what) {
case 1:
if (!isFinish) {
return;
} else {
isFinish = false;
count = 0;
s = (float) Math.sqrt(Math.sqrt(minScale));
//縮放操作
BeginScale(matrix, s);
handler.sendEmptyMessage(2);
}
break;
case 2:
BeginScale(matrix, s);
if (count < 4) {
handler.sendEmptyMessage(2);
} else {
isFinish = true;
if (!isActionMove && onclick != null) {
onclick.onClick();
}
}
count++;
break;
case 6:
if (!isFinish) {
handler.sendEmptyMessage(6);
} else {
isFinish = false;
count = 0;
s = (float) Math.sqrt(Math.sqrt(1.0f / minScale));
//縮放操作
BeginScale(matrix, s);
handler.sendEmptyMessage(2);
}
break;
}
}
};
用於邊緣縮放的handler類
private Handler rolateHandler = new Handler() {
private Matrix matrix = new Matrix();
private float count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
matrix.set(getImageMatrix());
switch (msg.what) {
case 1:
count = 0;
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
rolateHandler.sendEmptyMessage(2);
break;
case 2:
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
if (count < getDegree()) {
rolateHandler.sendEmptyMessage(2);
} else {
isFinish = true;
}
count++;
count++;
break;
case 3:
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
if (count > 0) {
rolateHandler.sendEmptyMessage(3);
} else {
isFinish = true;
// if (!isActionMove && onclick != null) {
// onclick.onClick();
// }
}
count--;
count--;
break;
case 6:
count = getDegree();
//縮放操作
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
rolateHandler.sendEmptyMessage(3);
break;
}
Log.e("renk", "rolate>" + msg.what);
}
};
上面都呼叫了縮放操作方法,下面看縮放操作方法的具體實現程式碼
private synchronized void BeginRolate(Matrix matrix, float rolateX,
float rolateY) {
// Bitmap bm = getImageBitmap();
int scaleX = (int) (vWidth * 0.5f);
int scaleY = (int) (vHeight * 0.5f);
//儲存狀態,與restore()成對出現
camera.save();
camera.rotateX(RolateY > 0 ? rolateY : -rolateY);
//圍繞Y軸作的旋轉
camera.rotateY(RolateX < 0 ? rolateX : -rolateX);
//將camera產生的效果加於引數matrix上
camera.getMatrix(matrix);
//在使用camera設定動畫之前,進行了camera.save(),將狀態恢復到儲存的狀態。
camera.restore();
// 控制中心點
if (RolateX > 0 && rolateX != 0) {
matrix.preTranslate(-vWidth, -scaleY);
matrix.postTranslate(vWidth, scaleY);
} else if (RolateY > 0 && rolateY != 0) {
matrix.preTranslate(-scaleX, -vHeight);
matrix.postTranslate(scaleX, vHeight);
} else if (RolateX < 0 && rolateX != 0) {
matrix.preTranslate(-0, -scaleY);
matrix.postTranslate(0, scaleY);
} else if (RolateY < 0 && rolateY != 0) {
matrix.preTranslate(-scaleX, -0);
matrix.postTranslate(scaleX, 0);
}
setImageMatrix(matrix);
Log.e("renk", "rolate>BeginRolate()");
}
private synchronized void BeginScale(Matrix matrix, float scale) {
int scaleX = (int) (vWidth * 0.5f);
int scaleY = (int) (vHeight * 0.5f);
//縮放圖片動作
//設定Matrix以(px,py)為軸心進行縮放,sx、sy為X、Y方向上的縮放比例。
matrix.postScale(scale, scale, scaleX, scaleY);
//設定圖片矩陣
setImageMatrix(matrix);
}
Matrix提供了一些方法來控制圖片變換:
setTranslate(float dx, float dy)
:控制Matrix進行位移。
setSkew(float kx, float ky)
:控制Matrix進行傾斜,kx、ky為X、Y方向上的比例。
setSkew(float kx, float ky, float px, float py)
:控制Matrix以px、py為軸心進行傾斜,kx、ky為X、Y方向上的傾斜比例。
setRotate(float degrees)
:控制Matrix進行depress角度的旋轉,軸心為(0,0)。
setRotate(float degrees, float px, float py)
:控制Matrix進行depress角度的旋轉, 軸心為(px, py) 。
setScale(float sx, float sy)
:設定Matrix進行縮放,sx、sy為X、Y方向上的縮放比例。
setScale(float sx, float sy, float px, float py) :
設定Matrix以(px, py)為軸心進行縮放,sx、sy為X、Y方向上的縮放比例。
注意:以上的set方法,均有對應的post和pre方法,Matrix呼叫一系列set,pre,post方法時,可視為將這些方法插入到一個佇列.當然,按照佇列中從頭至尾的順序呼叫執行. 其中pre表示在隊頭插入一個方法,post表示在隊尾插入一個方法.而set表示把當前佇列清空,並且總是位於佇列的最中間位置.當執行了一次set後:pre方法總是插入到set前部的佇列的最前面, post方法總是插入到set後部的佇列的最後面。
現在基本上功能就完成了,但是,如果在使用的時候,給其設定setonclicklistener()是不能成功的,因為我們在自定義中重寫了OntuchEvent(),所以要設定監聽就要自己寫個介面然後設定,先定義一個介面。並初始化。
/**
* 點選介面
*/
OnViewClick onclick = null;
public interface OnViewClick {
public void onClick();
}
//在rolateHandler裡面新增如下方法。
isFinish = true;
if (!isActionMove && onclick != null) {
onclick.onClick();
}
還要注意一下,在佈局檔案中設定imageview的scaleType為matrix中,Android中使用Matrix對影象進行縮放、旋轉、平移、斜切等變換的。關於更詳細的Matrix的講解請看這裡Matrix詳解。
在最後貼一下全部程式碼。共享出來。謝謝大家
public class MyImageView extends ImageView {
/**
* 是否執行中
*/
private boolean onAnimation = true;
/**
* 旋轉角度
*/
private int rotateDegree = 10;
private boolean isFirst = true;
/**
* 比例
*/
private float minScale = 0.95f;
/**
* 控制元件寬度
*/
private int vWidth;
/**
* 控制元件高度
*/
private int vHeight;
private boolean isFinish = true, isActionMove = false, isScale = false;
/**
* 相機api
*/
private Camera camera;
boolean XbigY = false;
float RolateX = 0;
float RolateY = 0;
/**
* 點選介面
*/
OnViewClick onclick = null;
public MyImageView(Context context) {
super(context);
camera = new Camera();
}
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
camera = new Camera();
}
public void SetAnimationOnOff(boolean oo) {
onAnimation = oo;
}
public void setOnClickIntent(OnViewClick onclick) {
this.onclick = onclick;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isFirst) {
isFirst = false;
init();
}
//為畫布設定抗鋸齒
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG));
}
/**
* 初始化
*/
public void init() {
vWidth = getWidth() - getPaddingLeft() - getPaddingRight();
vHeight = getHeight() - getPaddingTop() - getPaddingBottom();
Drawable drawable = getDrawable();
BitmapDrawable bd = (BitmapDrawable) drawable;
/**畫筆設定抗鋸齒
*/
bd.setAntiAlias(true);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (!onAnimation)
return true;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
float X = event.getX();
float Y = event.getY();
RolateX = vWidth / 2 - X;
RolateY = vHeight / 2 - Y;
XbigY = Math.abs(RolateX) > Math.abs(RolateY) ? true : false;
isScale = X > vWidth / 3 && X < vWidth * 2 / 3 && Y > vHeight / 3 && Y < vHeight * 2 / 3;
isScale = true;
isActionMove = false;
if (isScale) {
handler.sendEmptyMessage(1);
} else {
rolateHandler.sendEmptyMessage(1);
}
break;
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
if (x > vWidth || y > vHeight || x < 0 || y < 0) {
isActionMove = true;
} else {
isActionMove = false;
}
break;
case MotionEvent.ACTION_UP:
if (isScale) {
handler.sendEmptyMessage(6);
} else {
rolateHandler.sendEmptyMessage(6);
}
break;
}
return true;
}
public interface OnViewClick {
public void onClick();
}
private Handler rolateHandler = new Handler() {
private Matrix matrix = new Matrix();
private float count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
matrix.set(getImageMatrix());
switch (msg.what) {
case 1:
count = 0;
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
rolateHandler.sendEmptyMessage(2);
break;
case 2:
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
if (count < getDegree()) {
rolateHandler.sendEmptyMessage(2);
} else {
isFinish = true;
}
count++;
count++;
break;
case 3:
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
if (count > 0) {
rolateHandler.sendEmptyMessage(3);
} else {
isFinish = true;
if (!isActionMove && onclick != null) {
onclick.onClick();
}
}
count--;
count--;
break;
case 6:
count = getDegree();
BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
rolateHandler.sendEmptyMessage(3);
break;
}
Log.e("renk", "rolate>" + msg.what);
}
};
private synchronized void BeginRolate(Matrix matrix, float rolateX,
float rolateY) {
// Bitmap bm = getImageBitmap();
int scaleX = (int) (vWidth * 0.5f);
int scaleY = (int) (vHeight * 0.5f);
//儲存狀態,與restore()成對出現
camera.save();
camera.rotateX(RolateY > 0 ? rolateY : -rolateY);
//圍繞Y軸作的旋轉
camera.rotateY(RolateX < 0 ? rolateX : -rolateX);
//將camera產生的效果加於引數matrix上
camera.getMatrix(matrix);
//在使用camera設定動畫之前,進行了camera.save(),將狀態恢復到儲存的狀態。
camera.restore();
// 控制中心點
if (RolateX > 0 && rolateX != 0) {
matrix.preTranslate(-vWidth, -scaleY);
matrix.postTranslate(vWidth, scaleY);
} else if (RolateY > 0 && rolateY != 0) {
matrix.preTranslate(-scaleX, -vHeight);
matrix.postTranslate(scaleX, vHeight);
} else if (RolateX < 0 && rolateX != 0) {
matrix.preTranslate(-0, -scaleY);
matrix.postTranslate(0, scaleY);
} else if (RolateY < 0 && rolateY != 0) {
matrix.preTranslate(-scaleX, -0);
matrix.postTranslate(scaleX, 0);
}
setImageMatrix(matrix);
Log.e("renk", "rolate>BeginRolate()");
}
private Handler handler = new Handler() {
private Matrix matrix = new Matrix();
private float s;
int count = 0;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
matrix.set(getImageMatrix());
switch (msg.what) {
case 1:
if (!isFinish) {
return;
} else {
isFinish = false;
count = 0;
s = (float) Math.sqrt(Math.sqrt(minScale));
BeginScale(matrix, s);
handler.sendEmptyMessage(2);
}
break;
case 2:
BeginScale(matrix, s);
if (count < 4) {
handler.sendEmptyMessage(2);
} else {
isFinish = true;
if (!isActionMove && onclick != null) {
onclick.onClick();
}
}
count++;
break;
case 6:
if (!isFinish) {
handler.sendEmptyMessage(6);
} else {
isFinish = false;
count = 0;
s = (float) Math.sqrt(Math.sqrt(1.0f / minScale));
BeginScale(matrix, s);
handler.sendEmptyMessage(2);
}
break;
}
}
};
private synchronized void BeginScale(Matrix matrix, float scale) {
int scaleX = (int) (vWidth * 0.5f);
int scaleY = (int) (vHeight * 0.5f);
//縮放圖片動作
//設定Matrix以(px,py)為軸心進行縮放,sx、sy為X、Y方向上的縮放比例。
matrix.postScale(scale, scale, scaleX, scaleY);
//設定圖片矩陣
setImageMatrix(matrix);
}
public int getDegree() {
return rotateDegree;
}
public void setDegree(int degree) {
rotateDegree = degree;
}
public float getScale() {
return minScale;
}
public void setScale(float scale) {
minScale = scale;
}
}