Gallery of images using ViewPager + zoom in ImageViews

Juan Ramón picture Juan Ramón · Feb 1, 2012 · Viewed 12.5k times · Source

What am I trying to implement?

A gallery of images using ViewPager. I choose this option because the smooth transition between images (I'm using ImageView), it is nice and quite easy to implement.

What is my problem exactly?

I've been able to implement all this but zoom isn't working. I can see in LogCat how it's printed ZOOM (the code is at the end of the post) but the image is not enlarged. Just a few notes about the following code:

  • ImageViewHelperURL.setUrlDrawable((ImageView) img, url, R.drawable.no_image); I'm using UrlImageViewHelper to download asynchronously the images from the web.
  • api.getListUrls() It's an ArrayList where I have the image urls.
  • I've tried also using an ImageView from R.drawable instead of downloading the image

    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Matrix;
    import android.graphics.PointF;
    import android.os.Bundle;
    import android.support.v4.app.ActionBar;
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentActivity;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentPagerAdapter;
    import android.support.v4.view.Menu;
    import android.support.v4.view.MenuItem;
    import android.support.v4.view.ViewPager;
    import android.util.FloatMath;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.MenuInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnTouchListener;
    import android.view.ViewGroup;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.Gallery;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    public class Slide extends FragmentActivity {
        private ViewPager mPager;
        public static Api api;
        public static int POSITION;
        public static ActionBar topbar;
        public static Context ctx;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.fragment);
            ctx = Slide.this;
            POSITION = 0;
            topbar = getSupportActionBar();
    
            /* get portadas */
            api = new Api();
            api.getUrlsFromAPI();
    
            topbar.setDisplayShowHomeEnabled(false);
            topbar.setDisplayShowTitleEnabled(true);
    
            mPager = (ViewPager) findViewById(R.id.pager);
            mPager.setAdapter(new TestAdapter(getSupportFragmentManager()));
        }
    
        @Override
        protected void onResume() {
            // TODO Auto-generated method stub
            super.onResume();
            mPager.setCurrentItem(POSITION);
        }
    
        static final class TestAdapter extends FragmentPagerAdapter {
            public TestAdapter(FragmentManager fm) {
                super(fm);
            }
    
            @Override
            public int getCount() {
                return api.getListUrls().size();
            }
    
            @Override
            public Fragment getItem(int position) {
                TestFragment f = new TestFragment();
    
                f.url = api.getListUrls().get(position).getUrl();
                f.position = position;
                return f;
            }
        }
    
        public static class TestFragment extends Fragment {
            String url = "";
            Integer position = 0;
    
            public TestFragment() {
                setRetainInstance(true);
            }
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setHasOptionsMenu(true);
            }
    
            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                    Bundle savedInstanceState) {
                ImageView img = new ImageView(getActivity());
    
                img.setPadding(6, 6, 6, 6) ;
                img.setLayoutParams(new Gallery.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)) ;
    
                ImageViewHelperURL.setUrlDrawable((ImageView) img, url, R.drawable.no_image) ;
    
                img.setOnTouchListener(new OnTouchListener() {
                    private static final String TAG = "SlideImageView";
                    // These matrices will be used to move and zoom image
                    Matrix matrix = new Matrix();
                    Matrix savedMatrix = new Matrix();
    
                    // We can be in one of these 3 states
                    static final int NONE = 0;
                    static final int DRAG = 1;
                    static final int ZOOM = 2;
                    int mode = NONE;
    
                    // Remember some things for zooming
                    PointF start = new PointF();
                    PointF mid = new PointF();
                    float oldDist = 1f;
    
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        ImageView view = (ImageView) v;
    
                        // Dump touch event to log
                        dumpEvent(event);
    
                        // Handle touch events here...
                        switch (event.getAction() & MotionEvent.ACTION_MASK) {
                        case MotionEvent.ACTION_DOWN:
                            savedMatrix.set(matrix);
                            start.set(event.getX(), event.getY());
                            Log.d(TAG, "mode=DRAG");
                            mode = DRAG;
                            break;
                        case MotionEvent.ACTION_POINTER_DOWN:
                            oldDist = spacing(event);
                            Log.d(TAG, "oldDist=" + oldDist);
                            if (oldDist > 10f) {
                                savedMatrix.set(matrix);
                                midPoint(mid, event);
                                mode = ZOOM;
                                Log.d(TAG, "mode=ZOOM");
                            }
                            break;
                        case MotionEvent.ACTION_UP:
                        case MotionEvent.ACTION_POINTER_UP:
                            mode = NONE;
                            Log.d(TAG, "mode=NONE");
                            break;
                        case MotionEvent.ACTION_MOVE:
                            if (mode == DRAG) {
                                // ...
                                matrix.set(savedMatrix);
                                matrix.postTranslate(event.getX() - start.x,
                                        event.getY() - start.y);
                            } else if (mode == ZOOM) {
                                float newDist = spacing(event);
                                Log.d(TAG, "newDist=" + newDist);
                                if (newDist > 10f) {
                                    matrix.set(savedMatrix);
                                    float scale = newDist / oldDist;
                                    Log.d(TAG, "ZOOOOOOOM: " + scale);
                                    matrix.postScale(scale, scale, mid.x, mid.y);
                                }
                            }
                            break;
                        }
    
                        view.setImageMatrix(matrix);
                        return true; // indicate event was handled
                    }
    
                    /** Show an event in the LogCat view, for debugging */
                    private void dumpEvent(MotionEvent event) {
                        String names[] = { "DOWN", "UP", "MOVE", "CANCEL",
                                "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?",
                                "8?", "9?" };
                        StringBuilder sb = new StringBuilder();
                        int action = event.getAction();
                        int actionCode = action & MotionEvent.ACTION_MASK;
                        sb.append("event ACTION_").append(names[actionCode]);
                        if (actionCode == MotionEvent.ACTION_POINTER_DOWN
                                || actionCode == MotionEvent.ACTION_POINTER_UP) {
                            sb.append("(pid ").append(
                                    action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
                            sb.append(")");
                        }
                        sb.append("[");
                        for (int i = 0; i < event.getPointerCount(); i++) {
                            sb.append("#").append(i);
                            sb.append("(pid ").append(event.getPointerId(i));
                            sb.append(")=").append((int) event.getX(i));
                            sb.append(",").append((int) event.getY(i));
                            if (i + 1 < event.getPointerCount())
                                sb.append(";");
                        }
                        sb.append("]");
                        Log.d(TAG, sb.toString());
                    }
    
                    /** Determine the space between the first two fingers */
                    private float spacing(MotionEvent event) {
                        float x = event.getX(0) - event.getX(1);
                        float y = event.getY(0) - event.getY(1);
                        return FloatMath.sqrt(x * x + y * y);
                    }
    
                    /** Calculate the mid point of the first two fingers */
                    private void midPoint(PointF point, MotionEvent event) {
                        float x = event.getX(0) + event.getX(1);
                        float y = event.getY(0) + event.getY(1);
                        point.set(x / 2, y / 2);
                    }
                });
    
                return img;
            }
    
            @Override
            public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    
            }
    
            @Override
            public boolean onOptionsItemSelected(MenuItem item) {
    
                return super.onOptionsItemSelected(item);
            }
        }
    
    }
    

I've already tried the following tutorial with no success:

Answer

antonyt picture antonyt · Feb 9, 2012

If you want to use a Matrix with an ImageView to transform the image, you need to change the scale type to ScaleType.Matrix, otherwise there will be no effect. The default scale type is ScaleType.FIT_CENTER and so it will ignore the matrix.

Regarding your original task, if you get this working you may find that the touch gestures on the ImageView will interfere with the scrolling of the ViewPager. You will definitely have problems supporting single-touch drag because this directly corresponds with the natural ViewPager swipe motion to go to the next view.