Reputation: 81
I need to zoom the entire layout contains Image,TextViews etc.i found zoom functionalities only for Imageview.
Upvotes: 7
Views: 13615
Reputation: 59
Here's my slightly updated version of Asif's answer:
ZoomLinearLayout.kt:
class ZoomLinearLayout : LinearLayout, OnScaleGestureListener {
private enum class Mode {
NONE, DRAG, ZOOM
}
private var mode = Mode.NONE
private var scale = 1.0f
private var lastScaleFactor = 0f
private var startX = 0f
private var startY = 0f
private var dx = 0f
private var dy = 0f
private var prevDx = 0f
private var prevDy = 0f
var minZoom: Float = 0.2f
var maxZoom: Float = 4.0f
constructor(context: Context?) : super(context) {
init(context)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(context)
}
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle
) {
init(context)
}
fun init(context: Context?) {
val scaleDetector = ScaleGestureDetector(context!!, this)
setOnTouchListener { view, motionEvent ->
when (motionEvent.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> if (scale > minZoom) {
mode = Mode.DRAG
startX = motionEvent.x - prevDx
startY = motionEvent.y - prevDy
}
MotionEvent.ACTION_MOVE -> if (mode == Mode.DRAG) {
dx = motionEvent.x - startX
dy = motionEvent.y - startY
}
MotionEvent.ACTION_POINTER_DOWN -> mode = Mode.ZOOM
MotionEvent.ACTION_UP -> {
mode = Mode.NONE
prevDx = dx
prevDy = dy
}
}
scaleDetector.onTouchEvent(motionEvent)
if (mode == Mode.DRAG && scale >= minZoom || mode == Mode.ZOOM) {
parent.requestDisallowInterceptTouchEvent(true)
val maxDx = (child().width - child().width / scale) / 2 * scale
val maxDy = (child().height - child().height / scale) / 2 * scale
dx = Math.min(Math.max(dx, -maxDx), maxDx)
dy = Math.min(Math.max(dy, -maxDy), maxDy)
applyScaleAndTranslation()
}
view.performClick()
true
}
}
override fun onScaleBegin(scaleDetector: ScaleGestureDetector): Boolean {
return true
}
override fun onScale(scaleDetector: ScaleGestureDetector): Boolean {
val scaleFactor = scaleDetector.scaleFactor
if (lastScaleFactor == 0f || Math.signum(scaleFactor) == Math.signum(lastScaleFactor)) {
scale *= scaleFactor
scale = Math.max(minZoom, Math.min(scale, maxZoom))
lastScaleFactor = scaleFactor
} else {
lastScaleFactor = 0f
}
return true
}
override fun onScaleEnd(scaleDetector: ScaleGestureDetector) {}
private fun applyScaleAndTranslation() {
if (scale < 1.0f) {
val child = child() // Retrieve the child view
val childWidth = child.width * scale
val childHeight = child.height * scale
val deltaX = (childWidth - child.width) / 2 // Calculate translation for centering
val deltaY = (childHeight - child.height) / 2
child.scaleX = scale
child.scaleY = scale
child.translationX = dx - deltaX // Apply translation with adjustment
child.translationY = dy - deltaY
} else {
child().scaleX = scale
child().scaleY = scale
child().translationX = dx
child().translationY = dy
}
}
private fun child(): View {
return getChildAt(0)
}
}
What are the differences?
How you can use it:
MainActivity.kt:
val zoomView = findViewById(R.id.zoomableLinearLayout)
zoomView.setOnTouchListener { _, _ ->
zoomView.init(this) //pass a context as the parameter
false
}
activity_main.xml:
<com.example.geoguess.util.ZoomLinearLayout
android:id="@+id/zoomableLinearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
</LinearLayout>
</com.example.geoguess.util.ZoomLinearLayout>
Upvotes: 1
Reputation: 2484
Create a Custom Layout Class Called ZoomLayout. In this layout i have used Framelayout you can scale all chile layout.
ZoomLayout.java
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 16.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
ZoomViewListener listener;
public ZoomLayout(Context context) {
super(context);
init(context);
setListner(getListener());
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
setListner(getListener());
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
setListner(getListener());
}
private void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
/*float eventX = motionEvent.getX();
float eventY = motionEvent.getY();
float[] eventXY = new float[]{eventX, eventY};
// float[] src={motionEvent.getX(),motionEvent.getY()}; // float[] dst = new float[2];
Matrix invertMatrix = new Matrix();
((ImageView) getChildAt(1)).getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
//invertMatrix.mapPoints(src,dst);
int x = Integer.valueOf((int) eventXY[0]);
int y = Integer.valueOf((int) eventXY[1]);
Log.e("image x", "===img ki x==" + x);
Log.e("image y", "===img ki y==" + y);
Drawable imgDrawable = ((ImageView) getChildAt(1)).getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
// int color_value= getHitboxColour(x,y,(ImageView) getChildAt(0));
//Limit x, y range within bitmap
if (x < 0) {
x = 0;
} else if (x > bitmap.getWidth() - 1) {
x = bitmap.getWidth() - 1;
}
if (y < 0) {
y = 0;
} else if (y > bitmap.getHeight() - 1) {
y = bitmap.getHeight() - 1;
}
//Log.e("touched color: ", "" + "#" + Integer.toHexString(color_value));
int touchedRGB = bitmap.getPixel(x, y);
int redValue = Color.red(touchedRGB);
int blueValue = Color.blue(touchedRGB);
int greenValue = Color.green(touchedRGB);
Log.e("touched color: ", "" + "#" + Integer.toHexString(touchedRGB));
listener.onPlaceChosen(touchedRGB);*/
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.DRAG;
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "UP");
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
/* if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);
applyScaleAndTranslation();
}*/
if (( scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
// Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx + ", max " + maxDx);
// applyScaleAndTranslation();
child().setScaleX(scale);
child().setScaleY(scale);
float maxDx1 = (child2().getWidth() - (child2().getWidth() / scale)) / 2 * scale;
float maxDy1 = (child2().getHeight() - (child2().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx1), maxDx1);
dy = Math.min(Math.max(dy, -maxDy1), maxDy1);
// Log.i(TAG, "Width: " + child2().getWidth() + ", scale " + scale + ", dx " + dx + ", max " + maxDx1);
// applyScaleAndTranslation();
child2().setScaleX(scale);
child2().setScaleY(scale);
}
if(mode == Mode.DRAG ){
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
// Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx + ", max " + maxDx);
child().setTranslationX(dx);
child().setTranslationY(dy);
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx1 = (child2().getWidth() - (child2().getWidth() / scale)) / 2 * scale;
float maxDy1 = (child2().getHeight() - (child2().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx1), maxDx1);
dy = Math.min(Math.max(dy, -maxDy1), maxDy1);
// Log.i(TAG, "Width: " + child2().getWidth() + ", scale " + scale + ", dx " + dx + ", max " + maxDx);
child2().setTranslationX(dx);
child2().setTranslationY(dy);
}
return true;
}
});
}
public int getHitboxColour(int x, int y,ImageView iv) {
// ImageView iv = (ImageView) findViewById(R.id.img_hitbox);
Bitmap bmpHotspots;
int pixel;
// Fix any offsets by the positioning of screen elements such as Activity titlebar.
// This part was causing me issues when I was testing out Bill Lahti's code.
int[] location = new int[2];
iv.getLocationOnScreen(location);
x -= location[0];
y -= location[1];
// Prevent crashes, return background noise
if ((x < 0) || (y < 0)) {
return Color.WHITE;
}
// Draw the scaled bitmap into memory
iv.setDrawingCacheEnabled(true);
bmpHotspots = Bitmap.createBitmap(iv.getDrawingCache());
iv.setDrawingCacheEnabled(false);
pixel = bmpHotspots.getPixel(x, y);
bmpHotspots.recycle();
return pixel;
}
// ScaleGestureDetector
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleBegin");
return true;
}
@Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "onScale" + scaleFactor);
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
} else {
lastScaleFactor = 0;
}
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleEnd");
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
private View child2() {
return getChildAt(1);
}
public interface ZoomViewListener {
void onZoomStarted(float zoom, float zoomx, float zoomy);
void onZooming(float zoom, float zoomx, float zoomy);
void onZoomEnded(float zoom, float zoomx, float zoomy);
void onPlaceChosen(int color);
}
public ZoomViewListener getListener() {
return listener;
}
public void setListner(final ZoomViewListener listener) {
this.listener = listener;
}}
Upvotes: 1
Reputation: 1764
Create a Custom Layout
Class Called ZoomeLinearLayout
.
ZoomLinearLayout.java
public class ZoomLinearLayout extends LinearLayout implements ScaleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
private float startX = 0f;
private float startY = 0f;
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
public ZoomLinearLayout(Context context) {
super(context);
init(context);
}
public ZoomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.DRAG;
break;
case MotionEvent.ACTION_UP:
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
applyScaleAndTranslation();
}
return true;
}
});
}
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
return true;
}
@Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
} else {
lastScaleFactor = 0;
}
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
}
Then in Layout
file, Wrap your LinearLayout
which you want to zoom with ZoomLinearLayout
. Note that you have only one direct child for ZoomLinearLayout
.
<com.asif.test.ZoomLinearLayout
android:layout_width="match_parent"
android:id="@+id/zoom_linear_layout"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</com.asif.test.ZoomLinearLayout>
Now in Activity
, create ZoomLinearLayout
object and set the onTouch()
event for it.
final ZoomLinearLayout zoomLinearLayout = (ZoomLinearLayout) findViewById(R.id.zoom_linear_layout);
zoomLinearLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
zoomLinearLayout.init(MainActivity.this);
return false;
}
});
Upvotes: 12
Reputation: 4182
Xml where layout
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:layout_centerHorizontal="true"
android:id="@+id/llzoom"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/text"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="20dp"
android:text="School"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textColor="@android:color/black"
android:textStyle="bold" />
</LinearLayout>
import
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
For Zoomout animation
findViewById(R.id.llzoom).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation zoomout = AnimationUtils.loadAnimation(SelectBuyerActivity.this, R.anim.zoomout);
findViewById(R.id.llzoom).startAnimation(zoomout);
}
});
Animation in your res/anim folder
zoomout.xml
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/bounce_interpolator"
android:fromXScale="0.5"
android:toXScale="1"
android:fromYScale="0.5"
android:toYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:duration="500"
android:fillAfter="true">
</scale>
You used also more animation Like down
slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromYDelta="0%p"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="100%p" />
</set>
slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromYDelta="100%p"
android:toYDelta="0" />
</set>
Upvotes: 0
Reputation: 4845
The layout doesn't have the zoom by default. I fund this, https://code.google.com/archive/p/android-zoom-view/downloads
In this answer, the user explains well how to use it. https://stackoverflow.com/a/15850113/6093353
Hope this helps you!
Upvotes: 1