Carousel effect in android


            In this tutorial we learn how to code carousel effect in android using viewpager.
Carousel View is an extended ViewPager control that presents a collection of items in circular fashion.For Carousel view in android we use a customized view called as CarouselLinearLayout,which creates the view for coursel effect.
Lets see how this coursel effect make work for ourselves.
First implement the dependencies to make work coursel view. In build.gradle file add thefollowing dependencies
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'

build.gradle
                
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.coursel"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:support-v4:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

                
              
Add the followng theme for the application to look nice.In styles.xml file add the followng code:

styles.xml
     

       <resources>

    
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="Theme.AppCompat.Light.NoActionBar.FullScreen" parent="Theme.AppCompat.Light.NoActionBar">


        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>


</style>

</resources>

     
 
Now look into the main code .Create the custom carousel view in java by creating "CarouselLinearLayout.java".In this java file we write animation of the images and set the scaling of the images .CarouselLinearLayout i.e. is our custom view which is writtten in java file "CarouselLinearLayout.java" file. Using canvas we draw the outline of the images by setting their height and width."Canvas" is graphics component in android which does the job of calling Draw method and drawing the shape.

CarouselLinearLayout.java
   

     package com.example.coursel;

     import android.content.Context;
     import android.graphics.Canvas;
     import android.util.AttributeSet;
     import android.widget.LinearLayout;

     public class CarouselLinearLayout  extends LinearLayout {

         private float scale = CarouselPagerAdapter.BIG_SCALE;



         public CarouselLinearLayout(Context context,  AttributeSet attrs) {
             super(context, attrs);
         }

         public CarouselLinearLayout(Context context) {
             super(context);
         }

         public void setScaleBoth(float scale) {
             this.scale = scale;
             this.invalidate();
         }

         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);

             // The main mechanism to display scale animation, you can customize it as your needs
             int w = this.getWidth();
             int h = this.getHeight();
             canvas.scale(scale, scale, w/2, h/2);
         }
     }

   
 
Now go to the layouts and in activity_main.xml file include the ViewPager view so that in that view we insert our custom view CarouselLinearLayout in fragment file.create id "actvity_main_layout" for the LinearLayout and "my_viewpager" for the viewpager view.

activity-main.xml
                
      <?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"CarouselLinearLayout.java
      android:layout_width="match_parent"
      android:id="@+id/actvity_main_layout"
      android:layout_height="match_parent"
      android:orientation="vertical"
      tools:context=".MainActivity">

      <android.support.v4.view.ViewPager
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_gravity="center"
          android:id="@+id/my_viewpager">
      </LinearLayout>

                
              
Now create the new fragment named as "fragment_image" and open the "fragmnet_image.xml" file.In this fragment include CarouselLinearLayout as the rootview and id of the layout is "root_container" and in this layout add the imageView and textView which their id's are "pagerImg" and "text" respectively.

fragment_image.xml

  <?xml version="1.0" encoding="utf-8"?>
<com.example.coursel.CarouselLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ImageFragment"
    android:orientation="vertical"
    android:gravity="center"
    android:id="@+id/root_container"
    android:background="@android:color/transparent">

    <ImageView
        android:id="@+id/pagerImg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:contentDescription="@string/app_name"
        android:src="@drawable/wrinkleintime" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold" />


</com.example.coursel.CarouselLinearLayout>


We are now entering the important part of the code which is implementing adapter by using FragmentPagerAdapter by creating the class CarouselPagerAdapter.Now create a constructor for the class and implement the methods getItem , getCount ,which return what are the images and how many of them.Now Override the method onPageSelected to know the current position of the image and their scale of their images.By completing this code we return the position of the fragment and CarouselLinearLayout by using the methods getRootView and getFragment.

CarouselPagerAdapter.java
  
package com.example.coursel;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;

public class CarouselPagerAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener {

    public final static float BIG_SCALE = 1.0f;
    public final static float SMALL_SCALE = 0.8f;
    public final static float DIFF_SCALE = BIG_SCALE - SMALL_SCALE;
    private MainActivity context;
    private FragmentManager fm;
    private float scale;

    public CarouselPagerAdapter(MainActivity context , FragmentManager fm) {
        super(fm);
        this.context = context;
        this.fm = fm;
    }

    @Override
    public Fragment getItem(int position) {
        // make the first pager bigger than others
        try {
            if (position == MainActivity.FIRST_PAGE)
                scale = BIG_SCALE;
            else
                scale = SMALL_SCALE;

            position = position % MainActivity.count;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return ImageFragment.newInstance(context, position, scale);
    }

    @Override
    public int getCount() {

        int count = 0;
        try {
            count = MainActivity.count * MainActivity.LOOPS;
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return count;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        try {
            if (positionOffset >= 0f && positionOffset <= 1f) {
                CarouselLinearLayout cur = getRootView(position);
                CarouselLinearLayout next = getRootView(position + 1);

                cur.setScaleBoth(BIG_SCALE - DIFF_SCALE * positionOffset);
                next.setScaleBoth(SMALL_SCALE + DIFF_SCALE * positionOffset);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    @SuppressWarnings("ConstantConditions")

    private CarouselLinearLayout getRootView(int position) {
        return (CarouselLinearLayout) fm.findFragmentByTag(this.getFragmentTag(position))
                .getView().findViewById(R.id.root_container);
    }

    private String getFragmentTag(int position) {
        return "android:switcher:" + context.pager.getId() + ":" + position;
    }


}
  
In "ImageFragment.java" file we intialise the TextView and ImageView by defining them in arrays. We set the layout height and width in this file. By clicking on the image using the method OnClickListener it opens a newactivity "MovieDetailsActivity" , it displays the image in fullscreen mode. We using displaymetrics class ,we defining the parameters of the image.

ImageFragment.java
  
    package com.example.coursel;


import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;


/**
 * A simple {@link Fragment} subclass.
 */
public class ImageFragment extends Fragment {

    private static final String POSITON = "position";
    private static final String SCALE = "scale";
    private static final String DRAWABLE_RESOURE = "resource";

    private int screenWidth;
    private int screenHeight;

    private int[] imageArray = new int[]{R.drawable.alita, R.drawable.alladin,
            R.drawable.captain_marvel, R.drawable.dark_pheonix, R.drawable.dark_tower,
            R.drawable.jurassic_world, R.drawable.valeerian, R.drawable.wrinkleintime};

    private String[] nameArray = new String[]{"Alita","Alladin","Captain Marvel","Dark Pheonix","Dark Tower",
    "Jurassic World", "Valeerian","Wrinkle In Time"};



    public ImageFragment() {
        // Required empty public constructor
    }

    public static Fragment newInstance(MainActivity context, int pos, float scale) {
        Bundle b = new Bundle();
        b.putInt(POSITON, pos);
        b.putFloat(SCALE, scale);

        return Fragment.instantiate(context, ImageFragment.class.getName(), b);
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWidthAndHeight();
    }

    @SuppressLint("SetTextI18n")


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        if(container == null){
            return  null;
        }

         final int position = this.getArguments().getInt(POSITON);
        float scale = this.getArguments().getFloat(SCALE);

        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(screenWidth/2,screenHeight/2);
        LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.fragment_image, container, false);

        TextView textView = (TextView) linearLayout.findViewById(R.id.text);
        CarouselLinearLayout root = (CarouselLinearLayout) linearLayout.findViewById(R.id.root_container);
        ImageView imageView = (ImageView) linearLayout.findViewById(R.id.pagerImg);


        textView.setText(nameArray[position]);
        imageView.setLayoutParams(layoutParams);
        imageView.setImageResource(imageArray[position]);

        //handling click event
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), MovieDetailsActivity.class);
                intent.putExtra(DRAWABLE_RESOURE, imageArray[position]);
                startActivity(intent);
            }
        });

        root.setScaleBoth(scale);

        return linearLayout;
    }

    /**
     * Get device screen width and height
     */
    private void getWidthAndHeight() {
        DisplayMetrics displaymetrics = new DisplayMetrics();
        getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        screenHeight = displaymetrics.heightPixels;
        screenWidth = displaymetrics.widthPixels;
    }

}
  
In MainActivity we intilaise the viewpager,FragmentPagerAdapter,FragmentManager to include all the views in this activity. In the MainActivity we define the viewpager item size to control the array out of bounds execption. we are setting the no of images appear on mainscrren as the method setOffscreenPageLimit() and we set the setOffscreenPageLimit as 3 i.e it appears 3 images in the MainActivity.We include all our adapters,fragments,Viewx in the MainActivity.

MainActivity.java
                
package com.example.coursel;

import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;

public class MainActivity extends AppCompatActivity {

    public final static int LOOPS = 1000;
    public CarouselPagerAdapter adapter;
    public ViewPager pager;
    public static int count = 8; //ViewPager items size
    /**
     * You shouldn't define first page = 0.
     * Let define firstpage = 'number viewpager size' to make endless carousel
     */
    public static int FIRST_PAGE = 8;

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

        pager = (ViewPager) findViewById(R.id.my_viewpager);

        //set page margin between pages for viewpager
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int pageMargin = ((metrics.widthPixels / 4) * 2);
        pager.setPageMargin(-pageMargin);

        adapter = new CarouselPagerAdapter(this, getSupportFragmentManager());
        pager.setAdapter(adapter);
        adapter.notifyDataSetChanged();

        pager.addOnPageChangeListener(adapter);

        // Set current item to the middle page so we can fling to both
        // directions left and right
        pager.setCurrentItem(FIRST_PAGE);
        pager.setOffscreenPageLimit(3);

    }
}
                
              
In activity_moviedetails ,we show the details of the movie such as movie Image , move name and closing button for the MovieDetailsActivity.

activity_moviedetails.xml
  

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

    <ImageView
        android:id="@+id/img"
        android:src="@drawable/wrinkleintime"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />

    <ImageButton
        android:id="@+id/btnClose"
        android:padding="5dp"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentTop="true"
        android:layout_marginEnd="10dp"
        android:layout_marginTop="10dp"
        android:background="@drawable/close" />

</RelativeLayout>
  
In this java file we show the details of the movie and we close this activity by clicking on the button close.we use finish() method to get back the MainActivity .

MovieDetailsActivity.java
  

    package com.example.coursel;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;

public class MovieDetailsActivity extends AppCompatActivity {

    private ImageView imageView;
    private ImageButton button;
    private static final String DRAWABLE_RESOURE = "resource";


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

        imageView = (ImageView)findViewById(R.id.img);
        button = (ImageButton)findViewById(R.id.btnClose);

        int drawbleResource = getIntent().getIntExtra(DRAWABLE_RESOURE, 0);
        imageView.setImageResource(drawbleResource);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

    }

    @Override
    public void onBackPressed() {
        finish();
    }

}
  
You can access files here : Get them now