Kotlin Select Multiple Images From Gallery And Show In GridView Android Example.
Let us make an app which enables the user to select multiple images from the gallery of his android device.
After taking or selecting the multiple images, we will set them into the grid view.
First of all, see the following output material.
Step 1. Important Layout
Make a new project in the android studio and select Kotlin as the primary source language.
Now you will have one activity as the main one. For it’s layout file which is activity_main.xml , you should write the following code
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <GridView android:layout_width="match_parent" android:layout_height="0dp" android:id="@+id/gv" android:numColumns="3" android:layout_weight="1"> </GridView> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn" android:text="Select Multiple Images" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:layout_marginLeft="10dp" /> </LinearLayout>
This file will create the main screen of our app. It has one grid view and one button.
Grid view will cover the most of the screen while button is located at the bottom of the screen.
Step 2. Kotlin Lines For Main
Write down the below lines in MainActivity.kt file.
import android.annotation.SuppressLint import android.app.Activity import android.content.ClipData import android.content.Intent import android.database.Cursor import android.net.Uri import android.provider.MediaStore import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.GridView import android.widget.Toast import java.util.ArrayList class MainActivity : AppCompatActivity() { private var btn: Button? = null internal var PICK_IMAGE_MULTIPLE = 1 internal lateinit var imageEncoded: String internal lateinit var imagesEncodedList: MutableList<String> private var gvGallery: GridView? = null private var galleryAdapter: GalleryAdapter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) btn = findViewById(R.id.btn) gvGallery = findViewById(R.id.gv) as GridView btn!!.setOnClickListener { val intent = Intent() intent.type = "image/*" intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) intent.action = Intent.ACTION_GET_CONTENT startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_MULTIPLE) } } @SuppressLint("NewApi") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { try { // When an Image is picked if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == Activity.RESULT_OK && null != data ) { // Get the Image from data val filePathColumn = arrayOf(MediaStore.Images.Media.DATA) imagesEncodedList = ArrayList() if (data.data != null) { val mImageUri = data.data // Get the cursor val cursor = contentResolver.query( mImageUri!!, filePathColumn, null, null, null ) // Move to first row cursor!!.moveToFirst() val columnIndex = cursor.getColumnIndex(filePathColumn[0]) imageEncoded = cursor.getString(columnIndex) cursor.close() val mArrayUri = ArrayList<Uri>() mArrayUri.add(mImageUri) galleryAdapter = GalleryAdapter(applicationContext, mArrayUri) gvGallery!!.adapter = galleryAdapter gvGallery!!.verticalSpacing = gvGallery!!.horizontalSpacing val mlp = gvGallery!! .layoutParams as ViewGroup.MarginLayoutParams mlp.setMargins(0, gvGallery!!.horizontalSpacing, 0, 0) } else { if (data.clipData != null) { val mClipData = data.clipData val mArrayUri = ArrayList<Uri>() for (i in 0 until mClipData!!.itemCount) { val item = mClipData.getItemAt(i) val uri = item.uri mArrayUri.add(uri) // Get the cursor val cursor = contentResolver.query(uri, filePathColumn, null, null, null) // Move to first row cursor!!.moveToFirst() val columnIndex = cursor.getColumnIndex(filePathColumn[0]) imageEncoded = cursor.getString(columnIndex) imagesEncodedList.add(imageEncoded) cursor.close() galleryAdapter = GalleryAdapter(applicationContext, mArrayUri) gvGallery!!.adapter = galleryAdapter gvGallery!!.verticalSpacing = gvGallery!!.horizontalSpacing val mlp = gvGallery!! .layoutParams as ViewGroup.MarginLayoutParams mlp.setMargins(0, gvGallery!!.horizontalSpacing, 0, 0) } Log.v("LOG_TAG", "Selected Images" + mArrayUri.size) } } } else { Toast.makeText( this, "You haven't picked Image", Toast.LENGTH_LONG ).show() } } catch (e: Exception) { Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG) .show() } super.onActivityResult(requestCode, resultCode, data) } }
Deep Reading
Let us see what Main activity file is telling to the compiler.
First of all, see the following
private var btn: Button? = null internal var PICK_IMAGE_MULTIPLE = 1 internal lateinit var imageEncoded: String internal lateinit var imagesEncodedList: MutableList<String> private var gvGallery: GridView? = null private var galleryAdapter: GalleryAdapter? = null
First line is making the object of the button class. Then second line will make one variable whose value is 1.
Then compiler will create one string variable. After this, it will create one Mutable list variable.
Finally, compiler will make the object of the Grid View class and of Gallery adapter.
When the user clicks the button, compiler will run the following lines
btn!!.setOnClickListener { val intent = Intent() intent.type = "image/*" intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) intent.action = Intent.ACTION_GET_CONTENT startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_MULTIPLE) }
Compiler will create one intent and will set it’s type as an image. Then it will enable the intent to allow the user to select multiple images.
Finally, it will start the intent. Starting of the intent will make a new screen and it will show all the gallery images.
From here, user needs to select single or multiple images.
When the user is finished with selection of the images, compiler will execute the onActivityResult() method.
Below is the code lines for onActivityResult() method.
@SuppressLint("NewApi") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { try { // When an Image is picked if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == Activity.RESULT_OK && null != data ) { // Get the Image from data val filePathColumn = arrayOf(MediaStore.Images.Media.DATA) imagesEncodedList = ArrayList() if (data.data != null) { val mImageUri = data.data // Get the cursor val cursor = contentResolver.query( mImageUri!!, filePathColumn, null, null, null ) // Move to first row cursor!!.moveToFirst() val columnIndex = cursor.getColumnIndex(filePathColumn[0]) imageEncoded = cursor.getString(columnIndex) cursor.close() val mArrayUri = ArrayList<Uri>() mArrayUri.add(mImageUri) galleryAdapter = GalleryAdapter(applicationContext, mArrayUri) gvGallery!!.adapter = galleryAdapter gvGallery!!.verticalSpacing = gvGallery!!.horizontalSpacing val mlp = gvGallery!! .layoutParams as ViewGroup.MarginLayoutParams mlp.setMargins(0, gvGallery!!.horizontalSpacing, 0, 0) } else { if (data.clipData != null) { val mClipData = data.clipData val mArrayUri = ArrayList<Uri>() for (i in 0 until mClipData!!.itemCount) { val item = mClipData.getItemAt(i) val uri = item.uri mArrayUri.add(uri) // Get the cursor val cursor = contentResolver.query(uri, filePathColumn, null, null, null) // Move to first row cursor!!.moveToFirst() val columnIndex = cursor.getColumnIndex(filePathColumn[0]) imageEncoded = cursor.getString(columnIndex) imagesEncodedList.add(imageEncoded) cursor.close() galleryAdapter = GalleryAdapter(applicationContext, mArrayUri) gvGallery!!.adapter = galleryAdapter gvGallery!!.verticalSpacing = gvGallery!!.horizontalSpacing val mlp = gvGallery!! .layoutParams as ViewGroup.MarginLayoutParams mlp.setMargins(0, gvGallery!!.horizontalSpacing, 0, 0) } Log.v("LOG_TAG", "Selected Images" + mArrayUri.size) } } } else { Toast.makeText( this, "You haven't picked Image", Toast.LENGTH_LONG ).show() } } catch (e: Exception) { Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG) .show() } super.onActivityResult(requestCode, resultCode, data) }
This code has the main logic of the example. There is one if() condition in this onActivityResult() method.
For Single Image
When the user have selected only one image, if() condition is true and compiler will run the following
if (data.data != null) { val mImageUri = data.data // Get the cursor val cursor = contentResolver.query( mImageUri!!, filePathColumn, null, null, null ) // Move to first row cursor!!.moveToFirst() val columnIndex = cursor.getColumnIndex(filePathColumn[0]) imageEncoded = cursor.getString(columnIndex) cursor.close() val mArrayUri = ArrayList<Uri>() mArrayUri.add(mImageUri) galleryAdapter = GalleryAdapter(applicationContext, mArrayUri) gvGallery!!.adapter = galleryAdapter gvGallery!!.verticalSpacing = gvGallery!!.horizontalSpacing val mlp = gvGallery!! .layoutParams as ViewGroup.MarginLayoutParams mlp.setMargins(0, gvGallery!!.horizontalSpacing, 0, 0) }
In this, compiler will first get the URI from the data. Then there is one cursor. This cursor will fill the arraylist with the image URIs.
Then it will bind this arraylist to the adapter object. After this, compiler will attach the adapter to the Grid view.
When the user have selected multiple images, compiler will run the following
else { if (data.clipData != null) { val mClipData = data.clipData val mArrayUri = ArrayList<Uri>() for (i in 0 until mClipData!!.itemCount) { val item = mClipData.getItemAt(i) val uri = item.uri mArrayUri.add(uri) // Get the cursor val cursor = contentResolver.query(uri, filePathColumn, null, null, null) // Move to first row cursor!!.moveToFirst() val columnIndex = cursor.getColumnIndex(filePathColumn[0]) imageEncoded = cursor.getString(columnIndex) imagesEncodedList.add(imageEncoded) cursor.close() galleryAdapter = GalleryAdapter(applicationContext, mArrayUri) gvGallery!!.adapter = galleryAdapter gvGallery!!.verticalSpacing = gvGallery!!.horizontalSpacing val mlp = gvGallery!! .layoutParams as ViewGroup.MarginLayoutParams mlp.setMargins(0, gvGallery!!.horizontalSpacing, 0, 0) } Log.v("LOG_TAG", "Selected Images" + mArrayUri.size) } }
Here, compiler will also do the same thing as for single image, retrieving image URIs.
Because there are multiple images, compiler will run one for loop and then it will collect all the URIs.
Then after it will attach the adapter to the grid view.
Step 3. Making Grid View
Make a new resource XML file in app->res->layout directory. You should give this file a name like gv_item.xml
Below are the source lines for gv_item.xml file.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:layout_height="130dp" android:layout_width="130dp" android:padding="10dp" android:id="@+id/ivGallery" android:src="@mipmap/ic_launcher_round" android:scaleType="fitXY" /> </LinearLayout>
This file will make the look and feel for every cell of the grid view.
There is one image view in this file so every cell will have one image view in grid view.
Now make a new Kotlin file with the name like GalleryAdapter.kt
Below is the source snippet for GalleryAdapter.kt file.
import android.annotation.SuppressLint import android.content.Context import android.net.Uri import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.ImageView import java.util.ArrayList class GalleryAdapter(private val ctxs: Context, internal var mArrayUris: ArrayList<Uri>) : BaseAdapter() { private var pos: Int = 0 private var inflater: LayoutInflater? = null private var ivGallery: ImageView? = null lateinit var ctx: Context lateinit var mArrayUri: ArrayList<Uri> init { mArrayUri = mArrayUris ctx = ctxs } override fun getCount(): Int { return mArrayUri.size } override fun getItem(position: Int): Any { return mArrayUri[position] } override fun getItemId(position: Int): Long { return 0 } @SuppressLint("ViewHolder") override fun getView(position: Int, p1: View?, parent: ViewGroup?): View? { pos = position inflater = ctx .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val itemView: View? = this.inflater!!.inflate(R.layout.gv_item, parent, false) ivGallery = itemView?.findViewById(R.id.ivGallery) as ImageView ivGallery!!.setImageURI(mArrayUri[position]) return itemView } }
First of all, compiler will create the objects of LayoutInflater and ImageView class.
Inside the init() method, compiler will initialize the context and arraylist of URIs object.
Following is the code for getView() method.
@SuppressLint("ViewHolder") override fun getView(position: Int, p1: View?, parent: ViewGroup?): View? { pos = position inflater = ctx .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val itemView: View? = this.inflater!!.inflate(R.layout.gv_item, parent, false) ivGallery = itemView?.findViewById(R.id.ivGallery) as ImageView ivGallery!!.setImageURI(mArrayUri[position]) return itemView }
Compiler will first inflate the layout file gv_item.xml
Then it will find the imageView using it’s ID. Then it will set the image preview in the image view.
Download Kotlin Select Multiple Images From Gallery
https://github.com/demonuts/Kotlin-Select-Multiple-Images-From-Gallery-And-Show-In-GridView-Android