Ramona Ramona - 1 month ago 8
Android Question

Zoom in/out the whole page layout

I am trying to write code for zoom in/out the whole page/screen of the app. I was given this link

Android - zoom in/out RelativeLayout with spread/pinch

but it's really difficult for a beginner to understand all the procedures to follow.

If someone can help and provide clearer explanation on this topic, I and other beginners will surely appreciate it.

So far I have set

MainActivity
,
AnswerActivity
and
Fragments
.

Answer Source

First, lets start simple. Scaling is relatively easy. (this code is not used in the further examples):

    TextView rootView;
    rootView.setScaleX(sx);
    rootView.setScaleY(sx);

sx and sy is scale[X/Y]

That is the fundamentals of scaling. Now we go to the hard part: Pinch zoom. this requires user input in the form of touch events.

Start by setting an onTouchListener if you cannot use onTouchEvent for the root view. (I will not show this part)

Before you even start, declare a float called scaleFactor:

[ANY-ACCESS-MODIFIER] long scaleFactor = 1f;

First, we need a ScaleGestureListener. This can be a nested class if wanted:

class Scaler extends ScaleGestureDetector {
    public Scaler(Context context, OnScaleGestureListener listener) {
        super(context, listener);
    }

    @Override
    public float getScaleFactor() {//Leave this method empty.
        return super.getScaleFactor();
    }
}

Secondly we need the OnScaleGestureListener:

class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener{

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scaleFactor *= detector.getScaleFactor();

        if(scaleFactor > 2) scaleFactor = 2;//Limit to your liking
        else if(scaleFactor < 0.3f) scaleFactor = 0.3f;//Limit to your liking
        scaleFactor = (scaleFactor * 100) / 100;//jitter-protection
        //scaleMatrix.setScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());//This is for usage with a Matrix: Good for canvas and other areas where this is usable. This is from my own scaling code, so I keep the matrix around in this example in case it is needed
        tv.setScaleX(scaleFactor);
        tv.setScaleY(scaleFactor);
        tv.setPivotX(detector.getFocusX());
        tv.setPivotY(detector.getFocusY());
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {return true;}

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {}
}

Now, this is where it splits in two. If possible, use onTouchEvent(MotionEvent ev). If you cannot use this method(when you add @Override above it it shows an error) you have to use onTouchListener instead. set it on the TextView(tv.setOnTouchListener(this);. Make sure the class implements OnTouchListener)

Now, whatever method you picked, MAKE SURE IT RETURNS true!

This code should work in both methods, and it isn't limited to a specific method:

(ev is MotionEvent)

    int pointers = ev.getPointerCount();

    if(pointers == 2) {

        zoom = true;
        s.onTouchEvent(ev);//pass original motionevent(unscaled) to zoom

    }

Now, the base code is in place. Now we need to create the instance for s:

Globally declare:

private Scaler s;
private ScaleListener listener;

and where you inflate the layout:

listener = new ScaleListener();
s = new Scaler(c, listener);//c is a context. 

Now, assuming all the components are in place you have a functioning zoom-in/out system. Please note that this does not cover scrolling on the zoomed view. You have to create an offsetX/Y variable, and take input when there is one pointer and check how far a distance you want to move.

Using a TextView and touch events, you can use #setScrollX or #setScrollY along with an offset to set the new, scrolled position.

It may though be easier to create your own, custom text view. You do this by creating a new class and making it extend TextView. Then you put modifications as you want into there. This would allow you to add zoom and such into the custom TextView. This is though a prefered way to do it if you have multiple textviews in a single class or you have multiple activities with a zoomable and scrollable textview.


EDIT: Custom textview

Sadly, not a lot of the integrated tools does nto work. android:scrollbars on a textview doesn't work for an instance. So first the TextView has to have a ScrolLView around it:

<ScrollView android:id="@+id/textScroll"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.package.ZoomableTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Some hfndusijhgn hgnm hnixjkgbhn fvb uynfv bunfg vbuygn buy hgnyu gnui h  uh nuioiogfej uhud\nfhbnikjhgnuieskhg nmuimjhbnguijhgne \nfuyh ghfuisdghbuisjhgnuie dgjh\nifgb dsauingfbehja kbfiuej ksghbisdjkg nbhni\ngfdfjgdfh hdfh sdfhg sh "/>

</ScrollView>

And ZoomableTextView:

You need this dependency first:

compile 'com.android.support:appcompat-v7:25.3.1'

This is to get the AppCompat library so the TextView can use new features while maintaining support for earlier versions. now for the class:

public class ZoomableTextView extends AppCompatTextView/*This is why the AppCompat dependency is needed*/ {
    private float textSize,
            textScale;
    private Scaler s;
    private ScaleListener listener;
    public ZoomableTextView(Context context) {
        super(context);
        init();
    }

    public ZoomableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ZoomableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void init(){
        listener = new ScaleListener();
        s = new Scaler(getContext(), listener);
        textSize = getTextSize();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        super.onTouchEvent(ev);
        if(ev.getPointerCount() == 2){
            s.onTouchEvent(ev);
        }
        return true;
    }

    class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener{

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            textScale *= detector.getScaleFactor();

            if(textScale > 2) textScale = 2;//Limit to your liking
            else if(textScale < 0.3f) textScale = 0.3f;//Limit to your liking
            textScale = (textScale * 100) / 100;//jitter-protection
            if(textScale < 0.3f) textScale = 0.3f;
            if(textScale > 2) textScale = 2;

            setTextSize(textSize * textScale);
            setPivotX(detector.getFocusX());
            setPivotY(detector.getFocusY());
            return true;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {return true;}

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {}
    }

    class Scaler extends ScaleGestureDetector {
        public Scaler(Context context, OnScaleGestureListener listener) {
            super(context, listener);
        }

        @Override
        public float getScaleFactor() {//Leave this method empty.
            return super.getScaleFactor();
        }
    }
}