In this tutorial, we will develop a complete Android Photo Gallery application in Kotlin. I will share the source code of my gallery application from splash screen till the end.
we will also cover following topics in Kotlin.
If you are a beginner and you are here to learn Kotlin programming language. I recommend you go through these tutorials first.
So you will learn a lot about Kotlin if you develop this simple photo gallery application in Android using Kotlin programming language.
Create a new project in Android Studio and enable check “Add support for Kotlin” while creating a new project.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
This is our Model class which has information about all folders like Folder Name (Whatsapp Images, Camera), image path of the most recent image in a folder, total number of images in a folder.
As we have to pass this information from splash screen to our MainActivity using Intent. so “Albums” data class will implement Parcelable Interface.
data class Albums(var folderNames: String, var imagePath: String, var imgCount: Int, var isVideo: Boolean) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readInt(),
parcel.readByte() != 0.toByte()) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(folderNames)
parcel.writeString(imagePath)
parcel.writeInt(imgCount)
parcel.writeByte(if (isVideo) 1 else 0)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Albums> {
override fun createFromParcel(parcel: Parcel): Albums {
return Albums(parcel)
}
override fun newArray(size: Int): Array<Albums?> {
return arrayOfNulls(size)
}
}
}
In the onCreate method of our Splash Activity first of all we need access to external storage and for that, we have to ask a user to permit our application to access images stored on a device.
After a user has granted read_external_storage permission.
We can load images from storage and send image name, path and the total number of images in a folder to our Main Activity for showing all folders and their most recent image in Recyclerview.
// SplashActivity.kt
internal var SPLASH_TIME_OUT = 800
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
Handler().postDelayed(
{
// check if user has grannted permission to access device external storage.
// if not ask user for access to external storage.
if (!checkSelfPermission()) {
requestPermission()
} else {
// if permission granted read images from storage.
// source code for this function can be found below.
loadAllImages()
}
}, SPLASH_TIME_OUT.toLong())
}
private fun requestPermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 6036)
}
private fun checkSelfPermission(): Boolean {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
return false
} else
return true
}
If a user has granted or denied permission this method will be called.
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
6036 -> {
if (grantResults.size > 0) {
var permissionGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED
if (permissionGranted) {
// Now we are ready to access device storage and read images stored on device.
loadAllImages()
} else {
Toast.makeText(this, "Permission Denied! Cannot load images.", Toast.LENGTH_SHORT).show()
}
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
if permission granted we can load images from internal storage and pass Images to MainActivity.kt
This function will call another method getAllShownImagesPath(context) which will return List of type Albums.
And pass the returning Albums list to MainActivity via Intent.
MainActivity will show a list of all folders which has images and videos for example (Camera, Instagram, Whatsapp Images) in RecyclerView.
fun loadAllImages() {
var imagesList = getAllShownImagesPath(this)
var intent = Intent(this, MainActivity::class.java)
intent.putParcelableArrayListExtra("image_url_data", imagesList)
startActivity(intent)
finish()
}
In above code we have object of Data Class and we need to pass this data to another activity so we are passing as serialized data.
In this code snippet, we are reading all folders which has images and videos using MediaStore class.
And returning result as ArrayList<Albums>.
private fun getAllShownImagesPath(activity: Activity): ArrayList<Albums> {
val uri: Uri
val cursor: Cursor
var cursorBucket: Cursor
val column_index_data: Int
val column_index_folder_name: Int
val listOfAllImages = ArrayList<String>()
var absolutePathOfImage: String? = null
var albumsList = ArrayList<Albums>()
var album: Albums? = null
val BUCKET_GROUP_BY = "1) GROUP BY 1,(2"
val BUCKET_ORDER_BY = "MAX(datetaken) DESC"
uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.ImageColumns.BUCKET_ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATA)
cursor = activity.contentResolver.query(uri, projection, BUCKET_GROUP_BY, null, BUCKET_ORDER_BY)
if (cursor != null) {
column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
column_index_folder_name = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
while (cursor.moveToNext()) {
absolutePathOfImage = cursor.getString(column_index_data)
Log.d("title_apps", "bucket name:" + cursor.getString(column_index_data))
val selectionArgs = arrayOf("%" + cursor.getString(column_index_folder_name) + "%")
val selection = MediaStore.Images.Media.DATA + " like ? "
val projectionOnlyBucket = arrayOf(MediaStore.MediaColumns.DATA, MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
cursorBucket = activity.contentResolver.query(uri, projectionOnlyBucket, selection, selectionArgs, null)
Log.d("title_apps", "bucket size:" + cursorBucket.count)
if (absolutePathOfImage != "" && absolutePathOfImage != null) {
listOfAllImages.add(absolutePathOfImage)
albumsList.add(Albums(cursor.getString(column_index_folder_name), absolutePathOfImage, cursorBucket.count, false))
}
}
}
return getListOfVideoFolders(albumsList)
}
// This function is resposible to read all videos from all folders.
private fun getListOfVideoFolders(albumsList: ArrayList<Albums>): ArrayList<Albums> {
var cursor: Cursor
var cursorBucket: Cursor
var uri: Uri
val BUCKET_GROUP_BY = "1) GROUP BY 1,(2"
val BUCKET_ORDER_BY = "MAX(datetaken) DESC"
val column_index_album_name: Int
val column_index_album_video: Int
uri = android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI
val projection1 = arrayOf(MediaStore.Video.VideoColumns.BUCKET_ID,
MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME,
MediaStore.Video.VideoColumns.DATE_TAKEN,
MediaStore.Video.VideoColumns.DATA)
cursor = this.contentResolver.query(uri, projection1, BUCKET_GROUP_BY, null, BUCKET_ORDER_BY)
if (cursor != null) {
column_index_album_name = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME)
column_index_album_video = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)
while (cursor.moveToNext()) {
Log.d("title_apps", "bucket video:" + cursor.getString(column_index_album_name))
Log.d("title_apps", "bucket video:" + cursor.getString(column_index_album_video))
val selectionArgs = arrayOf("%" + cursor.getString(column_index_album_name) + "%")
val selection = MediaStore.Video.Media.DATA + " like ? "
val projectionOnlyBucket = arrayOf(MediaStore.MediaColumns.DATA, MediaStore.Video.Media.BUCKET_DISPLAY_NAME)
cursorBucket = this.contentResolver.query(uri, projectionOnlyBucket, selection, selectionArgs, null)
Log.d("title_apps", "bucket size:" + cursorBucket.count)
albumsList.add(Albums(cursor.getString(column_index_album_name), cursor.getString(column_index_album_video), cursorBucket.count, true))
}
}
return albumsList
}
We have learned What we need to do for developing a Image Gallery Application in Android and for that purpose we have
Now create another activity and name it MainActivity.kt class in your project.
What we will do in MainActivity
// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
app:titleTextColor="@color/colorIcons" />
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/my_toolbar"
android:fitsSystemWindows="true">
<!-- Your contents -->
<include layout="@layout/include_main_content"></include>
<android.support.design.widget.NavigationView
android:id="@+id/navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:onClick="toast"
app:menu="@menu/my_navigation_menu"
app:theme="@style/NavigationDrawerStyle" />
</android.support.v4.widget.DrawerLayout>
</RelativeLayout>
//include_main_content.xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rvAlbums"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@drawable/ic_photo_camera_white_24dp"
app:layout_anchor="@id/rvAlbums"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="com.title_apps.canalpic.util.ScrollAwareFABBehavior" />
</android.support.design.widget.CoordinatorLayout>
//MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
savedState = savedInstanceState
if (savedState != null)
folder_name = savedInstanceState!!.getString("folder_name")
setSupportActionBar(my_toolbar)
// Enable the Up button
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
supportActionBar!!.setHomeAsUpIndicator(resources.getDrawable(R.drawable.ic_menu_white_24dp))
setupNavigationView()
var extra = intent.extras;
if (extra != null) {
var extraData = extra.get("image_url_data") as ArrayList<Albums>
select_fragment(extraData)
}
drawer_layout_listener()
supportActionBar!!.setTitle("Folders")
}
// here we are sending folder name on which user has clicked, for example if user has
// clicked on downloads folder from list of all folders in our gallery app we will pass that
// folder name to our next activity which will load and display all images from that folder.
override fun onItemClick(position: String, isVideo: Boolean) {
var bundle = Bundle()
bundle.putString("folder_name", position)
var intent = Intent(this, AlbumActivity::class.java)
intent.putExtra("folder_name", position)
startActivity(intent)
}
Initialize Glide and Setup RecyclerView for displaying images in our Android Gallery Application.
private var folder_name: String = ""
public fun select_fragment(imagesList: ArrayList<Albums>) {
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE).override(160, 160).skipMemoryCache(true).error(R.drawable.ic_image_unavailable)
val glide = Glide.with(this)
val builder = glide.asBitmap()
rvAlbums?.layoutManager = GridLayoutManager(this, 2)
rvAlbums?.setHasFixedSize(true)
// AlbumFoldersAdapter.kt is RecyclerView Adapter class. we will implement shortly.
rvAlbums?.adapter = AlbumFoldersAdapter(imagesList, this, options, builder, glide, this)
rvAlbums?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
}
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> glide.resumeRequests()
AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL, AbsListView.OnScrollListener.SCROLL_STATE_FLING -> glide.pauseRequests()
}
}
}
)
fab_camera?.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
launchCamera()
}
}
)
}
NavigationView and drawerLayout click listeners in Kotlin code.
// drawer layout click listener in Kotlin source code.
private fun drawer_layout_listener() {
drawer_layout.addDrawerListener(object : DrawerLayout.DrawerListener {
override fun onDrawerStateChanged(newState: Int) {
}
override fun onDrawerSlide(drawerView: View?, slideOffset: Float) {
}
override fun onDrawerClosed(drawerView: View?) {
supportActionBar!!.setHomeAsUpIndicator(resources.getDrawable(R.drawable.ic_menu_white_24dp))
}
override fun onDrawerOpened(drawerView: View?) {
supportActionBar!!.setHomeAsUpIndicator(resources.getDrawable(R.drawable.ic_keyboard_backspace_white_24dp))
}
}
)
}
// Navigation item click listener Kotlin source code.
private fun setupNavigationView() {
navigation.setNavigationItemSelectedListener(object : NavigationView.OnNavigationItemSelectedListener {
override fun onNavigationItemSelected(item: MenuItem): Boolean {
drawer_layout.closeDrawer(Gravity.START)
when (item.itemId) {
R.id.nav_all_folders -> {
}
R.id.nav_hidden_folders -> {
}
}
return false
}
})
}
Now we will add code for AlbumsFolderAdapter.kt for our RecyclerView.
// list_layout.xml
// layout file for RecyclerView adapter.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:id="@+id/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/five_dp"
android:elevation="3dp"
card_view:cardCornerRadius="@dimen/zero_dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="@dimen/album_cover_height"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/thumbnail"
android:paddingLeft="@dimen/ten_dp"
android:paddingRight="@dimen/ten_dp"
android:paddingTop="@dimen/ten_dp"
android:text="Camera"
android:textColor="@color/colorPrimaryText"
android:textSize="@dimen/fifteen_dp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title">
<TextView
android:id="@+id/photoCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/five_dp"
android:paddingLeft="@dimen/ten_dp"
android:paddingRight="@dimen/six_dp"
android:textColor="@color/colorSecondaryText"
android:textSize="@dimen/twelve_dp" />
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Now create another class and name it AlbumFoldersAdapter.kt which is our RecyclerView adapter.
class AlbumFoldersAdapter(val albumList: ArrayList<Albums>, val context: Context, val options: RequestOptions, val glide: RequestBuilder<Bitmap>, val glideMain: RequestManager, val inOnItemClick: IOnItemClick) : RecyclerView.Adapter<AlbumFoldersAdapter.ViewHolder>() {
override fun onViewRecycled(holder: ViewHolder?) {
if (holder != null) {
//glideMain.clear(holder.itemView.thumbnail)
// glide.clear(holder.itemView.thumbnail)
//Glide.get(context).clearMemory()
// holder?.itemView?.thumbnail?.setImageBitmap(null)
}// Glide.clear(holder?.itemView?.thumbnail)
super.onViewRecycled(holder)
}
override fun onViewDetachedFromWindow(holder: ViewHolder) {
if (holder != null) {
// glideMain.clear(holder.itemView.thumbnail)
//Glide.get(context).clearMemory()
// holder?.itemView?.thumbnail?.setImageBitmap(null)
}
super.onViewDetachedFromWindow(holder)
}
override fun getItemCount(): Int {
return albumList.size
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
holder?.bindItems(albumList.get(position), glide, options, inOnItemClick, albumList.get(position).isVideo)
holder?.itemView?.title?.setText(albumList.get(position).folderNames)
if (albumList.get(position).isVideo)
holder?.itemView?.photoCount?.setText("" + albumList.get(position).imgCount + " videos")
else
holder?.itemView?.photoCount?.setText("" + albumList.get(position).imgCount + " photos")
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_layout, parent, false)
return ViewHolder(v)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(albumList: Albums, glide: RequestBuilder<Bitmap>, options: RequestOptions, inOnItemClick: IOnItemClick, isVideo: Boolean) {
glide.load(albumList.imagePath).apply { options }.thumbnail(0.4f)
.into(itemView.thumbnail)
itemView.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
inOnItemClick.onItemClick(albumList.folderNames, isVideo)
}
})
}}}
interface IOnItemClick {
fun onItemClick(position: String, isVideo: Boolean)
}
We are DONE with displaying all images folders and their most recent image in the particular folder in RecyclerView.
We have also added RecyclerView Adapter code, we have used Glide for loading images in RecyclerView adapter.
Now If user click on Whatsapp Images folder from the list of all folders.
We will create a new Activity which will show a list of images in that particular folder in RecyclerView.
Create Layout file for AlbumsActivity.kt
// activity_album.xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/my_album_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
app:titleTextColor="@color/colorIcons" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rvAlbumSelected"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:clipToPadding="false" />
</android.support.design.widget.CoordinatorLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_album)
setSupportActionBar(my_album_toolbar)
// Enable the Up button
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
val folder_name = intent.getStringExtra("folder_name")
supportActionBar!!.setTitle("" + folder_name)
val isVideo = intent.getBooleanExtra("isVideo", false)
init_ui_views(folder_name, isVideo)
}
var adapter: SingleAlbumAdapter? = null
private fun init_ui_views(folderName: String?, isVideo: Boolean?) {
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE).override(160, 160).skipMemoryCache(true).error(R.drawable.ic_image_unavailable)
val glide = Glide.with(this)
val builder = glide.asBitmap()
rvAlbumSelected.layoutManager = GridLayoutManager(this, 2)
rvAlbumSelected?.setHasFixedSize(true)
adapter = SingleAlbumAdapter(getAllShownImagesPath(this, folderName, isVideo), this, options, builder, glide, this)
rvAlbumSelected?.adapter = adapter
rvAlbumSelected?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
}
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> glide.resumeRequests()
AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL, AbsListView.OnScrollListener.SCROLL_STATE_FLING -> glide.pauseRequests()
}
}
}
)
}
// Read all images path from specified directory.
private fun getAllShownImagesPath(activity: Activity, folderName: String?, isVideo: Boolean?): MutableList<String> {
val uri: Uri
val cursorBucket: Cursor
val column_index_data: Int
val listOfAllImages = ArrayList<String>()
var absolutePathOfImage: String? = null
val selectionArgs = arrayOf("%" + folderName + "%")
uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val selection = MediaStore.Images.Media.DATA + " like ? "
val projectionOnlyBucket = arrayOf(MediaStore.MediaColumns.DATA, MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
cursorBucket = activity.contentResolver.query(uri, projectionOnlyBucket, selection, selectionArgs, null)
column_index_data = cursorBucket.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
while (cursorBucket.moveToNext()) {
absolutePathOfImage = cursorBucket.getString(column_index_data)
if (absolutePathOfImage != "" && absolutePathOfImage != null)
listOfAllImages.add(absolutePathOfImage)
}
return listOfAllImages.asReversed()
}
override fun onItemClick(position: String, isVideo: Boolean) {
val intent = Intent(this, PhotoActivity::class.java)
intent.putExtra("folder_name", position)
startActivity(intent)
}
class SingleAlbumAdapter(val albumList: MutableList<String>, val context: Context, val options: RequestOptions, val glide: RequestBuilder<Bitmap>, val glideMain: RequestManager, val inOnItemClick: IOnItemClick) : RecyclerView.Adapter<SingleAlbumAdapter.ViewHolder>() {
override fun onViewRecycled(holder: ViewHolder?) {
if (holder != null) {
}
super.onViewRecycled(holder)
}
override fun onViewDetachedFromWindow(holder: ViewHolder) {
if (holder != null) {
}
super.onViewDetachedFromWindow(holder)
}
override fun getItemCount(): Int {
return albumList.size
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
holder?.bindItems(albumList.get(position), glide, options, inOnItemClick)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_single_album_layout, parent, false)
return ViewHolder(v)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(albumList: String, glide: RequestBuilder<Bitmap>, options: RequestOptions, inOnItemClick: IOnItemClick) {
glide.load(albumList).apply { options }.thumbnail(0.4f)
.into(itemView.thumbnail)
itemView.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
inOnItemClick.onItemClick(albumList, false)
}
})
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:id="@+id/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/five_dp"
android:elevation="3dp"
card_view:cardCornerRadius="@dimen/zero_dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/album_cover_height"
android:layout_height="@dimen/album_cover_height"
android:scaleType="centerCrop" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
// SingleActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photo)
setSupportActionBar(toolbar)
// Enable the Up button
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
supportActionBar!!.setDisplayShowTitleEnabled(false)
val folder_name = intent.getStringExtra("folder_name")
Glide.with(this).load(folder_name).into(imageFullScreenView)
Handler().postDelayed(Runnable
{
if (supportActionBar != null)
appbar.animate().translationY(-appbar.bottom.toFloat()).setInterpolator(AccelerateInterpolator()).start()
isAppBarShown = false
}, 1500)
}
// activity_photo.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:background="@android:color/black"
tools:context="com.title_apps.canalpic.screens.detail.PhotoActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#93000000">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
app:titleTextColor="@color/colorIcons" />
</android.support.design.widget.AppBarLayout>
<ImageView
android:id="@+id/imageFullScreenView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</android.support.design.widget.CoordinatorLayout>
You have developed complete Image Gallery Android Application and along the way you learned Kotlin syntax, Glide Library, RecyclerView, NavigationView, Drawer Layout and their click listeners in Kotlin,