Skip to content

Commit

Permalink
Merge pull request #2 from rnpy/dev
Browse files Browse the repository at this point in the history
publishing config
  • Loading branch information
rnpy authored Mar 2, 2018
2 parents 260b9dd + b52cabb commit 609fe3b
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 60 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/build
/captures
.externalNativeBuild
/bintray.properties
65 changes: 44 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,44 @@
# ShimmerLayout

Memory efficient, simple yet highly customizable Shimmer Layout.
Simple, memory efficient and high performance Shimmer Layout for Android.

## About

`ShimmerLayout` can be used to create a shimmer effect to your Android apps loading states (similar to Facebook).

To allow better rendering on complex layouts (especially in `RecyclerView`), multiple `ShimmerLayout` can easily be synced together to use the exact same animation.
As loading states should be as fast and seamless as possible, this library leaves as much resources as possible for you to do the real work behind, by focusing on:

This implementation is very memory efficient, contrary to the most common implementations, it works without creating any Bitmaps or large objects. Rendering of the shimmer effect is also done in a single native operation, making its impact CPU and GPU usage very low.
- memory efficiency: contrary to other similar libraries that usually work using multiple large bitmaps, this library **does not use a single bitmap** or large object, making its memory footprint negligible.
- performance: on top of avoiding large bitmap manipulation, rendering of each shimmer effect frame is done using **one single native operation**, making its impact on CPU and GPU usage as low as possible.
- simplicity: `View.setVisibility()` is all you need to control this layout.
- lightweight: just over 150 methods and around 20Kb, *before* proguard does its magic.

`ShimmerLayout` also offers a unique feature allowing better rendering on complex layouts (especially when used in `RecyclerView` as a placeholder for each `ViewHolder`): multiple `ShimmerLayout` can easily be synchronized together to use the exact same animation.

Despite its simplicity and small size, `ShimmerLayout` can still be configured for much more complex use cases thanks to a few optional parameters.

Simple use case with default animation, in a `RecyclerView` allowing scrolling while elements are still loading:

![ShimmerLayout](images/shimmer.gif)

While it was originally made for placeholders, it can also be used to animate anything, a loading screen for example:
And a more complex loading screen using `ShimmerLayout` to create a color-changing, rotating shimmer effect on some animated views (in less than 20 lines!):

![ShimmerLayout](images/loading.gif)

Originally inspired by [Facebook Shimmer for Android](https://github.com/facebook/shimmer-android) and [Supercharge ShimmerLayout](https://github.com/team-supercharge/ShimmerLayout).

## Usage

// TODO gradle
Use gradle to get latest version, custom maven URL is needed for now pending inclusion in bintray jCenter:

```groovy
repositories { maven { url "https://dl.bintray.com/rnpy/shimmer" } }
implementation 'xyz.peridy.shimmer:shimmerlayout:1.0'
```

Wrap the layout you want to animate inside a `ShimmerLayout`. It is recommended to define a layout that looks like the content you're going to display:

```xml
<xyz.peridy.shimmerlayout.ShimmerLayout
android:id="@+id/shimmer_layout"
Expand Down Expand Up @@ -67,13 +83,13 @@ Wrap the layout you want to animate inside a `ShimmerLayout`. It is recommended
</xyz.peridy.shimmerlayout.ShimmerLayout>
```

For the most basic usage, that's all you have to do. When this layout is visible, it will start animating. It will automatically stop or start again when its visibility change.
For the most basic usage, that's all you need. When this layout is visible, it will start animating. It will automatically stop or start again when its visibility change, since it's intended to be a loading placeholder, you're probably already doing this anyway.

`ShimmerLayout` should work on any view, but since it's intended to be used as a loading indicator, you should keep them simple. Animated content can also be used in some cases (see `EvaluatorsDemoActivity`):
`ShimmerLayout` should work on any view, but remember this is only a loading indicator, and keep things simple. Animated content can also be used in some cases, just make sure to make it as lightweight as possible and use native Android animations (see `EvaluatorsDemoActivity`):

## Customization

### Default shimmer effect
### Basic configuration

By default, `ShimmerLayout` will create an effect based those parameters
- shimmerAngle: shadow angle
Expand All @@ -82,7 +98,7 @@ By default, `ShimmerLayout` will create an effect based those parameters
- shimmerDuration: duration in ms
- shimmerColor: color

All these can be set directly in xml layout:
All these can also be set directly in xml layout:

```xml
<xyz.peridy.shimmerlayout.ShimmerLayout
Expand All @@ -98,33 +114,38 @@ All these can be set directly in xml layout:

### Groups

Groups allow multiple `ShimmerLayout` to be synchronized with each other. It is highly recommended to use a group if using ShimmerLayout on multiple elements on the screen (in a RecyclerView for example), as if new elements are added, their animation would not be synced with existing ones:
Groups allow multiple `ShimmerLayout` to be synchronized with each other. It is highly recommended to use a group when using multiple `ShimmerLayout` on the same screen.

Without Group, if some views are added after animation is started (scrolling in a `RecyclerView` for example), you can end up in cases like this:
Without Group, if new `ShimmerLayout` are added after animation is started (scrolling down a `RecyclerView` for example), you can end up in cases like this, which would make your designers cry:

![Without group](images/nogroup.gif)

With a group, all views can be synchronizes together, no matter when they're added:
With a group, newly added views are synchronized with any existing one, making designers much happier:

![With group](images/group.gif)

To set a group, simply define a `ShimmerGroup` object in code, and pass it to all `ShimmerLayout` you want to synchronise:
To set a group, simply create a `ShimmerGroup` object in code, and pass it to all `ShimmerLayout` you want synchronised:
```kotlin
val myShimmerGroup = ShimmerGroup()

findViewById<ShimmerLayout>(R.id.shimmer_layout_1).shimmerGroup = myShimmerGroup
findViewById<ShimmerLayout>(R.id.shimmer_layout_2).shimmerGroup = myShimmerGroup
```

For the most common use (in RecyclerView), it is recommended to define the group in the adapter, and pass it to all ViewHolders (see demo app).
For the most common use (in `RecyclerView`), it is recommended to define the group in the adapter, and pass it to all ViewHolders (see demo app).

Multiple layouts using the same `ShimmerGroup` must use the same animation duration and `TimeInterpolator`.
### TimeInterpolator

A [TimeInterpolator](https://developer.android.com/reference/android/animation/TimeInterpolator.html) can be provided to modify the rate of change of the animation. For example, to have the shimmer accelerate then decelerate, simply use:
```kotlin
shimmerLayout.timeInterpolator = AccelerateDecelerateInterpolator()
```

### Evaluators

`ShimmerLayout` draws the effect on every frame using a single [drawPaint](https://developer.android.com/reference/android/graphics/Canvas.html#drawPaint\(android.graphics.Paint\)) operation. `ShimmerLayout` provides 3 ways to customize animation though `Evaluator` classes. Each of these expose a method that is called on each frame to customize the effect, so be mindful of performance when implementing them.
`ShimmerLayout` draws the effect on every frame using one single [drawPaint](https://developer.android.com/reference/android/graphics/Canvas.html#drawPaint\(android.graphics.Paint\)) operation. `ShimmerLayout` provides 3 ways to customize animation though `Evaluator` classes. Each of these expose a method that is called on each frame to customize the effect, so be mindful of performance when implementing them.

For example, this customizes the effect to use a radial gradient, growing and shrinking form the center of the view:
For example, this customizes the effect to use a color-changing radial gradient instead of the default linear gradient, growing and shrinking from the center of the view instead of using a translation from left to right:
```kotlin
matrixEvaluator = null
timeInterpolator = CycleInterpolator(1f)
Expand All @@ -145,23 +166,25 @@ colorEvaluator = object : ShimmerLayout.Evaluator<Int> {
}
}
```
And now your designers can cry again with this result:

![Radial](images/radial.gif)

#### Shader Evaluator

The shader is used to create the shadow effect on `ShimmerLayout`. Using a custom `Evaluator<Shader>` allows you to customize the shader to use for each animation frame.
The shader is used to create the shadow effect. Providing one allows you to customize the shader to use for each animation frame.

See example above for `RadialGradient`
See example above for use of a `RadialGradient`

#### Color Evaluator
By default, `ShimmerLayout` will use the `shimmerColor` attribute to tint the effect. Providing a custom `Evaluator` allows to define the color to use for each animation offset. For example, the following code rotates between 3 colors, using an `ArgbEvaluator` to smoothly transition from one to another:

The color used to tint the shadow effect. By default, `ShimmerLayout` will use the `shimmerColor` attribute to tint the effect. Providing an `Evaluator` allows you to define the color to use for each animation frame.

See example above for use of `ArgbEvaluator` to alternate between multiple colours.

#### Matrix Evaluator

Default Matrix uses a translation from left to right, this can be modified by providing a custom `Evaluator<Matrix>`. For example, this replaces the translation with a rotation:
Default Matrix uses a translation from left to right, this can be modified by providing a custom `Evaluator<Matrix>`. For example, this replaces the translation with a rotation (see `EvaluatorsDemoActivity` for an example using this):
```kotlin
val matrix = Matrix()
setMatrixEvaluator { fraction ->
Expand Down
48 changes: 31 additions & 17 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
buildscript {
apply from: '../common.gradle'

repositories {
google()
jcenter()
mavenLocal()
}
dependencies {
classpath "com.android.tools.build:gradle:$gradle_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

apply from: '../common.gradle'

android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
compileSdkVersion compileSdk
buildToolsVersion buildTools
defaultConfig {
applicationId "xyz.peridy.shimmerdemo"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
minSdkVersion minSdk
targetSdkVersion compileSdk
}
buildTypes {
release {
Expand All @@ -21,18 +35,18 @@ android {
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:26.0.2'
compile 'com.android.support:recyclerview-v7:26.0.2'
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile project(':shimmerlayout')
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.support:appcompat-v7:$android_support_version"
implementation "com.android.support:recyclerview-v7:$android_support_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation project(':shimmerlayout')

compile "com.trello.rxlifecycle2:rxlifecycle-kotlin:$rxlifecycle2_version"
compile "com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycle2_version"
compile "com.trello.rxlifecycle2:rxlifecycle:$rxlifecycle2_version"
compile "com.trello.rxlifecycle2:rxlifecycle-android:$rxlifecycle2_version"
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.5'
implementation "com.trello.rxlifecycle2:rxlifecycle-kotlin:$rxlifecycle2_version"
implementation "com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycle2_version"
implementation "com.trello.rxlifecycle2:rxlifecycle:$rxlifecycle2_version"
implementation "com.trello.rxlifecycle2:rxlifecycle-android:$rxlifecycle2_version"
implementation "io.reactivex.rxjava2:rxandroid:$rxandroid_version"
implementation "io.reactivex.rxjava2:rxjava:$rxjava_version"
}
repositories {
mavenCentral()
Expand Down
17 changes: 4 additions & 13 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.2.21'
ext.rxlifecycle2_version = '2.2.0'
apply from: 'common.gradle'

repositories {
jcenter()
google() // needed for command line gradle wrapper
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "com.android.tools.build:gradle:$gradle_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
}
}

Expand Down
21 changes: 21 additions & 0 deletions common.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
ext.version = "1.0"
ext.group = "xyz.peridy.shimmer"
ext.name = "ShimmerLayout"
ext.scm = 'https://github.com/rnpy/ShimmerLayout.git'
ext.description = 'Simple, memory efficient and high performance Shimmer Layout for Android.'

ext.minSdk = 15
ext.compileSdk = 26
ext.buildTools = "26.0.2"
ext.kotlin_version = '1.2.21'
ext.bintray_version = '1.7.3'
ext.gradle_version = '3.0.1'
ext.android_support_version = '27.0.2'
ext.maven_gradle_plugin_version = '2.0'
ext.dexcount_version = '0.8.2'

// Demo app
ext.rxlifecycle2_version = '2.2.0'
ext.google_play_services_version = '11.4.0'
ext.rxandroid_version = '2.0.1'
ext.rxjava_version = '2.1.5'
113 changes: 113 additions & 0 deletions publishing.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.bintray'

project.afterEvaluate {
if (project.plugins.hasPlugin("com.android.library")) {
project.android.libraryVariants.all { variant ->
variant.getOutputs().all { output ->
outputFileName = new File("${project.name}-v${project.version}.aar")
}
}
}
}

project.group = project.ext.group
project.version = project.ext.version

bintray {
user = BINTRAY_USERNAME
key = BINTRAY_API_KEY
pkg {
repo = 'shimmer'
name = getArtifactId()
licenses = ['BSD 2-Clause']
vcsUrl = project.ext.scm
version {
name = project.version
desc = project.ext.description
released = new Date()
vcsTag = project.version
}
publications = ['Bintray']
}
}

// Create the pom configuration:
def pomConfig = {
licenses {
license {
name "BSD 2-Clause"
url "http://www.opensource.org/licenses/bsd-license.php"
distribution "repo"
}
}
scm {
url ext.scm
}
}

//create a jar from source files
task sourceJar(type: Jar) {
if (project.plugins.hasPlugin("com.android.library")) {
from android.sourceSets.main.java.srcDirs
classifier "sources"
}
}

publishing.publications {
Bintray(MavenPublication) {
groupId project.ext.group
artifactId getArtifactId()
version project.ext.version

artifact sourceJar
artifact "$buildDir/outputs/aar/${project.name}-v${project.version}.aar"

//generate pom nodes for dependencies
//when a project references another project, it's artifact node is generated
pom.withXml {
def root = asNode()
root.appendNode('description', project.ext.description)
root.appendNode('name', project.ext.name)
root.appendNode('url', project.ext.scm)
root.children().last() + pomConfig

def dependenciesNode = root.appendNode('dependencies')

configurations.compile.allDependencies.each { dependency ->
if (dependency.group != null && dependency.name != null) {

if (dependency instanceof ProjectDependency) {
dependency.getDependencyProject().getArtifacts().each { artifact ->
addDependencyNodeToPom(dependenciesNode, dependency.group, getArtifactId(dependency.dependencyProject), dependency.version)
}
} else {
addDependencyNodeToPom(dependenciesNode, dependency.group, dependency.name, dependency.version)
}
}
}
}
}
}

def getArtifactId() {
return getArtifactId(project)

}

def getArtifactId(someProject) {
if (!someProject.ext.has('artifactId')) {
//default artifactId is the project/module name
return someProject.getName()

}
return someProject.ext.artifactId
}


def addDependencyNodeToPom(dependenciesNode, group, artifact, version) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode("groupId", group)
dependencyNode.appendNode("artifactId", artifact)
dependencyNode.appendNode("version", version)
}
Loading

0 comments on commit 609fe3b

Please sign in to comment.