I have many images made with viewpager and picasso. i want to implement zoom effect in all of those images.
I mean when we doubleclick/expand finger the photo in gallery app and again doubleclick or by pinching finger the photo come in its own position.
I want to implement that methods. I watched/read many but couln't find the solution that works for all of my images made with picasso and viewpager.
And the method should be compatible from API 19
This is mainactivty.javawhere pictures are shown:
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);
}
This is 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);
}
}
You can create your own custom ImageView. I found this useful.
/**
* 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;
}
}
}
Then instead of ImageView you can use ...yourpackage.ZoomableImageView