diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b75303 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..30aa626 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..29fe3ec --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9fdea7e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..911480f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Meow Bottom Navigation Kotlin +==================================== +version: 2.2.4 \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..fa5dd57 --- /dev/null +++ b/build.gradle @@ -0,0 +1,32 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + ext.compileSdk_version = 28 + ext.minSdk_version = 15 + ext.targetSdk_version = 28 + + ext.kotlin_version = '1.3.21' + + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.3.1' + 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 { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..58a1794 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# 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. +org.gradle.parallel=true +org.gradle.jvmargs=-Xmx4048m +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..17d6d62 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Feb 11 12:58:49 IRST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/meowbottomnavigation/.gitignore b/meowbottomnavigation/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/meowbottomnavigation/.gitignore @@ -0,0 +1 @@ +/build diff --git a/meowbottomnavigation/build.gradle b/meowbottomnavigation/build.gradle new file mode 100644 index 0000000..1281c4f --- /dev/null +++ b/meowbottomnavigation/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'com.android.library' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +def library_groupId = 'com.etebarian' +def library_artifactId = 'meow-bottom-navigation' +def library_versionCode = 1_0_00_00_01 +def library_versionName = '0.0.1' + +android { + compileSdkVersion compileSdk_version + + defaultConfig { + minSdkVersion minSdk_version + targetSdkVersion targetSdk_version + versionCode library_versionCode + versionName library_versionName + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +androidExtensions { + experimental = true +} + +dependencies { + implementation "androidx.appcompat:appcompat:1.0.2" + implementation "androidx.core:core-ktx:1.0.1" +} diff --git a/meowbottomnavigation/proguard-rules.pro b/meowbottomnavigation/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/meowbottomnavigation/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/meowbottomnavigation/src/androidTest/java/com/etebarian/meowbottomnavigation/ExampleInstrumentedTest.java b/meowbottomnavigation/src/androidTest/java/com/etebarian/meowbottomnavigation/ExampleInstrumentedTest.java new file mode 100644 index 0000000..79fef95 --- /dev/null +++ b/meowbottomnavigation/src/androidTest/java/com/etebarian/meowbottomnavigation/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.etebarian.meowbottomnavigation; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.etebarian.meowbottomnavigation.test", appContext.getPackageName()); + } +} diff --git a/meowbottomnavigation/src/main/AndroidManifest.xml b/meowbottomnavigation/src/main/AndroidManifest.xml new file mode 100644 index 0000000..493e5f9 --- /dev/null +++ b/meowbottomnavigation/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/BezierView.kt b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/BezierView.kt new file mode 100644 index 0000000..30a7817 --- /dev/null +++ b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/BezierView.kt @@ -0,0 +1,211 @@ +package com.etebarian.meowbottomnavigation + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.view.View + +/** + * Created by 1HE on 2/25/2019. + */ + +class BezierView : View { + + private var mainPaint: Paint? = null + private var shadowPaint: Paint? = null + private var mainPath: Path? = null + private var shadowPath: Path? = null + private lateinit var outerArray: Array + private lateinit var innerArray: Array + private lateinit var progressArray: Array + + private var width = 0f + private var height = 0f + private var bezierOuterWidth = 0f + private var bezierOuterHeight = 0f + private var bezierInnerWidth = 0f + private var bezierInnerHeight = 0f + private val shadowHeight = dipf(context, 8) + + var color = 0 + set(value) { + field = value + mainPaint?.color = field + } + + var bezierX = 0f + set(value) { + if (value == field) + return + field = value + invalidate() + } + + var progress = 0f + set(value) { + if (value == field) + return + field = value + + progressArray[1].x = bezierX - bezierInnerWidth / 2 + progressArray[2].x = bezierX - bezierInnerWidth / 4 + progressArray[3].x = bezierX - bezierInnerWidth / 4 + progressArray[4].x = bezierX + progressArray[5].x = bezierX + bezierInnerWidth / 4 + progressArray[6].x = bezierX + bezierInnerWidth / 4 + progressArray[7].x = bezierX + bezierInnerWidth / 2 + for (i in 2..6) { + if (progress <= 1f) {//convert to outer + progressArray[i].y = calculate(innerArray[i].y, outerArray[i].y) + } else { + progressArray[i].y = calculate(outerArray[i].y, innerArray[i].y) + } + } + if (field == 2f) + field = 0f + + invalidate() + } + + @SuppressLint("NewApi") + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { + initializeViews() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + initializeViews() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + initializeViews() + } + + constructor(context: Context) : super(context) { + initializeViews() + } + + private fun initializeViews() { + setWillNotDraw(false) + + mainPath = Path() + shadowPath = Path() + outerArray = Array(11) { PointF() } + innerArray = Array(11) { PointF() } + progressArray = Array(11) { PointF() } + + mainPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mainPaint?.apply { + strokeWidth = 0f + isAntiAlias = true + style = Paint.Style.FILL + color = this@BezierView.color + } + + color = color + + shadowPaint = Paint(Paint.ANTI_ALIAS_FLAG) + shadowPaint?.apply { + isAntiAlias = true + setShadowLayer(dipf(context, 4), 0f, 0f, -0x454546) + } + + setLayerType(View.LAYER_TYPE_SOFTWARE, shadowPaint) + } + + @SuppressLint("DrawAllocation") + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + width = View.MeasureSpec.getSize(widthMeasureSpec).toFloat() + height = View.MeasureSpec.getSize(heightMeasureSpec).toFloat() + bezierOuterWidth = dipf(context, 72) + bezierOuterHeight = dipf(context, 12) + bezierInnerWidth = dipf(context, 108) + bezierInnerHeight = dipf(context, 16) + + val extra = shadowHeight + outerArray[0] = PointF(0f, bezierOuterHeight + extra) + outerArray[1] = PointF((bezierX - bezierOuterWidth / 2), bezierOuterHeight + extra) + outerArray[2] = PointF(bezierX - bezierOuterWidth / 4, bezierOuterHeight + extra) + outerArray[3] = PointF(bezierX - bezierOuterWidth / 4, extra) + outerArray[4] = PointF(bezierX, extra) + outerArray[5] = PointF(bezierX + bezierOuterWidth / 4, extra) + outerArray[6] = PointF(bezierX + bezierOuterWidth / 4, bezierOuterHeight + extra) + outerArray[7] = PointF(bezierX + bezierOuterWidth / 2, bezierOuterHeight + extra) + outerArray[8] = PointF(width, bezierOuterHeight + extra) + outerArray[9] = PointF(width, height) + outerArray[10] = PointF(0f, height) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + mainPath!!.reset() + shadowPath!!.reset() + + if (progress == 0f) { + drawInner(canvas, true) + drawInner(canvas, false) + } else { + drawProgress(canvas, true) + drawProgress(canvas, false) + } + } + + private fun drawInner(canvas: Canvas, isShadow: Boolean) { + val paint = if (isShadow) shadowPaint else mainPaint + val path = if (isShadow) shadowPath else mainPath + + calculateInner() + + path!!.lineTo(innerArray[0].x, innerArray[0].y) + path.lineTo(innerArray[1].x, innerArray[1].y) + path.cubicTo(innerArray[2].x, innerArray[2].y, innerArray[3].x, innerArray[3].y, innerArray[4].x, innerArray[4].y) + path.cubicTo(innerArray[5].x, innerArray[5].y, innerArray[6].x, innerArray[6].y, innerArray[7].x, innerArray[7].y) + path.lineTo(innerArray[8].x, innerArray[8].y) + path.lineTo(innerArray[9].x, innerArray[9].y) + path.lineTo(innerArray[10].x, innerArray[10].y) + + progressArray = innerArray.clone() + + canvas.drawPath(path, paint!!) + } + + private fun calculateInner() { + val extra = shadowHeight + innerArray[0] = PointF(0f, bezierInnerHeight + extra) + innerArray[1] = PointF((bezierX - bezierInnerWidth / 2), bezierInnerHeight + extra) + innerArray[2] = PointF(bezierX - bezierInnerWidth / 4, bezierInnerHeight + extra) + innerArray[3] = PointF(bezierX - bezierInnerWidth / 4, height - extra) + innerArray[4] = PointF(bezierX, height - extra) + innerArray[5] = PointF(bezierX + bezierInnerWidth / 4, height - extra) + innerArray[6] = PointF(bezierX + bezierInnerWidth / 4, bezierInnerHeight + extra) + innerArray[7] = PointF(bezierX + bezierInnerWidth / 2, bezierInnerHeight + extra) + innerArray[8] = PointF(width, bezierInnerHeight + extra) + innerArray[9] = PointF(width, height) + innerArray[10] = PointF(0f, height) + } + + private fun drawProgress(canvas: Canvas, isShadow: Boolean) { + val paint = if (isShadow) shadowPaint else mainPaint + val path = if (isShadow) shadowPath else mainPath + + path!!.lineTo(progressArray[0].x, progressArray[0].y) + path.lineTo(progressArray[1].x, progressArray[1].y) + path.cubicTo(progressArray[2].x, progressArray[2].y, progressArray[3].x, progressArray[3].y, progressArray[4].x, progressArray[4].y) + path.cubicTo(progressArray[5].x, progressArray[5].y, progressArray[6].x, progressArray[6].y, progressArray[7].x, progressArray[7].y) + path.lineTo(progressArray[8].x, progressArray[8].y) + path.lineTo(progressArray[9].x, progressArray[9].y) + path.lineTo(progressArray[10].x, progressArray[10].y) + + canvas.drawPath(path, paint!!) + } + + private fun calculate(start: Float, end: Float): Float { + var p = progress + if (p > 1f) + p = progress - 1f + if (p in 0.9f..1f) + calculateInner() + return (p * (end - start)) + start + } +} diff --git a/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/CellImageView.kt b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/CellImageView.kt new file mode 100644 index 0000000..5094e17 --- /dev/null +++ b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/CellImageView.kt @@ -0,0 +1,155 @@ +package com.etebarian.meowbottomnavigation + +import android.animation.ValueAnimator +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatImageView +import androidx.interpolator.view.animation.FastOutSlowInInterpolator + +/** + * Created by 1HE on 2/23/2019. + */ + +@Suppress("unused", "LeakingThis", "MemberVisibilityCanBePrivate") +internal class CellImageView : AppCompatImageView { + + var isBitmap = false + set(value) { + field = value + draw() + } + var useColor = true + set(value) { + field = value + draw() + } + var resource = 0 + set(value) { + field = value + draw() + } + var color = 0 + set(value) { + field = value + draw() + } + var size = dip(context, 24) + set(value) { + field = value + requestLayout() + } + private var actionBackgroundAlpha = false + private var changeSize = true + private var fitImage = false + private var colorAnimator: ValueAnimator? = null + private var allowDraw = false + + constructor(context: Context) : super(context) { + initializeView() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + setAttributeFromXml(context, attrs) + initializeView() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setAttributeFromXml(context, attrs) + initializeView() + } + + private fun setAttributeFromXml(context: Context, attrs: AttributeSet) { + val a = context.theme.obtainStyledAttributes(attrs, R.styleable.CellImageView, 0, 0) + try { + a?.apply { + isBitmap = getBoolean(R.styleable.CellImageView_meow_imageview_isBitmap, isBitmap) + useColor = getBoolean(R.styleable.CellImageView_meow_imageview_useColor, useColor) + resource = getResourceId(R.styleable.CellImageView_meow_imageview_resource, resource) + color = getColor(R.styleable.CellImageView_meow_imageview_color, color) + size = getDimensionPixelSize(R.styleable.CellImageView_meow_imageview_size, size) + actionBackgroundAlpha = getBoolean(R.styleable.CellImageView_meow_imageview_actionBackgroundAlpha, actionBackgroundAlpha) + changeSize = getBoolean(R.styleable.CellImageView_meow_imageview_changeSize, changeSize) + fitImage = getBoolean(R.styleable.CellImageView_meow_imageview_fitImage, fitImage) + } + } finally { + a?.recycle() + } + } + + private fun initializeView() { + allowDraw = true + draw() + } + + private fun draw() { + if (!allowDraw) + return + + if (resource == 0) + return + + if (isBitmap) { + try { + val drawable = if (color == 0) context.getDrawableCompat(resource) else DrawableHelper.changeColorDrawableRes(context, resource, color) + setImageDrawable(drawable) + } catch (e: Exception) { + e.printStackTrace() + } + return + } + + if (useColor && color == 0) + return + + val c = if (useColor) color else -2 + try { + setImageDrawable(DrawableHelper.changeColorDrawableVector(context, resource, c)) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun changeColorByAnim(newColor: Int, d: Long = 250L) { + if (color == 0) { + color = newColor + return + } + val lastColor = color + + colorAnimator?.cancel() + + colorAnimator = ValueAnimator.ofFloat(0f, 1f) + colorAnimator?.apply { + duration = d + interpolator = FastOutSlowInInterpolator() + addUpdateListener { animation -> + val f = animation.animatedFraction + color = ColorHelper.mixTwoColors(newColor, lastColor, f) + } + start() + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + if (fitImage) { + val d = drawable + if (d != null) { + val width = MeasureSpec.getSize(widthMeasureSpec) + val height = Math.ceil((width.toFloat() * d.intrinsicHeight.toFloat() / d.intrinsicWidth).toDouble()).toInt() + setMeasuredDimension(width, height) + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } + return + } + + if (isBitmap || !changeSize) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + return + } + + val newSize = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY) + super.onMeasure(newSize, newSize) + } + +} \ No newline at end of file diff --git a/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/MeowBottomNavigation.kt b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/MeowBottomNavigation.kt new file mode 100644 index 0000000..49ca99a --- /dev/null +++ b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/MeowBottomNavigation.kt @@ -0,0 +1,235 @@ +@file:Suppress("unused") + +package com.etebarian.meowbottomnavigation + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Color +import android.graphics.Typeface +import android.util.AttributeSet +import android.view.Gravity +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.interpolator.view.animation.FastOutSlowInInterpolator + +/** + * Created by 1HE on 10/23/2018. + */ + +internal typealias IBottomNavigationListener = (model: MeowBottomNavigation.Model) -> Unit + +@Suppress("MemberVisibilityCanBePrivate") +class MeowBottomNavigation : FrameLayout {//todo test orientation & slow anim + + private var models = ArrayList() + private var cells = ArrayList() + + private var nowShowId = -1 + + private var mOnClickedListener: IBottomNavigationListener = {} + private var mOnShowListener: IBottomNavigationListener = {} + + var heightCell = 0 + + private var defaultIconColor = Color.parseColor("#757575") + private var selectedIconColor = Color.parseColor("#2196f3") + private var backgroundBottomColor = Color.parseColor("#ffffff") + private var countTextColor = Color.parseColor("#ffffff") + private var countBackgroundColor = Color.parseColor("#ff0000") + private var countTypeface: Typeface? = null + private var rippleColor = Color.parseColor("#757575") + + private lateinit var ll_cells: LinearLayout + private lateinit var bezierView: BezierView + + init { + heightCell = dip(context, 68) + } + + constructor(context: Context) : super(context) { + initializeViews() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + setAttributeFromXml(context, attrs) + initializeViews() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setAttributeFromXml(context, attrs) + initializeViews() + } + + private fun setAttributeFromXml(context: Context, attrs: AttributeSet) { + val a = context.theme.obtainStyledAttributes(attrs, R.styleable.MeowBottomNavigation, 0, 0) + try { + a?.apply { + defaultIconColor = getColor(R.styleable.MeowBottomNavigation_mbn_defaultIconColor, defaultIconColor) + selectedIconColor = getColor(R.styleable.MeowBottomNavigation_mbn_selectedIconColor, selectedIconColor) + backgroundBottomColor = getColor(R.styleable.MeowBottomNavigation_mbn_backgroundBottomColor, backgroundBottomColor) + countTextColor = getColor(R.styleable.MeowBottomNavigation_mbn_countTextColor, countTextColor) + countBackgroundColor = getColor(R.styleable.MeowBottomNavigation_mbn_countBackgroundColor, countBackgroundColor) + val typeface = getString(R.styleable.MeowBottomNavigation_mbn_countTypeface) + rippleColor = getColor(R.styleable.MeowBottomNavigation_mbn_rippleColor, rippleColor) + + if (typeface != null) + countTypeface = Typeface.createFromAsset(context.assets, typeface) + } + } finally { + a?.recycle() + } + } + + private fun initializeViews() { + ll_cells = LinearLayout(context) + ll_cells.apply { + val params = FrameLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, heightCell) + params.gravity = Gravity.BOTTOM + layoutParams = params + orientation = LinearLayout.HORIZONTAL + clipChildren = false + clipToPadding = false + } + + bezierView = BezierView(context) + bezierView.apply { + layoutParams = FrameLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, heightCell) + color = backgroundBottomColor + } + + addView(bezierView) + addView(ll_cells) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + if (nowShowId == -1 && models.isNotEmpty()) { + show(models.first().id, false) + } + } + + fun add(model: Model) { + val cell = MeowBottomNavigationCell(context) + cell.apply { + val params = LinearLayout.LayoutParams(0, heightCell, 1f) + layoutParams = params + icon = model.icon + count = model.count + circleColor = this@MeowBottomNavigation.backgroundBottomColor + countTextColor = this@MeowBottomNavigation.countTextColor + countBackgroundColor = this@MeowBottomNavigation.countBackgroundColor + countTypeface = this@MeowBottomNavigation.countTypeface + rippleColor = this@MeowBottomNavigation.rippleColor + defaultIconColor = this@MeowBottomNavigation.defaultIconColor + selectedIconColor = this@MeowBottomNavigation.selectedIconColor + setOnClickListener { + show(model.id) + mOnClickedListener(model) + } + disableCell() + ll_cells.addView(this) + } + + cells.add(cell) + models.add(model) + } + + fun show(id: Int, enableAnimation: Boolean = true) { + for (i in models.indices) { + val model = models[i] + val cell = cells[i] + if (model.id == id) { + anim(cell, id, enableAnimation) + cell.enableCell() + mOnShowListener(model) + } else { + cell.disableCell() + } + } + nowShowId = id + } + + private fun anim(cell: MeowBottomNavigationCell, id: Int, enableAnimation: Boolean = true) { + val animDuration = if (enableAnimation) 750L else 1L + val animInterpolator = FastOutSlowInInterpolator() + + val anim = ValueAnimator.ofFloat(0f, 1f) + anim.apply { + duration = animDuration + interpolator = animInterpolator + val beforeX = bezierView.bezierX + addUpdateListener { + val f = it.animatedFraction + val newX = cell.x + (cell.measuredWidth / 2) + if (newX > beforeX) + bezierView.bezierX = f * (newX - beforeX) + beforeX + else + bezierView.bezierX = beforeX - f * (beforeX - newX) + } + start() + } + + val pos = getModelPosition(id) + val nowPos = getModelPosition(nowShowId) + if (Math.abs(pos - nowPos) > 1) { + val progressAnim = ValueAnimator.ofFloat(0f, 1f) + progressAnim.apply { + duration = animDuration + interpolator = animInterpolator + addUpdateListener { + val f = it.animatedFraction + bezierView.progress = f * 2f + } + start() + } + } + + cell.isFromLeft = pos > nowPos + } + + fun isShowing(id: Int): Boolean { + return nowShowId == id + } + + fun getModelById(id: Int): Model? { + models.forEach { + if (it.id == id) + return it + } + return null + } + + fun getCellById(id: Int): MeowBottomNavigationCell? { + return cells[getModelPosition(id)] + } + + fun getModelPosition(id: Int): Int { + for (i in models.indices) { + val item = models[i] + if (item.id == id) + return i + } + return -1 + } + + fun setCount(id: Int, count: String) { + val model = getModelById(id) ?: return + val pos = getModelPosition(id) + model.count = count + cells[pos].count = count + } + + fun setOnShowListener(listener: IBottomNavigationListener) { + mOnShowListener = listener + } + + fun setOnClickMenuListener(listener: IBottomNavigationListener) { + mOnClickedListener = listener + } + + class Model(var id: Int, var icon: Int) { + + var count: String = MeowBottomNavigationCell.EMPTY_VALUE + + } +} \ No newline at end of file diff --git a/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/MeowBottomNavigationCell.kt b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/MeowBottomNavigationCell.kt new file mode 100644 index 0000000..99f03ae --- /dev/null +++ b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/MeowBottomNavigationCell.kt @@ -0,0 +1,212 @@ +package com.etebarian.meowbottomnavigation + +import android.animation.ValueAnimator +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.Typeface +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.RippleDrawable +import android.os.Build +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.RelativeLayout +import androidx.core.view.ViewCompat +import androidx.interpolator.view.animation.FastOutSlowInInterpolator +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.meow_navigation_cell.view.* + +/** + * Created by 1HE on 2/23/2019. + */ + +@Suppress("unused") +class MeowBottomNavigationCell : RelativeLayout, LayoutContainer { + + companion object { + const val EMPTY_VALUE = "empty" + } + + var defaultIconColor = 0 + var selectedIconColor = 0 + var circleColor = 0 + + var icon = 0 + set(value) { + field = value + if (allowDraw) + iv.resource = value + } + + var count: String? = EMPTY_VALUE + set(value) { + field = value + if (allowDraw) { + if (count != null && count == EMPTY_VALUE) { + tv_count.text = "" + tv_count.visibility = View.INVISIBLE + } else { + if (count != null && count?.length ?: 0 >= 3) { + field = count?.substring(0, 1) + ".." + } + tv_count.text = count + tv_count.visibility = View.VISIBLE + val scale = if (count?.isEmpty() == true) 0.5f else 1f + tv_count.scaleX = scale + tv_count.scaleY = scale + } + } + } + + private var iconSize = dip(context, 24) + set(value) { + field = value + if (allowDraw) + iv.size = value + } + + var countTextColor = 0 + set(value) { + field = value + if (allowDraw) + tv_count.setTextColor(field) + } + + var countBackgroundColor = 0 + set(value) { + field = value + if (allowDraw) { + val d = GradientDrawable() + d.setColor(field) + d.shape = GradientDrawable.OVAL + ViewCompat.setBackground(tv_count, d) + } + } + + var countTypeface : Typeface? = null + set(value) { + field = value + if (allowDraw && field != null) + tv_count.typeface = field + } + + var rippleColor = 0 + set(value) { + field = value + if (allowDraw) { + + } + } + + var isFromLeft = false + private var progress = 0f + set(value) { + field = value + fl.y = (1f - progress) * dip(context, 18) + dip(context, 8) + + iv.color = if (progress == 1f) selectedIconColor else defaultIconColor + + val d = GradientDrawable() + d.setColor(circleColor) + d.shape = GradientDrawable.OVAL + + ViewCompat.setBackground(v_circle, d) + + ViewCompat.setElevation(v_circle, if (progress > 0.7f) dipf(context, progress * 4f) else 0f) + + val m = dip(context, 24) + v_circle.x = (1f - progress) * (if (isFromLeft) -m else m) + ((measuredWidth - dip(context, 48)) / 2f) + v_circle.y = (1f - progress) * measuredHeight + dip(context, 4) + + } + + private var isEnabledCell = false + set(value) { + field =value + val d = GradientDrawable() + d.setColor(circleColor) + d.shape = GradientDrawable.OVAL + if (Build.VERSION.SDK_INT >= 21 && !isEnabledCell) { + fl.background = RippleDrawable(ColorStateList.valueOf(rippleColor), null, d) + }else{ + fl.runAfterDelay(200){ + fl.setBackgroundColor(Color.TRANSPARENT) + } + } + } + + override lateinit var containerView: View + private var allowDraw = false + + constructor(context: Context) : super(context) { + initializeView() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + setAttributeFromXml(context, attrs) + initializeView() + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + setAttributeFromXml(context, attrs) + initializeView() + } + + @Suppress("UNUSED_PARAMETER") + private fun setAttributeFromXml(context: Context, attrs: AttributeSet) { + } + + private fun initializeView() { + allowDraw = true + containerView = LayoutInflater.from(context).inflate(R.layout.meow_navigation_cell, this) + draw() + } + + private fun draw() { + if (!allowDraw) + return + + icon = icon + count = count + iconSize = iconSize + countTextColor = countTextColor + countBackgroundColor = countBackgroundColor + countTypeface = countTypeface + rippleColor= rippleColor + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + progress = progress + } + + fun disableCell() { + if (isEnabledCell) + animateProgress(false) + isEnabledCell = false + } + + fun enableCell() { + if (!isEnabledCell) + animateProgress(true) + isEnabledCell = true + } + + private fun animateProgress(enableCell: Boolean) { + val anim = ValueAnimator.ofFloat(0f, 1f) + anim.apply { + startDelay = if (enableCell) 250L else 0L + duration = 500L + interpolator = FastOutSlowInInterpolator() + addUpdateListener { + val f = it.animatedFraction + progress = if (enableCell) + f + else + 1f - f + } + start() + } + } +} \ No newline at end of file diff --git a/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/Utils.kt b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/Utils.kt new file mode 100644 index 0000000..c29a2b0 --- /dev/null +++ b/meowbottomnavigation/src/main/java/com/etebarian/meowbottomnavigation/Utils.kt @@ -0,0 +1,72 @@ +package com.etebarian.meowbottomnavigation + +import android.content.Context +import android.graphics.PorterDuff +import android.graphics.drawable.Drawable +import android.view.View +import androidx.core.content.ContextCompat +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import java.lang.Exception + +/** + * Created by 1HE on 2/23/2019. + */ + +private fun getDP(context: Context) = context.resources.displayMetrics.density + +internal fun dipf(context: Context,f: Float) = f * getDP(context) + +internal fun dipf(context: Context,i: Int) = i * getDP(context) + +internal fun dip(context: Context,i: Int) = (i * getDP(context)).toInt() + +internal object DrawableHelper{ + + fun changeColorDrawableVector(c: Context?, resDrawable: Int, color: Int): Drawable? { + if (c == null) + return null + + val d = VectorDrawableCompat.create(c.resources, resDrawable, null) ?: return null + d.mutate() + if (color != -2) + d.setColorFilter(color, PorterDuff.Mode.SRC_IN) + return d + } + + fun changeColorDrawableRes(c: Context?, resDrawable: Int, color: Int): Drawable? { + if (c == null) + return null + + val d = ContextCompat.getDrawable(c, resDrawable) ?: return null + d.mutate() + if (color != -2) + d.setColorFilter(color, PorterDuff.Mode.SRC_IN) + return d + } +} + +internal object ColorHelper{ + + fun mixTwoColors(color1: Int, color2: Int, amount: Float): Int { + val alphaChannel = 24 + val redChannel = 16 + val greenChannel = 8 + + val inverseAmount = 1.0f - amount + + val a = ((color1 shr alphaChannel and 0xff).toFloat() * amount + (color2 shr alphaChannel and 0xff).toFloat() * inverseAmount).toInt() and 0xff + val r = ((color1 shr redChannel and 0xff).toFloat() * amount + (color2 shr redChannel and 0xff).toFloat() * inverseAmount).toInt() and 0xff + val g = ((color1 shr greenChannel and 0xff).toFloat() * amount + (color2 shr greenChannel and 0xff).toFloat() * inverseAmount).toInt() and 0xff + val b = ((color1 and 0xff).toFloat() * amount + (color2 and 0xff).toFloat() * inverseAmount).toInt() and 0xff + + return a shl alphaChannel or (r shl redChannel) or (g shl greenChannel) or b + } +} + +internal fun Context.getDrawableCompat(res: Int) = ContextCompat.getDrawable(this, res) + +internal inline fun T.runAfterDelay(delay: Long, crossinline f: T.() -> Unit) { + this?.postDelayed({ + try { f() }catch (e:Exception){} + }, delay) +} diff --git a/meowbottomnavigation/src/main/res/layout/meow_navigation_cell.xml b/meowbottomnavigation/src/main/res/layout/meow_navigation_cell.xml new file mode 100644 index 0000000..44ff3d2 --- /dev/null +++ b/meowbottomnavigation/src/main/res/layout/meow_navigation_cell.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/meowbottomnavigation/src/main/res/values/attrs.xml b/meowbottomnavigation/src/main/res/values/attrs.xml new file mode 100644 index 0000000..835e8d3 --- /dev/null +++ b/meowbottomnavigation/src/main/res/values/attrs.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/meowbottomnavigation/src/main/res/values/strings.xml b/meowbottomnavigation/src/main/res/values/strings.xml new file mode 100644 index 0000000..e57297d --- /dev/null +++ b/meowbottomnavigation/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + MeowBottomNavigation + diff --git a/meowbottomnavigation/src/test/java/com/etebarian/meowbottomnavigation/ExampleUnitTest.java b/meowbottomnavigation/src/test/java/com/etebarian/meowbottomnavigation/ExampleUnitTest.java new file mode 100644 index 0000000..8ec20f6 --- /dev/null +++ b/meowbottomnavigation/src/test/java/com/etebarian/meowbottomnavigation/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.etebarian.meowbottomnavigation; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..9f3a975 --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.etebarian.meowbottomnavigaion.sample" + minSdkVersion 15 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.core:core-ktx:1.0.1' + + implementation project(':meowbottomnavigation') +} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ca577b8 --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/java/com/etebarian/meowbottomnavigaion/MainActivity.kt b/sample/src/main/java/com/etebarian/meowbottomnavigaion/MainActivity.kt new file mode 100644 index 0000000..64c89ab --- /dev/null +++ b/sample/src/main/java/com/etebarian/meowbottomnavigaion/MainActivity.kt @@ -0,0 +1,39 @@ +package com.etebarian.meowbottomnavigaion + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.etebarian.meowbottomnavigation.MeowBottomNavigation +import kotlinx.android.synthetic.main.activity_main.* + +class MainActivity : AppCompatActivity() {//todo add ic_launcher + + companion object { + private const val ID_HOME = 1 + private const val ID_EXPLORE = 2 + private const val ID_MESSAGE = 3 + private const val ID_NOTIFICATION = 4 + private const val ID_ACCOUNT = 5 + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + bottomNavigation.add(MeowBottomNavigation.Model(ID_HOME, R.drawable.ic_home)) + bottomNavigation.add(MeowBottomNavigation.Model(ID_EXPLORE, R.drawable.ic_explore)) + bottomNavigation.add(MeowBottomNavigation.Model(ID_MESSAGE, R.drawable.ic_message)) + bottomNavigation.add(MeowBottomNavigation.Model(ID_NOTIFICATION, R.drawable.ic_notification)) + bottomNavigation.add(MeowBottomNavigation.Model(ID_ACCOUNT, R.drawable.ic_account)) + + bt_update.setOnClickListener { + // val id = try { +// et.text.toString().toInt() +// }catch (e: Exception){ +// e.printStackTrace() +// 0 +// } +// bottomNavigation.show(id) + bottomNavigation.setCount(ID_NOTIFICATION, "1") + } + } +} diff --git a/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/sample/src/main/res/drawable/ic_account.xml b/sample/src/main/res/drawable/ic_account.xml new file mode 100644 index 0000000..35919f9 --- /dev/null +++ b/sample/src/main/res/drawable/ic_account.xml @@ -0,0 +1,5 @@ + + + + diff --git a/sample/src/main/res/drawable/ic_explore.xml b/sample/src/main/res/drawable/ic_explore.xml new file mode 100644 index 0000000..8f004e9 --- /dev/null +++ b/sample/src/main/res/drawable/ic_explore.xml @@ -0,0 +1,5 @@ + + + + diff --git a/sample/src/main/res/drawable/ic_home.xml b/sample/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..1dfa87f --- /dev/null +++ b/sample/src/main/res/drawable/ic_home.xml @@ -0,0 +1,5 @@ + + + + diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..2408e30 --- /dev/null +++ b/sample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/main/res/drawable/ic_message.xml b/sample/src/main/res/drawable/ic_message.xml new file mode 100644 index 0000000..681e68c --- /dev/null +++ b/sample/src/main/res/drawable/ic_message.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/sample/src/main/res/drawable/ic_notification.xml b/sample/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..a75dabf --- /dev/null +++ b/sample/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,4 @@ + + + diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..e7169d9 --- /dev/null +++ b/sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,36 @@ + + + + + + + +