In this tutorial we’ll implement a different kind of animation transition namely Shared Element Transition between activities.
Android Shared Element Transition determines how shared element views are animated from one Activity/Fragment to another during a scene transition.
In pre-Lollipop devices Android used to support transitions between activities and fragments that involved transitions of the entire view hierarchies. However there are many cases when a view (let’s say ListView) consists of different row items. More often than not, clicking any row would show details of that respective row in the next screen. So to emphasise continuity between the two activities, we’ll show a circular reveal animation. This improves the user experience by drawing their focus towards the relationship between the new screen and the previous screen. A Shared Element Transition like this is more commonly seen in music playlist apps.
Note: This type of transition works only for android SDK>21.
Let’s begin the implementation of the app. In this tutorial we’ll implement custom ListView rows and show the desired transition for each of them.
This project consists of 2 activities and a CustomAdapter for the ListView.
To enable this transitions add the following snippet inside the AppTheme tag in styles.xml
.
1 2 3 |
<item name="android:windowContentTransitions">true</item> |
For both the layouts with this transition we need to assign a android:transitionName
attribute.
The activity_main.xml
populates a ListView and the details_activity.xml
is for the the details screen. Both are shown below.
1 2 3 4 5 6 7 8 9 10 11 12 |
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:transitionName="@string/transition" android:orientation="vertical"> <ListView android:layout_width="wrap_content" android:id="@+id/list_view" android:layout_height="wrap_content"/> </LinearLayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/activity_horizontal_margin" android:id="@+id/layout" android:transitionName="@string/transition" tools:context="com.journaldev.sharedelementtransition.MainActivity"> <TextView android:gravity="center" android:textColor="@android:color/white" android:id="@+id/heading" android:layout_width="match_parent" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_height="wrap_content" /> <TextView android:gravity="center" android:id="@+id/language" android:textColor="@android:color/white" android:layout_width="match_parent" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_height="wrap_content" android:layout_below="@+id/heading" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <TextView android:gravity="center" android:id="@+id/desc" android:textColor="@android:color/white" android:layout_width="match_parent" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout> |
As you can see a android:transitionName
attribute is declared as a string in the root view of both the layouts.
We’ve created a custom ListView which populates its layout from a ArrayList of String arrays. The layout and adapter of the ListView are given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:padding="@dimen/activity_horizontal_margin" android:background="@color/md_black_1000" android:layout_margin="5dp" android:id="@+id/rl" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/primary_textview" android:gravity="center" android:textColor="@android:color/white" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:id="@+id/textView" android:layout_below="@+id/primary_textview" android:textColor="@android:color/white" android:gravity="center" /> </RelativeLayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
public class CustomAdapter extends BaseAdapter { ArrayList<String[]> arrayList; Context c; public CustomAdapter(Context c, ArrayList<String[]> list) { arrayList = list; this.c = c; } @Override public int getCount() { // TODO Auto-generated method stub return arrayList.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return arrayList.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub View row = null; LayoutInflater inflater = (LayoutInflater) c .getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (convertView == null) { row = inflater.inflate(R.layout.row_layout, parent, false); } else { row = convertView; } String[] detail = arrayList.get(position); RelativeLayout rl= (RelativeLayout)row.findViewById(R.id.rl); rl.setBackgroundColor(Color.parseColor(detail[3])); TextView name = (TextView) row.findViewById(R.id.primary_textview); name.setText(detail[0]); TextView email = (TextView) row.findViewById(R.id.textView); email.setText(detail[1]); return row; } } |
The MainActivity.java
and DetailsActivity.java
are given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.journaldev.sharedelementtransition; import android.content.Intent; import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityOptionsCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ArrayList<String[]> values = new ArrayList<String[]>(); values.add(new String[]{"Android", "Java", getString(R.string.android),"https://www.journaldev.com/10473/#" + Integer.toHexString(getResources().getColor(R.color.md_light_green_900))}); values.add(new String[]{"iOS", "Swift", getString(R.string.ios),"https://www.journaldev.com/10473/#" + Integer.toHexString(getResources().getColor(R.color.md_amber_A700))}); values.add(new String[]{"Xamarin", "C#",getString(R.string.xamarin),"https://www.journaldev.com/10473/#" + Integer.toHexString(getResources().getColor(R.color.md_pink_A700))}); values.add(new String[]{"PhoneGap", "HTML CSS and JScript",getString(R.string.phonegap),"https://www.journaldev.com/10473/#" + Integer.toHexString(getResources().getColor(R.color.md_brown_800))}); ListView listView = (ListView) findViewById(R.id.list_view); CustomAdapter adapter = new CustomAdapter(this, values); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(MainActivity.this, DetailsActivity.class); intent.putExtra("array",values.get(position)); // Get the transition name from the string String transitionName = getString(R.string.transition); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, view, // Starting view transitionName // The String ); ActivityCompat.startActivity(MainActivity.this, intent, options.toBundle()); } }); } } |
When an activity is finished, instead of finish() we invoke ActivityCompat.finishAfterTransition(this);
as shown in the code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class DetailsActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.details_activity); String[] array= getIntent().getStringArrayExtra("array"); RelativeLayout rl= (RelativeLayout)findViewById(R.id.layout); rl.setBackgroundColor(Color.parseColor(array[3])); TextView textView= (TextView)findViewById(R.id.heading); textView.setText(array[0]); TextView type= (TextView)findViewById(R.id.language); type.setText(array[1]); TextView desc=(TextView)findViewById(R.id.desc); desc.setText(array[2]); } @Override public void onBackPressed() { ActivityCompat.finishAfterTransition(this); } } |
The output of the application in action is given below.
This brings an end to this tutorial. You can download the final Android Transition Animation – Shared Element Transition Project from the link below.