Android进阶篇-自定义图片伸缩控件具体实例
zoomimageview.java:
/**
* @author gongchaobin
*
* 自定义可伸缩的imageview
*/
public class zoomimageview extends view{
/** 画笔类 **/
private paint mpaint;
private runnable mrefresh = null;
/** 缩放手势监听类 **/
private scalegesturedetector mscaledetector;
/** 手势识别类 **/
private gesturedetector mgesturedetector;
/** 当前被渲染的bitmap **/
private bitmap mbitmap;
private int mthiswidth = -1, mthisheight = -1;
private runnable monlayoutrunnable = null;
private matrix mbasematrix = new matrix();
private matrix mdisplaymatrix = new matrix();
private matrix msuppmatrix = new matrix();
private matrix mmatrix = new matrix();
/** 最大的拉伸比例 **/
private float mmaxzoom;
private float[] mmatrixvalues = new float[9];
private runnable mfling = null;
private double mlastdraw = 0;
static final int spaintdelay = 250;
public zoomimageview(context context, attributeset attrs, int defstyle) {
super(context, attrs, defstyle);
// todo auto-generated constructor stub
init();
}
public zoomimageview(context context, attributeset attrs) {
super(context, attrs);
// todo auto-generated constructor stub
init();
}
public zoomimageview(context context) {
super(context);
// todo auto-generated constructor stub
init();
}
private void init() {
mpaint = new paint();
// 图像抖动处理
mpaint.setdither(true);
// 过滤优化操作 加快显示
mpaint.setfilterbitmap(true);
// 去掉锯齿
mpaint.setantialias(true);
/** 刷新线程 **/
mrefresh = new runnable() {
@override
public void run() {
postinvalidate();
}
};
mscaledetector = new scalegesturedetector(getcontext(),new scalelistener());
mgesturedetector = new gesturedetector(getcontext(),new mygesturelistener());
// 判断是否是新的api 开启硬件加速
if(build.version.sdk_int >= build.version_codes.honeycomb) {
setlayertype(view.layer_type_hardware, null);
}
}
public bitmap getimagebitmap() {
return mbitmap;
}
/** 回收bitmap **/
public void clear() {
if(mbitmap != null && !mbitmap.isrecycled()) {
mbitmap.recycle();
mbitmap = null;
}
}
@override
protected void onlayout(boolean changed, int left, int top, int right,
int bottom) {
// todo auto-generated method stub
super.onlayout(changed, left, top, right, bottom);
mthiswidth = right - left;
mthisheight = bottom - top;
runnable r = monlayoutrunnable;
if (r != null) {
monlayoutrunnable = null;
r.run();
}
if (mbitmap != null) {
setbasematrix(mbitmap, mbasematrix);
setimagematrix(getimageviewmatrix());
}
}
private void setbasematrix(bitmap bitmap, matrix matrix) {
float viewwidth = getwidth();
float viewheight = getheight();
matrix.reset();
float widthscale = math.min(viewwidth / (float)bitmap.getwidth(), 1.0f);
float heightscale = math.min(viewheight / (float)bitmap.getheight(), 1.0f);
float scale;
if (widthscale > heightscale) {
scale = heightscale;
} else {
scale = widthscale;
}
/** 算取比例 进行平移 **/
matrix.setscale(scale, scale);
matrix.posttranslate(
(viewwidth - ((float)bitmap.getwidth() * scale))/2f,
(viewheight - ((float)bitmap.getheight() * scale))/2f);
}
protected matrix getimageviewmatrix() {
mdisplaymatrix.set(mbasematrix);
mdisplaymatrix.postconcat(msuppmatrix);
return mdisplaymatrix;
}
public void setimagematrix(matrix m){
/** matrix是否为空并是否定义 **/
if (m != null && m.isidentity()) {
m = null;
}
if (m == null && !this.mmatrix.isidentity() || m != null && !this.mmatrix.equals(m)) {
this.mmatrix.set(m);
invalidate();
}
}
static private void translatepoint(matrix matrix, float [] xy) {
matrix.mappoints(xy);
}
/**
* 设置bitmap
*
* @param bitmap
*/
public void setimagebitmap(final bitmap bitmap) {
final int viewwidth = getwidth();
// 开启硬件加速
if( build.version.sdk_int >= build.version_codes.honeycomb && bitmap!=null && bitmap.getheight()>1800 )
setlayertype(view.layer_type_software, null);
if (viewwidth <= 0) {
monlayoutrunnable = new runnable() {
public void run() {
setimagebitmap(bitmap);
}
};
return;
}
if (bitmap != null) {
setbasematrix(bitmap, mbasematrix);
this.mbitmap = bitmap;
} else {
mbasematrix.reset();
this.mbitmap = bitmap;
}
msuppmatrix.reset();
setimagematrix(getimageviewmatrix());
mmaxzoom = maxzoom();
zoomto(zoomdefault());
}
public void zoomto(float scale) {
float width = getwidth();
float height = getheight();
zoomto(scale, width/2f, height/2f);
}
protected void zoomto(float scale, float centerx, float centery) {
if (scale > mmaxzoom) {
scale = mmaxzoom;
}
float oldscale = getscale();
float deltascale = scale / oldscale;
/** 根据某个中心点按照比例缩放 **/
msuppmatrix.postscale(deltascale, deltascale, centerx, centery);
setimagematrix(getimageviewmatrix());
center(true, true, false);
}
/**
* 计算中心位置
*
* @param vertical
* @param horizontal
* @param animate
*/
protected void center(boolean vertical, boolean horizontal, boolean animate) {
if (mbitmap == null)
return;
matrix m = getimageviewmatrix();
float [] topleft = new float[] { 0, 0 };
float [] botright = new float[] { mbitmap.getwidth(), mbitmap.getheight() };
translatepoint(m, topleft);
translatepoint(m, botright);
float height = botright[1] - topleft[1];
float width = botright[0] - topleft[0];
float deltax = 0, deltay = 0;
if (vertical) {
int viewheight = getheight();
if (height < viewheight) {
deltay = (viewheight - height)/2 - topleft[1];
} else if (topleft[1] > 0) {
deltay = -topleft[1];
} else if (botright[1] < viewheight) {
deltay = getheight() - botright[1];
}
}
if (horizontal) {
int viewwidth = getwidth();
if (width < viewwidth) {
deltax = (viewwidth - width)/2 - topleft[0];
} else if (topleft[0] > 0) {
deltax = -topleft[0];
} else if (botright[0] < viewwidth) {
deltax = viewwidth - botright[0];
}
}
posttranslate(deltax, deltay);
if (animate) {
animation a = new translateanimation(-deltax, 0, -deltay, 0);
a.setstarttime(systemclock.elapsedrealtime());
a.setduration(250);
setanimation(a);
}
setimagematrix(getimageviewmatrix());
}
protected void posttranslate(float dx, float dy) {
msuppmatrix.posttranslate(dx, dy);
}
public float getscale() {
return getscale(msuppmatrix);
}
protected float getscale(matrix matrix) {
if(mbitmap!=null)
return getvalue(matrix, matrix.mscale_x);
else
return 1f;
}
protected float getvalue(matrix matrix, int whichvalue) {
matrix.getvalues(mmatrixvalues);
return mmatrixvalues[whichvalue];
}
/**
* 计算最大的拉伸比例
*
* @return
*/
protected float maxzoom() {
if (mbitmap == null)
return 1f;
float fw = (float) mbitmap.getwidth() / (float)mthiswidth;
float fh = (float) mbitmap.getheight() / (float)mthisheight;
float max = math.max(fw, fh) * 16;
return max;
}
/**
* 原始显示比例
*
* @return
*/
public float zoomdefault() {
if (mbitmap == null)
return 1f;
float fw = (float)mthiswidth/(float)mbitmap.getwidth();
float fh = (float)mthisheight/(float)mbitmap.getheight();
return math.max(math.min(fw, fh),1);
}
protected void zoomto(final float scale, final float centerx, final float centery, final float durationms) {
final float incrementperms = (scale - getscale()) / durationms;
final float oldscale = getscale();
final long starttime = system.currenttimemillis();
post(new runnable() {
public void run() {
long now = system.currenttimemillis();
float currentms = math.min(durationms, (float)(now - starttime));
float target = oldscale + (incrementperms * currentms);
zoomto(target, centerx, centery);
if (currentms < durationms) {
post(this);
}
}
});
}
protected void scrollby( float distancex, float distancey, final float durationms ){
final float dx = distancex;
final float dy = distancey;
final long starttime = system.currenttimemillis();
mfling = new runnable() {
float old_x = 0;
float old_y = 0;
public void run()
{
long now = system.currenttimemillis();
float currentms = math.min( durationms, now - starttime );
float x = easeout( currentms, 0, dx, durationms );
float y = easeout( currentms, 0, dy, durationms );
posttranslate( ( x - old_x ), ( y - old_y ) );
center(true, true, false);
old_x = x;
old_y = y;
if ( currentms < durationms ) {
post( this );
}
}
};
post( mfling );
}
private float easeout( float time, float start, float end, float duration){
return end * ( ( time = time / duration - 1 ) * time * time + 1 ) + start;
}
@override
protected void ondraw(canvas canvas) {
// todo auto-generated method stub
if(mbitmap!=null && !mbitmap.isrecycled() ){
if( build.version.sdk_int >= build.version_codes.honeycomb && getlayertype() == view.layer_type_hardware ){
canvas.drawbitmap(mbitmap, mmatrix, null);
}else{
if( (system.currenttimemillis()-mlastdraw) > spaintdelay ){
canvas.drawbitmap(mbitmap, mmatrix, mpaint);
mlastdraw = system.currenttimemillis();
}
else{
canvas.drawbitmap(mbitmap, mmatrix, null);
removecallbacks(mrefresh);
postdelayed(mrefresh, spaintdelay);
}
}
}
}
/**
* @author administrator
*
* 手势缩放监听
*/
class scalelistener extends scalegesturedetector.simpleonscalegesturelistener {
@override
public boolean onscale(scalegesturedetector detector) {
// todo auto-generated method stub
log.i("zoomimageview", "onscale");
if(detector!=null && detector.isinprogress()){
try{
float targetscale = getscale() * detector.getscalefactor();
targetscale = math.min(maxzoom(), math.max(targetscale, 1.0f) );
zoomto(targetscale, detector.getfocusx(), detector.getfocusy() );
invalidate();
return true;
}catch(illegalargumentexception e){
e.printstacktrace();
}
}
return false;
}
};
/**
* @author administrator
*
* 手势识别监听
*/
class mygesturelistener extends gesturedetector.simpleongesturelistener {
@override
public boolean onscroll(motionevent e1, motionevent e2,
float distancex, float distancey) {
// todo auto-generated method stub
log.i("zoomimageview", "onscroll");
if((e1 != null && e1.getpointercount() > 1) || (e2 != null && e2.getpointercount() > 1)
|| (mscaledetector != null && mscaledetector.isinprogress())){
return false;
}
if(getscale() > zoomdefault() ) {
removecallbacks(mfling);
posttranslate(-distancex, -distancey);
center(true, true, false);
}
return true;
}
@override
public boolean onfling(motionevent e1, motionevent e2, float velocityx,
float velocityy) {
log.i("zoomimageview", "onfling");
// todo auto-generated method stub
if ((e1!=null && e1.getpointercount() > 1) || ( e2!=null && e2.getpointercount() > 1))
return false;
if (mscaledetector.isinprogress())
return false;
try{
float diffx = e2.getx() - e1.getx();
float diffy = e2.gety() - e1.gety();
if ( math.abs( velocityx ) > 800 || math.abs( velocityy ) > 800 ) {
scrollby( diffx / 2, diffy / 2, 300 );
invalidate();
}
}catch(nullpointerexception e){
e.printstacktrace();
}
return super.onfling( e1, e2, velocityx, velocityy );
}
@override
public boolean ondoubletap(motionevent e) {
// todo auto-generated method stub
log.i("zoomimageview", "ondoubletap");
if ( getscale() > zoomdefault() ){
zoomto(zoomdefault());
}
else
zoomto(zoomdefault()*3, e.getx(), e.gety(),200);
return true;
}
@override
public boolean onsingletapconfirmed(motionevent e) {
// todo auto-generated method stub
log.i("zoomimageview", "onsingletapconfirmed");
// 设置点击事件
if(mimagetouchedlistener != null) {
mimagetouchedlistener.onimagetouched();
return false;
}
return super.onsingletapconfirmed(e);
}
}
@override
public boolean ontouchevent(motionevent event) {
// todo auto-generated method stub
if(mbitmap != null) {
mscaledetector.ontouchevent(event);
if(!mscaledetector.isinprogress()) {
mgesturedetector.ontouchevent(event);
}
}
return true;
};
/**
*
* @author administrator
*
* 点击接口
*/
private onimagetouchedlistener mimagetouchedlistener;
public interface onimagetouchedlistener {
void onimagetouched();
}
public void setonimagetouchedlistener(onimagetouchedlistener listener ){
this.mimagetouchedlistener = listener;
}
xml布局:
<com.example.pay.zoomimageview
android:scaletype="matrix"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/zommimageview"
/>
activity调用方法:
mzoomimageview = (zoomimageview) findviewbyid(r.id.zommimageview);
mzoomimageview.setimagebitmap(bitmapfactory.decoderesource(getresources(), r.drawable.ic_launcher));
mzoomimageview.setonimagetouchedlistener(new onimagetouchedlistener() {
@override
public void onimagetouched() {
// todo auto-generated method stub
toast.maketext(mainactivity.this, "11111", toast.length_long).show();
}
});