Mobile Development

Shared Element Transition

  • Android
  • Software
  • Mobile Dev
  • Animations



The purpose of this article is to show how to implement the Shared Element Transition in Android. This transition will make your app look nicer and more elegant.

The final result looks as follows:


Let’s get started.

Step #1: Project Dependencies

    // recyclerview
    implementation "com.android.support:recyclerview-v7:28.0.0"
    // Butterknife
    implementation "com.jakewharton:butterknife:8.8.1"
    annotationProcessor "com.jakewharton:butterknife-compiler:8.8.1"
    Important: Your minSdkVersion must be 21 for this animation to work.
  

Finally, add various images to your drawable folder. These images are the ones to be displayed in the recycler view. One website I like to use for getting free images is Pexels.com

Step #2: Style.xml

The style that is applied to all activities in this project is the following.

      <resources>
         <!-- Base application theme. -->
         <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:windowContentTransitions">true</item>
         </style>
      </resources>
    

The most important line in the style is

    <item name="android:windowContentTransitions">true</item>
    

Notice that we have set the parent to Theme.AppCompat.Light.NoActionBar to remove the action bar.

Step #3: String.xml

As you will see in later steps, we need to set a transition name in multiple parts of the application. To avoid errors, define a string in your strings.xml file:

    <string name="transition_string">sharedElementTransition</string>
  

Finally, add an item that holds a long dummy text. This will be used for showing dummy data.

    <string name="long_text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, deserunt mollit anim id est laborum</string>
  

Step #4: card.xml

Create a new XML file called card.xml. This is the file containing the design of the cards shown in the recycler view in MainActivity.java.

The complete code for card.xml is the following:

      <LinearLayout
          xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:id="@+id/linearLayout"
          android:layout_width="match_parent"
          android:layout_height="200dp"
          android:background="@android:color/transparent"
          android:transitionName="@string/transition_string"
          android:orientation="vertical"
          android:foreground="?android:attr/selectableItemBackground"
          tools:ignore="MissingPrefix">

          <ImageView
              android:id="@+id/imageView"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:scaleType="centerCrop"/>

      </LinearLayout>
  

Important: Notice that we placed the transition string created in Step #3 inside the root view.

Step #5: RecyclerViewAdapter.java

As shown in the video above, the first activity displays images. To accomplish such, we use a recycler view, which in turn needs an adapter.

I will show you the complete code for the adapter in a second. However, when reading it, pay attention to the following methods and interfaces.

    // Interface
        public interface ClickListener {
            void onCardSelected(View view, int position);
        }
    

The interface ClickListener is used for communicating the position of the image that was clicked as well as the view that was clicked. This interface will be implemented by MainActivity.java.

    public void setClickListener(RecyclerViewAdapter.ClickListener listener){
        clickListener = listener;
    }
  

The method setClickListener() is used by other activities to indicate the adapter who is interested in listening to click of items in the recycler view.

Finally, the constructor of this adapter accepts a List of integers. Each integer represents the id of an image stored in the drawables folder.

With that said, below is the complete code for the adapter class.

    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

        // Interface
        public interface ClickListener {
            void onCardSelected(View view, int position);
        }

        // Variables
        private List<Integer> items;
        private static RecyclerViewAdapter.ClickListener clickListener;

        // Constructor
        public RecyclerViewAdapter(List<Integer> items) {
            this.items = items;
        }

        @Override
        public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card, parent, false);
            return new RecyclerViewAdapter.ViewHolder(itemView);
        }

        @Override
        public int getItemCount() {
            return items.size();
        }

        @Override
        public void onBindViewHolder(RecyclerViewAdapter.ViewHolder card, int position) {

            // Setting the image. Remember that each items is a drawable resource file.
            card.imageView.setBackgroundResource(items.get(position));
        }

        //Method for getting a listener to card clicks
        public void setClickListener(RecyclerViewAdapter.ClickListener listener){
            clickListener = listener;
        }

        //region ViewHolder Class
        static class ViewHolder extends RecyclerView.ViewHolder {

            @BindView(R.id.linearLayout)
            LinearLayout linearLayout;
            @BindView(R.id.imageView)
            ImageView imageView;

            ViewHolder(View itemView) {
                super(itemView);

                // Binding the view
                ButterKnife.bind(this,itemView);

                // Set click listener for the linearlayout
                linearLayout.setOnClickListener(view -> {
                    if(clickListener != null){
                        clickListener.onCardSelected(view, getLayoutPosition());
                    }
                });
            }
        }
    }
  

Step #6: activity_main.xml

This is the xml file for the first activity (the one that contains the recycler view).

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout
         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=".MainActivity"
         android:gravity="center">

         <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

      </LinearLayout>
    

Nothing special about this xml file. It just contains the recycler view that will display the images.

Step #7: MainActivity.java

Before showing you the complete code for the first activity, pay attention to the method doSharedElementTransition(): This is the method responsible for moving to the next activity and executing the transition. It looks like this:

    // Method for executing animation
    public void doSharedElementTransition(View view, int position) {

        // Intent for launching activity
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra(SecondActivity.BUNDLE_KEY,items.get(position));

        // Get the transition name from the strings xml file
        String transitionName = getString(R.string.transition_string);

        // Creating options
        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
                        view,   // clicked view
                        transitionName    // The transition string
                );

        // Start the Intent
        ActivityCompat.startActivity(this, intent, options.toBundle());
    }
  

Notice here we use the transition string we created in step #3.

Important: Since this activity is interested in knowing what items from the recyclerview are clicked, this activity implements the interface RecyclerViewAdapter.ClickListener. This is the interface we created in the adapter class (see step #4).

With that said, the complete code for MainActivity.java is the following:

    public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.ClickListener {

        // Widgets
        @BindView(R.id.recyclerView)
        RecyclerView recyclerView;

        // Variables
        private List<Integer> items = new ArrayList<>();

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

            // Binding views
            ButterKnife.bind(this);

            // Adding images to list: Put your own images here
            items.add(R.drawable.panama);
            items.add(R.drawable.panama2);
            items.add(R.drawable.tampa);
            items.add(R.drawable.gothenburg);
            items.add(R.drawable.amsterdam);
            items.add(R.drawable.singapore);
            items.add(R.drawable.madrid);
            items.add(R.drawable.warsaw);
            items.add(R.drawable.india);
            items.add(R.drawable.china);

            // Configuring the recycler view adapter
            RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(items);
            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.setAdapter(recyclerViewAdapter);

            // Listening to view clicks
            recyclerViewAdapter.setClickListener(this);
        }

        // Listening to card clicks
        @Override
        public void onCardSelected(View view, int position) {
            doSharedElementTransition(view, position);
        }

        // Method for executing animation
        public void doSharedElementTransition(View view, int position) {

            // Intent for launching activity
            Intent intent = new Intent(this, SecondActivity.class);
            intent.putExtra(SecondActivity.BUNDLE_KEY,items.get(position));

            // Get the transition name from the strings xml file
            String transitionName = getString(R.string.transition_string);

            // Creating options
            ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this,
                            view,   // clicked view
                            transitionName    // The transition string
                    );

            // Start the Intent
            ActivityCompat.startActivity(this, intent, options.toBundle());
        }
    }
  

Step #8: SecondActivity.java

At this step, your MainActivity.java is complaining that it does not find the SecondActivity.java. To solve that problem, let’s create a new Empty Activity and call it SecondActivity.

This activity is very simple. The only thing it does is read the intent and display the passed image in the image view. The complete code for SecondActivity.java is the following:

    public class SecondActivity extends AppCompatActivity {

        // Views
        @BindView(R.id.imageView)
        ImageView imageView;

        // Variables
        public static final String BUNDLE_KEY = "place";

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

            // Binding view
            ButterKnife.bind(this);

            // Get the passed image. With Panama as default
            int image = getIntent().getIntExtra(BUNDLE_KEY, R.drawable.panama);

            // Set the image in the image view
            imageView.setImageResource(image);
        }
    }
  

Step #9: activity_second.xml

This is the xml file for SecondActivity.java. The complete code for activity_second.xml is the following:

      <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:transitionName="@string/transition_string"
         android:orientation="vertical">
         <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:layout_margin="10dp"
            android:scaleType="centerCrop"/>
         <TextView
            android:textColor="#000000"
            android:text="@string/long_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:ellipsize="end"
            android:layout_margin="10dp"/>
      </LinearLayout>
  

To keep it simple, we have just added an ImageView that displays the image selected and some dummy text (The dummy text used in the one created in step #3).

Important: Notice how in the root view (the linear layout) we have used the transition string we created in step #3.

That is it! You made it!

At this point, your application should be running and performing the Shared Element Transition.


You can find the complete code in my Github repo.

● ● ● ● ● ●

See you soon and don’t forget to clap and follow me.

● ● ●

How would you rate this article?