diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b16812 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild +.idea \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..c20bca1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,23 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.2.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1 @@ +/build diff --git a/demo/build.gradle b/demo/build.gradle new file mode 100644 index 0000000..06fccda --- /dev/null +++ b/demo/build.gradle @@ -0,0 +1,37 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.1" + + defaultConfig { + applicationId "com.loopeer.android.demo" + minSdkVersion 14 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + dataBinding { + enabled true + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:25.0.1' + testCompile 'junit:junit:4.12' + compile project(':library') +} diff --git a/demo/proguard-rules.pro b/demo/proguard-rules.pro new file mode 100644 index 0000000..63f7a19 --- /dev/null +++ b/demo/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in E:\program\AndroidSDk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/demo/src/androidTest/java/com/loopeer/android/demo/ExampleInstrumentedTest.java b/demo/src/androidTest/java/com/loopeer/android/demo/ExampleInstrumentedTest.java new file mode 100644 index 0000000..ae955ba --- /dev/null +++ b/demo/src/androidTest/java/com/loopeer/android/demo/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.loopeer.android.demo; + +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.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.loopeer.android.demo", appContext.getPackageName()); + } +} diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..09bbb30 --- /dev/null +++ b/demo/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/java/com/loopeer/android/demo/MainActivity.java b/demo/src/main/java/com/loopeer/android/demo/MainActivity.java new file mode 100644 index 0000000..94ae42e --- /dev/null +++ b/demo/src/main/java/com/loopeer/android/demo/MainActivity.java @@ -0,0 +1,145 @@ +package com.loopeer.android.demo; + +import android.databinding.DataBindingUtil; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.Toast; + +import com.loopeer.android.demo.databinding.ActivityMainBinding; +import com.loopeer.android.popuputils.RecyclerAdapter; +import com.loopeer.android.popuputils.SimpleThreeLayersPopup; +import com.loopeer.android.popuputils.SimpleTwoLayersPopup; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class MainActivity extends AppCompatActivity { + + private List mPrimaryData; + private HashMap> mMapDatas; + + private List mPrimary; + private List mSecondary; + private List mTertiary; + + private SimpleTwoLayersPopup mSimpleTwoLayersPopup; + private SimpleThreeLayersPopup mThreeLayersPopup; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final ActivityMainBinding bind = DataBindingUtil.setContentView(this, R.layout.activity_main); + setSupportActionBar(bind.anchor); + + mPrimaryData = new ArrayList<>(); + mMapDatas = new HashMap<>(); + + mPrimary = new ArrayList<>(); + mSecondary = new ArrayList<>(); + mTertiary = new ArrayList<>(); + +// buildTwoLayersPopup(); + + buildThreeLayersPopup(); + + bind.btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { +// mSimpleTwoLayersPopup.show(bind.anchor, 0, 0); + mThreeLayersPopup.show(bind.anchor, 0, 0); + } + }); + } + + + private void buildTwoLayersPopup() { + + for (int i = 0; i < 15; i++) { + String primaryItem = "primaryItem: " + i; + mPrimaryData.add(primaryItem); + List strings = new ArrayList<>(); + for (int j = 0; j < 50; j++) { + strings.add(i + "-secondaryData: " + j); + } + + mMapDatas.put(primaryItem, strings); + } + + mSimpleTwoLayersPopup = new SimpleTwoLayersPopup<>(this); + mSimpleTwoLayersPopup.setTwoLayersAdapter(new SimpleTwoLayersPopup.SimpleTwoLayersAdapter() { + @Override + public List setPrimaryData() { + return mPrimaryData; + } + + @Override + public List setSecondaryData(String s) { + return mMapDatas.get(s); + } + }); + + mSimpleTwoLayersPopup.setSecondaryItemClickListener(new RecyclerAdapter.OnItemClickListener() { + + @Override + public void onItemClick(String data, int position) { + Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show(); + mSimpleTwoLayersPopup.dismiss(); + } + + @Override + public void setItemStyle(RecyclerView.ViewHolder preItemViewHolder, RecyclerView.ViewHolder curItemViewHolder) { + if (preItemViewHolder != null) + preItemViewHolder.itemView.setBackgroundColor(getResources().getColor(R.color.bg_layers)); + if (curItemViewHolder != null) + curItemViewHolder.itemView.setBackgroundColor(Color.LTGRAY); + } + }); + + mSimpleTwoLayersPopup.setPrimaryItemClickListener(null); + } + + private void buildThreeLayersPopup() { + + + for (int i = 0; i < 5; i++) { + Model model1 = new Model("primary--" + i); + List data1 = new ArrayList<>(); + for (int j = 0; j < 40; j++) { + Model model2 = new Model(i + "secondary--" + i); + List data2 = new ArrayList<>(); + for (int k = 0; k < 50; k++) { + data2.add(new Model(j + "tertiary--" + k)); + } + model2.setData(data2); + data1.add(model2); + } + model1.setData(data1); + mPrimary.add(model1); + } + + mThreeLayersPopup = new SimpleThreeLayersPopup<>(this); + mThreeLayersPopup.setThreeLayersAdapter(new SimpleThreeLayersPopup.SimpleThreeLayersAdapter() { + @Override + public List setTertiaryData(Model model) { + return model.getData(); + } + + @Override + public List setPrimaryData() { + return mPrimary; + } + + @Override + public List setSecondaryData(Model model) { + return model.getData(); + } + }); + + /*mThreeLayersPopup.setPrimaryItemClickListener(null); + mThreeLayersPopup.setSecondaryItemClickListener(null);*/ + } +} diff --git a/demo/src/main/java/com/loopeer/android/demo/Model.java b/demo/src/main/java/com/loopeer/android/demo/Model.java new file mode 100644 index 0000000..71bfa5a --- /dev/null +++ b/demo/src/main/java/com/loopeer/android/demo/Model.java @@ -0,0 +1,38 @@ +package com.loopeer.android.demo; + +import java.util.List; + +public class Model { + String name; + List data; + + public Model(String name) { + this.name = name; + } + + public Model(List data, String name) { + this.data = data; + this.name = name; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..e2bb820 --- /dev/null +++ b/demo/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/demo/src/main/res/mipmap-hdpi/ic_launcher.png b/demo/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/demo/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/demo/src/main/res/mipmap-mdpi/ic_launcher.png b/demo/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/demo/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/demo/src/main/res/mipmap-xhdpi/ic_launcher.png b/demo/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/demo/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png b/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/demo/src/main/res/values-w820dp/dimens.xml b/demo/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/demo/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/demo/src/main/res/values/colors.xml b/demo/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/demo/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/demo/src/main/res/values/dimens.xml b/demo/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/demo/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml new file mode 100644 index 0000000..b2250a8 --- /dev/null +++ b/demo/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Demo + diff --git a/demo/src/main/res/values/styles.xml b/demo/src/main/res/values/styles.xml new file mode 100644 index 0000000..0eb88fe --- /dev/null +++ b/demo/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/demo/src/test/java/com/loopeer/android/demo/ExampleUnitTest.java b/demo/src/test/java/com/loopeer/android/demo/ExampleUnitTest.java new file mode 100644 index 0000000..3185f13 --- /dev/null +++ b/demo/src/test/java/com/loopeer/android/demo/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.loopeer.android.demo; + +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() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# 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.jvmargs=-Xmx1536m + +# 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 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae 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..04e285f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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/library/.gitignore b/library/.gitignore new file mode 100644 index 0000000..6b122cb --- /dev/null +++ b/library/.gitignore @@ -0,0 +1,3 @@ +/build +/.idea/ +/local.properties diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..8e380d9 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 24 + buildToolsVersion "25.0.1" + defaultConfig { + minSdkVersion 14 + targetSdkVersion 24 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:25.0.1' + testCompile 'junit:junit:4.12' + compile 'com.android.support:design:25.0.1' +} diff --git a/library/gradle/wrapper/gradle-wrapper.jar b/library/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/library/gradle/wrapper/gradle-wrapper.jar differ diff --git a/library/gradle/wrapper/gradle-wrapper.properties b/library/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..04e285f --- /dev/null +++ b/library/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/library/gradlew b/library/gradlew new file mode 100644 index 0000000..9d82f78 --- /dev/null +++ b/library/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/library/gradlew.bat b/library/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/library/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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/library/proguard-rules.pro b/library/proguard-rules.pro new file mode 100644 index 0000000..63f7a19 --- /dev/null +++ b/library/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in E:\program\AndroidSDk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/library/src/androidTest/java/com/loopeer/android/popuputils/ExampleInstrumentedTest.java b/library/src/androidTest/java/com/loopeer/android/popuputils/ExampleInstrumentedTest.java new file mode 100644 index 0000000..dcf8b9c --- /dev/null +++ b/library/src/androidTest/java/com/loopeer/android/popuputils/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.loopeer.android.popuputils; + +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.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.loopeer.android.popuputils", appContext.getPackageName()); + } +} diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3bad7bd --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/library/src/main/java/com/loopeer/android/popuputils/AbstractVH.java b/library/src/main/java/com/loopeer/android/popuputils/AbstractVH.java new file mode 100644 index 0000000..29f52de --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/AbstractVH.java @@ -0,0 +1,12 @@ +package com.loopeer.android.popuputils; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +public abstract class AbstractVH extends RecyclerView.ViewHolder{ + public AbstractVH(View itemView) { + super(itemView); + } + + public abstract void bind(T item); +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/BasePopup.java b/library/src/main/java/com/loopeer/android/popuputils/BasePopup.java new file mode 100644 index 0000000..383fdba --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/BasePopup.java @@ -0,0 +1,64 @@ +package com.loopeer.android.popuputils; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.PopupWindow; + +public abstract class BasePopup { + private PopupWindow mPopupWindow; + + public BasePopup(Context context) { + mPopupWindow = initPopup(context); + if (mPopupWindow == null) { + throw new NullPointerException("must init popup window."); + } + } + + public void setPopSize(int width, int height) { + mPopupWindow.setWidth(width); + mPopupWindow.setHeight(height); + } + + public void setFocusable(boolean focusable) { + mPopupWindow.setFocusable(focusable); + } + + public void setBackgroundDrawable(Drawable drawable) { + mPopupWindow.setBackgroundDrawable(drawable); + } + + public void setOutsideTouchable(boolean touchable) { + mPopupWindow.setOutsideTouchable(touchable); + } + + private void setOnDismissListener(PopupWindow.OnDismissListener onDismissListener) { + mPopupWindow.setOnDismissListener(onDismissListener); + } + + public void setInputMethodMode(int mode) { + mPopupWindow.setInputMethodMode(mode); + } + + public boolean isShowing() { + return mPopupWindow.isShowing(); + } + + public void showAtLocation(View parent, int gravity, int x, int y) { + mPopupWindow.showAtLocation(parent, gravity, x, y); + } + + public void update() { + mPopupWindow.update(); + } + + public void show(View anchor, int xOff, int yOff) { + mPopupWindow.showAsDropDown(anchor, xOff, yOff); + } + + public void dismiss() { + mPopupWindow.dismiss(); + } + + public abstract PopupWindow initPopup(Context context); +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/RecyclerAdapter.java b/library/src/main/java/com/loopeer/android/popuputils/RecyclerAdapter.java new file mode 100644 index 0000000..4e92b4c --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/RecyclerAdapter.java @@ -0,0 +1,90 @@ +package com.loopeer.android.popuputils; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + + +public abstract class RecyclerAdapter extends RecyclerView.Adapter { + + private List mData; + private T mCurItem; + + private RecyclerView.ViewHolder mPreViewHolder; + private RecyclerView.ViewHolder mCurViewHolder; + + private OnItemClickListener mOnItemClickListener; + + public RecyclerAdapter(List data) { + mData = new ArrayList<>(); + mData.addAll(data); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return createViewHolder(parent); + } + + @Override + public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { + final T data = mData.get(position); + ((AbstractVH) holder).bind(data); + + if (mOnItemClickListener != null) { + if (mCurItem != data) { + mOnItemClickListener.setItemStyle(holder, null); + } else { + mCurViewHolder = holder; + mOnItemClickListener.setItemStyle(null, holder); + } + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mCurItem = data; + mPreViewHolder = mCurViewHolder; + mCurViewHolder = holder; + + + if (mOnItemClickListener != null) { + mOnItemClickListener.setItemStyle(mPreViewHolder, mCurViewHolder); + mOnItemClickListener.onItemClick(data, position); + } + } + }); + } + + @Override + public int getItemCount() { + return mData.size(); + } + + public T getCurItem() { + return mCurItem; + } + + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + mOnItemClickListener = onItemClickListener; + } + + public void updateData(List data) { + if (data == null) return; + + mData.clear(); + mData.addAll(data); + notifyDataSetChanged(); + } + + public abstract AbstractVH createViewHolder(ViewGroup parent); + + public static abstract class OnItemClickListener { + public abstract void onItemClick(T data, int position); + + public void setItemStyle(RecyclerView.ViewHolder preItemViewHolder, RecyclerView.ViewHolder curItemViewHolder) { + } + } +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/SimpleThreeLayersPopup.java b/library/src/main/java/com/loopeer/android/popuputils/SimpleThreeLayersPopup.java new file mode 100644 index 0000000..ebe18c3 --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/SimpleThreeLayersPopup.java @@ -0,0 +1,114 @@ +package com.loopeer.android.popuputils; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class SimpleThreeLayersPopup extends ThreeLayersPopup { + + public SimpleThreeLayersPopup(Context context) { + super(context); + } + + @Override + public void setPrimaryItemClickListener(final RecyclerAdapter.OnItemClickListener

onItemClickListener) { + RecyclerAdapter.OnItemClickListener

listener = new RecyclerAdapter.OnItemClickListener

() { + @Override + public void onItemClick(P data, int position) { + mSecondaryAdapter.updateData(mThreeLayersAdapter.setSecondaryData(data)); + if (onItemClickListener != null) + onItemClickListener.onItemClick(data, position); + } + }; + super.setPrimaryItemClickListener(listener); + } + + @Override + public void setSecondaryItemClickListener(final RecyclerAdapter.OnItemClickListener onItemClickListener) { + RecyclerAdapter.OnItemClickListener listener = new RecyclerAdapter.OnItemClickListener() { + @Override + public void onItemClick(S data, int position) { + mTertiaryAdapter.updateData(mThreeLayersAdapter.setTertiaryData(data)); + if (onItemClickListener != null) + onItemClickListener.onItemClick(data, position); + } + }; + super.setSecondaryItemClickListener(listener); + } + + @Override + public void setThreeLayersAdapter(ThreeLayersAdapter threeLayersAdapter) { + super.setThreeLayersAdapter(threeLayersAdapter); + try{ + setPrimaryItemClickListener(null); + setSecondaryItemClickListener(null); + } catch (NullPointerException e){ + // + } + } + + public abstract static class SimpleThreeLayersAdapter implements ThreeLayersAdapter { + @Override + public AbstractVH

createPrimaryVH(ViewGroup parent) { + return new PrimaryVH<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_simple_primary_layer, parent, false)); + } + + @Override + public AbstractVH createSecondaryVH(ViewGroup parent) { + return new SecondaryVH<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_simple_secondary_layer, parent, false)); + } + + @Override + public AbstractVH createTertiaryVH(ViewGroup parent) { + return new TertiaryVH<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_simple_tertiary_layer, parent, false)); + } + } + + + private static class PrimaryVH

extends AbstractVH

{ + + private TextView mFirstText; + + PrimaryVH(View itemView) { + super(itemView); + mFirstText = (TextView) itemView.findViewById(R.id.text_first_layer); + } + + @Override + public void bind(P item) { + if (item != null) mFirstText.setText(item.toString()); + } + } + + private static class SecondaryVH extends AbstractVH { + + private TextView mFirstText; + + SecondaryVH(View itemView) { + super(itemView); + mFirstText = (TextView) itemView.findViewById(R.id.text_second_layer); + } + + @Override + public void bind(S item) { + if (item != null) mFirstText.setText(item.toString()); + } + } + + private static class TertiaryVH extends AbstractVH { + + private TextView mFirstText; + + TertiaryVH(View itemView) { + super(itemView); + mFirstText = (TextView) itemView.findViewById(R.id.text_tertiary_layer); + } + + @Override + public void bind(T item) { + if (item != null) mFirstText.setText(item.toString()); + } + } +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/SimpleTwoLayersPopup.java b/library/src/main/java/com/loopeer/android/popuputils/SimpleTwoLayersPopup.java new file mode 100644 index 0000000..2427221 --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/SimpleTwoLayersPopup.java @@ -0,0 +1,80 @@ +package com.loopeer.android.popuputils; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class SimpleTwoLayersPopup extends TwoLayersPopup { + + public SimpleTwoLayersPopup(Context context) { + super(context); + } + + @Override + public void setPrimaryItemClickListener(final RecyclerAdapter.OnItemClickListener

onItemClickListener) { + RecyclerAdapter.OnItemClickListener

listener = new RecyclerAdapter.OnItemClickListener

() { + @Override + public void onItemClick(P data, int position) { + mSecondaryAdapter.updateData(mTwoLayersAdapter.setSecondaryData(data)); + if (onItemClickListener != null) + onItemClickListener.onItemClick(data, position); + } + }; + super.setPrimaryItemClickListener(listener); + } + + @Override + public void setTwoLayersAdapter(TwoLayersAdapter twoLayersAdapter) { + super.setTwoLayersAdapter(twoLayersAdapter); + try{ + setPrimaryItemClickListener(null); + } catch (NullPointerException e){ + // + } + } + + public abstract static class SimpleTwoLayersAdapter implements TwoLayersAdapter { + @Override + public AbstractVH

createPrimaryVH(ViewGroup parent) { + return new PrimaryVH<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_simple_primary_layer, parent, false)); + } + + @Override + public AbstractVH createSecondaryVH(ViewGroup parent) { + return new SecondaryVH<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_simple_secondary_layer, parent, false)); + } + } + + private static class PrimaryVH

extends AbstractVH

{ + + private TextView mFirstText; + + PrimaryVH(View itemView) { + super(itemView); + mFirstText = (TextView) itemView.findViewById(R.id.text_first_layer); + } + + @Override + public void bind(P item) { + if (item != null) mFirstText.setText(item.toString()); + } + } + + private static class SecondaryVH extends AbstractVH { + + private TextView mFirstText; + + SecondaryVH(View itemView) { + super(itemView); + mFirstText = (TextView) itemView.findViewById(R.id.text_second_layer); + } + + @Override + public void bind(S item) { + if (item != null) mFirstText.setText(item.toString()); + } + } + +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/ThreeLayersAdapter.java b/library/src/main/java/com/loopeer/android/popuputils/ThreeLayersAdapter.java new file mode 100644 index 0000000..cc05112 --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/ThreeLayersAdapter.java @@ -0,0 +1,12 @@ +package com.loopeer.android.popuputils; + +import android.view.ViewGroup; + +import java.util.List; + +public interface ThreeLayersAdapter extends TwoLayersAdapter { + + List setTertiaryData(S s); + + AbstractVH createTertiaryVH(ViewGroup parent); +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/ThreeLayersPopup.java b/library/src/main/java/com/loopeer/android/popuputils/ThreeLayersPopup.java new file mode 100644 index 0000000..2f8a9ec --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/ThreeLayersPopup.java @@ -0,0 +1,116 @@ +package com.loopeer.android.popuputils; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import java.util.List; + +public class ThreeLayersPopup extends BasePopup { + protected RecyclerView mPrimaryLayer; + protected RecyclerView mSecondaryLayer; + protected RecyclerView mTertiaryLayer; + + protected List

mPrimaryData; + protected List mSecondaryData; + protected List mTertiaryData; + + protected RecyclerAdapter

mPrimaryAdapter; + protected RecyclerAdapter mSecondaryAdapter; + protected RecyclerAdapter mTertiaryAdapter; + protected ThreeLayersAdapter mThreeLayersAdapter; + + public ThreeLayersPopup(Context context) { + super(context); + } + + public void setLayerWeight(int primaryLayerWeight, int secondaryLayerWeight, int tertiaryAdapter) { + LinearLayout.LayoutParams lp1 = (LinearLayout.LayoutParams) mPrimaryLayer.getLayoutParams(); + lp1.weight = primaryLayerWeight; + mPrimaryLayer.setLayoutParams(lp1); + mPrimaryLayer.requestLayout(); + + LinearLayout.LayoutParams lp2 = (LinearLayout.LayoutParams) mSecondaryLayer.getLayoutParams(); + lp1.weight = secondaryLayerWeight; + mSecondaryLayer.setLayoutParams(lp2); + mSecondaryLayer.requestLayout(); + + + LinearLayout.LayoutParams lp3 = (LinearLayout.LayoutParams) mTertiaryLayer.getLayoutParams(); + lp1.weight = tertiaryAdapter; + mTertiaryLayer.setLayoutParams(lp3); + mTertiaryLayer.requestLayout(); + } + + public ThreeLayersAdapter getThreeLayersAdapter() { + return mThreeLayersAdapter; + } + + public void setThreeLayersAdapter(ThreeLayersAdapter threeLayersAdapter) { + mThreeLayersAdapter = threeLayersAdapter; + + mPrimaryData = mThreeLayersAdapter.setPrimaryData(); + mSecondaryData = mThreeLayersAdapter.setSecondaryData(mPrimaryData.get(0)); + mTertiaryData = mThreeLayersAdapter.setTertiaryData(mSecondaryData.get(0)); + + mPrimaryAdapter = new RecyclerAdapter

(mPrimaryData) { + @Override + public AbstractVH

createViewHolder(ViewGroup parent) { + return mThreeLayersAdapter.createPrimaryVH(parent); + } + }; + + mSecondaryAdapter = new RecyclerAdapter(mSecondaryData) { + @Override + public AbstractVH createViewHolder(ViewGroup parent) { + return mThreeLayersAdapter.createSecondaryVH(parent); + } + }; + + mTertiaryAdapter = new RecyclerAdapter(mTertiaryData) { + @Override + public AbstractVH createViewHolder(ViewGroup parent) { + return mThreeLayersAdapter.createTertiaryVH(parent); + } + }; + + mPrimaryLayer.setAdapter(mPrimaryAdapter); + mSecondaryLayer.setAdapter(mSecondaryAdapter); + mTertiaryLayer.setAdapter(mTertiaryAdapter); + } + + public void setPrimaryItemClickListener(RecyclerAdapter.OnItemClickListener

onItemClickListener) { + mPrimaryAdapter.setOnItemClickListener(onItemClickListener); + } + + public void setSecondaryItemClickListener(RecyclerAdapter.OnItemClickListener onItemClickListener) { + mSecondaryAdapter.setOnItemClickListener(onItemClickListener); + } + + public void setTertiaryItemClickListener(RecyclerAdapter.OnItemClickListener onItemClickListener) { + mTertiaryAdapter.setOnItemClickListener(onItemClickListener); + } + + @Override + public PopupWindow initPopup(Context context) { + View v = View.inflate(context, R.layout.view_three_layers, null); + mPrimaryLayer = (RecyclerView) v.findViewById(R.id.primary_layer); + mSecondaryLayer = (RecyclerView) v.findViewById(R.id.secondary_layer); + mTertiaryLayer = (RecyclerView) v.findViewById(R.id.tertiary_layer); + + mPrimaryLayer.setLayoutManager(new LinearLayoutManager(context)); + mSecondaryLayer.setLayoutManager(new LinearLayoutManager(context)); + mTertiaryLayer.setLayoutManager(new LinearLayoutManager(context)); + + PopupWindow popupWindow = new PopupWindow(v, ViewGroup.LayoutParams.MATCH_PARENT, context.getResources().getDimensionPixelSize(R.dimen.layers_height), true); + popupWindow.setBackgroundDrawable(new ColorDrawable()); + popupWindow.setOutsideTouchable(true); + + return popupWindow; + } +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/TwoLayersAdapter.java b/library/src/main/java/com/loopeer/android/popuputils/TwoLayersAdapter.java new file mode 100644 index 0000000..8cda433 --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/TwoLayersAdapter.java @@ -0,0 +1,15 @@ +package com.loopeer.android.popuputils; + +import android.view.ViewGroup; + +import java.util.List; + +public interface TwoLayersAdapter { + List

setPrimaryData(); + + List setSecondaryData(P p); + + AbstractVH

createPrimaryVH(ViewGroup parent); + + AbstractVH createSecondaryVH(ViewGroup parent); +} diff --git a/library/src/main/java/com/loopeer/android/popuputils/TwoLayersPopup.java b/library/src/main/java/com/loopeer/android/popuputils/TwoLayersPopup.java new file mode 100644 index 0000000..d47eb55 --- /dev/null +++ b/library/src/main/java/com/loopeer/android/popuputils/TwoLayersPopup.java @@ -0,0 +1,101 @@ +package com.loopeer.android.popuputils; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import java.util.List; + +public class TwoLayersPopup extends BasePopup { + protected RecyclerView mPrimaryLayer; + protected RecyclerView mSecondaryLayer; + + protected List

mPrimaryData; + protected List mSecondaryData; + + protected RecyclerAdapter

mPrimaryAdapter; + protected RecyclerAdapter mSecondaryAdapter; + protected TwoLayersAdapter mTwoLayersAdapter; + + public TwoLayersPopup(Context context) { + super(context); + } + + public void setLayerWeight(int primaryLayerWeight, int secondaryLayerWeight) { + LinearLayout.LayoutParams lp1 = (LinearLayout.LayoutParams) mPrimaryLayer.getLayoutParams(); + lp1.weight = primaryLayerWeight; + mPrimaryLayer.setLayoutParams(lp1); + mPrimaryLayer.requestLayout(); + + LinearLayout.LayoutParams lp2 = (LinearLayout.LayoutParams) mSecondaryLayer.getLayoutParams(); + lp1.weight = secondaryLayerWeight; + mSecondaryLayer.setLayoutParams(lp2); + mSecondaryLayer.requestLayout(); + } + + + public TwoLayersAdapter getTwoLayersAdapter() { + return mTwoLayersAdapter; + } + + public void setTwoLayersAdapter(TwoLayersAdapter twoLayersAdapter) { + mTwoLayersAdapter = twoLayersAdapter; + + mPrimaryData = mTwoLayersAdapter.setPrimaryData(); + mSecondaryData = mTwoLayersAdapter.setSecondaryData(mPrimaryData.get(0)); + + mPrimaryAdapter = new RecyclerAdapter

(mPrimaryData) { + @Override + public AbstractVH

createViewHolder(ViewGroup parent) { + return mTwoLayersAdapter.createPrimaryVH(parent); + } + }; + + mSecondaryAdapter = new RecyclerAdapter(mSecondaryData) { + @Override + public AbstractVH createViewHolder(ViewGroup parent) { + return mTwoLayersAdapter.createSecondaryVH(parent); + } + }; + + mPrimaryLayer.setAdapter(mPrimaryAdapter); + mSecondaryLayer.setAdapter(mSecondaryAdapter); + } + + + public void setPrimaryItemClickListener(RecyclerAdapter.OnItemClickListener

onItemClickListener) { + mPrimaryAdapter.setOnItemClickListener(onItemClickListener); + } + + public void setSecondaryItemClickListener(RecyclerAdapter.OnItemClickListener onItemClickListener) { + mSecondaryAdapter.setOnItemClickListener(onItemClickListener); + } + + public P getCurPrimaryItem() { + return mPrimaryAdapter.getCurItem(); + } + + public S getCurSecondaryItem() { + return mSecondaryAdapter.getCurItem(); + } + + @Override + public PopupWindow initPopup(Context context) { + View v = View.inflate(context, R.layout.view_two_layers, null); + mPrimaryLayer = (RecyclerView) v.findViewById(R.id.primary_layer); + mSecondaryLayer = (RecyclerView) v.findViewById(R.id.secondary_layer); + mPrimaryLayer.setLayoutManager(new LinearLayoutManager(context)); + mSecondaryLayer.setLayoutManager(new LinearLayoutManager(context)); + + PopupWindow popupWindow = new PopupWindow(v, ViewGroup.LayoutParams.MATCH_PARENT, context.getResources().getDimensionPixelSize(R.dimen.layers_height), true); + popupWindow.setBackgroundDrawable(new ColorDrawable()); + popupWindow.setOutsideTouchable(true); + + return popupWindow; + } +} diff --git a/library/src/main/res/layout/divider_layer.xml b/library/src/main/res/layout/divider_layer.xml new file mode 100644 index 0000000..84b1d85 --- /dev/null +++ b/library/src/main/res/layout/divider_layer.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/library/src/main/res/layout/item_simple_primary_layer.xml b/library/src/main/res/layout/item_simple_primary_layer.xml new file mode 100644 index 0000000..ad28b6e --- /dev/null +++ b/library/src/main/res/layout/item_simple_primary_layer.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/library/src/main/res/layout/item_simple_secondary_layer.xml b/library/src/main/res/layout/item_simple_secondary_layer.xml new file mode 100644 index 0000000..fe70a13 --- /dev/null +++ b/library/src/main/res/layout/item_simple_secondary_layer.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/library/src/main/res/layout/item_simple_tertiary_layer.xml b/library/src/main/res/layout/item_simple_tertiary_layer.xml new file mode 100644 index 0000000..dd98982 --- /dev/null +++ b/library/src/main/res/layout/item_simple_tertiary_layer.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/library/src/main/res/layout/view_three_layers.xml b/library/src/main/res/layout/view_three_layers.xml new file mode 100644 index 0000000..d5ff2da --- /dev/null +++ b/library/src/main/res/layout/view_three_layers.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/view_two_layers.xml b/library/src/main/res/layout/view_two_layers.xml new file mode 100644 index 0000000..8d351f4 --- /dev/null +++ b/library/src/main/res/layout/view_two_layers.xml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/values/colors.xml b/library/src/main/res/values/colors.xml new file mode 100644 index 0000000..f66e7e9 --- /dev/null +++ b/library/src/main/res/values/colors.xml @@ -0,0 +1,8 @@ + + + #222222 + #222222 + #222222 + #E7E7E7 + #F9F9F9 + \ No newline at end of file diff --git a/library/src/main/res/values/dimens.xml b/library/src/main/res/values/dimens.xml new file mode 100644 index 0000000..1f9ff19 --- /dev/null +++ b/library/src/main/res/values/dimens.xml @@ -0,0 +1,12 @@ + + + 16sp + 16sp + 16sp + 64dp + 64dp + 64dp + 8dp + 360dp + 0.5dp + \ No newline at end of file diff --git a/library/src/test/java/com/loopeer/android/popuputils/ExampleUnitTest.java b/library/src/test/java/com/loopeer/android/popuputils/ExampleUnitTest.java new file mode 100644 index 0000000..1c8b337 --- /dev/null +++ b/library/src/test/java/com/loopeer/android/popuputils/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.loopeer.android.popuputils; + +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() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..2150f22 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':library', ':demo'