向 ImageView 添加缩放效果,例如图库图像
Adding azoom effect to an ImageView like gallery images
我有很多用 viewpager 和 picasso 制作的图片。我想在所有这些图像中实现缩放效果。
我的意思是,当我们 doubleclick/expand 在图库应用程序中用手指触摸照片并再次双击或用手指捏合时,照片就会回到自己的位置。
我想实现那个方法。我 watched/read 很多,但找不到适用于我用 picasso 和 viewpager 制作的所有图像的解决方案。
并且该方法应该兼容 API 19
这是mainactivty.javawhere 图片显示:
public class MainActivity extends AppCompatActivity {
ViewPager viewPager;
private int[] imageUrls = new int[]{
R.raw.oooo,
R.raw.o8,
R.raw.oa,
R.raw.oad,
R.raw.oap,
R.raw.ok,
//there are many others
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.view_pager);
ViewPageAdapter adapter = new ViewPageAdapter(this, imageUrls);
viewPager.setAdapter(adapter);
}
这是 ViewPagerAdapter:
public class ViewPageAdapter extends PagerAdapter {
private Context context;
private int[] imageUrls;
ViewPageAdapter(Context context, int[] imageUrls) {
this.context = context;
this.imageUrls = imageUrls;
}
@Override
public int getCount() {
return imageUrls.length;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
Picasso.get()
.load(imageUrls[position])
.resize(400, 400)
.centerCrop()
.into(imageView);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
您可以创建自己的自定义 ImageView。我发现这很有用。
/**
* Copyright 2012-2013 Jeremie Martinez (jeremiemartinez@gmail.com)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
/**
* @author jmartinez
*
* Simple Android ImageView that enables dragging and zooming.
*
*/
public class ZoomableImageView extends AppCompatImageView {
private float maxScale = 3f;
private float minScale = 1f;
private enum State {
INIT, DRAG, ZOOM
}
private State state;
private Matrix matrix;
private float[] finalTransformation = new float[9];
private PointF last = new PointF();
private float currentScale = 1f;
private int viewWidth;
private int viewHeight;
private float afterScaleDrawableWidth;
private float afterScaleDrawableHeight;
private ScaleGestureDetector scaleDetector;
private GestureDetector doubleTapDetecture;
public ZoomableImageView(Context context) {
super(context);
setUp(context);
}
public ZoomableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setUp(context);
}
public ZoomableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setUp(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
// Set up drawable at first load
if (hasDrawable()) {
resetImage();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
scaleDetector.onTouchEvent(event);
doubleTapDetecture.onTouchEvent(event);
PointF current = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(current);
state = State.DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (state == State.DRAG) {
drag(current);
last.set(current);
}
break;
case MotionEvent.ACTION_UP:
state = State.INIT;
break;
case MotionEvent.ACTION_POINTER_UP:
state = State.INIT;
break;
}
setImageMatrix(matrix);
invalidate();
return true;
}
/**
* Set up the class. Method called by constructors.
*
* @param context
*/
private void setUp(Context context) {
super.setClickable(false);
matrix = new Matrix();
state = State.INIT;
scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
doubleTapDetecture = new GestureDetector(context, new GestureListener());
setScaleType(ScaleType.MATRIX);
}
private void resetImage() {
// Scale Image
float scale = getScaleForDrawable();
matrix.setScale(scale, scale);
// Center Image
float marginY = ((float) viewHeight - (scale * getDrawable().getIntrinsicHeight())) / 2;
float marginX = ((float) viewWidth - (scale * getDrawable().getIntrinsicWidth())) / 2;
matrix.postTranslate(marginX, marginY);
afterScaleDrawableWidth = (float) viewWidth - 2 * marginX;
afterScaleDrawableHeight = (float) viewHeight - 2 * marginY;
setImageMatrix(matrix);
}
/**
* Getter and setter for max/min scale. Default are min=1 and max=3
*/
public float getMaxScale() {
return maxScale;
}
public void setMaxScale(float maxScale) {
this.maxScale = maxScale;
}
public float getMinScale() {
return minScale;
}
public void setMinScale(float minScale) {
this.minScale = minScale;
}
/**
* Drag method
*
* @param current
* Current point to drag to.
*/
private void drag(PointF current) {
float deltaX = getMoveDraggingDelta(current.x - last.x, viewWidth, afterScaleDrawableWidth * currentScale);
float deltaY = getMoveDraggingDelta(current.y - last.y, viewHeight, afterScaleDrawableHeight * currentScale);
matrix.postTranslate(deltaX, deltaY);
limitDrag();
}
/**
* Scale method for zooming
*
* @param focusX
* X of center of scale
* @param focusY
* Y of center of scale
* @param scaleFactor
* scale factor to zoom in/out
*/
private void scale(float focusX, float focusY, float scaleFactor) {
float lastScale = currentScale;
float newScale = lastScale * scaleFactor;
// Calculate next scale with resetting to max or min if required
if (newScale > maxScale) {
currentScale = maxScale;
scaleFactor = maxScale / lastScale;
} else if (newScale < minScale) {
currentScale = minScale;
scaleFactor = minScale / lastScale;
} else {
currentScale = newScale;
}
// Do scale
if (requireCentering()) {
matrix.postScale(scaleFactor, scaleFactor, (float) viewWidth / 2, (float) viewHeight / 2);
} else
matrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
limitDrag();
}
/**
* This method permits to keep drag and zoom inside the drawable. It makes sure the drag is staying in bound.
*/
private void limitDrag() {
matrix.getValues(finalTransformation);
float finalXTransformation = finalTransformation[Matrix.MTRANS_X];
float finalYTransformation = finalTransformation[Matrix.MTRANS_Y];
float deltaX = getScaleDraggingDelta(finalXTransformation, viewWidth, afterScaleDrawableWidth * currentScale);
float deltaY = getScaleDraggingDelta(finalYTransformation, viewHeight, afterScaleDrawableHeight * currentScale);
matrix.postTranslate(deltaX, deltaY);
}
private float getScaleDraggingDelta(float delta, float viewSize, float contentSize) {
float minTrans = 0;
float maxTrans = 0;
if (contentSize <= viewSize) {
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
}
if (delta < minTrans)
return minTrans - delta;
else if (delta > maxTrans)
return maxTrans - delta;
else
return 0;
}
// Check if dragging is still possible if so return delta otherwise return 0 (do nothing)
private float getMoveDraggingDelta(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
private float getScaleForDrawable() {
float scaleX = (float) viewWidth / (float) getDrawable().getIntrinsicWidth();
float scaleY = (float) viewHeight / (float) getDrawable().getIntrinsicHeight();
return Math.min(scaleX, scaleY);
}
private boolean hasDrawable() {
return getDrawable() != null && getDrawable().getIntrinsicWidth() != 0 && getDrawable().getIntrinsicHeight() != 0;
}
private boolean requireCentering() {
return afterScaleDrawableWidth * currentScale <= (float) viewWidth || afterScaleDrawableHeight * currentScale <= (float) viewHeight;
}
private boolean isZoom() {
return currentScale != 1f;
}
/**
* Listener for detecting scale.
*
* @author jmartinez
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
state = State.ZOOM;
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
scale(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor());
return true;
}
}
/**
* Listener for double tap detection
*
* @author jmartinez
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDoubleTap(MotionEvent e) {
if (isZoom()) {
resetImage();
currentScale = 1f;
state = State.INIT;
} else {
scale(e.getX(), e.getY(), maxScale);
}
return true;
}
}
}
那么您可以使用 ...yourpackage.ZoomableImageView
而不是 ImageView
我有很多用 viewpager 和 picasso 制作的图片。我想在所有这些图像中实现缩放效果。
我的意思是,当我们 doubleclick/expand 在图库应用程序中用手指触摸照片并再次双击或用手指捏合时,照片就会回到自己的位置。
我想实现那个方法。我 watched/read 很多,但找不到适用于我用 picasso 和 viewpager 制作的所有图像的解决方案。
并且该方法应该兼容 API 19
这是mainactivty.javawhere 图片显示:
public class MainActivity extends AppCompatActivity {
ViewPager viewPager;
private int[] imageUrls = new int[]{
R.raw.oooo,
R.raw.o8,
R.raw.oa,
R.raw.oad,
R.raw.oap,
R.raw.ok,
//there are many others
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.view_pager);
ViewPageAdapter adapter = new ViewPageAdapter(this, imageUrls);
viewPager.setAdapter(adapter);
}
这是 ViewPagerAdapter:
public class ViewPageAdapter extends PagerAdapter {
private Context context;
private int[] imageUrls;
ViewPageAdapter(Context context, int[] imageUrls) {
this.context = context;
this.imageUrls = imageUrls;
}
@Override
public int getCount() {
return imageUrls.length;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
Picasso.get()
.load(imageUrls[position])
.resize(400, 400)
.centerCrop()
.into(imageView);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
您可以创建自己的自定义 ImageView。我发现这很有用。
/**
* Copyright 2012-2013 Jeremie Martinez (jeremiemartinez@gmail.com)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
/**
* @author jmartinez
*
* Simple Android ImageView that enables dragging and zooming.
*
*/
public class ZoomableImageView extends AppCompatImageView {
private float maxScale = 3f;
private float minScale = 1f;
private enum State {
INIT, DRAG, ZOOM
}
private State state;
private Matrix matrix;
private float[] finalTransformation = new float[9];
private PointF last = new PointF();
private float currentScale = 1f;
private int viewWidth;
private int viewHeight;
private float afterScaleDrawableWidth;
private float afterScaleDrawableHeight;
private ScaleGestureDetector scaleDetector;
private GestureDetector doubleTapDetecture;
public ZoomableImageView(Context context) {
super(context);
setUp(context);
}
public ZoomableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setUp(context);
}
public ZoomableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setUp(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
viewHeight = MeasureSpec.getSize(heightMeasureSpec);
// Set up drawable at first load
if (hasDrawable()) {
resetImage();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
scaleDetector.onTouchEvent(event);
doubleTapDetecture.onTouchEvent(event);
PointF current = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(current);
state = State.DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (state == State.DRAG) {
drag(current);
last.set(current);
}
break;
case MotionEvent.ACTION_UP:
state = State.INIT;
break;
case MotionEvent.ACTION_POINTER_UP:
state = State.INIT;
break;
}
setImageMatrix(matrix);
invalidate();
return true;
}
/**
* Set up the class. Method called by constructors.
*
* @param context
*/
private void setUp(Context context) {
super.setClickable(false);
matrix = new Matrix();
state = State.INIT;
scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
doubleTapDetecture = new GestureDetector(context, new GestureListener());
setScaleType(ScaleType.MATRIX);
}
private void resetImage() {
// Scale Image
float scale = getScaleForDrawable();
matrix.setScale(scale, scale);
// Center Image
float marginY = ((float) viewHeight - (scale * getDrawable().getIntrinsicHeight())) / 2;
float marginX = ((float) viewWidth - (scale * getDrawable().getIntrinsicWidth())) / 2;
matrix.postTranslate(marginX, marginY);
afterScaleDrawableWidth = (float) viewWidth - 2 * marginX;
afterScaleDrawableHeight = (float) viewHeight - 2 * marginY;
setImageMatrix(matrix);
}
/**
* Getter and setter for max/min scale. Default are min=1 and max=3
*/
public float getMaxScale() {
return maxScale;
}
public void setMaxScale(float maxScale) {
this.maxScale = maxScale;
}
public float getMinScale() {
return minScale;
}
public void setMinScale(float minScale) {
this.minScale = minScale;
}
/**
* Drag method
*
* @param current
* Current point to drag to.
*/
private void drag(PointF current) {
float deltaX = getMoveDraggingDelta(current.x - last.x, viewWidth, afterScaleDrawableWidth * currentScale);
float deltaY = getMoveDraggingDelta(current.y - last.y, viewHeight, afterScaleDrawableHeight * currentScale);
matrix.postTranslate(deltaX, deltaY);
limitDrag();
}
/**
* Scale method for zooming
*
* @param focusX
* X of center of scale
* @param focusY
* Y of center of scale
* @param scaleFactor
* scale factor to zoom in/out
*/
private void scale(float focusX, float focusY, float scaleFactor) {
float lastScale = currentScale;
float newScale = lastScale * scaleFactor;
// Calculate next scale with resetting to max or min if required
if (newScale > maxScale) {
currentScale = maxScale;
scaleFactor = maxScale / lastScale;
} else if (newScale < minScale) {
currentScale = minScale;
scaleFactor = minScale / lastScale;
} else {
currentScale = newScale;
}
// Do scale
if (requireCentering()) {
matrix.postScale(scaleFactor, scaleFactor, (float) viewWidth / 2, (float) viewHeight / 2);
} else
matrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
limitDrag();
}
/**
* This method permits to keep drag and zoom inside the drawable. It makes sure the drag is staying in bound.
*/
private void limitDrag() {
matrix.getValues(finalTransformation);
float finalXTransformation = finalTransformation[Matrix.MTRANS_X];
float finalYTransformation = finalTransformation[Matrix.MTRANS_Y];
float deltaX = getScaleDraggingDelta(finalXTransformation, viewWidth, afterScaleDrawableWidth * currentScale);
float deltaY = getScaleDraggingDelta(finalYTransformation, viewHeight, afterScaleDrawableHeight * currentScale);
matrix.postTranslate(deltaX, deltaY);
}
private float getScaleDraggingDelta(float delta, float viewSize, float contentSize) {
float minTrans = 0;
float maxTrans = 0;
if (contentSize <= viewSize) {
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
}
if (delta < minTrans)
return minTrans - delta;
else if (delta > maxTrans)
return maxTrans - delta;
else
return 0;
}
// Check if dragging is still possible if so return delta otherwise return 0 (do nothing)
private float getMoveDraggingDelta(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
private float getScaleForDrawable() {
float scaleX = (float) viewWidth / (float) getDrawable().getIntrinsicWidth();
float scaleY = (float) viewHeight / (float) getDrawable().getIntrinsicHeight();
return Math.min(scaleX, scaleY);
}
private boolean hasDrawable() {
return getDrawable() != null && getDrawable().getIntrinsicWidth() != 0 && getDrawable().getIntrinsicHeight() != 0;
}
private boolean requireCentering() {
return afterScaleDrawableWidth * currentScale <= (float) viewWidth || afterScaleDrawableHeight * currentScale <= (float) viewHeight;
}
private boolean isZoom() {
return currentScale != 1f;
}
/**
* Listener for detecting scale.
*
* @author jmartinez
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
state = State.ZOOM;
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
scale(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor());
return true;
}
}
/**
* Listener for double tap detection
*
* @author jmartinez
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDoubleTap(MotionEvent e) {
if (isZoom()) {
resetImage();
currentScale = 1f;
state = State.INIT;
} else {
scale(e.getX(), e.getY(), maxScale);
}
return true;
}
}
}
那么您可以使用 ...yourpackage.ZoomableImageView
而不是 ImageView