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.
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).
- Create three instances of GridLayoutManagers (first with 1 column, 2nd with 2 columns and 3rd with 3 columns).
- Get the instance of RecyclerView in activity’s onCreate()
- Add GridLayoutManager with 2 columns to it (by default we are keeping two columns)
- Create an adapter and add it to recycler view
- 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
good one
+1
Nice tutorial to present pinch gesture
pinch and zoom is very fast, how to slow?