├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── themes.xml
│ │ ├── mipmap-xhdpi
│ │ │ ├── user_po.png
│ │ │ └── test_face.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── test_face.png
│ │ │ ├── test_icon.png
│ │ │ ├── test_img.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── test_face_igg.png
│ │ │ └── test_face_music.png
│ │ └── layout
│ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── aiven
│ │ └── guide
│ │ └── view
│ │ └── demo
│ │ └── MainActivity.java
├── proguard-rules.pro
└── build.gradle
├── library
├── .gitignore
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── aiven
│ │ └── guide
│ │ └── view
│ │ ├── base
│ │ └── LayerBaseHold.java
│ │ ├── layer
│ │ ├── FaceHold.java
│ │ ├── ClipHold.java
│ │ ├── Layer.java
│ │ ├── LayerCreator.java
│ │ └── GuidView.java
│ │ ├── clip
│ │ ├── BaseClipPosition.java
│ │ ├── CustomClip.java
│ │ └── ViewRectClip.java
│ │ ├── util
│ │ ├── MLog.java
│ │ └── SmartUtils.java
│ │ ├── face
│ │ └── IntroPanel.java
│ │ └── SmartGuide.java
├── proguard-rules.pro
└── build.gradle
├── .idea
├── .gitignore
├── compiler.xml
├── vcs.xml
├── misc.xml
├── gradle.xml
└── jarRepositories.xml
├── screens
├── img_1.jpg
├── img_2.jpg
├── img_3.jpg
├── img_4.jpg
└── img_5.jpg
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── dependency.gradle
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /j-publish.gradle
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/screens/img_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/screens/img_1.jpg
--------------------------------------------------------------------------------
/screens/img_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/screens/img_2.jpg
--------------------------------------------------------------------------------
/screens/img_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/screens/img_3.jpg
--------------------------------------------------------------------------------
/screens/img_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/screens/img_4.jpg
--------------------------------------------------------------------------------
/screens/img_5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/screens/img_5.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 | include ':app'
3 | rootProject.name = "SmartGuideView"
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SmartGuideView
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/user_po.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xhdpi/user_po.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/test_face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xhdpi/test_face.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/test_face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xxhdpi/test_face.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/test_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xxhdpi/test_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/test_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xxhdpi/test_img.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/test_face_igg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xxhdpi/test_face_igg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/test_face_music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiven163/SmartGuideView/HEAD/app/src/main/res/mipmap-xxhdpi/test_face_music.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF607D8B
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 25 15:17:31 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /library/j-publish.gradle
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 | local.properties
17 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/dependency.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | android = [
3 | toolVersion :"29.0.2",
4 | compileSdk : 29,
5 | applicationId : "aiven.guide.view.demo",
6 |
7 | minSdk : 21,
8 | targetSdk : 28,
9 | versionCode : 1,
10 | versionName : "20.02.14.21.40",
11 | ]
12 | dependencies = [
13 | support : "28.0.0",
14 | design : "28.0.0",
15 | ]
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/base/LayerBaseHold.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.base;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.Fragment;
9 |
10 | public abstract class LayerBaseHold {
11 |
12 | abstract public void build(@Nullable Activity activity);
13 | abstract public void build(@Nullable Fragment fragment);
14 |
15 | abstract public void draw(Canvas canvas, Paint paint,@Nullable RectF clipRectF, float parentWidth, float parentHeight);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | }
4 |
5 | def deps = rootProject.ext.dependencies
6 | def config = rootProject.ext.android
7 |
8 | android {
9 | compileSdkVersion config.compileSdk
10 | buildToolsVersion config.toolVersion
11 | defaultConfig {
12 | minSdkVersion config.minSdk
13 | targetSdkVersion config.targetSdk
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | compileOptions {
23 | sourceCompatibility JavaVersion.VERSION_1_8
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | }
26 | }
27 |
28 | dependencies {
29 | api "com.android.support:support-v4:${deps.support}"
30 | }
31 |
32 |
33 |
34 | apply from:'j-publish.gradle'
35 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=false
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=false
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | def deps = rootProject.ext.dependencies
6 | def config = rootProject.ext.android
7 |
8 | android {
9 | compileSdkVersion config.compileSdk
10 | buildToolsVersion config.toolVersion
11 |
12 | defaultConfig {
13 | applicationId config.applicationId
14 | minSdkVersion config.minSdk
15 | targetSdkVersion config.targetSdk
16 | versionCode 1
17 | versionName "1.0"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(include: ['*.jar'], dir: 'libs')
34 | api "com.android.support:appcompat-v7:${deps.support}"
35 | api "com.android.support:support-v4:${deps.support}"
36 |
37 | api project(':library')
38 | // implementation 'aiven.guide.view:library:1.0.0'
39 | }
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/layer/FaceHold.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.layer;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.app.Fragment;
9 |
10 | import aiven.guide.view.base.LayerBaseHold;
11 | import aiven.guide.view.face.IntroPanel;
12 |
13 | class FaceHold extends LayerBaseHold {
14 | protected IntroPanel facePanel;
15 |
16 | public void setFacePanel(IntroPanel facePanel) {
17 | this.facePanel = facePanel;
18 | }
19 |
20 | @Override
21 | public void build(@Nullable Activity activity) {
22 | }
23 |
24 | @Override
25 | public void build(@Nullable Fragment activity) {
26 | }
27 |
28 | @Override
29 | public void draw(Canvas canvas, Paint paint,@Nullable RectF clipRectF, float parentWidth, float parentHeight) {
30 | if(this.facePanel != null) {
31 | this.facePanel.draw(canvas, paint, clipRectF, parentWidth, parentHeight);
32 | }
33 | }
34 |
35 |
36 | @Nullable
37 | public RectF getRectF() {
38 | if(facePanel != null){
39 | return facePanel.getRectF();
40 | }
41 | return null;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/clip/BaseClipPosition.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.clip;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.RectF;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 |
11 | public abstract class BaseClipPosition {
12 | protected float offsetX;
13 | protected float offsetY;
14 | protected float radius;
15 | @Nullable
16 | protected Bitmap irregularClip;
17 |
18 | @Nullable
19 | protected RectF rectF;
20 |
21 | protected boolean eventPassThrough;
22 |
23 | public float getOffsetX() {
24 | return offsetX;
25 | }
26 |
27 | public float getOffsetY() {
28 | return offsetY;
29 | }
30 |
31 |
32 | public float getRadius() {
33 | return radius;
34 | }
35 |
36 | @Nullable
37 | public RectF getRectF() {
38 | return rectF;
39 | }
40 |
41 | @Nullable
42 | public Bitmap getIrregularClip() {
43 | return irregularClip;
44 | }
45 |
46 | public void build(@Nullable Activity activity) {
47 | }
48 |
49 | public void build(@Nullable Fragment activity) {
50 | }
51 |
52 | abstract public void draw(Canvas canvas, Paint paint,float parentWidth, float parentHeight);
53 |
54 | public boolean isEventPassThrough() {
55 | return eventPassThrough;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/layer/ClipHold.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.layer;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 |
11 | import aiven.guide.view.base.LayerBaseHold;
12 | import aiven.guide.view.clip.BaseClipPosition;
13 |
14 | class ClipHold extends LayerBaseHold {
15 |
16 | @Nullable
17 | protected BaseClipPosition target;
18 |
19 | @Override
20 | public void build(@Nullable Activity activity) {
21 | if(target != null){
22 | target.build(activity);
23 | }
24 | }
25 |
26 | @Override
27 | public void build(@Nullable Fragment fragment) {
28 | if(target != null){
29 | target.build(fragment);
30 | }
31 | }
32 |
33 | @Override
34 | public void draw(Canvas canvas, Paint paint,@Nullable RectF clipRectF, float parentWidth, float parentHeight) {
35 | if(target != null){
36 | target.draw(canvas,paint,parentWidth,parentHeight);
37 | }
38 | }
39 |
40 | @Nullable
41 | public RectF getRectF(){
42 | if(target != null){
43 | return target.getRectF();
44 | }
45 | return null;
46 | }
47 |
48 | public boolean isEventPassThrough() {
49 | if(target != null){
50 | return target.isEventPassThrough();
51 | }
52 | return false;
53 | }
54 |
55 |
56 | public void setTarget(@NonNull BaseClipPosition target) {
57 | this.target = target;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/util/MLog.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.util;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.PrintWriter;
6 | import java.io.StringWriter;
7 |
8 |
9 | /**
10 | * @author Aiven (aiven163@aliyun.com)
11 | * @desc
日志输出工具
12 | */
13 | public class MLog {
14 |
15 | public static boolean IS_DEBUG = true;
16 | public static String DEFAULT_TAG = "SmartGuide>>>";
17 |
18 | public static void I(String msg){
19 | I(DEFAULT_TAG,msg);
20 | }
21 |
22 | public static void I(String tag,String msg){
23 | if(IS_DEBUG && !SmartUtils.strIsEmpty(msg)){
24 | if(SmartUtils.strIsEmpty(tag)){
25 | tag = DEFAULT_TAG;
26 | }
27 | Log.i(tag,msg);
28 | }
29 | }
30 |
31 | public static void V(String msg){
32 | V(DEFAULT_TAG,msg);
33 | }
34 |
35 | public static void V(String tag,String msg){
36 | if(IS_DEBUG && !SmartUtils.strIsEmpty(msg)){
37 | if(SmartUtils.strIsEmpty(tag)){
38 | tag = DEFAULT_TAG;
39 | }
40 | Log.v(tag,msg);
41 | }
42 | }
43 |
44 | public static void E(String tag,String msg){
45 | if(IS_DEBUG && !SmartUtils.strIsEmpty(msg)){
46 | if(SmartUtils.strIsEmpty(tag)){
47 | tag = DEFAULT_TAG;
48 | }
49 | Log.e(tag,msg);
50 | }
51 | }
52 |
53 | public static void E(Throwable throwable){
54 | if(throwable != null && IS_DEBUG){
55 | E(DEFAULT_TAG,saveException2String(throwable));
56 | }
57 | }
58 |
59 |
60 | private static String saveException2String(Throwable var0) {
61 | StringBuilder var1 = new StringBuilder("\n");
62 | try {
63 | var1.append("异常原因:").append(var0.toString()).append("\n");
64 | var1.append("具体如下:\n=======================================================\n");
65 | StringWriter var2 = new StringWriter();
66 | PrintWriter var3 = new PrintWriter(var2);
67 | var0.printStackTrace(var3);
68 |
69 | for (var0 = var0.getCause(); var0 != null; var0 = var0.getCause()) {
70 | var0.printStackTrace(var3);
71 | }
72 | var3.close();
73 | String var5 = var2.toString();
74 | var1.append(var5);
75 | } catch (Exception var4) {
76 | var4.printStackTrace();
77 | }
78 | return var1.toString();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/layer/Layer.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.layer;
2 |
3 |
4 | import android.app.Activity;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 |
11 | import aiven.guide.view.clip.BaseClipPosition;
12 | import aiven.guide.view.face.IntroPanel;
13 |
14 | class Layer {
15 | @NonNull
16 | private ClipHold clipHold;
17 | @NonNull
18 | private FaceHold face;
19 | @NonNull
20 | protected String tag;
21 |
22 | public Layer(String tag) {
23 | this.clipHold = new ClipHold();
24 | this.face = new FaceHold();
25 | this.tag = tag;
26 | }
27 |
28 |
29 | public void setClipTarget(BaseClipPosition target){
30 | clipHold.setTarget(target);
31 | }
32 |
33 | public void setFacePanel(IntroPanel facePanel){
34 | this.face.setFacePanel(facePanel);
35 | }
36 |
37 |
38 | public void draw(Canvas canvas, Paint paint, boolean clip, float parentWidth, float parentHeight){
39 | if(clip){
40 | clipHold.draw(canvas,paint,null,parentWidth,parentHeight);
41 | }else{
42 | face.draw(canvas,paint,clipHold.getRectF(),parentWidth,parentHeight);
43 | }
44 | }
45 |
46 | protected void build(@Nullable Activity activity) {
47 | if(clipHold != null){
48 | clipHold.build(activity);
49 | }
50 | if(face != null){
51 | face.build(activity);
52 | }
53 | }
54 |
55 |
56 | protected void build(@Nullable Fragment fragment) {
57 | if(clipHold != null){
58 | clipHold.build(fragment);
59 | }
60 | if(face != null){
61 | face.build(fragment);
62 | }
63 | }
64 |
65 | @NonNull
66 | public String getTag() {
67 | return tag;
68 | }
69 |
70 | public boolean isClipEventPassThrough(){
71 | if(clipHold != null){
72 | return clipHold.isEventPassThrough();
73 | }
74 | return false;
75 | }
76 |
77 |
78 | public boolean isTouchInClip(float x,float y){
79 | if(clipHold != null && clipHold.getRectF() != null){
80 | if(clipHold.getRectF().contains(x,y)){
81 | return true;
82 | }
83 | }
84 | return false;
85 | }
86 |
87 | public boolean isTouchInIntro(float x,float y){
88 | if(face != null && face.getRectF() != null){
89 | if(face.getRectF().contains(x,y)){
90 | return true;
91 | }
92 | }
93 | return false;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/util/SmartUtils.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.util;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Point;
6 | import android.os.Build;
7 | import android.support.annotation.NonNull;
8 | import android.util.DisplayMetrics;
9 |
10 |
11 | /**
12 | * @author Aiven (aiven163@aliyun.com)
13 | * 工具方法集合
14 | */
15 | public class SmartUtils {
16 | /**
17 | * 获取屏幕信息,包含状态栏
18 | * @param activity
19 | * @return
20 | */
21 | public static Point getScreenSize(@NonNull Activity activity) {
22 | if (activity != null) {
23 | DisplayMetrics dm = new DisplayMetrics();
24 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
25 | activity.getWindowManager().getDefaultDisplay().getRealMetrics(dm);
26 | } else {
27 | dm = activity.getResources().getDisplayMetrics();
28 | }
29 | int screenWidth = dm.widthPixels;
30 | int screenHeight = dm.heightPixels;
31 | return new Point(screenWidth, screenHeight);
32 | }
33 | return new Point(0, 0);
34 | }
35 |
36 | /**
37 | * 获取屏幕信息,包含状态栏
38 | * @param context
39 | * @return
40 | */
41 | public static int getScreenWidth(@NonNull Context context) {
42 | if (context != null) {
43 | DisplayMetrics dm = new DisplayMetrics();
44 | dm = context.getResources().getDisplayMetrics();
45 | int screenWidth = dm.widthPixels;
46 | return screenWidth;
47 | }
48 | return 0;
49 | }
50 |
51 | public static int getScreenHeight(@NonNull Context context) {
52 | DisplayMetrics dm = context.getResources().getDisplayMetrics();
53 | return dm.heightPixels;
54 | }
55 |
56 | /**
57 | * 获取状态栏高度
58 | *
59 | * @param context context
60 | * @return 状态栏高度
61 | */
62 | public static int getStatusBarHeight(@NonNull Context context) {
63 | try {
64 | // 获得状态栏高度
65 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
66 | return context.getResources().getDimensionPixelSize(resourceId);
67 | } catch (Exception e) {
68 | MLog.E(e);
69 | return dip2px(context, 24);
70 | }
71 | }
72 |
73 |
74 |
75 |
76 | public static int dip2px(Context context, float dipValue) {
77 | final float scale = context.getResources().getDisplayMetrics().density;
78 | return (int) (dipValue * scale + 0.5f);
79 | }
80 |
81 | public static int px2dip(Context context, float pxValue) {
82 | final float scale = context.getResources().getDisplayMetrics().density;
83 | return (int) (pxValue / scale + 0.5f);
84 | }
85 |
86 |
87 | /**
88 | * 检查字符串是否为空
89 | * @param value
90 | * @return
91 | */
92 | public static boolean strIsEmpty(String value){
93 | if(value != null && value.trim().length() > 0){
94 | return false;
95 | }
96 | return true;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
37 |
45 |
46 |
47 |
55 |
56 |
64 |
65 |
66 |
74 |
75 |
76 |
86 |
87 |
88 |
97 |
98 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/face/IntroPanel.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.face;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.graphics.RectF;
9 | import android.support.annotation.DrawableRes;
10 | import android.support.annotation.NonNull;
11 | import android.support.annotation.Nullable;
12 |
13 | import aiven.guide.view.SmartGuide;
14 |
15 | public class IntroPanel {
16 | private Context context;
17 | protected int with;
18 | protected int height;
19 |
20 | protected float offsetX;
21 | protected float offsetY;
22 |
23 | protected Bitmap introBitmap;
24 |
25 | private RectF rectF;
26 |
27 |
28 | protected SmartGuide.AlignX alignX = SmartGuide.AlignX.ALIGN_RIGHT;
29 | protected SmartGuide.AlignY alignY = SmartGuide.AlignY.ALIGN_BOTTOM;
30 |
31 | private IntroPanel(@NonNull Context context){
32 | this.context = context;
33 | }
34 |
35 |
36 | public static IntroPanel newIntroPanel(@NonNull Context context){
37 | return new IntroPanel(context);
38 | }
39 |
40 |
41 | public IntroPanel setOffset(float offsetX,float offsetY){
42 | this.offsetX = offsetX;
43 | this.offsetY = offsetY;
44 | return this;
45 | }
46 |
47 | public IntroPanel setSize(int width,int height){
48 | this.with = width;
49 | this.height = height;
50 | return this;
51 | }
52 |
53 | public IntroPanel setAlign(SmartGuide.AlignX x,SmartGuide.AlignY y){
54 | this.alignX = x;
55 | this.alignY = y;
56 | return this;
57 | }
58 |
59 | public IntroPanel setIntroBmp(Bitmap bmp){
60 | this.introBitmap = bmp;
61 | return this;
62 | }
63 | public IntroPanel setIntroBmp(@DrawableRes int bmpRes){
64 | this.introBitmap = BitmapFactory.decodeResource(context.getResources(),bmpRes);
65 | return this;
66 | }
67 |
68 | public void draw(Canvas canvas, Paint paint, @Nullable RectF clipRectF, float parentWidth, float parentHeight){
69 | if(clipRectF == null){
70 | return;
71 | }
72 | if(introBitmap == null){
73 | return;
74 | }
75 | if(rectF == null){
76 | initRectF(clipRectF);
77 | }
78 | initBitmap();
79 | canvas.drawBitmap(introBitmap,rectF.left,rectF.top,paint);
80 | }
81 |
82 |
83 |
84 |
85 | private void initRectF(@NonNull RectF clipRectF){
86 | float left;
87 | float top;
88 | if(alignX == SmartGuide.AlignX.ALIGN_LEFT){
89 | left = clipRectF.left - with - offsetX;
90 | }else{
91 | left = clipRectF.right + offsetX;
92 | }
93 |
94 | if(alignY == SmartGuide.AlignY.ALIGN_TOP){
95 | top = clipRectF.top - height - offsetY;
96 | }else{
97 | top = clipRectF.bottom + offsetY;
98 | }
99 |
100 | rectF = new RectF(left,top,left + with,top + height);
101 | }
102 |
103 |
104 | private void initBitmap(){
105 | if(introBitmap != null){
106 | if(Math.abs(introBitmap.getWidth() - with) > 1 || Math.abs(introBitmap.getHeight() - height) > 1){
107 | introBitmap = Bitmap.createScaledBitmap(introBitmap,with,height,true);
108 | }
109 | }
110 | }
111 |
112 |
113 | public RectF getRectF() {
114 | return rectF;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/layer/LayerCreator.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.layer;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import aiven.guide.view.SmartGuide;
7 | import aiven.guide.view.clip.CustomClip;
8 | import aiven.guide.view.clip.ViewRectClip;
9 |
10 | public class LayerCreator implements GuidView.InnerOnGuidClickListener {
11 |
12 | @NonNull
13 | private GuidView mView;
14 |
15 | @NonNull
16 | private SmartGuide smartGuide;
17 |
18 | private Layer currentLayer;
19 |
20 | @Nullable
21 | private SmartGuide.OnGuidClickListener mListener;
22 |
23 |
24 |
25 | private LayerCreator(GuidView view,SmartGuide guide,String tag){
26 | this.mView = view;
27 | this.mView.setOnInnerOnGuidClickListener(this);
28 | this.smartGuide = guide;
29 | currentLayer = new Layer(tag);
30 | }
31 |
32 | public static LayerCreator newCreator(GuidView guidView,SmartGuide guide,String tag){
33 | LayerCreator creator = new LayerCreator(guidView,guide,tag);
34 | return creator;
35 | }
36 |
37 |
38 |
39 | /**
40 | * 根据VIEW 所在区域定位裁剪区域位置
41 | * @param clipBuilder
42 | * @return
43 | */
44 | public LayerCreator buildViewRectClip(SmartGuide.ClipPositionBuilder clipBuilder){
45 | buildDstTarget(clipBuilder);
46 | return this;
47 | }
48 |
49 |
50 | /**
51 | * 自定义裁剪区域位置(全屏定位)
52 | * @param clipBuilder
53 | * @return
54 | */
55 | public LayerCreator buildCustomClip(SmartGuide.ClipPositionBuilder clipBuilder){
56 | buildDstTarget(clipBuilder);
57 | return this;
58 | }
59 |
60 |
61 | /**
62 | * 设置一个引导说明图形
63 | * @param builder
64 | * @return
65 | */
66 | public LayerCreator buildIntroPanel(SmartGuide.IntroPanelBuilder builder){
67 | if(currentLayer == null){
68 | throw new RuntimeException("newLayer first,please!");
69 | }
70 | currentLayer.setFacePanel(builder.buildFacePanel());
71 | return this;
72 | }
73 |
74 |
75 | /**
76 | * 添加点击事件
77 | * @param listener
78 | * @return
79 | */
80 | public LayerCreator setOnGuidClickListener(SmartGuide.OnGuidClickListener listener){
81 | this.mListener = listener;
82 | return this;
83 | }
84 |
85 |
86 |
87 | public void show(){
88 | over();
89 | if(smartGuide != null) {
90 | smartGuide.show();
91 | }
92 | }
93 |
94 |
95 |
96 | private void buildDstTarget(SmartGuide.ClipPositionBuilder targetBuilder){
97 | if(currentLayer == null){
98 | throw new RuntimeException("newLayer first,please!");
99 | }
100 | if(targetBuilder!=null){
101 | currentLayer.setClipTarget(targetBuilder.buildTarget());
102 | }
103 | }
104 |
105 | public SmartGuide over(){
106 | mView.addLayer(currentLayer);
107 | return smartGuide;
108 | }
109 |
110 |
111 |
112 | @Override
113 | public void destroyed() {
114 | if(mListener != null){
115 | mListener.destroyed();
116 | }
117 | }
118 |
119 | @Override
120 | public boolean emptyErrorClicked() {
121 | if(mListener != null){
122 | return mListener.emptyErrorClicked(smartGuide);
123 | }
124 | return false;
125 | }
126 |
127 | @Override
128 | public void clipClicked(String tag) {
129 | if(mListener != null){
130 | mListener.clipClicked(smartGuide,mView,tag);
131 | }
132 | }
133 |
134 | @Override
135 | public void introClicked(String tag) {
136 | if(mListener != null){
137 | mListener.introClicked(smartGuide,mView,tag);
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/SmartGuide.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.app.Fragment;
8 |
9 | import aiven.guide.view.clip.BaseClipPosition;
10 | import aiven.guide.view.face.IntroPanel;
11 | import aiven.guide.view.layer.GuidView;
12 | import aiven.guide.view.layer.LayerCreator;
13 | import aiven.guide.view.util.SmartUtils;
14 |
15 | public class SmartGuide {
16 |
17 | @NonNull
18 | private GuidView mView;
19 | @Nullable
20 | private Activity mActivity;
21 | @Nullable
22 | private Fragment mFragment;
23 |
24 | private SmartGuide(Context context){
25 | mView = new GuidView(context);
26 | }
27 |
28 | public static SmartGuide newGuide(Activity activity){
29 | SmartGuide guide = new SmartGuide(activity);
30 | guide.mActivity = activity;
31 | return guide;
32 | }
33 |
34 | public static SmartGuide newGuide(Fragment fragment){
35 | SmartGuide guide = new SmartGuide(fragment.requireActivity());
36 | guide.mFragment = fragment;
37 | return guide;
38 | }
39 |
40 |
41 | /**
42 | * layer层基础颜色
43 | */
44 | public SmartGuide initBaseColor(int color){
45 | mView.setBackgroundColor(color);
46 | return this;
47 | }
48 |
49 |
50 | /**
51 | * 新建一个引导
52 | * @return
53 | */
54 | public LayerCreator newLayer(){
55 | LayerCreator creator = LayerCreator.newCreator(mView,this,createTag());
56 | return creator;
57 | }
58 |
59 | /**
60 | * 新建一个引导
61 | * @return
62 | */
63 | public LayerCreator newLayer(@NonNull String tag){
64 | if(SmartUtils.strIsEmpty(tag)){
65 | tag = createTag();
66 | }
67 | LayerCreator creator = LayerCreator.newCreator(mView,this,tag);
68 | return creator;
69 | }
70 |
71 |
72 |
73 | public void show(){
74 | if(mActivity != null) {
75 | mView.build(mActivity);
76 | }else if(mFragment != null){
77 | mView.build(mFragment);
78 | }
79 | }
80 |
81 |
82 |
83 | public void clearLayers(){
84 | if(mView != null){
85 | mView.clearLayers();
86 | }
87 | }
88 |
89 | public void removeLayerByTag(String tag){
90 | if(mView != null){
91 | mView.removeLayerByTag(tag);
92 | }
93 | }
94 |
95 |
96 | public void dismiss(){
97 | mView.dismiss();
98 | }
99 |
100 |
101 |
102 |
103 | private String createTag(){
104 | StringBuilder builder = new StringBuilder();
105 | builder.append("layer_")
106 | .append(System.currentTimeMillis())
107 | .append((int)(Math.random()* 99999))
108 | .append("_")
109 | .append((int)(Math.random()* 99999));
110 | return builder.toString();
111 | }
112 |
113 |
114 | public interface ClipPositionBuilder{
115 | T buildTarget();
116 | }
117 |
118 | public interface IntroPanelBuilder {
119 | IntroPanel buildFacePanel();
120 | }
121 |
122 |
123 | /**
124 | * 引导层点击事件监听器
125 | */
126 | public static abstract class OnGuidClickListener{
127 | /**
128 | * 引导层销毁回调
129 | */
130 | public void destroyed(){};
131 |
132 | /**
133 | * 点击蒙层非裁剪和信息区域回调,返回true,直接退出引导,返回false则不退出
134 | * @return
135 | */
136 | public abstract boolean emptyErrorClicked(SmartGuide guide);
137 |
138 | /**
139 | * 引导镂空区域点击回调,如果镂空区域设置了事件透传,则不回调
140 | * @param guide
141 | * @param tag
142 | */
143 | public abstract void clipClicked(SmartGuide guide,GuidView view,String tag);
144 |
145 | /**
146 | * 引导介绍区域点击回调
147 | * @param guide
148 | * @param tag
149 | */
150 | public abstract void introClicked(SmartGuide guide,GuidView view,String tag);
151 | }
152 |
153 |
154 | public enum AlignY{
155 | ALIGN_TOP(0),
156 | ALIGN_BOTTOM(1);
157 |
158 |
159 | int align;
160 |
161 | AlignY(int align) {
162 | this.align = align;
163 | }
164 | }
165 |
166 | public enum AlignX{
167 | ALIGN_LEFT(0),
168 | ALIGN_RIGHT(1);
169 |
170 |
171 | int align;
172 |
173 | AlignX(int align) {
174 | this.align = align;
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/clip/CustomClip.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.clip;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Canvas;
8 | import android.graphics.Paint;
9 | import android.graphics.PorterDuff;
10 | import android.graphics.PorterDuffXfermode;
11 | import android.graphics.RectF;
12 | import android.support.annotation.DrawableRes;
13 | import android.support.annotation.NonNull;
14 | import android.support.annotation.Nullable;
15 | import android.support.v4.app.Fragment;
16 |
17 | import aiven.guide.view.SmartGuide;
18 |
19 | public class CustomClip extends BaseClipPosition{
20 |
21 | protected SmartGuide.AlignX alignX = SmartGuide.AlignX.ALIGN_LEFT;
22 | protected SmartGuide.AlignY alignY = SmartGuide.AlignY.ALIGN_TOP;
23 | protected int clipWidth;
24 | protected int clipHeight;
25 |
26 | private float parentWidth;
27 | private float parentHeight;
28 |
29 | private CustomClip(){}
30 |
31 |
32 | public static CustomClip newClipPos(){
33 | return new CustomClip();
34 | }
35 |
36 | public CustomClip setClipSize(int width, int height){
37 | this.clipWidth = width;
38 | this.clipHeight = height;
39 | return this;
40 | }
41 |
42 | public CustomClip setAlignX(@NonNull SmartGuide.AlignX alignX){
43 | this.alignX = alignX;
44 | return this;
45 | }
46 |
47 | public CustomClip setAlignY(@NonNull SmartGuide.AlignY alignY){
48 | this.alignY = alignY;
49 | return this;
50 | }
51 |
52 | public CustomClip setOffsetX(float offsetX){
53 | this.offsetX = offsetX;
54 | return this;
55 | }
56 |
57 | public CustomClip setOffsetY(float offsetY){
58 | this.offsetY = offsetY;
59 | return this;
60 | }
61 |
62 | public CustomClip clipRadius(float radius){
63 | this.radius = radius;
64 | return this;
65 | }
66 |
67 |
68 | /**
69 | * 设置不规则裁剪图形PNG资源
70 | * @param context
71 | * @param bitmapId
72 | * @return
73 | */
74 | public CustomClip asIrregularShape(@NonNull Context context, @DrawableRes int bitmapId){
75 | irregularClip = BitmapFactory.decodeResource(context.getResources(),bitmapId);
76 | return this;
77 | }
78 |
79 | /**
80 | * 设置不规则裁剪图形PNG资源
81 | * @param bitmap
82 | * @return
83 | */
84 | public CustomClip asIrregularShape(@NonNull Bitmap bitmap){
85 | irregularClip = bitmap;
86 | return this;
87 | }
88 |
89 | /**
90 | * 是否镂空区域事件穿透
91 | * @param eventPassThrough
92 | */
93 | public CustomClip setEventPassThrough(boolean eventPassThrough) {
94 | this.eventPassThrough = eventPassThrough;
95 | return this;
96 | }
97 |
98 |
99 | @Override
100 | public void build(@Nullable Activity activity) {
101 | buildDstSizeBitmap();
102 | }
103 |
104 | @Override
105 | public void build(@Nullable Fragment activity) {
106 | buildDstSizeBitmap();
107 | }
108 |
109 | @Override
110 | public void draw(Canvas canvas, Paint paint,float parentWidth, float parentHeight) {
111 | initRect(parentWidth,parentHeight);
112 | this.parentHeight = parentHeight;
113 | this.parentWidth = parentWidth;
114 | if(irregularClip == null){
115 | drawClipShape(canvas,paint);
116 | }else{
117 | drawClipBitmap(canvas,paint);
118 | }
119 | }
120 |
121 |
122 | private void initRect(float parentWidth, float parentHeight){
123 | if(rectF != null || parentHeight != this.parentHeight || parentWidth != this.parentWidth){
124 | float top = 0;
125 | float left = 0;
126 | if(alignX == SmartGuide.AlignX.ALIGN_RIGHT){
127 | left = parentWidth - offsetX - clipWidth;
128 | }else{
129 | left = offsetX;
130 | }
131 | if(alignY == SmartGuide.AlignY.ALIGN_BOTTOM){
132 | top = parentHeight - offsetY - clipHeight;
133 | }else{
134 | top = offsetY;
135 | }
136 | rectF = new RectF(left,top,left + clipWidth,top + clipHeight);
137 | }
138 | }
139 |
140 |
141 | private void drawClipShape(Canvas canvas, Paint paint){
142 | if(rectF == null){
143 | return;
144 | }
145 | PorterDuffXfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
146 | paint.setXfermode(xFermode);
147 | canvas.drawRoundRect(rectF, radius, radius, paint);
148 | paint.setXfermode(null);
149 | }
150 |
151 | private void drawClipBitmap(Canvas canvas,Paint paint){
152 | if(rectF == null){
153 | return;
154 | }
155 | PorterDuffXfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
156 | paint.setXfermode(xFermode);
157 | canvas.drawBitmap(irregularClip, rectF.left, rectF.top, paint);
158 | paint.setXfermode(null);
159 | }
160 |
161 |
162 | private void buildDstSizeBitmap(){
163 | if(irregularClip != null){
164 | Bitmap bitmap = Bitmap.createScaledBitmap(irregularClip,clipWidth,clipHeight,true);
165 | if(bitmap != null && !bitmap.isRecycled()){
166 | irregularClip = bitmap;
167 | }
168 | }
169 | }
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/clip/ViewRectClip.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.clip;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Canvas;
8 | import android.graphics.Paint;
9 | import android.graphics.PorterDuff;
10 | import android.graphics.PorterDuffXfermode;
11 | import android.graphics.RectF;
12 | import android.support.annotation.DrawableRes;
13 | import android.support.annotation.IdRes;
14 | import android.support.annotation.NonNull;
15 | import android.support.annotation.Nullable;
16 | import android.support.v4.app.Fragment;
17 | import android.view.View;
18 |
19 | public class ViewRectClip extends BaseClipPosition{
20 |
21 | protected float padding;
22 | protected @IdRes int clipViewId;
23 | @Nullable
24 | protected View clipDestView;
25 |
26 | private float parentWidth;
27 | private float parentHeight;
28 |
29 |
30 | private ViewRectClip(){}
31 |
32 |
33 | public static ViewRectClip newClipPos(){
34 | return new ViewRectClip();
35 | }
36 |
37 | public ViewRectClip setDstView(View view){
38 | this.clipDestView = view;
39 | return this;
40 | }
41 |
42 | public ViewRectClip setDstView(@IdRes int viewId){
43 | this.clipViewId = viewId;
44 | return this;
45 | }
46 |
47 | public ViewRectClip setPadding(float padding){
48 | this.padding = padding;
49 | return this;
50 | }
51 |
52 | public ViewRectClip setOffsetX(float offsetX){
53 | this.offsetX = offsetX;
54 | return this;
55 | }
56 |
57 | public ViewRectClip setOffsetY(float offsetY){
58 | this.offsetY = offsetY;
59 | return this;
60 | }
61 |
62 | public ViewRectClip clipRadius(float radius){
63 | this.radius = radius;
64 | return this;
65 | }
66 |
67 | /**
68 | * 设置不规则裁剪图形PNG资源
69 | * @param context
70 | * @param bitmapId
71 | * @return
72 | */
73 | public ViewRectClip asIrregularShape(@NonNull Context context, @DrawableRes int bitmapId){
74 | irregularClip = BitmapFactory.decodeResource(context.getResources(),bitmapId);
75 | return this;
76 | }
77 |
78 | /**
79 | * 设置不规则裁剪图形PNG资源
80 | * @param bitmap
81 | * @return
82 | */
83 | public ViewRectClip asIrregularShape(@NonNull Bitmap bitmap){
84 | irregularClip = bitmap;
85 | return this;
86 | }
87 |
88 |
89 | /**
90 | * 是否镂空区域事件穿透
91 | * @param eventPassThrough
92 | */
93 | public ViewRectClip setEventPassThrough(boolean eventPassThrough) {
94 | this.eventPassThrough = eventPassThrough;
95 | return this;
96 | }
97 |
98 |
99 | @Override
100 | public void build(@Nullable Activity activity) {
101 | if(clipDestView == null && clipViewId != 0){
102 | clipDestView = activity.findViewById(clipViewId);
103 | }
104 | }
105 |
106 | @Override
107 | public void build(@Nullable Fragment fragment) {
108 | if(clipDestView == null && clipViewId != 0 && fragment.getView() != null){
109 | clipDestView = fragment.getView().findViewById(clipViewId);
110 | }
111 | }
112 |
113 | @Override
114 | public void draw(Canvas canvas, Paint paint, float parentWidth, float parentHeight) {
115 | initRect(parentWidth,parentHeight);
116 | this.parentHeight = parentHeight;
117 | this.parentWidth = parentWidth;
118 | if(irregularClip == null){
119 | drawClipShape(canvas,paint);
120 | }else{
121 | drawClipBitmap(canvas,paint);
122 | }
123 | }
124 |
125 | private void initRect(float parentWidth, float parentHeight){
126 | if(rectF != null || parentHeight != this.parentHeight || parentWidth != this.parentWidth){
127 | if(clipDestView != null) {
128 | int[] pos = new int[2];
129 | clipDestView.getLocationInWindow(pos);
130 | float left = pos[0] + offsetX - padding;
131 | float top = pos[1] + offsetY - padding;
132 |
133 | float width = clipDestView.getMeasuredWidth() + (padding * 2);
134 | float height = clipDestView.getHeight()+ (padding * 2);
135 |
136 | rectF = new RectF(left,top,left+width,top+height);
137 | buildDstSizeBitmap();
138 | }
139 | }
140 | }
141 |
142 | private void drawClipShape(Canvas canvas, Paint paint){
143 | if(rectF == null){
144 | return;
145 | }
146 | PorterDuffXfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
147 | paint.setXfermode(xFermode);
148 | canvas.drawRoundRect(rectF, radius, radius, paint);
149 | paint.setXfermode(null);
150 | }
151 |
152 | private void drawClipBitmap(Canvas canvas,Paint paint){
153 | if(rectF == null){
154 | return;
155 | }
156 | PorterDuffXfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
157 | paint.setXfermode(xFermode);
158 | canvas.drawBitmap(irregularClip, rectF.left, rectF.top, paint);
159 | paint.setXfermode(null);
160 | }
161 |
162 | private void buildDstSizeBitmap(){
163 | if(irregularClip != null){
164 | Bitmap bitmap = Bitmap.createScaledBitmap(irregularClip,Math.round(rectF.width()),Math.round(rectF.height()),true);
165 | if(bitmap != null && !bitmap.isRecycled()){
166 | irregularClip = bitmap;
167 | }
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
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 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/library/src/main/java/aiven/guide/view/layer/GuidView.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.layer;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.RectF;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.Fragment;
10 | import android.util.AttributeSet;
11 | import android.view.MotionEvent;
12 | import android.view.View;
13 | import android.view.ViewConfiguration;
14 | import android.view.ViewGroup;
15 | import android.widget.FrameLayout;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | import aiven.guide.view.util.MLog;
21 | import aiven.guide.view.util.SmartUtils;
22 |
23 | public class GuidView extends View {
24 | private static final String TAG ="smartGuide998";
25 | private Paint mPaint;
26 | private List mLayerList;
27 | private int drawTaskId;
28 | private RectF mRectF;
29 | private int backgroundColor = 0X80000000;
30 | private float mMinTouchSlop;
31 |
32 | @Nullable
33 | private InnerOnGuidClickListener mClickListener;
34 |
35 |
36 | public GuidView(Context context) {
37 | this(context,null);
38 | }
39 |
40 | public GuidView(Context context, AttributeSet attrs) {
41 | this(context, attrs,0);
42 | }
43 |
44 | public GuidView(Context context, AttributeSet attrs, int defStyleAttr) {
45 | super(context, attrs, defStyleAttr);
46 | init(context);
47 | }
48 |
49 |
50 | private void init(Context context){
51 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
52 | mLayerList = new ArrayList<>();
53 | mMinTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
54 | }
55 |
56 | @Override
57 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
58 | super.onLayout(changed, left, top, right, bottom);
59 | int width = right - left;
60 | int height = bottom - top;
61 | if(mRectF == null || mRectF.width() < width || mRectF.height() < height){
62 | mRectF = new RectF(0,0,width,height);
63 | }
64 | postInvalidate();
65 | }
66 |
67 | @Override
68 | public void setBackgroundColor(int backgroundColor) {
69 | this.backgroundColor = backgroundColor;
70 | }
71 |
72 | @Override
73 | protected void onDraw(Canvas canvas) {
74 | super.onDraw(canvas);
75 | if(mRectF == null){
76 | return;
77 | }
78 | drawTaskId = canvas.saveLayer(mRectF,mPaint);
79 | canvas.drawColor(backgroundColor);
80 | if(mLayerList != null){
81 | for(int i = 0;i < mLayerList.size(); i++){
82 | mLayerList.get(i).draw(canvas,mPaint,true,mRectF.width(),mRectF.height());
83 | }
84 | for(int i = 0;i < mLayerList.size(); i++){
85 | mLayerList.get(i).draw(canvas,mPaint,false,mRectF.width(),mRectF.height());
86 | }
87 | }
88 | canvas.restoreToCount(drawTaskId);
89 | }
90 |
91 |
92 | public void addLayer(Layer layer){
93 | if(layer == null){
94 | return;
95 | }
96 | mLayerList.add(layer);
97 | postInvalidate();
98 | }
99 |
100 |
101 |
102 | public void build(@Nullable Activity activity) {
103 | FrameLayout rootView = (FrameLayout) activity.getWindow().getDecorView();
104 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
105 | if(rootView != null){
106 | View oldView = rootView.findViewWithTag(TAG);
107 | if(oldView != null) {
108 | rootView.removeView(oldView);
109 | }
110 | setTag(TAG);
111 | rootView.addView(this,params);
112 | }else{
113 | return;
114 | }
115 | if(mLayerList != null){
116 | for(int i = 0; i < mLayerList.size(); i ++) {
117 | mLayerList.get(i).build(activity);
118 | }
119 | }
120 | postInvalidate();
121 | }
122 |
123 |
124 | public void build(@Nullable Fragment fragment) {
125 | FrameLayout rootView = (FrameLayout) fragment.requireActivity().getWindow().getDecorView();
126 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
127 | if(rootView != null){
128 | View oldView = rootView.findViewWithTag(TAG);
129 | if(oldView != null) {
130 | rootView.removeView(oldView);
131 | }
132 | setTag(TAG);
133 | rootView.addView(this,params);
134 | }else{
135 | return;
136 | }
137 |
138 | if(mLayerList != null){
139 | for(int i = 0;i
7 |
8 |
9 | 代码实例二用图
10 |
11 |
12 | 代码实例三用图
13 |
14 |
15 |
16 | 当前版本:1.0.1,当前还处在最初版本,可能有bug
17 |
18 | 使用方式
19 | 在需要引用的module 的gradle中
20 | ```
21 | dependencies {
22 | implementation fileTree(include: ['*.jar'], dir: 'libs')
23 | implementation 'aiven.guide.view:library:1.0.1'
24 | //或者使用api
25 | // api 'aiven.guide.view:library:1.0.1'
26 | }
27 | ```
28 |
29 | 这个库不需要在Layout布局文件的xml中写代码,思路是直接通过当前activity的DecorView 添加和移除View的方式。当然已经分装了,用起来比较简单,只是有部分偏移需要根据自己的UI布局来计算一下。
30 |
31 | * * *
32 | ### 具体代码用法 ###
33 | 直接通过上面三张图片,实现的代码进行注释说明吧,程序员的表达不好,谅解哈。
34 |
35 | ###### 实例一: 左上角用户个人中心引导 ######
36 |
37 | 要求:个人中心搬家到这里的指示引导图片可点击,点击后弹出一个Toast
38 | 右上角个人中心图片原型镂空,切镂空区域点击事件不能拦截,点击后要直接进入个人中心,有个人中心入口图标响应事件
39 | 这个实例通过根布局左上角绝对位置偏移实现,实例二中音乐图标会通过View自身相对位置实现。
40 | 代码:
41 | ```
42 | SmartGuide.newGuide(this) //这里的this是activity 或者fragment。需要注意的是fragment必须要在显示出来后(attach之后)才能实例这个,因为归根接地是用的activity
43 | .initBaseColor(0X80000000)//设置引蒙层背景颜色,可以不设置,默认是50%的黑色半透明,当然也可以自己制定
44 | //新建一个引导
45 | .newLayer(TAG_USER_HEADER) // TAG_USER_HEADER 是一个字符串,标识Layer的tag,后面在点击事件中用到,可以当做一个ID看待
46 | //创建一个镂空区域(也就是被裁剪挖空区域)
47 | .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
48 | @Override
49 | public CustomClip buildTarget() {//返回一个被挖空的镂空区域实例
50 | //构建镂空区域图形,支持CustomClip 和 ViewRectClip,ViewRectClip 后面第二个实例紫色音乐图标实例说明
51 | return CustomClip.newClipPos()
52 | //设置异形图片(实现见第三个按钮)
53 | //.asIrregularShape(getApplicationContext(),R.mipmap.test_img) 这里屏蔽了是因为在实例3中是实现,标识一个要挖空一个异形区域,由不规则图片区域决定
54 | .setAlignX(SmartGuide.AlignX.ALIGN_RIGHT)//设置定位水平定位偏向(这里的偏向是屏幕根而言,类似于RelativeLayout的align,局屏幕顶部,左边、右边、下边 自己定义)
55 | .setAlignY(SmartGuide.AlignY.ALIGN_TOP)//设置定位垂直定位偏向 (这里的偏向是屏幕根而言,类似于RelativeLayout的align)
56 | .setEventPassThrough(true)//镂空区域是否事件穿透,上面说的需要个人中心图标自己响应事件,设置后后面的点击事件 clipClicked 将不再响应改Tag的事件
57 | .setOffsetX(SmartUtils.dip2px(getApplicationContext(),14))//根据水平定位偏向设置偏移,类似于margin,如果未ALIGN_LEFT,则是距离屏幕左侧偏移量,如果是ALIGN_RIGHT 则是距离屏幕右侧偏移量
58 | .setOffsetY(SmartUtils.getStatusBarHeight(getApplicationContext())+SmartUtils.dip2px(getApplicationContext(),4)) //同offsetX基本相同的意思,只不过这个是垂直方向的
59 | .setClipSize(SmartUtils.dip2px(getApplicationContext(),48),SmartUtils.dip2px(getApplicationContext(),48)) //设置镂空裁剪区域尺寸,这里一定要指定,否则没有
60 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),24));//设置镂空区域半径,默认是90度直角,也就是方形的,这里显示一个圆圈,所以半径是宽高的一半,也可以自己指定一个圆角值
61 | }
62 | })
63 | //添加一个介绍图片(也就是图一中的个人中心搬家到这里的箭头白色文字图片)
64 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
65 | @Override
66 | public IntroPanel buildFacePanel() {
67 | return IntroPanel.newIntroPanel(getApplicationContext())
68 | .setIntroBmp(R.mipmap.test_face)//设置一个图片,这里就是刚说那张说明图片
69 | //这里的Align 同CustomClip的Align有点点区别,这里的Align不再是界面根布局为标准,这里是clip的镂空区域,也就是局于镂空区域的,左边、上面、下面、右面,自己定义
70 | //类似与RelativeLayout的相对布局toLeft、toRight、above、below
71 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_BOTTOM)
72 | .setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),97)) //设置图片展示的尺寸
73 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-20),0);//设置水平和垂直方向的偏移值,这里要注意根据Align来计算,也相当于margin,不过是相对于clip镂空区域
74 | }
75 | })
76 | //添加点击事件
77 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
78 | @Override
79 | public boolean emptyErrorClicked(SmartGuide smartGuide) {//点击蒙层中无裁剪和无镂空区域部分,部分需求是点击旁边无内容区域蒙层直接消失,所以这里增加了
80 | return true;//返回true,引导消失,false不消失
81 | }
82 |
83 | @Override
84 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
85 | //由于设置了setEventPassThrough 为true,所以tag为TAG_USER_HEADER 的时候这里这个方法不会回调
86 | }
87 |
88 | @Override
89 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
90 | //点击文字区域
91 | Toast.makeText(getApplicationContext(), "点击了右上角裁剪区域的说明图片引导", Toast.LENGTH_SHORT).show();
92 | }
93 | })
94 | .show(); //最后不要忘记了调用show把蒙层显示出来;
95 | ```
96 | 实例一的代码就完了,看上去代码有点多,但是都是流式的代码,看上去还是比较清晰,为了便于理解,对外的实例我基本上都直接采用流式API了。
97 |
98 | ##### 我要显示多个怎么办?有没有退出函数?有没有蒙层消失回调?别急,后面都有说明 ####
99 |
100 | - - -
101 | ###### 实例二: 这个实例是显示多个Layer,同时介绍相对View本身的Clip ######
102 | 要求:可以显示多个Layer在蒙层上,音乐图标镂空区域根据图标自己定位和尺寸
103 | 与上面带个Layer的不同点,主要注意有个over方法衔接两个Layer,over函数表示上一个Layer设置结束
104 |
105 | 直接上代码
106 | ```
107 | SmartGuide.newGuide(this) //这里的this是activity 或者fragment。需要注意的是fragment必须要在显示出来后(attach之后)才能实例这个,因为归根接地是用的activity
108 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
109 | //新建一个引导
110 | .newLayer(TAG_USER_HEADER)
111 | //创建一个镂空区域
112 | .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
113 | @Override
114 | public CustomClip buildTarget() {
115 | //这里代码我省略了,为了篇幅,通上面实例1的代码一致,唯一不同是没有设置setEventPassThrough(true) 这句。
116 | }
117 | })
118 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
119 | @Override
120 | public IntroPanel buildFacePanel() {
121 | //这里代码我省略了,为了篇幅,通上面实例1的代码一致
122 | }
123 | })
124 | .over() //多个newLayer必须用over作为上一个newLayer的结束连接,用于衔接下面一个newLayer
125 | .newLayer(TAG_MUSIC_IMG)
126 | //创建一个镂空区域
127 | .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {
128 | @Override
129 | public ViewRectClip buildTarget() { //构建一个View自身挖空区域
130 | return ViewRectClip.newClipPos()
131 | .setDstView(R.id.text_pos) //指定一个目标View的Id,或者直接传一个view。当然这个view一定要是前面实例化是this 这个当前的activity或者fragment中的view
132 | .setPadding(SmartUtils.dip2px(getApplicationContext(),5)) //由于由View自身决定尺寸和位置,所以可能刚好和view的边缘重叠,为了扩展大一点,所以设置一定d的padding的a
133 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),51));//设置挖空区域形状半径,默认为0,是一个矩形
134 | }
135 | })
136 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
137 | @Override
138 | public IntroPanel buildFacePanel() {
139 | return IntroPanel.newIntroPanel(getApplicationContext())
140 | //设置介绍图片与clipInfo的对齐信息,这里也不再过多解释,通上面的一个理解一样,只是参数不同
141 | .setIntroBmp(R.mipmap.test_face_music)
142 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)
143 | .setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))
144 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);
145 | }
146 | })
147 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
148 | @Override
149 | public boolean emptyErrorClicked(SmartGuide smartGuide) {
150 | return true;
151 | }
152 |
153 | @Override
154 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
155 | if (TAG_USER_HEADER.equals(tag)) {
156 | Toast.makeText(getApplicationContext(), "点击了左上角头像裁剪区域", Toast.LENGTH_SHORT).show();
157 | }else if(TAG_MUSIC_IMG.equals(tag)){
158 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();
159 | }
160 | }
161 |
162 | @Override
163 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
164 | if (TAG_USER_HEADER.equals(tag)) {
165 | Toast.makeText(getApplicationContext(), "点击了左上角头像图片介绍区域", Toast.LENGTH_SHORT).show();
166 | }else if(TAG_MUSIC_IMG.equals(tag)){
167 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图片介绍区域", Toast.LENGTH_SHORT).show();
168 | }
169 | }
170 | })
171 | .show();
172 | ```
173 | 实例二代码完了,主要注意一下over方法的使用地方和含义
174 |
175 | - - -
176 | ###### 实例三: 界面上有个异形的图片区域需要镂空 ######
177 | 要求:根据界面上一个ImageView上展示的图片来显示挖空区域
178 | 这个麻烦点在,是一个不规则的形状,之前都是一个矩形,或者矩形设置半径变为圆角矩形或者圆形。
179 | api上封装了,比较简单,主要是asIrregularShape 函数,上代码
180 | ```
181 | SmartGuide.newGuide(this)
182 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
183 | //新建一个引导
184 | .newLayer(TAG_IGG_SHAPE)
185 | //创建一个镂空区域
186 | .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {
187 | @Override
188 | public ViewRectClip buildTarget() {
189 | return ViewRectClip.newClipPos()
190 | .setDstView(R.id.text_pos2) //这个和实例二一样的
191 | //设置异形图片
192 | .asIrregularShape(getApplicationContext(),R.mipmap.test_img) //重要方法,设置一个不规则图片,标识挖空区域,CustomClip 也支持的,这里就不多说了
193 | .setPadding(SmartUtils.dip2px(getApplicationContext(),10))
194 | .setOffsetX(SmartUtils.dip2px(getApplicationContext(),-5))
195 | .setOffsetY(SmartUtils.dip2px(getApplicationContext(),-5));
196 | }
197 | })
198 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
199 | @Override
200 | public IntroPanel buildFacePanel() {
201 | //为了篇幅,这里代码省略了
202 | }
203 | })
204 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
205 | //为了篇幅,这里代码省略了
206 | })
207 | .show();
208 |
209 | ```
210 | - - -
211 | 三个实例都说完了,说几个没有介绍的方法:
212 | SmartGuide.clearLayers() 清空所有蒙层中的Layer,几乎不会用到。
213 | SmartGuide.removeLayerByTag(String tag) 根据tag 移除某个Layer,当多个引导指示跳转的时候会用到
214 | SmartGuide.dismiss() 直接退出用户引导蒙层
215 |
216 | 另外OnGuidClickListener 是抽象类,不是接口,里面可以复现方法,监听引导蒙层的退出
217 | destroyed() 方法,当蒙层退出时会调用
218 |
219 | ## 完了,谢谢! ##
220 | 有bug或者有什么建议欢迎大家直接邮件我 aiven163@aliyun.com
221 |
222 |
223 |
--------------------------------------------------------------------------------
/app/src/main/java/aiven/guide/view/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package aiven.guide.view.demo;
2 |
3 |
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.Toolbar;
7 | import android.view.View;
8 | import android.widget.Toast;
9 |
10 | import aiven.guide.view.SmartGuide;
11 | import aiven.guide.view.clip.CustomClip;
12 | import aiven.guide.view.clip.ViewRectClip;
13 | import aiven.guide.view.face.IntroPanel;
14 | import aiven.guide.view.layer.GuidView;
15 | import aiven.guide.view.util.MLog;
16 | import aiven.guide.view.util.SmartUtils;
17 |
18 | public class MainActivity extends AppCompatActivity {
19 | protected static final String TAG_USER_HEADER = "userHeadImg";
20 | protected static final String TAG_MUSIC_IMG = "music_img";
21 | protected static final String TAG_IGG_SHAPE = "igg_shape";
22 |
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_main);
28 | Toolbar toolbar = findViewById(R.id.toolbar);
29 | setSupportActionBar(toolbar);
30 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
31 | setTitle(R.string.app_name);
32 |
33 | int[] pos = new int[2];
34 | findViewById(R.id.root).getLocationOnScreen(pos);
35 | MLog.E(null,"rootWindow:"+pos[1]);
36 | }
37 |
38 | public void userHeadClick(View view){
39 | Toast.makeText(getApplicationContext(),"进入个人中心",Toast.LENGTH_SHORT).show();
40 | }
41 |
42 |
43 | /**
44 | * 通过绝对定位添加
45 | * @param view
46 | */
47 | public void showAbsPosLayer(View view){
48 | //构建引导
49 | SmartGuide.newGuide(this)
50 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
51 | //新建一个引导
52 | .newLayer(TAG_USER_HEADER)
53 | //创建一个镂空区域
54 | .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
55 | @Override
56 | public CustomClip buildTarget() {
57 | //构建镂空区域图形,支持CustomClip 和 ViewRectClip
58 | return CustomClip.newClipPos()
59 | //设置异形图片(实现见第三个按钮)
60 | // .asIrregularShape(getApplicationContext(),R.mipmap.test_img)
61 | .setAlignX(SmartGuide.AlignX.ALIGN_RIGHT)//设置定位水平定位偏向
62 | .setAlignY(SmartGuide.AlignY.ALIGN_TOP)//设置定位垂直定位偏向
63 | .setEventPassThrough(true)//镂空区域是否事件穿透
64 | .setOffsetX(SmartUtils.dip2px(getApplicationContext(),14))//根据水平定位偏向设置偏移,如果未ALIGN_LEFT,则是距离屏幕左侧偏移量,如果是ALIGN_RIGHT 则是距离屏幕右侧偏移量
65 | .setOffsetY(SmartUtils.getStatusBarHeight(getApplicationContext())+SmartUtils.dip2px(getApplicationContext(),4))
66 | //设置镂空裁剪区域尺寸
67 | .setClipSize(SmartUtils.dip2px(getApplicationContext(),48),SmartUtils.dip2px(getApplicationContext(),48))
68 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),24));
69 | }
70 | })
71 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
72 | @Override
73 | public IntroPanel buildFacePanel() {
74 | return IntroPanel.newIntroPanel(getApplicationContext())
75 | //设置介绍图片与clipInfo的对齐信息
76 | .setIntroBmp(R.mipmap.test_face)
77 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_BOTTOM)
78 | .setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),97))
79 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-20),0);
80 | }
81 | })
82 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
83 | @Override
84 | public boolean emptyErrorClicked(SmartGuide smartGuide) {//点击蒙层空白区域
85 | return true;//返回true,引导消失,false不消失
86 | }
87 |
88 | @Override
89 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
90 | //由于设置了setEventPassThrough 为true,所以这里这个方法不会回调
91 | }
92 |
93 | @Override
94 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
95 | //点击文字区域
96 | Toast.makeText(getApplicationContext(), "点击了右上角裁剪区域的说明图片引导", Toast.LENGTH_SHORT).show();
97 | }
98 | })
99 | .show();
100 | }
101 |
102 |
103 | /**
104 | * 根据View 自身位置定位
105 | * @param view
106 | */
107 | public void showViewPosLayer(View view){
108 | //构建引导
109 | SmartGuide.newGuide(this)
110 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
111 | //新建一个引导
112 | .newLayer(TAG_MUSIC_IMG)
113 | //创建一个镂空区域
114 | .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {
115 | @Override
116 | public ViewRectClip buildTarget() {
117 | return ViewRectClip.newClipPos()
118 | .setDstView(R.id.text_pos)
119 | .setPadding(SmartUtils.dip2px(getApplicationContext(),5))
120 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),51));
121 | }
122 | })
123 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
124 | @Override
125 | public IntroPanel buildFacePanel() {
126 | return IntroPanel.newIntroPanel(getApplicationContext())
127 | //设置介绍图片与clipInfo的对齐信息
128 | .setIntroBmp(R.mipmap.test_face_music)
129 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)
130 | .setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))
131 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);
132 | }
133 | })
134 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
135 | @Override
136 | public boolean emptyErrorClicked(SmartGuide smartGuide) {
137 | return true;
138 | }
139 |
140 | @Override
141 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
142 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();
143 | }
144 |
145 | @Override
146 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
147 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图标介绍图片区域", Toast.LENGTH_SHORT).show();
148 | }
149 | })
150 | .show();
151 | }
152 |
153 |
154 | /**
155 | *
156 | * 显示不规则异形镂空引导
157 | * @param view
158 | */
159 | public void showIgg(View view){
160 | SmartGuide.newGuide(this)
161 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
162 | //新建一个引导
163 | .newLayer(TAG_IGG_SHAPE)
164 | //创建一个镂空区域
165 | .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {
166 | @Override
167 | public ViewRectClip buildTarget() {
168 | return ViewRectClip.newClipPos()
169 | .setDstView(R.id.text_pos2)
170 | //设置异形图片
171 | .asIrregularShape(getApplicationContext(),R.mipmap.test_img)
172 | .setPadding(SmartUtils.dip2px(getApplicationContext(),10))
173 | .setOffsetX(SmartUtils.dip2px(getApplicationContext(),-5))
174 | .setOffsetY(SmartUtils.dip2px(getApplicationContext(),-5));
175 | }
176 | })
177 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
178 | @Override
179 | public IntroPanel buildFacePanel() {
180 | return IntroPanel.newIntroPanel(getApplicationContext())
181 | //设置介绍图片与clipInfo的对齐信息
182 | .setIntroBmp(R.mipmap.test_face_igg)
183 | .setAlign(SmartGuide.AlignX.ALIGN_RIGHT,SmartGuide.AlignY.ALIGN_TOP)
184 | .setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),151));
185 | }
186 | })
187 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
188 | @Override
189 | public boolean emptyErrorClicked(SmartGuide smartGuide) {
190 | return true;
191 | }
192 |
193 | @Override
194 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
195 | Toast.makeText(getApplicationContext(), "点击了不规则图形镂空区域", Toast.LENGTH_SHORT).show();
196 | }
197 |
198 | @Override
199 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
200 | Toast.makeText(getApplicationContext(), "点击了不规则图形图片说明区域", Toast.LENGTH_SHORT).show();
201 | }
202 | })
203 | .show();
204 | }
205 |
206 |
207 | /**
208 | * 单屏显示多个layer
209 | * @param view
210 | */
211 | public void showMultLayer(View view){
212 | SmartGuide.newGuide(this)
213 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
214 | //新建一个引导
215 | .newLayer(TAG_USER_HEADER)
216 | //创建一个镂空区域
217 | .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
218 | @Override
219 | public CustomClip buildTarget() {
220 | //构建镂空区域图形,支持CustomClip 和 ViewRectClip
221 | return CustomClip.newClipPos()
222 | .setAlignX(SmartGuide.AlignX.ALIGN_RIGHT)//设置定位水平定位偏向
223 | .setAlignY(SmartGuide.AlignY.ALIGN_TOP)//设置定位垂直定位偏向
224 | .setOffsetX(SmartUtils.dip2px(getApplicationContext(),14))//根据水平定位偏向设置偏移,如果未ALIGN_LEFT,则是距离屏幕左侧偏移量,如果是ALIGN_RIGHT 则是距离屏幕右侧偏移量
225 | .setOffsetY(SmartUtils.getStatusBarHeight(getApplicationContext())+SmartUtils.dip2px(getApplicationContext(),4))
226 | //设置镂空裁剪区域尺寸
227 | .setClipSize(SmartUtils.dip2px(getApplicationContext(),48),SmartUtils.dip2px(getApplicationContext(),48))
228 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),24));
229 | }
230 | })
231 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
232 | @Override
233 | public IntroPanel buildFacePanel() {
234 | return IntroPanel.newIntroPanel(getApplicationContext())
235 | //设置介绍图片与clipInfo的对齐信息
236 | .setIntroBmp(R.mipmap.test_face)
237 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_BOTTOM)
238 | .setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),97))
239 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-20),0);
240 | }
241 | })
242 | .over() //多个newLayer必须用over作为上一个newLayer的结束连接
243 | .newLayer(TAG_MUSIC_IMG)
244 | //创建一个镂空区域
245 | .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {
246 | @Override
247 | public ViewRectClip buildTarget() {
248 | return ViewRectClip.newClipPos()
249 | .setDstView(R.id.text_pos)
250 | .setPadding(SmartUtils.dip2px(getApplicationContext(),5))
251 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),51));
252 | }
253 | })
254 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
255 | @Override
256 | public IntroPanel buildFacePanel() {
257 | return IntroPanel.newIntroPanel(getApplicationContext())
258 | //设置介绍图片与clipInfo的对齐信息
259 | .setIntroBmp(R.mipmap.test_face_music)
260 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)
261 | .setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))
262 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);
263 | }
264 | })
265 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
266 | @Override
267 | public boolean emptyErrorClicked(SmartGuide smartGuide) {
268 | return true;
269 | }
270 |
271 | @Override
272 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
273 | if (TAG_USER_HEADER.equals(tag)) {
274 | Toast.makeText(getApplicationContext(), "点击了左上角头像裁剪区域", Toast.LENGTH_SHORT).show();
275 | }else if(TAG_MUSIC_IMG.equals(tag)){
276 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();
277 | }
278 | }
279 |
280 | @Override
281 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
282 | if (TAG_USER_HEADER.equals(tag)) {
283 | Toast.makeText(getApplicationContext(), "点击了左上角头像图片介绍区域", Toast.LENGTH_SHORT).show();
284 | }else if(TAG_MUSIC_IMG.equals(tag)){
285 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图片介绍区域", Toast.LENGTH_SHORT).show();
286 | }
287 | }
288 | })
289 | .show();
290 | }
291 |
292 |
293 | /**
294 | * 多步骤切换展示
295 | * @param view
296 | */
297 | public void releaseTest(View view){
298 | SmartGuide.newGuide(this)
299 | .initBaseColor(0X80000000)//设置引蒙层背景颜色
300 | //新建一个引导
301 | .newLayer(TAG_USER_HEADER)
302 | //创建一个镂空区域
303 | .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
304 | @Override
305 | public CustomClip buildTarget() {
306 | //构建镂空区域图形,支持CustomClip 和 ViewRectClip
307 | return CustomClip.newClipPos()
308 | //设置异形图片(实现见第三个按钮)
309 | // .asIrregularShape(getApplicationContext(),R.mipmap.test_img)
310 | .setAlignX(SmartGuide.AlignX.ALIGN_RIGHT)//设置定位水平定位偏向
311 | .setAlignY(SmartGuide.AlignY.ALIGN_TOP)//设置定位垂直定位偏向
312 | .setOffsetX(SmartUtils.dip2px(getApplicationContext(),14))//根据水平定位偏向设置偏移,如果未ALIGN_LEFT,则是距离屏幕左侧偏移量,如果是ALIGN_RIGHT 则是距离屏幕右侧偏移量
313 | .setOffsetY(SmartUtils.getStatusBarHeight(getApplicationContext())+SmartUtils.dip2px(getApplicationContext(),4))
314 | //设置镂空裁剪区域尺寸
315 | .setClipSize(SmartUtils.dip2px(getApplicationContext(),48),SmartUtils.dip2px(getApplicationContext(),48))
316 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),24));
317 | }
318 | })
319 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
320 | @Override
321 | public IntroPanel buildFacePanel() {
322 | return IntroPanel.newIntroPanel(getApplicationContext())
323 | //设置介绍图片与clipInfo的对齐信息
324 | .setIntroBmp(R.mipmap.test_face)
325 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_BOTTOM)
326 | .setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),97))
327 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-20),0);
328 | }
329 | })
330 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
331 | @Override
332 | public boolean emptyErrorClicked(SmartGuide smartGuide) {//点击蒙层空白区域
333 | return false;//返回true,引导消失,false不消失
334 | }
335 |
336 | @Override
337 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
338 | //由于设置了setEventPassThrough 为true,所以这里这个方法不会回调
339 | }
340 |
341 | @Override
342 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
343 | showStep2(guide);
344 | }
345 | })
346 | .show();
347 | }
348 |
349 |
350 |
351 |
352 | private void showStep2(SmartGuide guide){
353 | guide.clearLayers();
354 | guide.newLayer(TAG_MUSIC_IMG)
355 | //创建一个镂空区域
356 | .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {
357 | @Override
358 | public ViewRectClip buildTarget() {
359 | return ViewRectClip.newClipPos()
360 | .setDstView(R.id.text_pos)
361 | .setPadding(SmartUtils.dip2px(getApplicationContext(),5))
362 | .clipRadius(SmartUtils.dip2px(getApplicationContext(),51));
363 | }
364 | })
365 | .buildIntroPanel(new SmartGuide.IntroPanelBuilder() {
366 | @Override
367 | public IntroPanel buildFacePanel() {
368 | return IntroPanel.newIntroPanel(getApplicationContext())
369 | //设置介绍图片与clipInfo的对齐信息
370 | .setIntroBmp(R.mipmap.test_face_music)
371 | .setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)
372 | .setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))
373 | .setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);
374 | }
375 | })
376 | .setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {
377 | @Override
378 | public boolean emptyErrorClicked(SmartGuide smartGuide) {
379 | return true;
380 | }
381 |
382 | @Override
383 | public void clipClicked(SmartGuide guide, GuidView view, String tag) {
384 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();
385 | }
386 |
387 | @Override
388 | public void introClicked(SmartGuide guide, GuidView view, String tag) {
389 | Toast.makeText(getApplicationContext(), "点击了紫色音乐图标介绍图片区域", Toast.LENGTH_SHORT).show();
390 | }
391 | })
392 | .show();
393 | }
394 |
395 |
396 |
397 | }
--------------------------------------------------------------------------------