├── .gitignore ├── LICENSE.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sdsmdg │ │ └── harjot │ │ └── materialshadowsdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sdsmdg │ │ │ └── harjot │ │ │ └── materialshadowsdemo │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable │ │ ├── airbnb.png │ │ ├── facebook.png │ │ ├── google.png │ │ ├── google_play.png │ │ ├── playstore.png │ │ └── poly.png │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sdsmdg │ └── harjot │ └── materialshadowsdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── materialshadows ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sdsmdg │ │ └── harjot │ │ └── materialshadows │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sdsmdg │ │ │ └── harjot │ │ │ └── materialshadows │ │ │ ├── Constants.java │ │ │ ├── MaterialShadowFrameLayoutWrapper.java │ │ │ ├── MaterialShadowViewWrapper.java │ │ │ ├── outlineprovider │ │ │ └── CustomViewOutlineProvider.java │ │ │ ├── shadowutils │ │ │ └── ShadowGenerator.java │ │ │ └── utilities │ │ │ ├── GrahamScan.java │ │ │ └── Point2D.java │ └── res │ │ └── values │ │ ├── attrs.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sdsmdg │ └── harjot │ └── materialshadows │ └── ExampleUnitTest.java ├── screens ├── cover.png ├── example_1.png ├── example_2.png ├── example_3.png └── example_4.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Android ### 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea/ 40 | 41 | # Keystore files 42 | *.jks 43 | 44 | # External native build folder generated in Android Studio 2.2 and later 45 | .externalNativeBuild 46 | 47 | # Google Services (e.g. APIs or Firebase) 48 | google-services.json 49 | 50 | ### Android Patch ### 51 | gen-external-apklibs 52 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2016-17 Harjot Singh Oberai 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # MaterialShadows 4 | [![Platform](https://img.shields.io/badge/platform-Android-yellow.svg)](https://www.android.com) 5 | [![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 7 | 8 | A library for seamlessly integrating Material shadows. 9 | The library takes existing material shadows to next level by adding the following features : 10 | 11 | - Convex shadows : The shadows are not only rectangular or circular, they can take any convex shape depending on the view and its content. 12 | - Support for shadow offsets : The library allows developers to set X and Y offset for the shadows. 13 | - Support for shadow intensity : The library also has support for setting shadow intensity via `shadowAlpha` attribute. 14 | - Shadows for semi-transparent views : The library allows shadows for semi-transparent views. 15 | - Support for Async Shadow calculations : The library allows the operations to be async to avoid blocking the UI thread for long calculations. 16 | - Shadow animations : The library supports fade out animation for shadow. 17 | 18 | # Usage 19 | Just add the following dependency in your app's `build.gradle` 20 | ```groovy 21 | dependencies { 22 | implementation 'com.sdsmdg.harjot:materialshadows:1.2.5' 23 | } 24 | ``` 25 | 26 | # Third Party Bindings 27 | [React Native](https://github.com/facebook/react-native)
28 | You may now use this library with `react-native` via the module [here](https://github.com/prscX/react-native-material-shadows), built by [Pranav Raj Singh Chauhan](https://github.com/prscX). 29 | 30 | # How does this work ? 31 | The `MaterialShadowViewWrapper` is an extension of `Relative Layout`. The `MaterialShadowFrameLayoutWrapper` is an extension of `FrameLayout`. Use any one of them as per your convenience. 32 | 33 | All the child views go through the same process of generating shadow as given below : 34 | 1. First a bitmap is generated from the drawing cache of the view. 35 | 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. 36 | 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. 37 | 4. A path is created from the points of the resulting convex hull. 38 | 5. This path is passed to a `CustomViewOutlineProvider` object that is later attached to the view itself. 39 | 6. Hence we get a convex shadow for any type of view based on its content. 40 | 41 | P.S. : All the calculations related to graham scan are done asynchronously by default. This behavior can be controlled by `calculateAsync` parameter. (Thanks [Yaroslav!](https://github.com/yarolegovich)) 42 | 43 | # Example Usage 1 (Simple) 44 | #### XML 45 | ```xml 46 | 49 | 50 | 55 | 56 | 57 | ``` 58 | #### Result 59 | 60 | 61 | # Example Usage 2 (Offset) 62 | #### XML 63 | ```xml 64 | 69 | 70 | 75 | 76 | 77 | ``` 78 | #### Result 79 | 80 | 81 | # Example Usage 3 (Shadow intensity) 82 | #### XML 83 | ```xml 84 | 90 | 91 | 96 | 97 | 98 | ``` 99 | #### Result 100 | 101 | 102 | # Example Usage 4 (Semi-transparent views) 103 | #### XML 104 | ```xml 105 | 110 | 111 | 116 | 117 | 118 | ``` 119 | #### Result 120 | 121 | 122 | # Using MaterialShadows with custom ViewGroups 123 | Since the `ShadowGenerator.java` 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. 124 | 125 | ```java 126 | public class CustomShadowWrapper extends CustomViewGroup { 127 | 128 | ShadowGenerator shadowGenerator; 129 | 130 | @Override 131 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 132 | super.onLayout(changed, l, t, r, b); 133 | if (shadowGenerator == null) { 134 | shadowGenerator = new ShadowGenerator(this, offsetX, offsetY, shadowAlpha, shouldShowWhenAllReady, shouldCalculateAsync, shouldAnimateShadow, animationDuration); 135 | } 136 | shadowGenerator.generate(); 137 | } 138 | 139 | @Override 140 | protected void onDetachedFromWindow() { 141 | super.onDetachedFromWindow(); 142 | if (shadowGenerator != null) { 143 | shadowGenerator.releaseResources(); 144 | } 145 | } 146 | 147 | } 148 | ``` 149 | 150 | Note : Make sure to define all the 7 parameters required for `ShadowGenerator`, namely `offsetX`, `offsetY`, `shadowAlpha`, `shouldShowWhenAllReady`, `shouldCalculateAsync`, `shouldAnimateShadow`, `animationDuration` in the custom wrapper and handle changes in their values. 151 | 152 | In case any parameter value changes, say `OffsetX`, add the following code inside the setter method for `OffsetX` : 153 | ```java 154 | public void setOffsetX(float offsetX) { 155 | this.offsetX = offsetX; 156 | if (shadowGenerator != null) { 157 | shadowGenerator.setOffsetX(offsetX); 158 | } 159 | } 160 | ``` 161 | 162 | If you want to add custom XML attributes with your `CustomShadowWrapper` class, add this to `attrs.xml` ([here](/materialshadows/src/main/res/values/attrs.xml)) and handle them in your `CustomShadowWrapper` class accordingly. 163 | ```xml 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | ``` 175 | 176 | See [MaterialShadowViewWrapper](/materialshadows/src/main/java/com/sdsmdg/harjot/materialshadows/MaterialShadowViewWrapper.java) for more details. 177 | 178 | # Documentation 179 | |XML attribute |Java set methods |Description | Default Value | 180 | |----------------------|------------------------------|----------------------------------------|-------------------| 181 | |shadowOffsetX |setOffsetX(...) |Set the X-offset of the shadow |0.0f | 182 | |shadowOffsetY |setOffsetX(...) |Set the Y-offset of the shadow |0.0f | 183 | |shadowAlpha |setShadowAlpha(...) |Set the value of shadow intensity (alpha)|1.0f | 184 | |calculateAsync |setShouldCalculateAsync(...) |Set the flag for async shadow calculations. |true | 185 | |showWhenAllReady |setShowShadowsWhenAllReady(...)|Set the flag for showing all shadows after all calculations are over|true| 186 | |animateShadow |setShouldAnimateShadow(...) |Set the flag for shadow animation |true | 187 | |animationDuration |setAnimationDuration(...) |Set the value of shadow animation duration.|300ms | 188 | 189 | # Limitations 190 | 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. 191 | 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. 192 | 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`. 193 | 194 | # Credits 195 | 1. [Yaroslav](https://github.com/yarolegovich) : Implementation of asynchronous calculations and shadow animations. 196 | 2. [Pranav Raj Singh Chauhan](https://github.com/prscX) : Building react-native bridge plugin for the library. 197 | 198 | 199 | # License 200 | MaterialShadows is licensed under `MIT license`. View [license](LICENSE.md). 201 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '26.0.2' 6 | defaultConfig { 7 | applicationId "com.sdsmdg.harjot.materialshadowsdemo" 8 | minSdkVersion 21 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:25.3.0' 25 | compile project(":materialshadows") 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Harjot\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sdsmdg/harjot/materialshadowsdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sdsmdg.harjot.materialshadowsdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sdsmdg.harjot.materialshadowsdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/sdsmdg/harjot/materialshadowsdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sdsmdg.harjot.materialshadowsdemo; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.CheckBox; 7 | 8 | import com.sdsmdg.harjot.materialshadows.MaterialShadowViewWrapper; 9 | 10 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 11 | 12 | private CheckBox calculateAsync; 13 | private CheckBox showWhenAllReady; 14 | 15 | private MaterialShadowViewWrapper shadowViewWrapper; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | 22 | shadowViewWrapper = (MaterialShadowViewWrapper) findViewById(R.id.shadow_wrapper); 23 | calculateAsync = (CheckBox) findViewById(R.id.cb_calculate_async); 24 | showWhenAllReady = (CheckBox) findViewById(R.id.cb_show_when_all_ready); 25 | 26 | findViewById(R.id.btn_re_render).setOnClickListener(this); 27 | } 28 | 29 | @Override 30 | public void onClick(View v) { 31 | shadowViewWrapper.setShouldCalculateAsync(calculateAsync.isChecked()); 32 | shadowViewWrapper.setShowShadowsWhenAllReady(showWhenAllReady.isChecked()); 33 | shadowViewWrapper.requestLayout(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/airbnb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harjot-oberai/MaterialShadows/62870f336acb75fb41fa426f6779a1111b1f1f26/app/src/main/res/drawable/airbnb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harjot-oberai/MaterialShadows/62870f336acb75fb41fa426f6779a1111b1f1f26/app/src/main/res/drawable/facebook.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harjot-oberai/MaterialShadows/62870f336acb75fb41fa426f6779a1111b1f1f26/app/src/main/res/drawable/google.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/google_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harjot-oberai/MaterialShadows/62870f336acb75fb41fa426f6779a1111b1f1f26/app/src/main/res/drawable/google_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harjot-oberai/MaterialShadows/62870f336acb75fb41fa426f6779a1111b1f1f26/app/src/main/res/drawable/playstore.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/poly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harjot-oberai/MaterialShadows/62870f336acb75fb41fa426f6779a1111b1f1f26/app/src/main/res/drawable/poly.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 27 | 28 | 36 | 37 | 45 | 46 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 |