1、背景

现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~

我相信看图的整个步骤,大家或者说用户应该不希望被打断把~~~“我擦,竟然不能放大,什么玩意,卸了~~“ , "我擦,竟然不能移动,留有何用,卸了~~"。

哈~所以对于图片的预览,一来,我们要让用户爽;二来,我们作为开发者,也得知道如何实现~~~

2、概述

想要做到图片支持多点触控,自由的进行缩放、平移,需要了解几个知识点:Matrix , GestureDetector , ScaleGestureDetector 以及事件分发机制,ps:不会咋办,不会你懂的。

1、Matrix

矩阵,看深入了都是3维矩阵的乘啊什么的,怪麻烦的~~

其实这么了解下就行了:

Matrix

数据结构:3维矩阵;

内部存储:new Float[9] ; 内部就是个一维数组,内部9个元素;可以进行setValues(float[] values)进行初始化

每个元素代表的意思

  1. {
  2. MSCALE_X, MSKEW_X, MTRANS_X,
  3. MSKEW_Y, MSCALE_Y, MTRANS_Y,
  4. MPERSP_0, MPERSP_1, MPERSP_2
  5. };

字面上,应该能看出来哪个代表x方向缩放,哪个代表垂直方向的偏移量吧~~有不认识的3个,没事,请无视。

操作

比如你想要设置matrix的偏移量为200,100

你可以这么写:

  1. Matrix transMatrix = new Matrix();
  2. float[] values = new float[] { 1.0, 0, 200, 0, 1.0, 100, 0, 0, 1.0 };
  3. transMatrix.setValues(values);

如果需要在旋转30度,放大两倍~~

这么写其实怪麻烦的~~

Matrix提供了一些常用的API:例如我们可以这么写:

  1. Matrix transMatrix = new Matrix();
  2. transMatrix.postTranslate(200, 100);

如何获取值

当然了,我们对一个Matrix进行了各种操作,一会postScale,一会postTranslate;那么现在如何获得当前的缩放比例:
前面说setValues可以初始化,那么getValues就能拿到当前矩阵的值,拿到的是个一维数组,9个元素;再通过下标取对应值就可以。

比如我想知道现在x方向缩放比例:

  1. public final float getScale()
  2. {
  3. scaleMatrix.getValues(matrixValues);
  4. return matrixValues[Matrix.MSCALE_X];
  5. }

好了,知道这些就够了~~

2、GestureDetector

嗯,自己看API,能够捕捉到长按、双击什么的;用法会在例子中

3、ScaleGestureDetector

嗯,有点像继承来的,其实不是的,独立的一个类~用于检测缩放的手势~~~用法会在例子中

3、实战

为了大家更好的理解,我会独立出每个功能,最后再整合到一起~~也方面大家对每个API的使用的学习。

1、自由的缩放

需求:当图片加载时,将图片在屏幕中居中;图片宽或高大于屏幕的,缩小至屏幕大小;自由对图片进行方法或缩小;

代码不是很长,直接贴代码了:

  1. package com.zhy.view;
  2. import android.content.Context;
  3. import android.graphics.Matrix;
  4. import android.graphics.drawable.Drawable;
  5. import android.util.AttributeSet;
  6. import android.util.Log;
  7. import android.view.MotionEvent;
  8. import android.view.ScaleGestureDetector;
  9. import android.view.ScaleGestureDetector.OnScaleGestureListener;
  10. import android.view.View;
  11. import android.view.View.OnTouchListener;
  12. import android.view.ViewTreeObserver;
  13. import android.widget.ImageView;
  14. public class ZoomImageView extends ImageView implements OnScaleGestureListener,
  15. OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener
  16. {
  17. private static final String TAG = ZoomImageView.class.getSimpleName();
  18. public static final float SCALE_MAX = 4.0f;
  19. /**
  20. * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0
  21. */
  22. private float initScale = 1.0f;
  23. /**
  24. * 用于存放矩阵的9个值
  25. */
  26. private final float[] matrixValues = new float[9];
  27. private boolean once = true;
  28. /**
  29. * 缩放的手势检测
  30. */
  31. private ScaleGestureDetector mScaleGestureDetector = null;
  32. private final Matrix mScaleMatrix = new Matrix();
  33. public ZoomImageView(Context context)
  34. {
  35. this(context, null);
  36. }
  37. public ZoomImageView(Context context, AttributeSet attrs)
  38. {
  39. super(context, attrs);
  40. super.setScaleType(ScaleType.MATRIX);
  41. mScaleGestureDetector = new ScaleGestureDetector(context, this);
  42. this.setOnTouchListener(this);
  43. }
  44. @Override
  45. public boolean onScale(ScaleGestureDetector detector)
  46. {
  47. float scale = getScale();
  48. float scaleFactor = detector.getScaleFactor();
  49. if (getDrawable() == null)
  50. return true;
  51. /**
  52. * 缩放的范围控制
  53. */
  54. if ((scale < SCALE_MAX && scaleFactor > 1.0f)
  55. || (scale > initScale && scaleFactor < 1.0f))
  56. {
  57. /**
  58. * 最大值最小值判断
  59. */
  60. if (scaleFactor * scale < initScale)
  61. {
  62. scaleFactor = initScale / scale;
  63. }
  64. if (scaleFactor * scale > SCALE_MAX)
  65. {
  66. scaleFactor = SCALE_MAX / scale;
  67. }
  68. /**
  69. * 设置缩放比例
  70. */
  71. mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2,
  72. getHeight() / 2);
  73. setImageMatrix(mScaleMatrix);
  74. }
  75. return true;
  76. }
  77. @Override
  78. public boolean onScaleBegin(ScaleGestureDetector detector)
  79. {
  80. return true;
  81. }
  82. @Override
  83. public void onScaleEnd(ScaleGestureDetector detector)
  84. {
  85. }
  86. @Override
  87. public boolean onTouch(View v, MotionEvent event)
  88. {
  89. return mScaleGestureDetector.onTouchEvent(event);
  90. }
  91. /**
  92. * 获得当前的缩放比例
  93. *
  94. * @return
  95. */
  96. public final float getScale()
  97. {
  98. mScaleMatrix.getValues(matrixValues);
  99. return matrixValues[Matrix.MSCALE_X];
  100. }
  101. @Override
  102. protected void onAttachedToWindow()
  103. {
  104. super.onAttachedToWindow();
  105. getViewTreeObserver().addOnGlobalLayoutListener(this);
  106. }
  107. @SuppressWarnings("deprecation")
  108. @Override
  109. protected void onDetachedFromWindow()
  110. {
  111. super.onDetachedFromWindow();
  112. getViewTreeObserver().removeGlobalOnLayoutListener(this);
  113. }
  114. @Override
  115. public void onGlobalLayout()
  116. {
  117. if (once)
  118. {
  119. Drawable d = getDrawable();
  120. if (d == null)
  121. return;
  122. Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());
  123. int width = getWidth();
  124. int height = getHeight();
  125. // 拿到图片的宽和高
  126. int dw = d.getIntrinsicWidth();
  127. int dh = d.getIntrinsicHeight();
  128. float scale = 1.0f;
  129. // 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
  130. if (dw > width && dh <= height)
  131. {
  132. scale = width * 1.0f / dw;
  133. }
  134. if (dh > height && dw <= width)
  135. {
  136. scale = height * 1.0f / dh;
  137. }
  138. // 如果宽和高都大于屏幕,则让其按按比例适应屏幕大小
  139. if (dw > width && dh > height)
  140. {
  141. scale = Math.min(dw * 1.0f / width, dh * 1.0f / height);
  142. }
  143. initScale = scale;
  144. // 图片移动至屏幕中心
  145. mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
  146. mScaleMatrix
  147. .postScale(scale, scale, getWidth() / 2, getHeight() / 2);
  148. setImageMatrix(mScaleMatrix);
  149. once = false;
  150. }
  151. }
  152. }

1、我们在onGlobalLayout的回调中,根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。如果图片很小,那就正常显示,不放大了~

2、我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理

  1. @Override
  2. public boolean onTouch(View v, MotionEvent event)
  3. {
  4. return mScaleGestureDetector.onTouchEvent(event);
  5. }

3、在onScale的回调中对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值

现在的效果:

1、小于屏幕的宽和高

2、大于屏幕的宽和高

真机录的效果不太好~~凑合看~

现在已经能够~~~随意的放大缩小了~~~

源码点击下载

可是,可是,存在问题:

1、缩放的中心点,我们设置是固定的,屏幕中间

2、放大后,无法移动~

下面,我们先解决缩放的中心点问题,不能一直按屏幕中心么,像我这样的,我比较关注妹子的眼睛,我要放大那一块~~~

2、设置缩放中心

1、单纯的设置缩放中心

仅仅是设置中心很简单,直接修改下中心点 :

  1. /**
  2. * 设置缩放比例
  3. */
  4. mScaleMatrix.postScale(scaleFactor, scaleFactor,
  5. detector.getFocusX(), detector.getFocusX());
  6. setImageMatrix(mScaleMatrix);

但是,随意的中心点放大、缩小,会导致图片的位置的变化,最终导致,图片宽高大于屏幕时,图片与屏幕间出现白边;图片小于屏幕,但是不居中。

2、控制缩放时图片显示的范围

所以我们在缩放的时候需要手动控制下范围:

  1. /**
  2. * 在缩放时,进行图片显示范围的控制
  3. */
  4. private void checkBorderAndCenterWhenScale()
  5. {
  6. RectF rect = getMatrixRectF();
  7. float deltaX = 0;
  8. float deltaY = 0;
  9. int width = getWidth();
  10. int height = getHeight();
  11. // 如果宽或高大于屏幕,则控制范围
  12. if (rect.width() >= width)
  13. {
  14. if (rect.left > 0)
  15. {
  16. deltaX = -rect.left;
  17. }
  18. if (rect.right < width)
  19. {
  20. deltaX = width - rect.right;
  21. }
  22. }
  23. if (rect.height() >= height)
  24. {
  25. if (rect.top > 0)
  26. {
  27. deltaY = -rect.top;
  28. }
  29. if (rect.bottom < height)
  30. {
  31. deltaY = height - rect.bottom;
  32. }
  33. }
  34. // 如果宽或高小于屏幕,则让其居中
  35. if (rect.width() < width)
  36. {
  37. deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
  38. }
  39. if (rect.height() < height)
  40. {
  41. deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
  42. }
  43. Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);
  44. mScaleMatrix.postTranslate(deltaX, deltaY);
  45. }
  46. /**
  47. * 根据当前图片的Matrix获得图片的范围
  48. *
  49. * @return
  50. */
  51. private RectF getMatrixRectF()
  52. {
  53. Matrix matrix = mScaleMatrix;
  54. RectF rect = new RectF();
  55. Drawable d = getDrawable();
  56. if (null != d)
  57. {
  58. rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
  59. matrix.mapRect(rect);
  60. }
  61. return rect;
  62. }

在onScale里面记得调用:

  1. /**
  2. * 设置缩放比例
  3. */
  4. mScaleMatrix.postScale(scaleFactor, scaleFactor,
  5. detector.getFocusX(), detector.getFocusY());
  6. checkBorderAndCenterWhenScale();
  7. setImageMatrix(mScaleMatrix);

这样就好了,可以自由的放大任何地方,并且不会出现边界出现白边,也能很好的让图片显示在屏幕中间(当图片宽或高小于屏幕);

3、贴下布局文件

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <com.zhy.view.ZoomImageView
  6. android:layout_width="fill_parent"
  7. android:layout_height="fill_parent"
  8. android:scaleType="matrix"
  9. android:src="@drawable/xx" />
  10. </RelativeLayout>

眼睛是心灵的窗户,咱们来放大看看,效果图:

好了,到此我们的图片随意的方法缩小~~~已经完成了~~~如果只需要缩放功能的,就可以拿去用了 不过码农是需要拿来主义的 否则哪来的时间撩妹 今天心情好 再将 测试完后bug 修改后的代码在贴下

package com.library.custom.view;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Matrix;

import android.graphics.RectF;

import android.graphics.drawable.Drawable;

import android.util.AttributeSet;

import android.view.GestureDetector;

import android.view.GestureDetector.SimpleOnGestureListener;

import android.view.MotionEvent;

import android.view.ScaleGestureDetector;

import android.view.ScaleGestureDetector.OnScaleGestureListener;

import android.view.View;

import android.view.View.OnTouchListener;

import android.view.ViewTreeObserver;

import android.widget.ImageView;

public class CustomZoomImageView extends ImageView implements OnTouchListener {

/**

* 最大放大比率(相较于原图尺寸)

*/

public static float SCALE_MAX = 4.0f;

/**

* 最小放大比率(相较于原图尺寸)

*/

private static float SCALE_MID = 2.0f;

/**

* 初始化时的缩放比例,如果图片宽或高最小值大于屏幕,此值将小于1.0

*/

private float initScale = 1.0f;

private boolean once = true;

/**

* 用于存放矩阵的9个值

*/

private final float[] matrixValues = new float[9];

/**

* 缩放的手势检测

*/

private ScaleGestureDetector mScaleGestureDetector;

private Matrix mScaleMatrix = new Matrix();

/**

* 用于双击检测

*/

private GestureDetector mGestureDetector;

private boolean isAutoScale;

private int mTouchSlop;

private float mLastX;

private float mLastY;

private boolean isCanDrag;

private int lastPointerCount;

private int oldwidth;

private int oldHeight;

/**

* 水平方向与View的边距

*/

private int mHorizontalPadding;

/**

* 垂直方向与View的边距

*/

private int mVerticalPadding;

public CustomZoomImageView(Context context) {

this(context, null);

}

public CustomZoomImageView(Context context, AttributeSet attrs) {

super(context, attrs);

setScaleType(ScaleType.MATRIX);

mGestureDetector = new GestureDetector(context, onDoubleListener);

mScaleGestureDetector = new ScaleGestureDetector(context, mOnScaleListener);

this.setOnTouchListener(this);

getViewTreeObserver().addOnGlobalLayoutListener(onLayoutListener);

}

private SimpleOnGestureListener onDoubleListener = new SimpleOnGestureListener() {

@Override

public boolean onDoubleTap(MotionEvent e) {

if (isAutoScale == true)

return true;

float x = e.getX();

float y = e.getY();

if (getScale() < SCALE_MID) {

CustomZoomImageView.this.postDelayed(

new AutoScaleRunnable(SCALE_MID, x, y), 16);

isAutoScale = true;

} else {

CustomZoomImageView.this.postDelayed(

new AutoScaleRunnable(initScale, x, y), 16);

isAutoScale = true;

}

return true;

}

};

private OnScaleGestureListener mOnScaleListener = new OnScaleGestureListener() {

@Override

public boolean onScale(ScaleGestureDetector detector) {

float scale = getScale();

float scaleFactor = detector.getScaleFactor();

if (getDrawable() == null)

return true;

/**

* 缩放的范围控制

*/

if ((scale < SCALE_MAX && scaleFactor > 1.0f)

|| (scale > initScale && scaleFactor < 1.0f)) {

/**

* 最大值最小值判断

*/

if (scaleFactor * scale < initScale) {

scaleFactor = initScale / scale;

}

if (scaleFactor * scale > SCALE_MAX) {

scaleFactor = SCALE_MAX / scale;

}

/**

* 设置缩放比例

*/

mScaleMatrix.postScale(scaleFactor, scaleFactor,

detector.getFocusX(), detector.getFocusY());

checkBorder();

setImageMatrix(mScaleMatrix);

}

return true;

}

@Override

public boolean onScaleBegin(ScaleGestureDetector detector) {

return true;

}

@Override

public void onScaleEnd(ScaleGestureDetector detector) {

}

};

private ViewTreeObserver.OnGlobalLayoutListener onLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

if (once) {

getViewTreeObserver().removeGlobalOnLayoutListener(onLayoutListener);

initScale();

once = false;

}

}

};

/**

* 自动缩放的任务

*

* @author zhy

*/

private class AutoScaleRunnable implements Runnable {

static final float BIGGER = 1.07f;

static final float SMALLER = 0.93f;

private float mTargetScale;

private float tmpScale;

/**

* 缩放的中心

*/

private float x;

private float y;

/**

* 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小

*

* @param targetScale

*/

public AutoScaleRunnable(float targetScale, float x, float y) {

this.mTargetScale = targetScale;

this.x = x;

this.y = y;

if (getScale() < mTargetScale) {

tmpScale = BIGGER;

} else {

tmpScale = SMALLER;

}

}

@Override

public void run() {

// 进行缩放

mScaleMatrix.postScale(tmpScale, tmpScale, x, y);

checkBorder();

setImageMatrix(mScaleMatrix);

final float currentScale = getScale();

// 如果值在合法范围内,继续缩放

if (((tmpScale > 1f) && (currentScale < mTargetScale))

|| ((tmpScale < 1f) && (mTargetScale < currentScale))) {

CustomZoomImageView.this.postDelayed(this, 16);

} else

// 设置为目标的缩放比例

{

final float deltaScale = mTargetScale / currentScale;

mScaleMatrix.postScale(deltaScale, deltaScale, x, y);

checkBorder();

setImageMatrix(mScaleMatrix);

isAutoScale = false;

}

}

}

/**

* 根据当前图片的Matrix获得图片的范围

*

* @return

*/

private RectF getMatrixRectF() {

Matrix matrix = mScaleMatrix;

RectF rect = new RectF();

Drawable d = getDrawable();

if (null != d) {

rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());

matrix.mapRect(rect);

}

return rect;

}

@Override

public boolean onTouch(View v, MotionEvent event) {

if (mGestureDetector.onTouchEvent(event))

return true;

mScaleGestureDetector.onTouchEvent(event);

float x = 0, y = 0;

// 拿到触摸点的个数

final int pointerCount = event.getPointerCount();

// 得到多个触摸点的x与y均值

for (int i = 0; i < pointerCount; i++) {

x += event.getX(i);

y += event.getY(i);

}

x = x / pointerCount;

y = y / pointerCount;

/**

* 每当触摸点发生变化时,重置mLasX , mLastY

*/

if (pointerCount != lastPointerCount) {

isCanDrag = false;

mLastX = x;

mLastY = y;

}

lastPointerCount = pointerCount;

switch (event.getAction()) {

case MotionEvent.ACTION_MOVE:

float dx = x - mLastX;

float dy = y - mLastY;

if (!isCanDrag) {

isCanDrag = isCanDrag(dx, dy);

}

if (isCanDrag) {

if (getDrawable() != null) {

RectF rectF = getMatrixRectF();

// 如果宽度小于屏幕宽度,则禁止左右移动

if (rectF.width() <= getWidth() - mHorizontalPadding * 2) {

dx = 0;

}

// 如果高度小雨屏幕高度,则禁止上下移动

if (rectF.height() <= getHeight() - mVerticalPadding * 2) {

dy = 0;

}

mScaleMatrix.postTranslate(dx, dy);

checkBorder();

setImageMatrix(mScaleMatrix);

}

}

mLastX = x;

mLastY = y;

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

lastPointerCount = 0;

break;

}

return true;

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

Drawable d = getDrawable();

if (d == null || getWidth() == 0 || once)

return;

initScale();

}

private void initScale() {

Drawable d = getDrawable();

if (d == null)

return;

if (oldwidth != d.getIntrinsicWidth() || oldHeight != d.getIntrinsicHeight()) {

oldwidth = d.getIntrinsicWidth();

oldHeight = d.getIntrinsicHeight();

// 垂直方向的边距

int width = getWidth();

int height = getHeight();

mVerticalPadding = (height - (width - 2 * mHorizontalPadding)) / 2;

// 拿到图片的宽和高

int dw = d.getIntrinsicWidth();

int dh = d.getIntrinsicHeight();

float scale = 1.0f;

int contentWidth = width - mHorizontalPadding * 2;

int contentHeight = height - mVerticalPadding * 2;

if (dw < contentWidth && dh >= contentHeight) {

scale = (width * 1.0f - mHorizontalPadding * 2) / dw;

}

if (dw < contentWidth && dh < contentHeight) {

float scaleW = (width * 1.0f - mHorizontalPadding * 2)

/ dw;

float scaleH = (height * 1.0f - mVerticalPadding * 2) / dh;

scale = Math.max(scaleW, scaleH);

}

if (dw >= contentWidth && dh >= contentHeight) {

float scaleW = (width * 1.0f - mHorizontalPadding * 2)

/ dw;

float scaleH = (height * 1.0f - mVerticalPadding * 2) / dh;

scale = Math.max(scaleW, scaleH);

}

if (dw >= contentWidth && dh < contentHeight) {

scale = (height * 1.0f - mVerticalPadding * 2) / dh;

}

initScale = scale;

SCALE_MID = initScale * 2;

SCALE_MAX = initScale * 4;

mScaleMatrix = new Matrix();

mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);

mScaleMatrix.postScale(scale, scale, width / 2,

height / 2);

// 图片移动至屏幕中心

setImageMatrix(mScaleMatrix);

}

}

/**

* 获得当前的缩放比例

*

* @return

*/

public final float getScale() {

mScaleMatrix.getValues(matrixValues);

return matrixValues[Matrix.MSCALE_X];

}

/**

* 剪切图片,返回剪切后的bitmap对象

*

* @return

*/

public Bitmap clip() {

Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),

Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

draw(canvas);

Bitmap chipResult = Bitmap.createBitmap(bitmap, mHorizontalPadding,

mVerticalPadding, getWidth() - 2 * mHorizontalPadding,

getWidth() - 2 * mHorizontalPadding);

if (chipResult != bitmap) {

bitmap.recycle();

}

return chipResult;

}

/**

* 边界检测

*/

private void checkBorder() {

RectF rect = getMatrixRectF();

float deltaX = 0;

float deltaY = 0;

int width = getWidth();

int height = getHeight();

// 如果宽或高大于屏幕,则控制范围 ; 这里的0.001是因为精度丢失会产生问题,但是误差一般很小,所以我们直接加了一个0.01

if (rect.width() + 0.01 >= width - 2 * mHorizontalPadding) {

if (rect.left > mHorizontalPadding) {

deltaX = -rect.left + mHorizontalPadding;

}

if (rect.right < width - mHorizontalPadding) {

deltaX = width - mHorizontalPadding - rect.right;

}

}

if (rect.height() + 0.01 >= height - 2 * mVerticalPadding) {

if (rect.top > mVerticalPadding) {

deltaY = -rect.top + mVerticalPadding;

}

if (rect.bottom < height - mVerticalPadding) {

deltaY = height - mVerticalPadding - rect.bottom;

}

}

mScaleMatrix.postTranslate(deltaX, deltaY);

}

/**

* 是否是拖动行为

*

* @param dx

* @param dy

* @return

*/

private boolean isCanDrag(float dx, float dy) {

return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;

}

public void setHorizontalPadding(int mHorizontalPadding) {

this.mHorizontalPadding = mHorizontalPadding;

}

}

android 缩放平移自定义View 显示图片的更多相关文章

  1. 图解android开发在界面上显示图片

    图解android开发在界面上显示图片<申明:转自百度> <原文章地址:http://jingyan.baidu.com/article/49711c6153a277fa441b7c ...

  2. Android中使用自定义View实现下载进度的显示

    一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态.之前在公司的项目中写过一个,今天抽空来整理一下. 一般下载都会有这么几种状态:未开始.等待.正在下载.下载结束,当然有时候会有下载 ...

  3. 安卓自定义View实现图片上传进度显示(仿QQ)

    首先看下我们想要实现的效果如下图(qq聊天中发送图片时的效果): 再看下图我们实现的效果: 实现原理很简单,首先我们上传图片时需要一个进度值progress,这个不管是自己写的上传的方法还是使用第三方 ...

  4. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

  5. Android 初阶自定义 View 字符头像

    自己很少做自定义 View ,只有最开始的时候跟着郭神写了一个小 Demo ,后来随着见识的越来越多,特别是在开源社区看到很多优秀的漂亮的控件,都是羡慕的要死,但是拉下来的代码还是看不明白,而且当时因 ...

  6. Android开发之自定义圆角矩形图片ImageView的实现

    android中的ImageView只能显示矩形的图片,这样一来不能满足我们其他的需求,比如要显示圆角矩形的图片,这个时候,我们就需要自定义ImageView了,其原理就是首先获取到图片的Bitmap ...

  7. Android开发之自定义圆角矩形图片ImageView的实现 - Jamy Cai

    android中的ImageView只能显示矩形的图片,这样一来不能满足我们其他的需求,比如要显示圆角矩形的图片,这个时候,我们就需要自定义ImageView了,其原理就是首先获取到图片的Bitmap ...

  8. 自定义View实现图片热区效果

    我司主要从事工业物联网领域软件的开发,现有个需求,在外废品处理时需要对产品的不良位置进行标记,点选图片实现图片网格的着色功能. 需求是通过自定义view来实现,实现思路如下: 首先将点击的小方格对象实 ...

  9. android开发之自定义View 详解 资料整理 小冰原创整理,原创作品。

    2019独角兽企业重金招聘Python工程师标准>>> /** * 作者:David Zheng on 2015/11/7 15:38 * * 网站:http://www.93sec ...

随机推荐

  1. OrchardCore 如何实现模块化( Modular )和 Multi-Tenancy

    一.概述 通常我们会在 Startup 类通过 void ConfigureServices(IServiceCollection services) 配置应用的服务.常见的形如 AddXXX 的方法 ...

  2. Jmeter实现dubbo接口压测案例

    当前项目中重构了消息服务,需要对消息服务接口做性能压测,评估消息服务的性能情况 通过和开发对接,目前消息服务是通过dubbo接口对内提供服务,所以才有了这边文章的记录 最初的压测这个dubbo接口有三 ...

  3. [enum]enum的用法

    ENUM概况 enum枚举类型是C/C++中的一种数据类型,与struct和class一样是用户自定义的类型,其特点在于enum类型的变量取值是有限的,是可以一一列举出来的. ENUM定义 C++ e ...

  4. django自制后台左侧导航代码

    自定义一个sort.py页面: 写入一下代码: class lanmusort(object): def __init__(self): self.arr=[] def lanmuget(self,o ...

  5. SQL语句练习题【主供自己学习、记忆】

    1.这是我在面试中遇到的一道sql题,没有答出来,o(╥﹏╥)o 这是我刚才在网上查找函数之后写的SQL语句,能得到这个结果.[谁有不同的方法,欢迎底下评论留言哈] select (DATENAME( ...

  6. Y1吐槽001 怎么做产品

    做一个产品,这个产品是做给用户用的还是做给领导看的完全是两个不同的出发点..做给领导看有好处,毕竟领导有知道进展的权利和指导方向的作用,还有一个好处就是表现得好. 忽略了使用者的感受是非常致命的,标模 ...

  7. android BLE Peripheral 模拟 ibeacon 发出ble 广播

    Android对外模模式(peripheral)的支持: 从Android 5.0+开始才支持. api level >= 21 所以5.0 之前设备,是不能向外发送广播的. Android中心 ...

  8. ajaj简介

    1. 什么是ajax? ajax的全称 Asynchronous(异步) JavaScript and XML. ajax是一种用于创建快速动态网页的技术. 主要用于前后台的交互,在前后台的交互中还有 ...

  9. window10 Docker仓库访问

    window10 Docker仓库访问 docer官网 docker仓库 windown10 安装docker可以参考 window10安装docker 配置了加速器以后还访问不了,点击托盘处dock ...

  10. Linux 管理进程

    探查进程 参数 描述 -A 显示所有进程 -N 显示与指定参数不符的所有进程 -a 显示除控制进程(session leader1)和无终端进程外的所有进程 -d 显示除控制进程外的所有进程 -e 显 ...