Introduction
In this article, we will learn how to create layout reveal animation in Android. This feature is available by default from Lollipop. For pre-Lollipop Android versions, an awesome library is available and it is very easy to use.
Circular Reveal animations can be found in WhatsApp while clicking the attachment option. It’s more interactive to users. We will learn in detail how to use in Lollipop and Pre-Lollipop devices separately.
Circular Reveal Animation
We already know this feature is available from Lollipop devices. To perform the same features in pre-Lollipop devices we will go for a third party
library and it’s awesome to use.
Steps
I have divided this Implementation into 4 steps as shown in the following.
- Step 1 - Creating a New Project with Android Studio
- Step 2 - Setting up the library for the project.
- Step 3 - Implementation of Circular Reveal for Pre-Lollipop devices.
- Step 4 - Implementation of Circular Reveal for Lollipop devices.
Step 1 - Creating a New Project with Android Studio
- Open Android Studio and select Create a new project.
- Name the project as you wish and select your activity template.
- Click the “Finish” button to create a new project in Android Studio.
Step 2 - Setting up the library for the project
- Open the App Level gradle file and add the following lines to add ButterKnife Library.
- dependencies {
- ...
- implementation 'com.android.support:appcompat-v7:26.1.0'
- implementation 'com.android.support:support-annotations:27.1.1'
- implementation 'com.android.support.constraint:constraint-layout:1.1.2'
- implementation 'com.android.support:design:26.1.0'
-
-
- implementation 'com.github.ozodrukh:CircularReveal:1.1.0'
- ...
- }
-
- repositories {
- maven {
- url "https://jitpack.io"
- }
- }
- Then click “Sync Now” to add the library.
Step 3 - Implementation of Circular Reveal for Pre-Lollipop devices
- Open your designer file that is your XML layout file (In my case, I have opened my xml file) and paste the following code below toolbar if you want.
- <io.codetail.widget.RevealFrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id="@+id/reveal_items"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/white"
- android:orientation="horizontal"
- android:gravity="center"
- android:padding="16dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:layout_gravity="center"
- android:id="@+id/imageView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/ic_search_green" />
-
- <EditText
- android:id="@+id/search_box"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="Search Here..."
- android:inputType="text"
- android:imeOptions="actionSearch"
- android:maxLines="1"/>
- </LinearLayout>
- </LinearLayout>
-
- </io.codetail.widget.RevealFrameLayout>
- Here, Reveal Frame Layout itself is used to handle Reveal animation. The Reveal effect can be applied to the whole screen.
- We have initialized the immediate child of the RevealFrameLayout and by default, its state should be GONE or INVISIBLE. While changing its View states, the RevealFrameLayout will clip its child’s shape
- revealLayout = findViewById(R.id.reveal_items);
- revealLayout.setVisibility(View.INVISIBLE);
- To perform the animation, import SupportAnimator class from the third party library included for Pre-Lollipop devices.
- import io.codetail.animation.SupportAnimator;
- import io.codetail.animation.ViewAnimationUtils;
- Then add the following to perform the animation in forth and reverse. We need X, Y co-ordinates to perform the animation.
- SupportAnimator animator = ViewAnimationUtils.createCircularReveal(layout, cx, cy, 0, radius);
- animator.setInterpolator(new AccelerateDecelerateInterpolator());
- animator.setDuration(800);
-
- SupportAnimator animator_reverse = animator.reverse();
-
- if (hidden) {
- layout.setVisibility(View.VISIBLE);
- animator.start();
- invalidateOptionsMenu();
- hidden = false;
- } else {
- animator_reverse.addListener(new SupportAnimator.AnimatorListener() {
- @Override
- public void onAnimationStart() {
-
- }
-
- @Override
- public void onAnimationEnd() {
- layout.setVisibility(View.INVISIBLE);
- invalidateOptionsMenu();
- hidden = true;
- }
-
- @Override
- public void onAnimationCancel() {
-
- }
-
- @Override
- public void onAnimationRepeat() {
-
- }
- });
- animator_reverse.start();
- }
- Here,
- cx - co-ordinate in X axis. cy – co-ordinate in Y axis. These two are required for starting the animation in X, Y axis of the screen. It can be calculated by the following code.
- int cx = revealLayout.getRight();
- int cy = revealLayout.getTop();
- radius
can be calculated by the following code.
- int radius = Math.max(layout.getWidth(), layout.getHeight());
- Animation can be started by SupportAnimator and it is assigned to another SupportAnimator which is used to start the reverse animation. So, the reverse animation will be handled by itself.
- SupportAnimator animator = ViewAnimationUtils.createCircularReveal(layout, cx, cy, 0, radius);
- animator.setInterpolator(new AccelerateDecelerateInterpolator());
- animator.setDuration(800);
-
- SupportAnimator animator_reverse = animator.reverse();
-
- if (hidden) {
- layout.setVisibility(View.VISIBLE);
- animator.start();
- invalidateOptionsMenu();
- hidden = false;
- } else {
- animator_reverse.addListener(new SupportAnimator.AnimatorListener() {
- @Override
- public void onAnimationStart() {
-
- }
-
- @Override
- public void onAnimationEnd() {
- layout.setVisibility(View.INVISIBLE);
- invalidateOptionsMenu();
- hidden = true;
- }
-
- @Override
- public void onAnimationCancel() {
-
- }
-
- @Override
- public void onAnimationRepeat() {
-
- }
- });
- animator_reverse.start();
- }
- hidden – is a Boolean value used to know the View State.
- Probably this does not seem to work from Lollipop devices. So we have to go for a different method which is shown in the next step.
Step 4 - Implementation of Circular Reveal for Lollipop devices
- No 5 in the previous step is slightly changed for Lollipop devices. Instead of “io.codetail.animation.ViewAnimationUtils”, use “android.view.ViewAnimationUtils”.
- No need to assign animation.reverse(). Instead of it, use the following
- if (hidden) {
- Animator anim = android.view.ViewAnimationUtils.createCircularReveal(layout, cx, cy, 0, radius);
- layout.setVisibility(View.VISIBLE);
- anim.start();
- invalidateOptionsMenu();
- hidden = false;
-
- } else {
- Animator anim = android.view.ViewAnimationUtils.createCircularReveal(layout, cx, cy, radius, 0);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- layout.setVisibility(View.INVISIBLE);
- hidden = true;
- invalidateOptionsMenu();
- }
- });
- anim.start();
- }
- To reverse the animation, the radius value is interchanged. The animation is handled from toolbar menu options.
Full Code
You can find the full code implementation here.
- public class MainActivity extends AppCompatActivity {
-
- Toolbar toolbar;
- LinearLayout revealLayout;
- EditText searchBox;
- boolean hidden = true;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- revealLayout = findViewById(R.id.reveal_items);
- revealLayout.setVisibility(View.INVISIBLE);
- searchBox = findViewById(R.id.search_box);
- searchBox.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
- if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- handleKeyboard(searchBox);
- searchBox.clearFocus();
- Toast.makeText(MainActivity.this, textView.getText().toString(), Toast.LENGTH_SHORT).show();
- return true;
- }
- return false;
- }
- });
- }
-
- void handleKeyboard(View view) {
- InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- assert inputManager != null;
- inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_reveal, menu);
- if (hidden) {
- menu.findItem(R.id.action_search).setIcon(R.drawable.ic_search);
- } else {
- menu.findItem(R.id.action_search).setIcon(R.drawable.ic_close);
- }
- return true;
-
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_search:
- int cx = revealLayout.getRight();
- int cy = revealLayout.getTop();
- makeEffect(revealLayout, cx, cy);
- searchBox.setText(null);
- return true;
- case android.R.id.home:
- supportFinishAfterTransition();
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- private void makeEffect(final LinearLayout layout, int cx, int cy) {
-
- int radius = Math.max(layout.getWidth(), layout.getHeight());
-
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
-
- SupportAnimator animator = ViewAnimationUtils.createCircularReveal(layout, cx, cy, 0, radius);
- animator.setInterpolator(new AccelerateDecelerateInterpolator());
- animator.setDuration(800);
-
- SupportAnimator animator_reverse = animator.reverse();
-
- if (hidden) {
- layout.setVisibility(View.VISIBLE);
- animator.start();
- invalidateOptionsMenu();
- hidden = false;
- } else {
- animator_reverse.addListener(new SupportAnimator.AnimatorListener() {
- @Override
- public void onAnimationStart() {
-
- }
-
- @Override
- public void onAnimationEnd() {
- layout.setVisibility(View.INVISIBLE);
- invalidateOptionsMenu();
- hidden = true;
- }
-
- @Override
- public void onAnimationCancel() {
-
- }
-
- @Override
- public void onAnimationRepeat() {
-
- }
- });
- animator_reverse.start();
- }
- } else {
- if (hidden) {
- Animator anim = android.view.ViewAnimationUtils.createCircularReveal(layout, cx, cy, 0, radius);
- layout.setVisibility(View.VISIBLE);
- anim.start();
- invalidateOptionsMenu();
- hidden = false;
-
- } else {
- Animator anim = android.view.ViewAnimationUtils.createCircularReveal(layout, cx, cy, radius, 0);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- layout.setVisibility(View.INVISIBLE);
- hidden = true;
- invalidateOptionsMenu();
- }
- });
- anim.start();
- }
- }
- }
- }
Output Demo
You can find the output of this example.
Download Code
You can download the full source code of the article in
GitHub. If you like this article, do star the repo in GitHub.