├── .gitignore ├── README.md ├── art └── square_blue_light.png ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ryanharter │ │ └── android │ │ └── tooltips │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ryanharter │ │ └── android │ │ └── tooltips │ │ ├── ToolTip.java │ │ ├── ToolTipLayout.java │ │ └── ToolTipPointerView.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ └── values │ └── strings.xml ├── sample ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── ryanharter │ │ └── android │ │ └── tooltips │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ryanharter │ │ └── android │ │ └── tooltips │ │ └── sample │ │ └── MyActivity.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ ├── bg_tooltip_blue.xml │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ └── activity_my.xml │ ├── menu │ └── my.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea 4 | .DS_Store 5 | /gradle 6 | /build 7 | *.iml 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android ToolTips [Play Store Demo](https://play.google.com/store/apps/details?id=com.ryanharter.android.tooltips.sample) 2 | 3 | ToolTips is an Open Source Android library that allows developers to easily display tooltips for views. 4 | 5 | ## Getting It 6 | 7 | Android ToolTips is available on Maven Central, so you can use the normal methods to acquire the library. 8 | 9 | Gradle 10 | 11 | ``` 12 | compile 'com.ryanharter.android.tooltips:library:0.0.3' 13 | ``` 14 | 15 | Maven 16 | 17 | ``` 18 | 19 | com.ryanharter.android.tooltips 20 | library 21 | 0.0.3 22 | 23 | ``` 24 | 25 | ## Usage 26 | 27 | 1. Add a `com.ryanharter.android.tooltips.ToolTipLayout` view to your layout such that it fills the view view and overlays everything else. 28 | 1. Create `ToolTip` instances using the `Builder` class. 29 | 1. Add the `ToolTip`s to your view. 30 | 31 | ## Example 32 | 33 | ### layout.xml 34 | 35 | ```xml 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | ``` 54 | 55 | ### Activity.java (or Fragment.java) 56 | 57 | ```java 58 | // Get the tooltip layout 59 | ToolTipLayout tipContainer = (ToolTipLayout) findViewById(R.id.tooltip_container); 60 | 61 | // Create a content view however you'd like 62 | // ... 63 | 64 | // Create a ToolTip using the Builder class 65 | ToolTip t = new Builder(myContext) 66 | .anchor(myAnchorView) // The view to which the ToolTip should be anchored 67 | .gravity(Gravity.TOP) // The location of the view in relation to the anchor (LEFT, RIGHT, TOP, BOTTOM) 68 | .color(Color.RED) // The color of the pointer arrow 69 | .pointerSize(POINTER_SIZE) // The size of the pointer 70 | .contentView(contentView) // The actual contents of the ToolTip 71 | .build(); 72 | 73 | // Add the ToolTip to the view, using the default animations 74 | tipContainer.addTooltip(t); 75 | ``` 76 | 77 | ## Developed By 78 | 79 | * Ryan Harter 80 | 81 | ## License 82 | 83 | ``` 84 | Copyright 2013 Ryan Harter 85 | 86 | Licensed under the Apache License, Version 2.0 (the "License"); 87 | you may not use this file except in compliance with the License. 88 | You may obtain a copy of the License at 89 | 90 | http://www.apache.org/licenses/LICENSE-2.0 91 | 92 | Unless required by applicable law or agreed to in writing, software 93 | distributed under the License is distributed on an "AS IS" BASIS, 94 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 95 | See the License for the specific language governing permissions and 96 | limitations under the License. 97 | ``` -------------------------------------------------------------------------------- /art/square_blue_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/art/square_blue_light.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:0.12.+' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=0.0.4 2 | VERSION_CODE=1 3 | GROUP=com.ryanharter.android.tooltips 4 | 5 | POM_DESCRIPTION=An easy to use library for displaying tooltips in Android. 6 | POM_URL=https://github.com/rharter/android-tooltips 7 | POM_SCM_URL=https://github.com/rharter/android-tooltips 8 | POM_SCM_CONNECTION=scm:git@github.com:rharter/android-tooltips.git 9 | POM_SCM_DEV_CONNECTION=scm:git@github.com:rharter/android-tooltips.git 10 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 11 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 12 | POM_LICENCE_DIST=repo 13 | POM_DEVELOPER_ID=rharter 14 | POM_DEVELOPER_NAME=Ryan Harter -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | // TODO Change this back to the main script once the PR is merged. 3 | apply from: 'https://raw.githubusercontent.com/jpardogo/gradle-mvn-push/master/gradle-mvn-push.gradle' 4 | 5 | android { 6 | compileSdkVersion 20 7 | buildToolsVersion "20.0.0" 8 | 9 | defaultConfig { 10 | applicationId "com.ryanharter.android.tooltips" 11 | minSdkVersion 15 12 | targetSdkVersion 20 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | 17 | compileOptions { 18 | sourceCompatibility JavaVersion.VERSION_1_7 19 | targetCompatibility JavaVersion.VERSION_1_7 20 | } 21 | buildTypes { 22 | release { 23 | runProguard false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | compile fileTree(dir: 'libs', include: ['*.jar']) 31 | } 32 | -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Android ToolTips Library 2 | POM_ARTIFACT_ID=library 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/ryanharter/android/tooltips/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.ryanharter.android.tooltips; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/ryanharter/android/tooltips/ToolTip.java: -------------------------------------------------------------------------------- 1 | package com.ryanharter.android.tooltips; 2 | 3 | import android.content.Context; 4 | import android.view.Gravity; 5 | import android.view.View; 6 | import android.view.ViewGroup.LayoutParams; 7 | import android.widget.LinearLayout; 8 | 9 | import java.lang.ref.WeakReference; 10 | 11 | /** 12 | * Created by rharter on 7/24/14. 13 | */ 14 | public class ToolTip { 15 | 16 | private WeakReference mAnchorView; 17 | private WeakReference mContentView; 18 | private ToolTipPointerView mPointerView; 19 | private int mGravity; 20 | private int mColor; 21 | private int mPointerSize; 22 | private boolean mDismissOnTouch; 23 | 24 | private View mView; 25 | 26 | public View getView() { 27 | return mView; 28 | } 29 | 30 | public void setView(View view) { 31 | mView = view; 32 | } 33 | 34 | public View getAnchorView() { 35 | return mAnchorView != null ? mAnchorView.get() : null; 36 | } 37 | 38 | public void setAnchorView(View anchorView) { 39 | mAnchorView = new WeakReference<>(anchorView); 40 | } 41 | 42 | public View getContentView() { 43 | return mContentView != null ? mContentView.get() : null; 44 | } 45 | 46 | public void setContentView(View view) { 47 | mContentView = new WeakReference(view); 48 | } 49 | 50 | public ToolTipPointerView getPointerView() { 51 | return mPointerView; 52 | } 53 | 54 | public void setPointerView(ToolTipPointerView pointerView) { 55 | mPointerView = pointerView; 56 | } 57 | 58 | public int getGravity() { 59 | return mGravity; 60 | } 61 | 62 | public void setGravity(int gravity) { 63 | mGravity = gravity; 64 | } 65 | 66 | public int getColor() { 67 | return mColor; 68 | } 69 | 70 | public void setColor(int color) { 71 | mColor = color; 72 | } 73 | 74 | public int getPointerSize() { 75 | return mPointerSize; 76 | } 77 | 78 | public void setPointerSize(int pointerSize) { 79 | mPointerSize = pointerSize; 80 | } 81 | 82 | public boolean isDismissOnTouch() { 83 | return mDismissOnTouch; 84 | } 85 | 86 | public void setDismissOnTouch(boolean dismissOnTouch) { 87 | mDismissOnTouch = dismissOnTouch; 88 | } 89 | 90 | /** 91 | * Builds the tooltip view for display. The callout arrow is 92 | * defined by gravity, but defaults to alignment 93 | * at 0, it's up to the caller to position it appropriately 94 | * @param context The context used to build the view. 95 | * @return The constructed view, with added arrow. 96 | */ 97 | public View makeView(Context context) { 98 | LinearLayout layout = new LinearLayout(context); 99 | 100 | // If the tooltip is above or below, we make is fill the view. 101 | if (Gravity.isVertical(mGravity)) { 102 | layout.setLayoutParams(new LayoutParams( 103 | LayoutParams.MATCH_PARENT, 104 | LayoutParams.WRAP_CONTENT)); 105 | } 106 | 107 | mPointerView = new ToolTipPointerView(context, mColor, mGravity); 108 | 109 | if (Gravity.isHorizontal(mGravity)) { 110 | mPointerView.setLayoutParams(new LayoutParams(mPointerSize, mPointerSize * 2)); 111 | layout.setOrientation(LinearLayout.HORIZONTAL); 112 | } else { 113 | mPointerView.setLayoutParams(new LayoutParams(mPointerSize * 2, mPointerSize)); 114 | layout.setOrientation(LinearLayout.VERTICAL); 115 | } 116 | 117 | // For right and bottom alignment, the arrow goes at the beginning 118 | if ((mGravity & Gravity.RIGHT) == Gravity.RIGHT 119 | || (mGravity & Gravity.BOTTOM) == Gravity.BOTTOM) { 120 | layout.addView(mPointerView); 121 | } 122 | 123 | layout.addView(mContentView.get()); 124 | 125 | // For left or top alignment, the arrow goes at the end 126 | if ((mGravity & Gravity.LEFT) == Gravity.LEFT || (mGravity & Gravity.TOP) == Gravity.TOP) { 127 | layout.addView(mPointerView); 128 | } 129 | 130 | return layout; 131 | } 132 | 133 | public static class Builder { 134 | private Context context; 135 | private View contentView; 136 | private View anchor; 137 | private int gravity; 138 | private int color; 139 | private int pointerSize; 140 | private boolean dismissOnTouch; 141 | 142 | public Builder(Context context) { 143 | this.context = context; 144 | } 145 | 146 | public Builder contentView(View v) { 147 | contentView = v; 148 | return this; 149 | } 150 | 151 | public Builder gravity(int gravity) { 152 | this.gravity = gravity; 153 | return this; 154 | } 155 | 156 | public Builder color(int color) { 157 | this.color = color; 158 | return this; 159 | } 160 | 161 | public Builder pointerSize(int size) { 162 | this.pointerSize = size; 163 | return this; 164 | } 165 | 166 | public Builder anchor(View anchor) { 167 | this.anchor = anchor; 168 | return this; 169 | } 170 | 171 | public Builder dismissOnTouch(boolean dismiss) { 172 | this.dismissOnTouch = dismiss; 173 | return this; 174 | } 175 | 176 | public ToolTip build() { 177 | return new ToolTip(this); 178 | } 179 | } 180 | 181 | private ToolTip(Builder builder) { 182 | setContentView(builder.contentView); 183 | setGravity(builder.gravity); 184 | setColor(builder.color); 185 | setPointerSize(builder.pointerSize); 186 | setAnchorView(builder.anchor); 187 | setView(makeView(builder.context)); 188 | setDismissOnTouch(builder.dismissOnTouch); 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /library/src/main/java/com/ryanharter/android/tooltips/ToolTipLayout.java: -------------------------------------------------------------------------------- 1 | package com.ryanharter.android.tooltips; 2 | 3 | import android.animation.Animator; 4 | import android.animation.Animator.AnimatorListener; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.content.Context; 8 | import android.util.AttributeSet; 9 | import android.view.Gravity; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | import android.view.ViewTreeObserver.OnPreDrawListener; 13 | import android.widget.RelativeLayout; 14 | 15 | import java.lang.ref.WeakReference; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * Created by rharter on 7/24/14. 23 | */ 24 | public class ToolTipLayout extends RelativeLayout { 25 | 26 | private ToolTip mToolTip; 27 | private View mToolTipView; 28 | 29 | private List mToolTips = new ArrayList<>(); 30 | 31 | private boolean mDimensionsKnown; 32 | 33 | private int mTargetX; 34 | private int mTargetY; 35 | private WeakReference mAnchorView; 36 | 37 | public ToolTipLayout(Context context) { 38 | this(context, null); 39 | } 40 | 41 | public ToolTipLayout(Context context, AttributeSet attrs) { 42 | this(context, attrs, 0); 43 | } 44 | 45 | public ToolTipLayout(Context context, AttributeSet attrs, int defStyle) { 46 | super(context, attrs, defStyle); 47 | } 48 | 49 | /** 50 | * Dismisses all tooltips currently being displayed using 51 | * the default animation. 52 | */ 53 | public void dismiss() { 54 | dismiss(true); 55 | } 56 | 57 | /** 58 | * Dismisses all tooltips currently being displayed. 59 | * 60 | * @param animate True to animate the transition. 61 | */ 62 | public void dismiss(boolean animate) { 63 | mToolTips.clear(); 64 | 65 | if (animate) { 66 | 67 | final List viewsToRemove = new ArrayList<>(); 68 | List a = new ArrayList<>(); 69 | for (int i = 0; i < getChildCount(); i++) { 70 | a.add(ObjectAnimator.ofFloat(getChildAt(i), View.ALPHA, 0)); 71 | viewsToRemove.add(getChildAt(i)); 72 | } 73 | 74 | AnimatorSet s = new AnimatorSet(); 75 | s.playTogether(a); 76 | s.addListener(new AnimatorListener() { 77 | @Override public void onAnimationStart(Animator animation) { 78 | } 79 | 80 | @Override public void onAnimationEnd(Animator animation) { 81 | for (View v : viewsToRemove) { 82 | removeView(v); 83 | } 84 | } 85 | 86 | @Override public void onAnimationCancel(Animator animation) { 87 | } 88 | 89 | @Override public void onAnimationRepeat(Animator animation) { 90 | } 91 | }); 92 | s.start(); 93 | } else { 94 | removeAllViews(); 95 | } 96 | } 97 | 98 | public void addTooltip(ToolTip tooltip) { 99 | addTooltip(tooltip, true); 100 | } 101 | 102 | private Map mFinalPositions = new HashMap<>(); 103 | private boolean mShouldRemoveObserver; 104 | 105 | public void addTooltip(ToolTip tooltip, boolean animate) { 106 | mToolTips.add(tooltip); 107 | View v = tooltip.getView(); 108 | if (tooltip.isDismissOnTouch()) { 109 | v.setOnClickListener(new OnClickListener() { 110 | @Override public void onClick(View v) { 111 | dismiss(); 112 | } 113 | }); 114 | } 115 | addView(v); 116 | requestLayout(); 117 | 118 | if (animate) { 119 | // We use a two pass preDrawListener so we can get the post animation 120 | // position of the item and then animate it. 121 | getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { 122 | @Override public boolean onPreDraw() { 123 | 124 | if (!mShouldRemoveObserver) { 125 | mShouldRemoveObserver = true; 126 | 127 | mFinalPositions.clear(); 128 | 129 | for (ToolTip t : mToolTips) { 130 | float position = 0; 131 | if (Gravity.isVertical(t.getGravity())) { 132 | position = t.getView().getY(); 133 | } else { 134 | position = t.getView().getX(); 135 | } 136 | 137 | mFinalPositions.put(t, position); 138 | } 139 | 140 | return false; 141 | } 142 | 143 | mShouldRemoveObserver = false; 144 | getViewTreeObserver().removeOnPreDrawListener(this); 145 | 146 | List animators = new ArrayList<>(); 147 | 148 | for (ToolTip t : mToolTips) { 149 | t.getView().setAlpha(0); 150 | animators.add(ObjectAnimator.ofFloat(t.getView(), View.ALPHA, 0, 1)); 151 | 152 | // TODO Restore this once I can fix the flicker 153 | // final float finalPosition = mFinalPositions.get(t); 154 | // if ((Gravity.VERTICAL_GRAVITY_MASK & t.getGravity()) == Gravity.TOP) { 155 | // float start = finalPosition - t.getView().getHeight() / 2; 156 | // t.getView().setY(start); 157 | // 158 | // animators.add(ObjectAnimator 159 | // .ofFloat(t.getView(), View.TRANSLATION_Y, 160 | // start, 161 | // finalPosition)); 162 | // } else if ((Gravity.VERTICAL_GRAVITY_MASK & t.getGravity()) 163 | // == Gravity.BOTTOM) { 164 | // float start = finalPosition + t.getView().getHeight() / 2; 165 | // t.getView().setY(start); 166 | // 167 | // animators.add(ObjectAnimator 168 | // .ofFloat(t.getView(), View.TRANSLATION_Y, 169 | // start, 170 | // finalPosition)); 171 | // } else if ((Gravity.HORIZONTAL_GRAVITY_MASK & t.getGravity()) 172 | // == Gravity.LEFT) { 173 | // float start = finalPosition - t.getView().getWidth() / 2; 174 | // t.getView().setX(start); 175 | // 176 | // animators.add(ObjectAnimator 177 | // .ofFloat(t.getView(), View.TRANSLATION_X, 178 | // start, 179 | // finalPosition)); 180 | // } else if ((Gravity.HORIZONTAL_GRAVITY_MASK & t.getGravity()) 181 | // == Gravity.RIGHT) { 182 | // float start = finalPosition + t.getView().getWidth() / 2; 183 | // t.getView().setX(start); 184 | // 185 | // animators.add(ObjectAnimator 186 | // .ofFloat(t.getView(), View.TRANSLATION_X, 187 | // start, 188 | // finalPosition)); 189 | // } 190 | } 191 | 192 | AnimatorSet s = new AnimatorSet(); 193 | s.playTogether(animators); 194 | s.start(); 195 | 196 | return true; 197 | } 198 | }); 199 | } 200 | } 201 | 202 | @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { 203 | super.onLayout(changed, l, t, r, b); 204 | 205 | int[] containerLocation = new int[2]; 206 | getLocationOnScreen(containerLocation); 207 | 208 | for (ToolTip tip : mToolTips) { 209 | int[] anchorLocation = new int[2]; 210 | if (tip.getAnchorView() != null) { 211 | tip.getAnchorView().getLocationOnScreen(anchorLocation); 212 | } else { 213 | // Default to a centered view 214 | anchorLocation[0] = containerLocation[0] / 2; 215 | anchorLocation[1] = containerLocation[1] / 2; 216 | } 217 | 218 | int x = anchorLocation[0] - containerLocation[0]; 219 | int y = anchorLocation[1] - containerLocation[1]; 220 | 221 | if ((tip.getGravity() & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.TOP) { 222 | y -= tip.getView().getHeight(); 223 | } else if ((tip.getGravity() & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { 224 | if (tip.getAnchorView() != null) { 225 | y += tip.getAnchorView().getHeight(); 226 | } 227 | } else if ((tip.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) { 228 | x -= tip.getView().getWidth(); 229 | } else if ((tip.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.RIGHT) { 230 | if (tip.getAnchorView() != null) { 231 | x += tip.getAnchorView().getWidth(); 232 | } 233 | } 234 | 235 | // The tooltip is above or below the anchor view, so 236 | // center the location on the X axis, otherwise center 237 | // on the Y axis. 238 | ToolTipPointerView pointer = tip.getPointerView(); 239 | if (Gravity.isVertical(tip.getGravity())) { 240 | x += tip.getAnchorView().getWidth() / 2; 241 | pointer.setX(x - containerLocation[0] - pointer.getWidth() / 2); 242 | tip.getView().setY(y); 243 | } else { 244 | y += tip.getAnchorView().getHeight() / 2 - tip.getView().getHeight() / 2; 245 | tip.getView().setY(y); 246 | tip.getView().setX(x); 247 | tip.getPointerView().setY(tip.getView().getHeight() / 2 - tip.getPointerView().getHeight() / 2); 248 | } 249 | } 250 | } 251 | 252 | @Override public boolean onTouchEvent(MotionEvent event) { 253 | return super.onTouchEvent(event); 254 | } 255 | 256 | public void alignPointer(View pointer, int position, int gravity) { 257 | View parent = (View) pointer.getParent(); 258 | int[] parentLocation = new int[2]; 259 | parent.getLocationOnScreen(parentLocation); 260 | 261 | if (Gravity.isVertical(gravity)) { 262 | pointer.setX(position - parentLocation[0] - pointer.getWidth() / 2); 263 | } else { 264 | pointer.setY(position - parentLocation[1] - pointer.getHeight() / 2); 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /library/src/main/java/com/ryanharter/android/tooltips/ToolTipPointerView.java: -------------------------------------------------------------------------------- 1 | package com.ryanharter.android.tooltips; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.Paint.Style; 7 | import android.graphics.Path; 8 | import android.view.Gravity; 9 | import android.view.View; 10 | 11 | /** 12 | * Created by rharter on 7/24/14. 13 | */ 14 | public class ToolTipPointerView extends View { 15 | 16 | private Paint mPaint; 17 | private int mGravity; 18 | 19 | public ToolTipPointerView(Context context, int color, int gravity) { 20 | super(context); 21 | mGravity = gravity; 22 | 23 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 24 | mPaint.setColor(color); 25 | mPaint.setStyle(Style.FILL); 26 | } 27 | 28 | @Override protected void onDraw(Canvas canvas) { 29 | super.onDraw(canvas); 30 | 31 | Path p = new Path(); 32 | if ((Gravity.VERTICAL_GRAVITY_MASK & mGravity) == Gravity.TOP) { 33 | // Top 34 | p.moveTo(0, 0); 35 | p.lineTo(getWidth() / 2, getHeight()); 36 | p.lineTo(getWidth(), 0); 37 | p.lineTo(0, 0); 38 | } else if ((Gravity.VERTICAL_GRAVITY_MASK & mGravity) == Gravity.BOTTOM) { 39 | // Bottom 40 | p.moveTo(0, getHeight()); 41 | p.lineTo(getWidth() / 2, 0); 42 | p.lineTo(getWidth(), getHeight()); 43 | p.lineTo(0, getHeight()); 44 | } else if ((Gravity.LEFT & mGravity) == Gravity.LEFT) { 45 | // Left 46 | p.moveTo(0, 0); 47 | p.lineTo(getWidth(), getHeight() / 2); 48 | p.lineTo(0, getHeight()); 49 | p.lineTo(0, 0); 50 | } else if ((Gravity.RIGHT & mGravity) == Gravity.RIGHT) { 51 | // Right 52 | p.moveTo(getWidth(), 0); 53 | p.lineTo(0, getHeight() / 2); 54 | p.lineTo(getWidth(), getHeight()); 55 | p.lineTo(getWidth(), 0); 56 | } 57 | canvas.drawPath(p, mPaint); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/library/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/library/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/library/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/library/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ToolTips Library 3 | 4 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | // TODO Change this back to the main script once the PR is merged. 3 | apply from: 'https://raw.githubusercontent.com/jpardogo/gradle-mvn-push/master/gradle-mvn-push.gradle' 4 | 5 | android { 6 | compileSdkVersion 20 7 | buildToolsVersion "20.0.0" 8 | 9 | defaultConfig { 10 | applicationId "com.ryanharter.android.tooltips.sample" 11 | minSdkVersion 15 12 | targetSdkVersion 20 13 | versionCode 2 14 | versionName "1.0.1" 15 | } 16 | buildTypes { 17 | release { 18 | runProguard false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | compile project(':library') 27 | } 28 | -------------------------------------------------------------------------------- /sample/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Android ToolTips Sample 2 | POM_ARTIFACT_ID=sample 3 | POM_PACKAGING=apk -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/ryanharter/android/tooltips/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.ryanharter.android.tooltips; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/src/main/java/com/ryanharter/android/tooltips/sample/MyActivity.java: -------------------------------------------------------------------------------- 1 | package com.ryanharter.android.tooltips.sample; 2 | 3 | import com.ryanharter.android.tooltips.ToolTip; 4 | import com.ryanharter.android.tooltips.ToolTip.Builder; 5 | import com.ryanharter.android.tooltips.ToolTipLayout; 6 | 7 | import org.w3c.dom.Text; 8 | 9 | import android.app.Activity; 10 | import android.graphics.Color; 11 | import android.os.Bundle; 12 | import android.view.Gravity; 13 | import android.view.Menu; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.View.OnClickListener; 17 | import android.view.ViewGroup.LayoutParams; 18 | import android.widget.Button; 19 | import android.widget.TextView; 20 | 21 | public class MyActivity extends Activity { 22 | 23 | private static final int POINTER_SIZE = 15; 24 | 25 | private ToolTipLayout mToolTipLayout; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_my); 31 | 32 | mToolTipLayout = (ToolTipLayout) findViewById(R.id.tooltip_container); 33 | 34 | final Button below = (Button) findViewById(R.id.tip_below); 35 | below.setOnClickListener(new OnClickListener() { 36 | @Override public void onClick(View v) { 37 | View contentView = createToolTipView("Tooltip below the button", 38 | Color.WHITE, getResources().getColor(android.R.color.holo_red_light)); 39 | contentView.setLayoutParams(new LayoutParams( 40 | LayoutParams.MATCH_PARENT, 41 | LayoutParams.WRAP_CONTENT 42 | )); 43 | 44 | ToolTip t = new Builder(MyActivity.this) 45 | .anchor(below) 46 | .color(getResources().getColor(android.R.color.holo_red_light)) 47 | .gravity(Gravity.BOTTOM) 48 | .pointerSize(POINTER_SIZE) 49 | .contentView(contentView) 50 | .build(); 51 | mToolTipLayout.addTooltip(t); 52 | } 53 | }); 54 | 55 | final Button left = (Button) findViewById(R.id.tip_left); 56 | left.setOnClickListener(new OnClickListener() { 57 | @Override public void onClick(View v) { 58 | View contentView = createToolTipView("Tooltip left of the button", 59 | Color.WHITE, getResources().getColor(android.R.color.holo_blue_light)); 60 | contentView.setLayoutParams(new LayoutParams( 61 | LayoutParams.WRAP_CONTENT, 62 | LayoutParams.WRAP_CONTENT 63 | )); 64 | 65 | // This one has a custom background on the tooltip that 66 | // adds rounded corners 67 | contentView.setBackgroundResource(R.drawable.bg_tooltip_blue); 68 | 69 | ToolTip t = new Builder(MyActivity.this) 70 | .anchor(left) 71 | .color(getResources().getColor(android.R.color.holo_blue_light)) 72 | .gravity(Gravity.LEFT) 73 | .pointerSize(POINTER_SIZE) 74 | .contentView(contentView) 75 | .build(); 76 | mToolTipLayout.addTooltip(t); 77 | } 78 | }); 79 | 80 | final Button above = (Button) findViewById(R.id.tip_above); 81 | above.setOnClickListener(new OnClickListener() { 82 | @Override public void onClick(View v) { 83 | View contentView = createToolTipView("Tooltip above the button", 84 | Color.BLACK, getResources().getColor(android.R.color.holo_green_light)); 85 | contentView.setLayoutParams(new LayoutParams( 86 | LayoutParams.MATCH_PARENT, 87 | LayoutParams.WRAP_CONTENT 88 | )); 89 | 90 | ToolTip t = new Builder(MyActivity.this) 91 | .anchor(above) 92 | .color(getResources().getColor(android.R.color.holo_green_light)) 93 | .gravity(Gravity.TOP) 94 | .pointerSize(POINTER_SIZE) 95 | .contentView(contentView) 96 | .build(); 97 | mToolTipLayout.addTooltip(t); 98 | } 99 | }); 100 | 101 | final Button right = (Button) findViewById(R.id.tip_right); 102 | right.setOnClickListener(new OnClickListener() { 103 | @Override public void onClick(View v) { 104 | View contentView = createToolTipView("Tooltip right of the button", 105 | Color.WHITE, getResources().getColor(R.color.trans_black)); 106 | contentView.setLayoutParams(new LayoutParams( 107 | LayoutParams.WRAP_CONTENT, 108 | LayoutParams.WRAP_CONTENT 109 | )); 110 | 111 | ToolTip t = new Builder(MyActivity.this) 112 | .anchor(right) 113 | .color(getResources().getColor(R.color.trans_black)) 114 | .gravity(Gravity.RIGHT) 115 | .pointerSize(POINTER_SIZE) 116 | .contentView(contentView) 117 | .build(); 118 | mToolTipLayout.addTooltip(t); 119 | } 120 | }); 121 | } 122 | 123 | public View createToolTipView(String text, int textColor, int bgColor) { 124 | float density = getResources().getDisplayMetrics().density; 125 | int padding = (int) (8 * density); 126 | 127 | TextView contentView = new TextView(MyActivity.this); 128 | contentView.setPadding(padding, padding, padding, padding); 129 | contentView.setText(text); 130 | contentView.setTextColor(textColor); 131 | contentView.setBackgroundColor(bgColor); 132 | return contentView; 133 | } 134 | 135 | @Override 136 | public boolean onCreateOptionsMenu(Menu menu) { 137 | // Inflate the menu; this adds items to the action bar if it is present. 138 | getMenuInflater().inflate(R.menu.my, menu); 139 | return true; 140 | } 141 | 142 | @Override 143 | public boolean onOptionsItemSelected(MenuItem item) { 144 | // Handle action bar item clicks here. The action bar will 145 | // automatically handle clicks on the Home/Up button, so long 146 | // as you specify a parent activity in AndroidManifest.xml. 147 | int id = item.getItemId(); 148 | if (id == R.id.action_settings) { 149 | return true; 150 | } 151 | return super.onOptionsItemSelected(item); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/bg_tooltip_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rharter/android-tooltips/5b67e600a5dc8e36792b6cd5ff68bc9160eac385/sample/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_my.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 |