android 伸缩控件,Android进阶篇-自定义图片伸缩控件具体实例

/**

* @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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中,可以使用 View 的布局参数来实现视图的伸缩效果。常用的布局参数有 LinearLayout.LayoutParams 和 RelativeLayout.LayoutParams。下面介绍两种常见的视图伸缩方式。 1. LinearLayout 中的权重(weight) LinearLayout 中的权重是一种常见的视图伸缩方式。可以通过设置子视图的权重来控制它们在 LinearLayout 中的占比。具体使用方法如下: 1)设置 LinearLayout 的方向为水平或垂直方向。 2)设置子视图的宽度或高度为 0dp。 3)设置子视图的权重(weight),权重越大,占比越大。 示例代码: ``` <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="View 1"/> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:text="View 2"/> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="View 3"/> </LinearLayout> ``` 上述代码中,三个 TextView 的占比分别为 1:2:1,即中间的 TextView 占比最大。 2. RelativeLayout 中的规则属性 RelativeLayout 中常用的视图伸缩方式是通过设置视图之间的相对位置关系来实现。可以使用 RelativeLayout.LayoutParams 中的规则属性来设置视图之间的相对位置关系。具体使用方法如下: 1)设置 RelativeLayout 的布局参数为 RelativeLayout.LayoutParams。 2)通过 addRule() 方法设置视图之间的相对位置关系,比如设置某个视图在另一个视图的下方或右侧等。 示例代码: ``` <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View 1"/> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View 2" android:layout_below="@id/textView1"/> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View 3" android:layout_below="@id/textView1" android:layout_toRightOf="@id/textView2"/> </RelativeLayout> ``` 上述代码中,textView2 在 textView1 的下方,textView3 在 textView1 的下方且在 textView2 的右侧。通过设置不同的规则属性,可以实现更复杂的布局效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值