Android Modern Image Slider using ViewPager 2 and KenBurnsView | Android Studio

Golap Gunjan Barman
5 min readFeb 17, 2021

In this tutorial, we will make a modern image slider using ViewPager2 in which we will implement a ken burns view to animate the image. Here we will use static data for the slider but you can use data from API as well.

The question is what is Ken Burns View effect? Let’s see what is Ken Burns effect

Ken Burns View

Android library that provides an extension to ImageView that creates an immersive experience by animating it’s drawable using the Ken Burns Effect.

KenBurnsView provides the following advantages:

  • Control: you can change the duration and the interpolator of transitions and pause/resume them. You can also listen to events like onTransitionStart() and onTransitionEnd();
  • Highly extensible: you can define how the rectangles to be zoomed and panned will be generated;
  • Libs friendly: since KenBurnsView is a direct extension of ImageView, it seamlessly works out of the box with your favorite image loader library;
  • Easy to use: you can start using it right away. All you need to do is to drop the JAR file into your project and replace ImageView elements in your XML layout files by com.flaviofaria.kenburnsview.KenBurnsView ones.

Now let’s create a modern Image Slider using Viewpager

Gradle Integration

  • add these dependencies in your build.gradle app folder
dependencies {
implementation 'com.google.android.material:material:1.3.0'
//Ppicasso- for image loading
implementation 'com.squareup.picasso:picasso:2.71828'
//Ken Burns view - for ken burns effect
implementation 'com.flaviofaria:kenburnsview:1.0.7'
//viewpager 2
implementation "androidx.viewpager2:viewpager2:1.0.0"
}
  • add maven repository in your build.gradle project folder
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}

Design the main view

now in the main XML file create your design and add the view pager.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="@color/white"
tools:context=".ModernImageSlider.ModernImageSliderActivity">
<androidx.cardview.widget.CardView
android:layout_width="40dp"
android:layout_height="40dp"
android:id="@+id/cardProfilePic"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
app:cardCornerRadius="20dp"
app:cardElevation="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/app_name"
android:src="@drawable/profile_pic"
android:scaleType="fitXY"/>
</androidx.cardview.widget.CardView> <TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/textHello"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:text="@string/hello_android"
android:textColor="#212121"
android:textSize="17sp"
android:fontFamily="@font/product_sans_bold"
app:layout_constraintBottom_toBottomOf="@id/cardProfilePic"
app:layout_constraintEnd_toStartOf="@id/cardProfilePic"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/cardProfilePic"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textWhereAreWe"
android:text="@string/seven_wonders"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:textColor="#212121"
android:textSize="35sp"
android:fontFamily="@font/antonio_bold"
android:includeFontPadding="false"
app:layout_constraintTop_toBottomOf="@id/cardProfilePic"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textGoing"
android:text="@string/of_the_world"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:textColor="#757575"
android:textSize="30sp"
android:includeFontPadding="false"
android:fontFamily="@font/antonio_bold"
app:layout_constraintTop_toBottomOf="@id/textWhereAreWe"/>
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/locationViewPager"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:paddingStart="40dp"
android:paddingEnd="40dp"
app:layout_constraintBottom_toTopOf="@id/bottomText"
app:layout_constraintTop_toBottomOf="@id/textGoing"/>
<TextView
android:layout_width="match_parent"
android:layout_height="70dp"
android:id="@+id/bottomText"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center"
android:text="@string/do_you_find_it_helpful"
android:fontFamily="@font/product_sans_bold"
android:textColor="#212121"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Design Viewpager item

  • now create a new Layout Resource File in the layout folder for the item contents of the view pager, where we add our ken burns effect for the images.
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_margin="5dp"
app:cardCornerRadius="12dp"
app:cardElevation="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.flaviofaria.kenburnsview.KenBurnsView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/kbvLocation"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:background="@drawable/background_star_rating"
android:gravity="center_vertical"
android:paddingStart="8dp"
android:paddingTop="2dp"
android:paddingEnd="8dp"
android:paddingBottom="2dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="16dp"
android:layout_height="16sp"
android:contentDescription="@string/app_name"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:src="@drawable/ic_baseline_star_24"
app:tint="#FFFFFF" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textStartRating"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:textColor="#FFFFFF"
android:textSize="15sp"/>
</LinearLayout> <View
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#D6000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="@id/textTitle"/>
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:id="@+id/imageLocation"
android:layout_marginStart="20dp"
android:contentDescription="@string/app_name"
android:src="@drawable/ic_baseline_location_on_24"
app:tint="#FFFFFF"
app:layout_constraintBottom_toBottomOf="@id/textLocation"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/textLocation"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/textLocation"
android:layout_marginStart="2dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="8dp"
android:textColor="#FFFFFF"
android:textSize="14sp"
android:fontFamily="@font/antonio_bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/imageLocation"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textTitle"
android:layout_marginStart="20dp"
android:layout_marginBottom="2dp"
android:layout_marginEnd="20dp"
android:paddingTop="4dp"
android:textColor="#FFFFFF"
android:textSize="19sp"
android:fontFamily="@font/antonio_bold"
app:layout_constraintBottom_toTopOf="@id/imageLocation"/>
</androidx.constraintlayout.widget.ConstraintLayout></androidx.cardview.widget.CardView>

you can change it according to your needs.

Create a model class

now create a model class for the items data we’re going to set in the adapter class. For that create a new java file in your package.

package com.codewithgolap.androidtutorial.ModernImageSlider;public class TravelLocation {    public String title, location, imageUrl;
public Float startRating;
}

Create an adapter

Now create an adapter class for your item layout that we need to inflate.

package com.codewithgolap.androidtutorial.ModernImageSlider;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.codewithgolap.androidtutorial.R;
import com.flaviofaria.kenburnsview.KenBurnsView;
import com.squareup.picasso.Picasso;
import java.util.List;public class TravelLocationAdapter extends RecyclerView.Adapter<TravelLocationAdapter.TravelLocationViewHolder>{ private List<TravelLocation> travelLocations; public TravelLocationAdapter(List<TravelLocation> travelLocations) {
this.travelLocations = travelLocations;
}
@NonNull
@Override
public TravelLocationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new TravelLocationViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_container_location,
parent, false
)
);
}
@Override
public void onBindViewHolder(@NonNull TravelLocationViewHolder holder, int position) {
holder.setLocationData(travelLocations.get(position));
}
@Override
public int getItemCount() {
return travelLocations.size();
}
static class TravelLocationViewHolder extends RecyclerView.ViewHolder { private KenBurnsView kbvLocation;
private TextView textTitle, textLocation, textStartRating;
TravelLocationViewHolder(@NonNull View itemView) {
super(itemView);
kbvLocation = itemView.findViewById(R.id.kbvLocation);
textTitle = itemView.findViewById(R.id.textTitle);
textStartRating = itemView.findViewById(R.id.textStartRating);
textLocation = itemView.findViewById(R.id.textLocation);
}
void setLocationData(TravelLocation travelLocation){
Picasso.get().load(travelLocation.imageUrl).into(kbvLocation);
textTitle.setText(travelLocation.title);
textLocation.setText(travelLocation.location);
textStartRating.setText(String.valueOf(travelLocation.startRating));
}
}
}

Add functionality

now in the main java file, add your view pager and create a list of the model class to add the static data. Here we use static data.

package com.codewithgolap.androidtutorial.ModernImageSlider;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.CompositePageTransformer;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import android.view.View;
import com.codewithgolap.androidtutorial.R;import java.util.ArrayList;
import java.util.List;
public class ModernImageSliderActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_modern_image_slider);
ViewPager2 locationViewPager = findViewById(R.id.locationViewPager); List<TravelLocation> travelLocations = new ArrayList<>(); TravelLocation travelLocationET = new TravelLocation();
travelLocationET.imageUrl = "https://images.unsplash.com/photo-1500297726361-1715d90aec00?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1047&q=80";
travelLocationET.title = "China";
travelLocationET.location = "Grate Wall of China";
travelLocationET.startRating = 4.8f;
travelLocations.add(travelLocationET);
TravelLocation travelLocationMV = new TravelLocation();
travelLocationMV.imageUrl = "https://images.unsplash.com/photo-1567930009485-07d60c813306?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80";
travelLocationMV.title = "Mexico";
travelLocationMV.location = "Chichén Itzá";
travelLocationMV.startRating = 4.5f;
travelLocations.add(travelLocationMV);
TravelLocation travelLocationTM = new TravelLocation();
travelLocationTM.imageUrl = "https://images.unsplash.com/photo-1589825274556-94746a018766?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80";
travelLocationTM.title = "Jordan";
travelLocationTM.location = "Petra";
travelLocationTM.startRating = 4.7f;
travelLocations.add(travelLocationTM);
TravelLocation travelLocationF = new TravelLocation();
travelLocationF.imageUrl = "https://images.unsplash.com/photo-1456244440184-1d494704a505?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80";
travelLocationF.title = "Peru";
travelLocationF.location = "Machu Picchu";
travelLocationF.startRating = 4.6f;
travelLocations.add(travelLocationF);
TravelLocation travelLocationFi = new TravelLocation();
travelLocationFi.imageUrl = "https://images.unsplash.com/photo-1595688878177-b72dfeeed683?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1151&q=80";
travelLocationFi.title = "Brazil";
travelLocationFi.location = "Christ the Redeemer";
travelLocationFi.startRating = 4.6f;
travelLocations.add(travelLocationFi);
TravelLocation travelLocationS = new TravelLocation();
travelLocationS.imageUrl = "https://images.unsplash.com/photo-1482401204742-eb3c31c24722?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1112&q=80";
travelLocationS.title = "Italy";
travelLocationS.location = "Colosseum";
travelLocationS.startRating = 4.7f;
travelLocations.add(travelLocationS);
TravelLocation travelLocationSe = new TravelLocation();
travelLocationSe.imageUrl = "https://images.unsplash.com/photo-1524492412937-b28074a5d7da?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1051&q=80";
travelLocationSe.title = "India";
travelLocationSe.location = "Taj Mahal";
travelLocationSe.startRating = 4.8f;
travelLocations.add(travelLocationSe);
locationViewPager.setAdapter(new TravelLocationAdapter(travelLocations)); locationViewPager.setClipToPadding(false);
locationViewPager.setClipChildren(false);
locationViewPager.setOffscreenPageLimit(3);
locationViewPager.getChildAt(0).setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
compositePageTransformer.addTransformer(new MarginPageTransformer(40));
compositePageTransformer.addTransformer(new ViewPager2.PageTransformer() {
@Override
public void transformPage(@NonNull View page, float position) {
float r = 1 - Math.abs(position);
page.setScaleY(0.90f + r * 0.04f);
}
});
locationViewPager.setPageTransformer(compositePageTransformer);
}
}

Output

Follow me on IG at @androidapps.development.blogs

--

--

Golap Gunjan Barman

Hi everyone, myself Golap an Android app developer with UI/UX designer.