Showing posts with label circular reveal. Show all posts

Circular Reveal Animation In Android

Figure 1

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 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 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 New Project with Android Studio

  1. Open Android Studio and Select create a new project.
  2. Name the project as per your wish and select an Empty activity.
    Android
    Figure 5
  3. Click finish button to create a new project in Android Studio.

Step 2 - Setting up the library for the project

  1. Open App Level gradle file and add the following lines to add the Libraries.
    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'  
      
        // Third Party Library  
        implementation 'com.github.ozodrukh:CircularReveal:1.1.0'  
        ...  
    }  
      
    repositories {  
        maven {  
            url "https://jitpack.io"  
        }  
    }
  2. Then click “Sync Now” to add the library.

Step 3 - Implementation of Circular Reveal for Pre-Lollipop devices

  1. 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>
  2. Here, Reveal Frame Layout itself is used to handle Reveal animation. The Reveal effect can be applied to the whole screen.
  3. 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 clips its child’s shape
    revealLayout = findViewById(R.id.reveal_items);  
    revealLayout.setVisibility(View.INVISIBLE);
  4. 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;
  5. 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();  
    }
  6. 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.
  7. 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

  1. No 5 in the previous step is slightly changed for Lollipop devices. Instead of “io.codetail.animation.ViewAnimationUtils”, use “android.view.ViewAnimationUtils”.
  2. 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();  
    }
  3. 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.