pinch gesture Archives - wiki

Pinch gesture in Recycler View grid layout

Goal: Change grid layout on pinch gesture in recycler view

In this tutorial we will fetch images from gallery and show them in a grid layout in recycler view. You will be able to change layout on pinch gesture. Following are the screen shots of different layouts.

Screenshot_2016-06-29-15-39-23      Screenshot_2016-06-29-15-40-52    Screenshot_2016-06-29-15-39-01

 

Step 1:

Create a new project in android studio and add read external storage permission in AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Step 2:

add recycler view in layout file for the activity

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:padding="5dp"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    tools:context=".PhotosActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_photos"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:scrollbars="vertical" />

</RelativeLayout>

Step 3:

Add code in activity. Here we are not checking if external storage permission is given or not for Android M and above. (You can add that in the code).

  1. Create three instances of GridLayoutManagers (first with 1 column,  2nd with 2 columns and 3rd with 3 columns).
  2. Get the instance of RecyclerView in activity’s onCreate()
  3. Add GridLayoutManager with 2 columns to it (by default we are keeping two columns)
  4. Create an adapter and add it to recycler view
  5. fetch photos from gallery and add to adapter and call notifyDatasetChanged() for the adapter.

Here is the activity code:

import android.app.ProgressDialog;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class PhotosActivity extends AppCompatActivity {

    private RecyclerView mRvPhotos;
    private ProgressDialog mProgressDialog;

    private List<String> mPhotoUris;
    private PhotosAdapter mPhotosAdapter;

    private GridLayoutManager mGridLayoutManager1, mGridLayoutManager2, mGridLayoutManager3;
    private RecyclerView.LayoutManager mCurrentLayoutManager;

    private ScaleGestureDetector mScaleGestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photos);

        //setup progress dialog
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setMessage("Fetching Photos...");
        mProgressDialog.setCancelable(false);

        //setup recycler view
        mRvPhotos = (RecyclerView) findViewById(R.id.rv_photos);

        if (mRvPhotos != null) {

            //initialize layout managers
            mGridLayoutManager1 = new GridLayoutManager(this, 1);
            mGridLayoutManager2 = new GridLayoutManager(this, 2);
            mGridLayoutManager3 = new GridLayoutManager(this, 3);

            //initialize photo uris list
            mPhotoUris = new ArrayList<>();

            //initialize adapter
            mPhotosAdapter = new PhotosAdapter(mPhotoUris);

            //set layout manager
            mCurrentLayoutManager = mGridLayoutManager2;
            mRvPhotos.setLayoutManager(mGridLayoutManager2);

            //set adapter
            mRvPhotos.setAdapter(mPhotosAdapter);

            //set scale gesture detector
            mScaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                @Override
                public boolean onScale(ScaleGestureDetector detector) {
                    if (detector.getCurrentSpan() > 200 && detector.getTimeDelta() > 200) {
                        if (detector.getCurrentSpan() - detector.getPreviousSpan() < -1) {
                            if (mCurrentLayoutManager == mGridLayoutManager1) {
                                mCurrentLayoutManager = mGridLayoutManager2;
                                mRvPhotos.setLayoutManager(mGridLayoutManager2);
                                return true;
                            } else if (mCurrentLayoutManager == mGridLayoutManager2) {
                                mCurrentLayoutManager = mGridLayoutManager3;
                                mRvPhotos.setLayoutManager(mGridLayoutManager3);
                                return true;
                            }
                        } else if(detector.getCurrentSpan() - detector.getPreviousSpan() > 1) {
                            if (mCurrentLayoutManager == mGridLayoutManager3) {
                                mCurrentLayoutManager = mGridLayoutManager2;
                                mRvPhotos.setLayoutManager(mGridLayoutManager2);
                                return true;
                            } else if (mCurrentLayoutManager == mGridLayoutManager2) {
                                mCurrentLayoutManager = mGridLayoutManager1;
                                mRvPhotos.setLayoutManager(mGridLayoutManager1);
                                return true;
                            }
                        }
                    }
                    return false;
                }
            });

            //set touch listener on recycler view
            mRvPhotos.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    mScaleGestureDetector.onTouchEvent(event);
                    return false;
                }
            });

            //fetch photos from gallery
            new FetchPhotosTask(this).execute();
        }
    }

    public static class FetchPhotosTask extends AsyncTask<Void, Void, List<String>> {

        private WeakReference<Context> mContextWeakReference;

        public FetchPhotosTask(Context context) {
            mContextWeakReference = new WeakReference<>(context);
        }

        @Override
        protected void onPreExecute() {
            Context context = mContextWeakReference.get();
            if (context != null) {
                ((PhotosActivity) context).mProgressDialog.show();
            }
        }

        @Override
        protected List<String> doInBackground(Void... params) {

            Context context = mContextWeakReference.get();

            if (context != null) {
                //get photos from gallery
                String[] projection = new String[]{
                        MediaStore.Images.Media.DATA,
                };

                Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

                Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);

                if (cursor != null) {
                    List<String> photoUris = new ArrayList<>(cursor.getCount());
                    while (cursor.moveToNext()) {
                        photoUris.add("file://" + cursor.getString(0));
                    }
                    cursor.close();

                    return photoUris;
                }
            }

            return null;
        }

        @Override
        protected void onPostExecute(List<String> photoUris) {
            Context context = mContextWeakReference.get();
            if (context != null) {
                ((PhotosActivity) context).mProgressDialog.dismiss();

                if (photoUris != null && photoUris.size() > 0) {
                    ((PhotosActivity) context).mPhotoUris.clear();
                    ((PhotosActivity) context).mPhotoUris.addAll(photoUris);
                    ((PhotosActivity) context).mPhotosAdapter.notifyDataSetChanged();
                }
            }
        }
    }
}

Step 4: 

Here is Adapter’s code

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.squareup.picasso.Picasso;

import java.util.List;

public class PhotosAdapter extends RecyclerView.Adapter<PhotosAdapter.PhotoViewHolder> {

    private List<String> mPhotoUris;

    public PhotosAdapter(List<String> photoUris) {
        this.mPhotoUris = photoUris;
    }

    @Override
    public PhotosAdapter.PhotoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new PhotoViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_photo_grid, parent, false));
    }

    @Override
    public void onBindViewHolder(PhotosAdapter.PhotoViewHolder holder, int position) {
        Picasso.with(holder.ivPhoto.getContext())
                .load(mPhotoUris.get(position))
                .fit()
                .centerCrop()
                .into(holder.ivPhoto);
    }

    @Override
    public int getItemCount() {
        return mPhotoUris.size();
    }

    public static class PhotoViewHolder extends RecyclerView.ViewHolder {
        ImageView ivPhoto;

        public PhotoViewHolder(View itemView) {
            super(itemView);
            ivPhoto = (ImageView) itemView.findViewById(R.id.iv_photo);
        }
    }
}

Step 5:

Add Grid item layout (for adapter items)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="1dp"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_photo"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />

</LinearLayout>

Complete code can be found here

By Ankit on June 29, 2016 | Android | 4 comments
Tags: