-1

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.

Zoe
  • 27,060
  • 21
  • 118
  • 148
Ramona
  • 159
  • 1
  • 10
  • Thank you Matthias. – Ramona May 16 '17 at 10:58
  • What are we dealing with here? TExtView? Canvas? – Zoe May 16 '17 at 14:14
  • @LunarWatcher. Thank that you here. I have `Fragments` with `TextView` and pictures. (Ps. Can you please upvote my post? I guess people downvote mainly just to gain `reputation` points which is a bit odd). – Ramona May 16 '17 at 16:37
  • What layout is the root view of the fragment you want to zoom?(type of layout). And a downvoter doesn't gain any reputation on downvoting. EDIT: From the link you supplied, I will assume you use a RelativeLayout. If you don't, please leave a comment with what you use – Zoe May 16 '17 at 16:42
  • 1
    All `Fragments` are in RelativeLayout. – Ramona May 16 '17 at 16:44
  • If you need any other info or code please let me know – Ramona May 16 '17 at 16:52

1 Answers1

2

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();
        }
    }
}
Zoe
  • 27,060
  • 21
  • 118
  • 148
  • Thanks a million Lunar. It still sounds very complicated to me. Hope you don't mind if I'll keep asking you for more help on the way regarding this topic:) Of course I'll upvote your answer right now. – Ramona May 17 '17 at 09:54
  • If there is something that doesn't add up, just ask and I will answer your concerns. Zooming is in general a hard topic because in addition to zoom, there is also touch events for pinch and pan that has to add up. The code I presented in my answer is something I used in a SurfaceView and using a Canvas. if this for some reason doesn't work, I will help you through the process of creating your own textview where you handle zoom inside the class. – Zoe May 17 '17 at 09:58
  • @Ramona I have just become aware of a better way when using textViews. Testing it out and will add it to the answer when I get it to work. – Zoe May 17 '17 at 10:27
  • Ok Lunar. Just also please let me know what I should implement the first. Reading your post I got a bit lost :) Thank you – Ramona May 17 '17 at 12:28
  • Well, it is really up to you. The second shows how you can create a zoomable textview yo ucan use anywhere. The first shows how you can implement zoom on anything else. The custom textview adds scroll if necessary, and can be implemented as a custom view even in xml. Which you select is up to you, but the second method works differently as it changes the size of the text based on zoom. – Zoe May 17 '17 at 12:39
  • But is zoomable textView going to zoom the whole page/screen? Or just the text itself , so the text will be zoomed but picture on the page stays the same? – Ramona May 17 '17 at 13:08
  • This is like with every other view: If you want it to cover the page, set width and height to match parent. If you want it to cover a specific dimension, you make it cover those dimensions. The zoom doesn't change the physical size, only the text size. It only changes the size of the view if either width or height is set to `wrap_content` . So if you set the width and height to 100x100, a scrolling will be added vertically to handle the new height. The textview changes size so it remains the same so the page itself doesn't zoom. For that you should change the RelativeLayout, but you should(1/2 – Zoe May 17 '17 at 13:13
  • (2/2) instead add a ScrollView inside the root view with a RelativeLayout with the page in it. you can keep the root relative layout if you want. Then you use the first method on the RelativeLayout. – Zoe May 17 '17 at 13:14
  • wow! now i'm totally lost :) you explain it so well and at the same time i don't know what to do :) will get back here in 2h – Ramona May 17 '17 at 13:27
  • The best thing you can do at this point is try the different solutions presented in my answer, so you can see what works for you while learning how it works. – Zoe May 17 '17 at 13:30
  • Thanks I will try and surely will be asking you for gudiance :) – Ramona May 17 '17 at 18:19
  • And first question - what to put here `[ANY-ACCESS-MODIFIER]`? – Ramona May 17 '17 at 18:57
  • [ANY-ACCESS-MODIFIER] is my way of saying you can put private, public or no special access. It is up to you and based on your needs. An access modifier are primitives that determines how it is accessed. Private means class only. No specific means package only. Public means any class can access it. There are also some other modifiers(ex: protected) but those are rarely used. – Zoe May 17 '17 at 18:59
  • So ` static long scaleFactor = 1f;` is ok? All my fields like for example ` static boolean[] isAnswered = new boolean[NUMBER_OF_QUESTIONS];` are static – Ramona May 17 '17 at 19:03
  • Lunar, maybe we could talk on chat here someday instead of writing it all here? There is some way to create a private chat for specific conversation, right? – Ramona May 17 '17 at 19:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/144493/discussion-between-lunarwatcher-and-ramona). – Zoe May 17 '17 at 19:18