From 70ede0d3f10acfe45760b56c6c194a7f1a61ceff Mon Sep 17 00:00:00 2001 From: flavienlaurent Date: Mon, 27 Jan 2014 13:01:16 +0100 Subject: [PATCH] commit for lazy developers --- README.md | 125 +++++++++++++ gradle.properties | 32 ++-- lib/build.gradle | 3 +- lib/gradle.properties | 3 + .../discrollview/lib/DiscrollView.java | 27 +-- .../discrollview/lib/DiscrollViewContent.java | 109 ++++++++++++ .../discrollview/lib/DiscrollvableView.java | 164 ++++++++++++++++++ lib/src/main/res/values/attrs.xml | 19 ++ maven_push.gradle | 102 +++++++++++ sample/src/main/res/layout/activity_main.xml | 59 ++++++- sample/src/main/res/layout/simple.xml | 70 ++++++++ 11 files changed, 673 insertions(+), 40 deletions(-) create mode 100644 lib/gradle.properties create mode 100644 lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollViewContent.java create mode 100644 lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollvableView.java create mode 100644 lib/src/main/res/values/attrs.xml create mode 100644 maven_push.gradle create mode 100644 sample/src/main/res/layout/simple.xml diff --git a/README.md b/README.md index 4c4459b..f277283 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,131 @@ Usage Using the library is simple, just look at the source code of the provided sample [here][3] +### build.gradle + +``` +compile 'com.github.flavienlaurent.discrollview:library:0.0.2@aar' +``` + +### The main layout + +You must use the DiscrollViewContent view. + +```xml + + + + + + + + + +``` + +### Discrollvable views + +You can apply some transformation on discroll: + +- alpha +- scale +- translation (fromLeft, fromBottom, fromRight, fromTop) +fromLeft+fromRight and fromBottom+fromTop are forbidden couples. +- bgcolor + +```xml +discrollve:discrollve_alpha="true" +discrollve:discrollve_translation="fromLeft|fromBottom" +discrollve:discrollve_scaleX="true" +discrollve:discrollve_scaleY="true" +discrollve:discrollve_fromBgColor="#88EE66" +discrollve:discrollve_toBgColor="#000000" +discrollve:discrollve_threshold="0.3" +``` + +The threshold attribute is used to trigger the discrollve at a specified ratio. For example, if threshold=0.3, the discrollve starts when the ratio >= 0.3. + +### A simple example + +```xml + + + + + + + + + + + + + + + + + + + + +``` + License ----------- diff --git a/gradle.properties b/gradle.properties index 5d08ba7..b7dcbff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,18 +1,14 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Settings specified in this file will override any Gradle settings -# configured through the IDE. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +VERSION_NAME=0.0.2 +VERSION_CODE=2 +GROUP=com.github.flavienlaurent.discrollview + +POM_DESCRIPTION=Android library to import the discrollver pattern on Android +POM_URL=https://github.com/flavienlaurent/discrollview +POM_SCM_URL=https://github.com/flavienlaurent/discrollview +POM_SCM_CONNECTION=scm:git@github.com:flavienlaurent/discrollview.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:flavienlaurent/discrollview.git +POM_LICENCE_NAME=Apache License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0 +POM_LICENCE_DIST=repo +POM_DEVELOPER_ID=flavienlaurent +POM_DEVELOPER_NAME=Flavien Laurent \ No newline at end of file diff --git a/lib/build.gradle b/lib/build.gradle index ec7d6d9..9d08314 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -27,5 +27,4 @@ android { } } -dependencies { -} +apply from: '../maven_push.gradle' diff --git a/lib/gradle.properties b/lib/gradle.properties new file mode 100644 index 0000000..21ea111 --- /dev/null +++ b/lib/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=Discrollview Library +POM_ARTIFACT_ID=library +POM_PACKAGING=aar \ No newline at end of file diff --git a/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollView.java b/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollView.java index 484c7c5..395f7b2 100644 --- a/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollView.java +++ b/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollView.java @@ -13,11 +13,12 @@ * else, the discrollvables starts to be discrollved when its top reaches the bottom of the DiscrollView * * @see com.flavienlaurent.discrollview.lib.Discrollvable + * @see com.flavienlaurent.discrollview.lib.DiscrollViewContent * */ public class DiscrollView extends ScrollView { - private ViewGroup mContent; + private DiscrollViewContent mContent; public DiscrollView(Context context) { super(context); @@ -31,8 +32,11 @@ public DiscrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } - public static float clamp(float value, float max, float min) { - return Math.max(Math.min(value, min), max); + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + setupFirstView(); + } @Override @@ -42,22 +46,15 @@ protected void onFinishInflate() { throw new IllegalStateException("Discrollview must host one child."); } View content = getChildAt(0); - if(!(content instanceof ViewGroup)) { - throw new IllegalStateException("Discrollview must host a ViewGroup."); + if(!(content instanceof DiscrollViewContent)) { + throw new IllegalStateException("Discrollview must host a DiscrollViewContent."); } - mContent = (ViewGroup) content; + mContent = (DiscrollViewContent) content; if(mContent.getChildCount() < 2) { throw new IllegalStateException("Discrollview must have at least 2 children."); } } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - setupFirstView(); - - } - private void setupFirstView() { //first first take all the parent height View first = mContent.getChildAt(0); @@ -72,6 +69,10 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { onScrollChanged(t); } + public static float clamp(float value, float max, float min) { + return Math.max(Math.min(value, min), max); + } + private int getAbsoluteBottom() { View last = getChildAt(getChildCount() - 1); if(last == null) { diff --git a/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollViewContent.java b/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollViewContent.java new file mode 100644 index 0000000..73c72bb --- /dev/null +++ b/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollViewContent.java @@ -0,0 +1,109 @@ +package com.flavienlaurent.discrollview.lib; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +/** + * + */ +public class DiscrollViewContent extends LinearLayout { + + public DiscrollViewContent(Context context) { + super(context); + setOrientation(VERTICAL); + } + + public DiscrollViewContent(Context context, AttributeSet attrs) { + super(context, attrs); + setOrientation(VERTICAL); + } + + public DiscrollViewContent(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setOrientation(VERTICAL); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + super.addView(asDiscrollvable(child, (LayoutParams) params), index, params); + } + + private View asDiscrollvable(View child, LayoutParams lp) { + if(! isDiscrollvable(lp)) { + return child; + } + DiscrollvableView discrollvableChild = new DiscrollvableView(getContext()); + discrollvableChild.setDiscrollveAlpha(lp.mDiscrollveAlpha); + discrollvableChild.setDiscrollveTranslation(lp.mDiscrollveTranslation); + discrollvableChild.setDiscrollveScaleX(lp.mDiscrollveScaleX); + discrollvableChild.setDiscrollveScaleY(lp.mDiscrollveScaleY); + discrollvableChild.setDiscrollveThreshold(lp.mDiscrollveThreshold); + discrollvableChild.setDiscrollveFromBgColor(lp.mDiscrollveFromBgColor); + discrollvableChild.setDiscrollveToBgColor(lp.mDiscrollveToBgColor); + discrollvableChild.addView(child); + return discrollvableChild; + } + + private boolean isDiscrollvable(LayoutParams lp) { + return lp.mDiscrollveAlpha || + lp.mDiscrollveTranslation != -1 || + lp.mDiscrollveScaleX || + lp.mDiscrollveScaleY || + (lp.mDiscrollveFromBgColor != -1 && lp.mDiscrollveToBgColor != -1); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected LinearLayout.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + public LinearLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p.width, p.height); + } + + public static class LayoutParams extends LinearLayout.LayoutParams { + + private int mDiscrollveFromBgColor; + private int mDiscrollveToBgColor; + private float mDiscrollveThreshold; + public boolean mDiscrollveAlpha; + public boolean mDiscrollveScaleX; + public boolean mDiscrollveScaleY; + private int mDiscrollveTranslation; + + public LayoutParams(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiscrollView_LayoutParams); + try { + mDiscrollveAlpha = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_alpha, false); + mDiscrollveScaleX = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleX, false); + mDiscrollveScaleY = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleY, false); + mDiscrollveTranslation = a.getInt(R.styleable.DiscrollView_LayoutParams_discrollve_translation, -1); + mDiscrollveThreshold = a.getFloat(R.styleable.DiscrollView_LayoutParams_discrollve_threshold, 0.0f); + mDiscrollveFromBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_fromBgColor, -1); + mDiscrollveToBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_toBgColor, -1); + } finally { + a.recycle(); + } + } + + public LayoutParams(int w, int h) { + super(w, h); + } + } +} diff --git a/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollvableView.java b/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollvableView.java new file mode 100644 index 0000000..694fb1d --- /dev/null +++ b/lib/src/main/java/com/flavienlaurent/discrollview/lib/DiscrollvableView.java @@ -0,0 +1,164 @@ +package com.flavienlaurent.discrollview.lib; + +import android.animation.ArgbEvaluator; +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * + */ +public class DiscrollvableView extends FrameLayout implements Discrollvable { + + private static final int TRANSLATION_FROM_TOP = 0x01; + private static final int TRANSLATION_FROM_BOTTOM = 0x02; + private static final int TRANSLATION_FROM_LEFT = 0x04;; + private static final int TRANSLATION_FROM_RIGHT = 0x08; + + private static ArgbEvaluator sArgbEvaluator = new ArgbEvaluator(); + + private float mDiscrollveThreshold; + private int mDiscrollveFromBgColor; + private int mDiscrollveToBgColor; + private boolean mDiscrollveAlpha; + private int mDiscrollveTranslation; + private boolean mDiscrollveScaleX; + private boolean mDiscrollveScaleY; + + private int mWidth; + private int mHeight; + + public DiscrollvableView(Context context) { + super(context); + } + + public DiscrollvableView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DiscrollvableView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + onResetDiscrollve(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mWidth = w; + mHeight = h; + } + + public void setDiscrollveTranslation(int discrollveTranslation) { + mDiscrollveTranslation = discrollveTranslation; + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_BOTTOM) && isDiscrollveTranslationFrom(TRANSLATION_FROM_TOP)) { + throw new IllegalArgumentException("cannot translate from bottom and top"); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_LEFT) && isDiscrollveTranslationFrom(TRANSLATION_FROM_RIGHT)) { + throw new IllegalArgumentException("cannot translate from left and right"); + } + } + + public void setDiscrollveThreshold(float discrollveThreshold) { + if(discrollveThreshold < 0.0f || discrollveThreshold > 1.0f) { + throw new IllegalArgumentException("threshold must be >= 0.0f and <= 1.0f"); + } + mDiscrollveThreshold = discrollveThreshold; + } + + public void setDiscrollveFromBgColor(int discrollveFromBgColor) { + mDiscrollveFromBgColor = discrollveFromBgColor; + } + + public void setDiscrollveToBgColor(int discrollveToBgColor) { + mDiscrollveToBgColor = discrollveToBgColor; + } + + public void setDiscrollveAlpha(boolean discrollveAlpha) { + mDiscrollveAlpha = discrollveAlpha; + } + + public void setDiscrollveScaleX(boolean discrollveScaleX) { + mDiscrollveScaleX = discrollveScaleX; + } + + public void setDiscrollveScaleY(boolean discrollveScaleY) { + mDiscrollveScaleY = discrollveScaleY; + } + + @Override + public void onDiscrollve(float ratio) { + if(ratio >= mDiscrollveThreshold) { + ratio = withThreshold(ratio); + float ratioInverse = 1 - ratio; + + if(mDiscrollveAlpha) { + setAlpha(ratio); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_BOTTOM)) { + setTranslationY(mHeight * ratioInverse); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_TOP)) { + setTranslationY(-mHeight * ratioInverse); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_LEFT)) { + setTranslationX(-mWidth * ratioInverse); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_RIGHT)) { + setTranslationX(mWidth * ratioInverse); + } + if(mDiscrollveScaleX) { + setScaleX(ratio); + } + if(mDiscrollveScaleY) { + setScaleY(ratio); + } + if(mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) { + setBackgroundColor((Integer) sArgbEvaluator.evaluate(ratio, mDiscrollveFromBgColor, mDiscrollveToBgColor)); + } + } + } + + private float withThreshold(float ratio) { + return (ratio - mDiscrollveThreshold) / (1.0f - mDiscrollveThreshold); + } + + @Override + public void onResetDiscrollve() { + if(mDiscrollveAlpha) { + setAlpha(0.0f); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_BOTTOM)) { + setTranslationY(mHeight); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_TOP)) { + setTranslationY(-mHeight); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_LEFT)) { + setTranslationX(-mWidth); + } + if(isDiscrollveTranslationFrom(TRANSLATION_FROM_RIGHT)) { + setTranslationX(mWidth); + } + if(mDiscrollveScaleX) { + setScaleX(0.0f); + } + if(mDiscrollveScaleY) { + setScaleY(0.0f); + } + if(mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) { + setBackgroundColor(mDiscrollveFromBgColor); + } + } + + private boolean isDiscrollveTranslationFrom(int translationMask) { + if(mDiscrollveTranslation == -1) { + return false; + } + return (mDiscrollveTranslation & translationMask) == translationMask; + } +} diff --git a/lib/src/main/res/values/attrs.xml b/lib/src/main/res/values/attrs.xml new file mode 100644 index 0000000..771fee1 --- /dev/null +++ b/lib/src/main/res/values/attrs.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/maven_push.gradle b/maven_push.gradle new file mode 100644 index 0000000..a9e6b68 --- /dev/null +++ b/maven_push.gradle @@ -0,0 +1,102 @@ +/* + * Copyright 2013 Chris Banes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'maven' +apply plugin: 'signing' + +def isReleaseBuild() { + return version.contains("SNAPSHOT") == false +} + +def sonatypeRepositoryUrl +if (isReleaseBuild()) { + println 'RELEASE BUILD' + sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" +} else { + println 'DEBUG BUILD' + sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" +} + +afterEvaluate { project -> + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + pom.artifactId = POM_ARTIFACT_ID + + repository(url: sonatypeRepositoryUrl) { + authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD) + } + + pom.project { + name POM_NAME + packaging POM_PACKAGING + description POM_DESCRIPTION + url POM_URL + + scm { + url POM_SCM_URL + connection POM_SCM_CONNECTION + developerConnection POM_SCM_DEV_CONNECTION + } + + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution POM_LICENCE_DIST + } + } + + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + } + } + } + } + } + } + + signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } + + task androidJavadocs(type: Javadoc) { + source = android.sourceSets.main.allJava + } + + task androidJavadocsJar(type: Jar) { + classifier = 'javadoc' + //basename = artifact_id + from androidJavadocs.destinationDir + } + + task androidSourcesJar(type: Jar) { + classifier = 'sources' + //basename = artifact_id + from android.sourceSets.main.allSource + } + + artifacts { + //archives packageReleaseJar + archives androidSourcesJar + archives androidJavadocsJar + } +} \ No newline at end of file diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 2a222e1..1d7f37c 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,15 +1,13 @@ - + android:layout_height="match_parent"> + + + + + + + + + + + + - + diff --git a/sample/src/main/res/layout/simple.xml b/sample/src/main/res/layout/simple.xml new file mode 100644 index 0000000..b04bd3e --- /dev/null +++ b/sample/src/main/res/layout/simple.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + +