Add Material Shadow in Android

Recently we discuss about Kotlin Native and daily used kotlin code, today we are going to discuss about Google’s Material Shadow library.

Material Shadow is a library for seamlessly integrating Material shadows. The library takes existing material shadows to next level by adding the following features :

  • Convex shadows : The shadows are not only rectangular or circular, they can take any convex shape depending on the view and its content.
  • Support for shadow offsets : The library allows developers to set X and Y offset for the shadows.
  • Support for shadow intensity : The library also has support for setting shadow intensity via shadowAlpha attribute.
  • Shadows for semi-transparent views : The library allows shadows for semi-transparent views.
  • Support for Async Shadow calculations : The library allows the operations to be async to avoid blocking the UI thread for long calculations.
  • Shadow animations : The library supports fade out animation for shadow.

How does Material Shadow work ?

The MaterialShadowViewWrapper is an extension of Relative Layout. The MaterialShadowFrameLayoutWrapper is an extension of FrameLayout. Use any one of them as per your convenience.

Did you use ever : Building UI with ConstraintLayout in Android?

All the child views go through the same process of generating shadow as given below :

  1. First a bitmap is generated from the drawing cache of the view.
  2. The bitmap is traversed pixel by pixel to remove all transparent pixels and get a list of points corresponding to the actual outline of the content of the view.
  3. Since the points corresponding to outline may give a concave path, hence GrahamScan algorithm is used to generate a convex hull of the outline points.
  4. A path is created from the points of the resulting convex hull.
  5. This path is passed to a CustomViewOutlineProvider object that is later attached to the view itself.
  6. Hence we get a convex shadow for any type of view based on its content.

[tmh_article_ads]

P.S. : All the calculations related to graham scan are done asynchronously by default. This behavior can be controlled by calculateAsync parameter.

Do you knowHow Android Instant Run Works?

How to use this library?

Just add the following dependency in your app’s build.gradle

dependencies {
      compile 'com.sdsmdg.harjot:materialshadows:1.2.5'
}

Example Usage 1 (Simple)

XML

<com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:elevation="5dp"
            android:src="@drawable/poly" />

</com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper>

Result

Simple example of Material Shadow
Simple example of Material Shadow

Example Usage 2 (Offset)

XML

<com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:shadowOffsetX="-15"
        app:shadowOffsetY="30">

        <ImageView
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:elevation="10dp"
            android:src="@drawable/poly" />

</com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper>

Result

Material Shadow example with offsets

You should never miss : Top 30 Android Tools Every Developer should Know

Example Usage 3 (Shadow intensity)

XML

<com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:shadowOffsetX="-15"
        app:shadowOffsetY="30"
        app:shadowAlpha="0.9">

        <ImageView
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:elevation="10dp"
            android:src="@drawable/poly" />

</com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper>

Result

Material Shadow with Intensity
Material Shadow with Intensity

Example Usage 4 (Semi-transparent views)

XML

<com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:shadowOffsetX="-30"
        app:shadowOffsetY="30">

        <ImageView
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:elevation="10dp"
            android:background="#55000000" />

</com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper>

Result

Material Shadow with Semi-transparent views

Recommended : Android Studio Shortcut key every developer need to know

Using MaterialShadows with custom ViewGroups

Since the ShadowGenerator.java (Download project from given link in Download section) encapsulates all the code related to the generation of convex shadows, it is really easy to plug in convex shadows with any custom ViewGroup or some platform ViewGroup like LinearLayout etc.

public class CustomShadowWrapper extends CustomViewGroup {
      
      ShadowGenerator shadowGenerator;
      
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
          super.onLayout(changed, l, t, r, b);
          if (shadowGenerator == null) {
              shadowGenerator = new ShadowGenerator(this, offsetX, offsetY, shadowAlpha, shouldShowWhenAllReady, shouldCalculateAsync, shouldAnimateShadow, animationDuration);
          }
          shadowGenerator.generate();
      }
    
      @Override
      protected void onDetachedFromWindow() {
          super.onDetachedFromWindow();
          if (shadowGenerator != null) {
              shadowGenerator.releaseResources();
          }
      }
      
}

Note : Make sure to define all the 7 parameters required for ShadowGenerator, namely offsetXoffsetYshadowAlphashouldShowWhenAllReadyshouldCalculateAsyncshouldAnimateShadowanimationDuration in the custom wrapper and handle changes in their values.

In case any parameter value changes, say OffsetX, add the following code inside the setter method for OffsetX :

public void setOffsetX(float offsetX) {
      this.offsetX = offsetX;
      if (shadowGenerator != null) {
            shadowGenerator.setOffsetX(offsetX);
      }
}

If you want to add custom XML attributes with your CustomShadowWrapper class, add this to attrs.xml  and handle them in your CustomShadowWrapper class accordingly.

<declare-styleable name="CustomShadowWrapper">
        <attr name="shadowOffsetX" />
        <attr name="shadowOffsetY" />
        <attr name="shadowAlpha" />

        <attr name="calculateAsync" />
        <attr name="showWhenAllReady" />
        <attr name="animateShadow" />
        <attr name="animationDuration" />
    </declare-styleable>

Download

Documentation

XML attribute Java set methods Description Default Value
shadowOffsetX setOffsetX(…) Set the X-offset of the shadow 0.0f
shadowOffsetY setOffsetX(…) Set the Y-offset of the shadow 0.0f
shadowAlpha setShadowAlpha(…) Set the value of shadow intensity (alpha) 1.0f
calculateAsync setShouldCalculateAsync(…) Set the flag for async shadow calculations. true
showWhenAllReady setShowShadowsWhenAllReady(…) Set the flag for showing all shadows after all calculations are over true
animateShadow setShouldAnimateShadow(…) Set the flag for shadow animation true
animationDuration setAnimationDuration(…) Set the value of shadow animation duration. 300ms

Limitations

  1. Since the bitmap is traversed pixel by pixel, the performance for large views is bad. Hence the use of the library is limited to small views.
  2. Currently the shadow is generated only for direct children of the MaterialShadowViewWrapper. Hence if the desired views are placed inside a Linear Layout or some other view group, then each view must be wrapped by a seperate MaterialShadowViewWrapper or MaterialShadowFrameLayoutWrapper or a custom view group wrapper may be implemented.
  3. Each child of MaterialShadowViewWrapper or custom view group wrapper is assigned the same offset and shadow intensity. If fine control over every view’s shadow is required then it must be wrapped inside its own MaterialShadowViewWrapper or MaterialShadowFrameLayoutWrapper.

 

By Tell Me How

It is a technology blog and admin has excellent experience in programming from 5+ year. You can contact us at ceo.tellmehow@gmail.com

Share your thoughts

Leave a Reply

Loading Facebook Comments ...
Loading Disqus Comments ...