├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── extra └── preview.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sorashiro │ │ └── chinamapinfoview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sorashiro │ │ │ └── chinamapinfoview │ │ │ ├── ChinaMapInfoView.java │ │ │ ├── CnMap.java │ │ │ ├── CnMapConfig.java │ │ │ └── CnSvgBigRenderer.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── sorashiro │ └── chinamapinfoview │ └── ExampleUnitTest.java ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sorashiro │ │ └── chinamapinformation │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sorashiro │ │ │ └── chinamapinformation │ │ │ ├── MainActivity.java │ │ │ └── tool │ │ │ └── LogAndToastUtil.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sorashiro │ └── chinamapinformation │ └── ExampleUnitTest.java ├── settings.gradle └── svg └── cnsvgbig.svg /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Sora-Shiro (https://github.com/Sora-Shiro) 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChinaMapInfoView 2 | 3 | This is a China map that can be translated and scaled. You can use it to visually display statistics based on provinces, such as the birth rate in each province, the monthly maximum temperature in the provinces. 4 | 5 | 这是一个可移动、缩放的中国地图,开发者可以使用它来直观显示基于省的统计数据,如各省某年人口出生率、各省每月最高气温等。 6 | 7 | Implement the interface included the province onClick / onLongClick event callback so you can use it to jump to the Activity that displays the province's detailed data. 8 | 9 | 实现了省份点击 / 长按事件回调的接口,可以用来跳转到显示该省详细数据的 Activity。 10 | 11 | Use [SVG-Android](https://github.com/MegatronKing/SVG-Android/blob/master/README.zh-cn.md) to generate Drawable, API 14 (Android 4.0)+ supported. 12 | 13 | 使用 [SVG-Android](https://github.com/MegatronKing/SVG-Android/blob/master/README.zh-cn.md) 生成 Drawable , 支持 API 14(Android 4.0)+ 版本。 14 | 15 | # What's New 16 | - v1.0.2 : Add onLongClick callback 17 | 18 | # Preview 19 | 20 | ![preview](https://github.com/Sora-Shiro/ChinaMapInfoView/blob/master/extra/preview.gif) 21 | 22 | # Function 23 | 24 | Supporting: 25 | - Translate 26 | - Scale 27 | - Highlight clicked province 28 | - Rollback when out of view 29 | - Implement ChinaMapViewProvinceListener callback to interact (onClick / onLongClick) 30 | - Custom fill / click / longClick (stroke) color 、stroke width 31 | 32 | 支持: 33 | - 平移 34 | - 缩放 35 | - 高亮选中省份 36 | - 当图片过度移动则回滚 37 | - 实现 ChinaMapViewProvinceListener 接口来进行回调交互(点击 / 长按) 38 | - 自定义填充 / 点击 / 长按(边界)颜色、边界宽度 39 | 40 | # Download 41 | 42 | ## Maven 43 | 44 | ``` 45 | 46 | com.sorashiro.ChinaMapInfoView 47 | library 48 | 1.0.2 49 | pom 50 | 51 | ``` 52 | 53 | ## Gradle 54 | 55 | ``` 56 | compile 'com.sorashiro.ChinaMapInfoView:library:1.0.2' 57 | ``` 58 | 59 | # How To Use 60 | 61 | ## In `activity_main.xml` 62 | 63 | ```xml 64 | 69 | ``` 70 | 71 | ## In `MainActivity.java` (using [Butter Knife](https://github.com/JakeWharton/butterknife)) 72 | 73 | ```java 74 | public class MainActivity extends AppCompatActivity implements ChinaMapInfoView.ChinaMapViewProvinceListener { 75 | 76 | @BindView(R2.id.imgCnMap) 77 | ChinaMapInfoView imgCnMap; 78 | 79 | @Override 80 | protected void onCreate(Bundle savedInstanceState) { 81 | super.onCreate(savedInstanceState); 82 | setContentView(R.layout.activity_main); 83 | 84 | ButterKnife.bind(this); 85 | 86 | initView(); 87 | } 88 | 89 | private void initView() { 90 | imgCnMap.setChinaMapViewProvinceListener(this); 91 | } 92 | 93 | @Override 94 | public void onProvinceClick(int i) { 95 | LogAndToastUtil.ToastOut(this, imgCnMap.getCnMap().PROVINCE[i] + " is clicked"); 96 | } 97 | 98 | @Override 99 | public void onProvinceLongClick(int i) { 100 | LogAndToastUtil.ToastOut(this, imgCnMap.getCnMap().PROVINCE[i] + " is long clicked"); 101 | } 102 | } 103 | ``` 104 | 105 | ## Other 106 | 107 | If you want to change Anhui's `fill color` and `highlight color` together, just do like this: 108 | 109 | 如果你想同时改变安徽省的 `填充颜色` 和 `高亮颜色` ,只要这么做: 110 | 111 | ```java 112 | // first get the config map 113 | // 首先获取总设置 map 114 | CnMap cnMap = imgCnMap.getCnMap(); 115 | HashMap cnConfigMap = cnMap.configMap; 116 | // or configMap.get("Anhui"); but use cnMap.PROVINCE[0] better 117 | // 或者 configMap.get("Anhui"); 但是用 cnMap.PROVINCE[0] 更好些 118 | CnMapConfig configAnhui = cnConfigMap.get(cnMap.PROVINCE[0]); 119 | // support method chaining 120 | // 支持链式调用(方法链) 121 | configAnhui 122 | .setFillColor(Color.parseColor("#ee0000")) 123 | .setClickColor(Color.parseColor("#99ffff")); 124 | ``` 125 | 126 | # Todo 127 | - Text showed (but when province too small they should invisible) 128 | 129 | # License 130 | 131 | ``` 132 | /* 133 | * Copyright (C) 2017, Sora Shiro (https://github.com/Sora-Shiro) 134 | * 135 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 136 | * in compliance with the License. You may obtain a copy of the License at 137 | * 138 | * http://www.apache.org/licenses/LICENSE-2.0 139 | * 140 | * Unless required by applicable law or agreed to in writing, software distributed under the License 141 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 142 | * or implied. See the License for the specific language governing permissions and limitations under 143 | * the License. 144 | */ 145 | ``` -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | 4 | buildscript { 5 | repositories { 6 | // 阿里镜像 7 | maven {url "http://maven.aliyun.com/nexus/content/groups/public"} 8 | jcenter() 9 | //ButterKnife 10 | mavenCentral() 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:2.2.0' 14 | 15 | // NOTE: Do not place your application dependencies here; they belong 16 | // in the individual module build.gradle files 17 | 18 | // Maven 19 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 20 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' 21 | 22 | // ButterKnife 23 | classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1' 24 | // SVG-Android 25 | classpath 'com.github.megatronking:svg-plugin:1.3.8' 26 | } 27 | } 28 | 29 | 30 | allprojects { 31 | repositories { 32 | // 阿里镜像 33 | maven {url "http://maven.aliyun.com/nexus/content/groups/public"} 34 | jcenter() 35 | } 36 | } 37 | 38 | task clean(type: Delete) { 39 | delete rootProject.buildDir 40 | } 41 | -------------------------------------------------------------------------------- /extra/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/extra/preview.gif -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | android { 6 | compileSdkVersion 25 7 | buildToolsVersion "25.0.3" 8 | 9 | defaultConfig { 10 | minSdkVersion 14 11 | targetSdkVersion 25 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 29 | exclude group: 'com.android.support', module: 'support-annotations' 30 | }) 31 | compile 'com.android.support:appcompat-v7:25.3.1' 32 | testCompile 'junit:junit:4.12' 33 | // SVG-Android 34 | compile 'com.github.megatronking:svg-support:1.3.2' 35 | } 36 | 37 | // 项目主页 38 | def siteUrl = 'https://github.com/Sora-Shiro/ChinaMapInfoView' 39 | // 项目的git地址 40 | def gitUrl = 'https://github.com/Sora-Shiro/ChinaMapInfoView.git' 41 | // 发布到JCenter上的项目名字 42 | def libName = "ChinaMapInfoView" 43 | 44 | // 发布到组织名称名字,必须填写 45 | group = "com.sorashiro.ChinaMapInfoView" 46 | // 版本号,下次更新是只需要更改版本号即可 47 | version = "1.0.2" 48 | // 上面配置后上传至JCenter后的编译路径是这样的: compile 'com.sorashiro.ChinaMapInfoView:library:1.0.2' 49 | 50 | // 生成源文件 51 | task sourcesJar(type: Jar) { 52 | from android.sourceSets.main.java.srcDirs 53 | classifier = 'sources' 54 | } 55 | 56 | // 生成Javadoc文档 57 | task javadoc(type: Javadoc) { 58 | source = android.sourceSets.main.java.srcDirs 59 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 60 | } 61 | 62 | // 文档打包成jar 63 | task javadocJar(type: Jar, dependsOn: javadoc) { 64 | classifier = 'javadoc' 65 | from javadoc.destinationDir 66 | } 67 | 68 | // 拷贝javadoc文件 69 | task copyDoc(type: Copy) { 70 | from "${buildDir}/docs/" 71 | into "docs" 72 | } 73 | 74 | // 上传到JCenter所需要的源码文件 75 | artifacts { 76 | archives javadocJar 77 | archives sourcesJar 78 | } 79 | 80 | // 配置maven库,生成POM.xml文件 81 | install { 82 | repositories.mavenInstaller { 83 | // This generates POM.xml with proper parameters 84 | pom { 85 | project { 86 | packaging 'aar' 87 | // 项目描述,随意填 88 | name 'Used to show geographic information.' 89 | url siteUrl 90 | licenses { 91 | license { 92 | // 开源协议 93 | name 'The Apache Software License, Version 2.0' 94 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 95 | } 96 | } 97 | developers { 98 | developer { 99 | // 开发者的个人信息 100 | id 'sora-shiro' 101 | name 'SoraShiro' 102 | email 'sora95shiro@gmail.com' 103 | } 104 | } 105 | scm { 106 | connection gitUrl 107 | developerConnection gitUrl 108 | url siteUrl 109 | } 110 | } 111 | } 112 | } 113 | } 114 | 115 | //上传到JCenter 116 | Properties properties = new Properties() 117 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 118 | 119 | bintray { 120 | user = properties.getProperty("bintray.user") //读取 local.properties 文件里面的 bintray.user 121 | key = properties.getProperty("bintray.apikey") //读取 local.properties 文件里面的 bintray.apikey 122 | configurations = ['archives'] 123 | pkg { 124 | //这里的repo值必须要和你创建Maven仓库的时候的名字一样 125 | repo = "ChinaMapInfoView" 126 | //发布到JCenter上的项目名字 127 | name = libName 128 | //项目描述 129 | desc = 'Used to show geographic information.' 130 | websiteUrl = siteUrl 131 | vcsUrl = gitUrl 132 | licenses = ["Apache-2.0"] 133 | publish = true 134 | } 135 | } 136 | 137 | javadoc { 138 | options{ 139 | //如果你的项目里面有中文注释的话,必须将格式设置为UTF-8,不然会出现乱码 140 | encoding "UTF-8" 141 | charSet 'UTF-8' 142 | author true 143 | version true 144 | links "http://docs.oracle.com/javase/7/docs/api" 145 | } 146 | } -------------------------------------------------------------------------------- /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 D:\WORK\Adt5.0-bundle\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/sorashiro/chinamapinfoview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sorashiro.chinamapinfoview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sorashiro.chinamapinfoview.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/sorashiro/chinamapinfoview/ChinaMapInfoView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017, Sora Shiro (https://github.com/Sora-Shiro) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.sorashiro.chinamapinfoview; 16 | 17 | import android.content.Context; 18 | import android.graphics.Canvas; 19 | import android.graphics.Matrix; 20 | import android.graphics.PointF; 21 | import android.graphics.RectF; 22 | import android.graphics.drawable.Drawable; 23 | import android.os.Handler; 24 | import android.os.Looper; 25 | import android.os.Message; 26 | import android.util.AttributeSet; 27 | import android.view.MotionEvent; 28 | import android.view.View; 29 | import android.widget.ImageView; 30 | 31 | import com.github.megatronking.svg.support.SVGDrawable; 32 | 33 | import java.util.LinkedList; 34 | 35 | /** 36 | * Created by SoraShiro on 2017/8/7. 37 | *

38 | * Implements ChinaMapViewProvinceListener to interact province click event :) 39 | * 40 | * @author Sora Shiro 41 | * @since 2017/8/7 42 | */ 43 | 44 | public class ChinaMapInfoView extends ImageView { 45 | 46 | // Context 只要实现该接口就可以处理区域点击事件了 47 | // Implements this so you can handle click and long-click event 48 | public interface ChinaMapViewProvinceListener { 49 | void onProvinceClick(int i); 50 | void onProvinceLongClick(int i); 51 | } 52 | 53 | ChinaMapViewProvinceListener mChinaMapViewProvinceListener; 54 | 55 | // 一个存储所有触摸点 ID 的 List 56 | // A List saved all pointers 57 | LinkedList pointerIdList = new LinkedList<>(); 58 | boolean canDrag = false; 59 | boolean canScale = false; 60 | // 图片所在区域 61 | // RectF of Map 62 | RectF mRectF; 63 | private Matrix mImageMatrix = new Matrix(); 64 | // 拖动点 65 | // Drag Point 66 | PointF dragPoint = new PointF(0, 0); 67 | // 缩放点 68 | // Scale Point, for scaling needs 2 fingers so use 2 variables 69 | PointF scaleFirstPoint = new PointF(0, 0); 70 | PointF scaleSecondPoint = new PointF(0, 0); 71 | // 缩放点的 pointId 72 | // Scale Points' Id 73 | int scaleFirstPid; 74 | int scaleSecondPid; 75 | // 最小缩放比率 76 | // Min Scale Rate 77 | private float mBaseScale; 78 | // 当前缩放比率 79 | // Current Scale Rate 80 | private float mCurrentScale; 81 | // 最大缩放比率,设定是最小的 10 倍 82 | // Max Scale Rate, 10 times of Min 83 | private float mMaxScale; 84 | 85 | // 用于设定地图信息 86 | // Using to config map information 87 | private CnMap mCnMap; 88 | // 用于获取地图各部分 Region 89 | // Using to get Region of map 90 | private CnSvgBigRenderer mCnSvgBigRenderer; 91 | 92 | public ChinaMapInfoView(Context context) { 93 | super(context, null); 94 | init(); 95 | } 96 | 97 | public ChinaMapInfoView(Context context, AttributeSet attrs) { 98 | super(context, attrs, 0); 99 | init(); 100 | } 101 | 102 | public ChinaMapInfoView(Context context, AttributeSet attrs, int defStyleAttr) { 103 | super(context, attrs, defStyleAttr); 104 | init(); 105 | } 106 | 107 | public ChinaMapViewProvinceListener getChinaMapViewProvinceListener() { 108 | return mChinaMapViewProvinceListener; 109 | } 110 | 111 | public void setChinaMapViewProvinceListener(ChinaMapViewProvinceListener chinaMapViewProvinceListener) { 112 | mChinaMapViewProvinceListener = chinaMapViewProvinceListener; 113 | } 114 | 115 | private void init() { 116 | super.setScaleType(ScaleType.MATRIX); 117 | 118 | // 禁止硬件加速,非常重要! 119 | // Prohibit hardware acceleration 120 | setLayerType(View.LAYER_TYPE_SOFTWARE, null); 121 | 122 | mImageMatrix = new Matrix(); 123 | mCnMap = new CnMap(); 124 | 125 | resetDrawable(); 126 | 127 | mMapMatrix = new Matrix(); 128 | } 129 | 130 | // 每次地图设置有所更改都要调用该函数重新生成 drawable 131 | // Regenerate(Re-onDraw) view 132 | private void resetDrawable() { 133 | mCnSvgBigRenderer = new CnSvgBigRenderer(getContext(), mCnMap); 134 | Drawable drawable = new SVGDrawable(mCnSvgBigRenderer); 135 | setImageDrawable(drawable); 136 | } 137 | 138 | public CnMap getCnMap() { 139 | return mCnMap; 140 | } 141 | 142 | public void setCnMap(CnMap cnMap) { 143 | mCnMap = cnMap; 144 | } 145 | 146 | @Override 147 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 148 | super.onSizeChanged(w, h, oldw, oldh); 149 | // 获取到对应图片的宽度和高度 150 | // Get original width and height of photo 151 | Drawable d = getDrawable(); 152 | if (null == d) { 153 | return; 154 | } 155 | int dw = d.getIntrinsicWidth(); 156 | int dh = d.getIntrinsicHeight(); 157 | 158 | mRectF = new RectF(0, 0, w, h); 159 | 160 | // 如果图片宽高任意大于控件宽高,则缩小 161 | // If photo's width and height less than view's, narrow it 162 | if (dw > w || dh > h) { 163 | mBaseScale = Math.min(w * 1.0f / dw, h * 1.0f / dh); 164 | } 165 | mCurrentScale = mBaseScale; 166 | // 最多放大 10 倍 167 | // Most rate is 10 168 | mMaxScale = mBaseScale * 10; 169 | 170 | // 将图片移动到 view 的中间位置 171 | // Lastly, move photo to center of view 172 | float dx = w / 2 - dw / 2; 173 | float dy = h / 2 - dh / 2; 174 | mImageMatrix.postTranslate(dx, dy); 175 | mImageMatrix.postScale(mBaseScale, mBaseScale, w / 2, 176 | h / 2); 177 | setImageMatrix(mImageMatrix); 178 | } 179 | 180 | @Override 181 | protected void onDraw(Canvas canvas) { 182 | super.onDraw(canvas); 183 | } 184 | 185 | // 记录触碰到的省份区域 186 | // Record touched province Region 187 | int saveTouchFlag = -1; 188 | // 记录按下时的省份区域 189 | // Record ACTION_DOWN province Region 190 | int currentTouchFlag = -1; 191 | private Matrix mMapMatrix; 192 | // 记录拖动点,拖动超过一定距离则取消点击事件 193 | // Record moving point, cancel click event if it move a certain distance 194 | float firstDownX = -1; 195 | float firstDownY = -1; 196 | // 长按功能实现 197 | // Long click function implement 198 | LongClickRunnable mLongClickRunnable; 199 | 200 | @Override 201 | public boolean onTouchEvent(MotionEvent event) { 202 | float[] original = new float[2]; 203 | float[] pts = new float[2]; 204 | pts[0] = original[0] = event.getX(); 205 | pts[1] = original[1] = event.getY(); 206 | // 根据当前图片矩阵特性转换点击坐标 207 | // According to current photo matrix, invert point's coordinate 208 | mImageMatrix.invert(mMapMatrix); 209 | float[] values = new float[9]; 210 | mImageMatrix.getValues(values); 211 | mMapMatrix.postScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]); 212 | mMapMatrix.mapPoints(pts); 213 | int x = (int) pts[0]; 214 | int y = (int) pts[1]; 215 | switch (event.getActionMasked()) { 216 | // 任何新的手指加入、移动、离开都会取消旧长按事件 217 | // Any new finger add, or move, or leave will cancel old long click event 218 | case MotionEvent.ACTION_DOWN: 219 | if(mLongClickRunnable != null) { 220 | mLongClickRunnable.cancelLongClick(); 221 | } 222 | firstDownX = original[0]; 223 | firstDownY = original[1]; 224 | currentTouchFlag = getTouchFlag(x, y); 225 | // 第一个手指按下,如果手指不在高亮区域而且已经有高亮区域,取消该高亮区域 226 | // 但这个功能在缩放的时候体验不好,故取消该功能 227 | // When first finger down, if it is not in the highlight Region and there 228 | // was a highlight Region already, cancel its highlight 229 | // But this function is bad user experience, so I remove it 230 | // if(currentTouchFlag != saveHighlightIndex && saveHighlightIndex != -1) { 231 | // disHighlightRegion(saveHighlightIndex, saveHighlightMode); 232 | // } 233 | saveTouchFlag = currentTouchFlag; 234 | // 点到了省份才开始长按判断线程 235 | // Only when user touch a certain province 236 | // will a long click judge thred start 237 | if(currentTouchFlag != -1) { 238 | mLongClickRunnable = new LongClickRunnable(currentTouchFlag); 239 | Thread longClickThread = new Thread(mLongClickRunnable); 240 | longClickThread.start(); 241 | } 242 | case MotionEvent.ACTION_POINTER_DOWN: 243 | pointerIdList.add(event.getPointerId(event.getActionIndex())); 244 | // 当只有一个手指时,另一个手指按下会触发 ACTION_POINTER_DOWN ,这个时候得到的手指数是 2 245 | // When there is only a finger, another finger's down will be ACTION_POINTER_DOWN 246 | // At this moment, event.getPointerCount() is 2 247 | // 当只有一个手指时才能进行拖动 248 | // Only when there is a finger will user can drag 249 | if (event.getPointerCount() == 1 && mRectF.contains((int) event.getX(), (int) event.getY())) { 250 | canDrag = true; 251 | dragPoint.set(event.getX(0), event.getY(0)); 252 | } else { 253 | canDrag = false; 254 | if(mLongClickRunnable != null) { 255 | mLongClickRunnable.cancelLongClick(); 256 | } 257 | } 258 | // 当刚好有两个手指时才能进行缩放 259 | // Only when there are 2 fingers will user can scale 260 | if (event.getPointerCount() == 2 && mRectF.contains((int) event.getX(), (int) event.getY())) { 261 | canScale = true; 262 | scaleFirstPoint.set(event.getX(0), event.getY(0)); 263 | scaleFirstPid = event.getPointerId(0); 264 | scaleSecondPoint.set(event.getX(1), event.getY(1)); 265 | scaleSecondPid = event.getPointerId(1); 266 | } else { 267 | canScale = false; 268 | } 269 | break; 270 | case MotionEvent.ACTION_UP: 271 | if(mLongClickRunnable != null) { 272 | mLongClickRunnable.cancelLongClick(); 273 | } 274 | currentTouchFlag = getTouchFlag(x, y); 275 | // 如果手指按下区域和抬起区域相同且不为空,则判断为点击事件 276 | // If ACTION_UP's and ACTION_DOWN's flag is same and not -1, it's click event 277 | if (currentTouchFlag == saveTouchFlag && currentTouchFlag != -1) { 278 | if (mChinaMapViewProvinceListener != null) { 279 | mChinaMapViewProvinceListener.onProvinceClick(currentTouchFlag); 280 | 281 | highlightRegion(currentTouchFlag, HIGHLIGHT_CLICK); 282 | } 283 | } else if(currentTouchFlag == -1 && saveHighlightIndex != -1){ 284 | disHighlightRegion(saveHighlightIndex, saveHighlightMode); 285 | } 286 | saveTouchFlag = currentTouchFlag = -1; 287 | case MotionEvent.ACTION_POINTER_UP: 288 | pointerIdList.remove(Integer.valueOf(event.getPointerId(event.getActionIndex()))); 289 | // 判断哪个手指留在最后(不进行该处理会造成“瞬移”现象): 290 | // 当只剩两个手指时,其中一个手指抬起触发 ACTION_POINTER_UP ,这个时候得到的手指数还是 2 291 | // pointerIdList 在这个时候会只剩下一个手指的id 292 | // 这个时候 event.findPointerIndex(pointerIdList.get(0)) 拿到的 293 | // 一定是最后一个仍然留在屏幕上的手指 294 | // Judge which finger is the last(If I don't handle this, "Flash Move" will occur): 295 | // When there are only 2 fingers, if 1 of them is leave(up), ACTION_POINTER_UP triggered, 296 | // event.getPointerCount() is 2(NOT 1) at this moment 297 | // But my pointerIdList will only save 1 finger stay on the screen at the moment 298 | // So event.findPointerIndex(pointerIdList.get(0)) must be the staying finger. 299 | if (event.getPointerCount() == 2) { 300 | canDrag = true; 301 | int index = event.findPointerIndex(pointerIdList.get(0)); 302 | dragPoint.set(event.getX(index), event.getY(index)); 303 | } 304 | // 小于两个手指时不能缩放 305 | // Can't scale when the number of fingers less than 2 306 | if (event.getPointerCount() <= 2) { 307 | canScale = false; 308 | } 309 | // 检查是否超出可见区域,如果是那么进行动画让其可见 310 | // Check if photo invisible 311 | if (event.getPointerCount() == 1) { 312 | invisibleCheck(); 313 | } 314 | break; 315 | case MotionEvent.ACTION_MOVE: 316 | if (canScale) { 317 | saveTouchFlag = -1; 318 | // 根据两指的始末距离不断计算缩放比例 319 | // Calculate 2 scale points distance to scale photo 320 | float afterFirstX = event.getX(event.findPointerIndex(scaleFirstPid)); 321 | float afterFirstY = event.getY(event.findPointerIndex(scaleFirstPid)); 322 | float afterSecondX = event.getX(event.findPointerIndex(scaleSecondPid)); 323 | float afterSecondY = event.getY(event.findPointerIndex(scaleSecondPid)); 324 | float pivotX = (scaleFirstPoint.x + scaleSecondPoint.x) / 2; 325 | float pivotY = (scaleFirstPoint.y + scaleSecondPoint.y) / 2; 326 | double beforeDistance = 327 | Math.sqrt( 328 | Math.pow((scaleFirstPoint.x - scaleSecondPoint.x), 2) + 329 | Math.pow((scaleFirstPoint.y - scaleSecondPoint.y), 2) 330 | ); 331 | double afterDistance = 332 | Math.sqrt( 333 | Math.pow((afterFirstX - afterSecondX), 2) + 334 | Math.pow((afterFirstY - afterSecondY), 2) 335 | ); 336 | float scaleFactor = (float) (afterDistance / beforeDistance); 337 | 338 | mCurrentScale = scaleFactor; 339 | onScale(scaleFactor, pivotX, pivotY); 340 | 341 | scaleFirstPoint.set(afterFirstX, afterFirstY); 342 | scaleSecondPoint.set(afterSecondX, afterSecondY); 343 | } 344 | if (canDrag) { 345 | // x 或 y 方向移动大于 20 像素时取消点击 / 长按事件 346 | // When drag point move more than 20 px, cancel click or long click evnet 347 | if (Math.abs(firstDownX - original[0]) > 20 || 348 | Math.abs(firstDownY - original[1]) > 20) { 349 | saveTouchFlag = -1; 350 | if(mLongClickRunnable != null) { 351 | mLongClickRunnable.cancelLongClick(); 352 | } 353 | } 354 | float tX = event.getX(0) - dragPoint.x; 355 | float tY = event.getY(0) - dragPoint.y; 356 | 357 | // 根据拖动距离平移 358 | // According to drag point movement to translate photo's matrix 359 | mImageMatrix.postTranslate(tX, tY); 360 | setImageMatrix(mImageMatrix); 361 | 362 | dragPoint.set(event.getX(0), event.getY(0)); 363 | } 364 | break; 365 | case MotionEvent.ACTION_CANCEL: 366 | canDrag = false; 367 | canScale = false; 368 | saveTouchFlag = -1; 369 | currentTouchFlag = -1; 370 | if(mLongClickRunnable != null) { 371 | mLongClickRunnable.cancelLongClick(); 372 | } 373 | break; 374 | } 375 | 376 | return true; 377 | } 378 | 379 | // 获取触摸区域 380 | // Get the touch Region flag 381 | private int getTouchFlag(int x, int y) { 382 | for (int i = 0; i < mCnSvgBigRenderer.mRegionList.size(); i++) { 383 | if (mCnSvgBigRenderer.mRegionList.get(i).contains(x, y)) { 384 | return i; 385 | } 386 | } 387 | return -1; 388 | } 389 | 390 | // 存储已被高亮的区域信息,同一时刻只能有一个区域被高亮 391 | // Save which Region highlighted, only a Region can be highlighted at the same time 392 | private int saveHighlightIndex = -1; 393 | private int saveHighlightMode = -1; 394 | // 高亮模式:点击 / 长按 395 | // Highlight mode 396 | private static final int HIGHLIGHT_CLICK = 0; 397 | private static final int HIGHLIGHT_LONG_CLICK = 1; 398 | 399 | // 高亮某一区域 400 | // Highlight a Region 401 | private void highlightRegion(int index, int mode) { 402 | if (index == saveHighlightIndex && mode == saveHighlightMode) { 403 | return; 404 | } 405 | if (saveHighlightIndex != -1) { 406 | disHighlightRegion(saveHighlightIndex, saveHighlightMode); 407 | } 408 | CnMapConfig config = mCnMap.configMap.get(mCnMap.PROVINCE[index]); 409 | switch (mode) { 410 | case HIGHLIGHT_CLICK: 411 | config.setIfClick(true); 412 | break; 413 | case HIGHLIGHT_LONG_CLICK: 414 | config.setIfLongClick(true); 415 | break; 416 | } 417 | saveHighlightMode = mode; 418 | saveHighlightIndex = index; 419 | 420 | resetDrawable(); 421 | } 422 | 423 | // 取消高亮某一区域 424 | // Dishighlight a Region 425 | private void disHighlightRegion(int index, int mode) { 426 | CnMapConfig config = mCnMap.configMap.get(mCnMap.PROVINCE[index]); 427 | switch (mode) { 428 | case HIGHLIGHT_CLICK: 429 | config.setIfClick(false); 430 | break; 431 | case HIGHLIGHT_LONG_CLICK: 432 | config.setIfLongClick(false); 433 | break; 434 | } 435 | saveHighlightMode = -1; 436 | saveHighlightIndex = -1; 437 | 438 | resetDrawable(); 439 | } 440 | 441 | private class LongClickRunnable implements Runnable { 442 | 443 | // 应该被长按高亮的位置 444 | // Index of Region shouble be highlight 445 | private int index; 446 | // 延时一定时间后是否继续 447 | // A flag to check if continue generate long click event 448 | private boolean ifContinue; 449 | 450 | LongClickRunnable(int i) { 451 | index = i; 452 | ifContinue = true; 453 | } 454 | 455 | @Override 456 | public void run() { 457 | Looper.prepare(); 458 | try { 459 | Thread.sleep(1000); 460 | } catch (InterruptedException e) { 461 | e.printStackTrace(); 462 | return ; 463 | } 464 | if(ifContinue) { 465 | // 既然触发了长按,自然不能触发点击事件 466 | // Long click evnet can not be triggered with click event 467 | saveTouchFlag = -1; 468 | 469 | Message disColorMsg = new Message(); 470 | disColorMsg.what = 0; 471 | mLongClickHandler.sendMessage(disColorMsg); 472 | 473 | Message onLongClickMsg = new Message(); 474 | onLongClickMsg.what = 1; 475 | onLongClickMsg.arg1 = index; 476 | mLongClickHandler.sendMessage(onLongClickMsg); 477 | } 478 | } 479 | 480 | // 取消该长按事件 481 | // Cancel long click event 482 | void cancelLongClick() { 483 | ifContinue = false; 484 | } 485 | 486 | } 487 | 488 | private Handler mLongClickHandler = new Handler() { 489 | 490 | @Override 491 | public void handleMessage(Message msg) { 492 | switch (msg.what) { 493 | case 0: 494 | if(saveHighlightIndex != -1) { 495 | disHighlightRegion(saveHighlightIndex, saveHighlightMode); 496 | } 497 | break; 498 | case 1: 499 | mChinaMapViewProvinceListener.onProvinceLongClick(msg.arg1); 500 | highlightRegion(msg.arg1, HIGHLIGHT_LONG_CLICK); 501 | saveHighlightIndex = msg.arg1; 502 | break; 503 | } 504 | } 505 | 506 | }; 507 | 508 | // 缩放处理函数 509 | // Scale function 510 | private void onScale(float scaleFactor, float pivotX, float pivotY) { 511 | float scale = getScale(); 512 | // 控件图片的缩放范围 513 | // Check photo's scale range 514 | if ((scale < mMaxScale && scaleFactor > 1.0f) 515 | || (scale > mBaseScale && scaleFactor < 1.0f)) { 516 | if (scale * scaleFactor < mBaseScale) { 517 | scaleFactor = mBaseScale / scale; 518 | } 519 | if (scale * scaleFactor > mMaxScale) { 520 | scaleFactor = mMaxScale / scale; 521 | } 522 | mImageMatrix.postScale(scaleFactor, scaleFactor, 523 | pivotX, pivotY); 524 | setImageMatrix(mImageMatrix); 525 | } 526 | } 527 | 528 | // 获取当前缩放值 529 | // Get current scale rate 530 | private float getScale() { 531 | float[] values = new float[9]; 532 | mImageMatrix.getValues(values); 533 | return values[Matrix.MSCALE_X]; 534 | } 535 | 536 | // 1:1 情况下,图片实际边缘离开控件边缘的像素单位,用于针对过度平移的回移处理 537 | // The values below are, when photo's border can't be seen, how many 538 | // px has the photo moved, in 1:1 case 539 | // They used to handle over-moving photo, see invisibleCheck() function 540 | private final float RIGHT_OUT = 600; 541 | private final float LEFT_OUT = 300; 542 | private final float BOTTOM_OUT = 500; 543 | private final float TOP_OUT = 450; 544 | private final float VIEW_MEASURE_WIDTH = 672; 545 | private final float VIEW_MEASURE_HEIGHT = 750; 546 | private boolean ifLeftOut = false; 547 | private boolean ifRightOut = false; 548 | private boolean ifTopOut = false; 549 | private boolean ifBottomOut = false; 550 | 551 | // 回移处理函数 552 | // Move back function 553 | private void invisibleCheck() { 554 | RectF rect = getMatrixRectF(); 555 | float deltaX = 0; 556 | float deltaY = 0; 557 | if (rect.right / mCurrentScale < RIGHT_OUT) { 558 | ifRightOut = true; 559 | deltaX = RIGHT_OUT * mCurrentScale - rect.right; 560 | } 561 | if ((VIEW_MEASURE_WIDTH - rect.left) / mCurrentScale < VIEW_MEASURE_WIDTH - LEFT_OUT) { 562 | ifLeftOut = true; 563 | deltaX = (VIEW_MEASURE_WIDTH - LEFT_OUT) * mCurrentScale - VIEW_MEASURE_WIDTH + rect.left; 564 | deltaX = -deltaX; 565 | } 566 | if ((VIEW_MEASURE_HEIGHT - rect.top) / mCurrentScale < VIEW_MEASURE_HEIGHT - TOP_OUT) { 567 | ifTopOut = true; 568 | deltaY = (VIEW_MEASURE_HEIGHT - TOP_OUT) * mCurrentScale - VIEW_MEASURE_HEIGHT + rect.top; 569 | deltaY = -deltaY; 570 | } 571 | if (rect.bottom / mCurrentScale < BOTTOM_OUT) { 572 | ifBottomOut = true; 573 | deltaY = BOTTOM_OUT * mCurrentScale - rect.bottom; 574 | } 575 | mImageMatrix.postTranslate(deltaX, deltaY); 576 | setImageMatrix(mImageMatrix); 577 | } 578 | 579 | // 获得当前图片的宽和高 580 | // Get current photo's width and height 581 | private RectF getMatrixRectF() { 582 | Matrix matrix = mImageMatrix; 583 | RectF rectF = new RectF(); 584 | Drawable d = getDrawable(); 585 | if (d != null) { 586 | rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 587 | matrix.mapRect(rectF); 588 | } 589 | return rectF; 590 | } 591 | } 592 | -------------------------------------------------------------------------------- /library/src/main/java/com/sorashiro/chinamapinfoview/CnMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017, Sora Shiro (https://github.com/Sora-Shiro) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.sorashiro.chinamapinfoview; 16 | 17 | import java.util.HashMap; 18 | 19 | /** 20 | * Created by SoraShiro on 2017/8/7. 21 | *

22 | * Use CnMap.configMap.get(CnMap.PROVINCE[index]) to config different province. 23 | *

24 | * PROVINCE[i] 省/市/自治区/特别行政区 25 | * 0 安徽 26 | * 1 北京 27 | * 2 重庆 28 | * 3 福建 29 | * 4 甘肃 30 | * 5 广东 31 | * 6 广西 32 | * 7 贵州 33 | * 8 海南 34 | * 9 河北 35 | * 10 黑龙江 36 | * 11 河南 37 | * 12 香港 38 | * 13 湖北 39 | * 14 湖南 40 | * 15 江苏 41 | * 16 江西 42 | * 17 吉林 43 | * 18 辽宁 44 | * 19 澳门 45 | * 20 内蒙古 46 | * 21 宁夏 47 | * 22 青海 48 | * 23 陕西 49 | * 24 上海 50 | * 25 山东 51 | * 26 山西 52 | * 27 四川 53 | * 28 台湾 54 | * 29 天津 55 | * 30 新疆 56 | * 31 西藏 57 | * 32 云南 58 | * 33 浙江 59 | * 60 | * @author Sora Shiro 61 | * @since 2017/8/7 62 | */ 63 | 64 | public class CnMap { 65 | 66 | public String[] PROVINCE = {"Anhui", "Beijing", "Chongqing", "Fujian", "Gansu", "Guangdong", 67 | "Guangxi", "Guizhou", "Hainan", "Hebei", "Heilongjiang", "Henan", "Hong Kong", "Hubei", 68 | "Hunan", "Jiangsu", "Jiangxi", "Jilin", "Liaoning", "Macau", "Nei Mongol", "Ningxia", 69 | "Qinghai", "Shaanxi", "Shanghai", "Shandong", "Shanxi", "Sichuan", "Taiwan", "Tianjin", 70 | "Xinjiang", "Xizang", "Yunnan", "Zhejiang"}; 71 | 72 | public HashMap configMap; 73 | 74 | public CnMap() { 75 | configMap = new HashMap<>(); 76 | for (String s : PROVINCE) { 77 | configMap.put(s, new CnMapConfig()); 78 | } 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /library/src/main/java/com/sorashiro/chinamapinfoview/CnMapConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017, Sora Shiro (https://github.com/Sora-Shiro) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.sorashiro.chinamapinfoview; 16 | 17 | import android.graphics.Color; 18 | 19 | /** 20 | * Created by SoraShiro on 2017/8/7. 21 | *

22 | * Use CnMap.configMap.get(CnMap.PROVINCE[i]) to config different province. 23 | * 24 | * @author Sora Shiro 25 | * @since 2017/8/7 26 | */ 27 | 28 | public class CnMapConfig { 29 | 30 | // 文本 31 | private String mText; 32 | // 文本颜色 33 | private int mTextColor; 34 | // 点击文本颜色 35 | private int mClickTextColor; 36 | // 长按文本颜色 37 | private int mLongClickTextColor; 38 | // 文本大小 39 | private int mTextSize; 40 | // 设定文本是否与图片一起缩放 41 | private boolean mIfTextScale; 42 | // 填充颜色 43 | private int mFillColor; 44 | // 点击颜色 45 | private int mClickColor; 46 | // 点击边缘颜色 47 | private int mClickStrokeColor; 48 | // 长按颜色 49 | private int mLongClickColor; 50 | // 长按边缘颜色 51 | private int mLongClickStrokeColor; 52 | // 是否被点击 53 | private boolean mIfClick; 54 | // 是否被长按 55 | private boolean mIfLongClick; 56 | // 边缘颜色 57 | private int mStrokeColor; 58 | // 边缘宽度 59 | private int mStrokeWidth; 60 | 61 | public CnMapConfig() { 62 | this.mText = ""; 63 | this.mTextColor = Color.parseColor("#ee82ee"); 64 | this.mClickTextColor = Color.parseColor("#000000"); 65 | this.mLongClickTextColor = Color.parseColor("#000000"); 66 | this.mTextSize = 30; 67 | this.mIfTextScale = true; 68 | this.mFillColor = Color.parseColor("#66ccff"); 69 | this.mClickColor = Color.parseColor("#8EDBFF"); 70 | this.mClickStrokeColor = Color.parseColor("#ffffff"); 71 | this.mLongClickColor = Color.parseColor("#FEE38A"); 72 | this.mLongClickStrokeColor = Color.parseColor("#ffffff"); 73 | this.mIfClick = false; 74 | this.mIfLongClick = false; 75 | this.mStrokeColor = Color.parseColor("#000000"); 76 | this.mStrokeWidth = 3; 77 | } 78 | 79 | public String getText() { 80 | return mText; 81 | } 82 | 83 | public CnMapConfig setText(String text) { 84 | mText = text; 85 | return this; 86 | } 87 | 88 | public int getTextColor() { 89 | return mTextColor; 90 | } 91 | 92 | public CnMapConfig setTextColor(int color) { 93 | mTextColor = color; 94 | return this; 95 | } 96 | 97 | public int getClickTextColor() { 98 | return mClickTextColor; 99 | } 100 | 101 | public CnMapConfig setClickTextColor(int clickTextColor) { 102 | mClickTextColor = clickTextColor; 103 | return this; 104 | } 105 | 106 | public int getLongClickTextColor() { 107 | return mLongClickTextColor; 108 | } 109 | 110 | public CnMapConfig setLongClickTextColor(int longClickTextColor) { 111 | mLongClickTextColor = longClickTextColor; 112 | return this; 113 | } 114 | 115 | public boolean getIfTextScale() { 116 | return mIfTextScale; 117 | } 118 | 119 | public CnMapConfig setIfTextScale(boolean ifTextScale) { 120 | mIfTextScale = ifTextScale; 121 | return this; 122 | } 123 | 124 | public int getFillColor() { 125 | return mFillColor; 126 | } 127 | 128 | public CnMapConfig setFillColor(int fillColor) { 129 | mFillColor = fillColor; 130 | return this; 131 | } 132 | 133 | public int getClickColor() { 134 | return mClickColor; 135 | } 136 | 137 | public CnMapConfig setClickColor(int clickColor) { 138 | mClickColor = clickColor; 139 | return this; 140 | } 141 | 142 | public int getClickStrokeColor() { 143 | return mClickStrokeColor; 144 | } 145 | 146 | public CnMapConfig setClickStrokeColor(int clickStrokeColor) { 147 | mClickStrokeColor = clickStrokeColor; 148 | return this; 149 | } 150 | 151 | public int getLongClickColor() { 152 | return mLongClickColor; 153 | } 154 | 155 | public CnMapConfig setLongClickColor(int longClickColor) { 156 | mLongClickColor = longClickColor; 157 | return this; 158 | } 159 | 160 | public int getLongClickStrokeColor() { 161 | return mLongClickStrokeColor; 162 | } 163 | 164 | public CnMapConfig setLongClickStrokeColor(int longClickStrokeColor) { 165 | mLongClickStrokeColor = longClickStrokeColor; 166 | return this; 167 | } 168 | 169 | protected boolean getIfClick() { 170 | return mIfClick; 171 | } 172 | 173 | protected CnMapConfig setIfClick(boolean ifClick) { 174 | mIfClick = ifClick; 175 | return this; 176 | } 177 | 178 | protected boolean getIfLongClick() { 179 | return mIfLongClick; 180 | } 181 | 182 | protected CnMapConfig setIfLongClick(boolean ifLongClick) { 183 | mIfLongClick = ifLongClick; 184 | return this; 185 | } 186 | 187 | public int getStrokeColor() { 188 | return mStrokeColor; 189 | } 190 | 191 | public CnMapConfig setStrokeColor(int strokeColor) { 192 | mStrokeColor = strokeColor; 193 | return this; 194 | } 195 | 196 | public int getStrokeWidth() { 197 | return mStrokeWidth; 198 | } 199 | 200 | public CnMapConfig setStrokeWidth(int strokeWidth) { 201 | mStrokeWidth = strokeWidth; 202 | return this; 203 | } 204 | 205 | public int getTextSize() { 206 | return mTextSize; 207 | } 208 | 209 | public CnMapConfig setTextSize(int textSize) { 210 | mTextSize = textSize; 211 | return this; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ChinaMapInfoView 3 | 4 | -------------------------------------------------------------------------------- /library/src/test/java/com/sorashiro/chinamapinfoview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sorashiro.chinamapinfoview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | // ButterKnife 4 | apply plugin: 'com.jakewharton.butterknife' 5 | // SVG-Android 6 | apply plugin: 'svg' 7 | 8 | android { 9 | compileSdkVersion 25 10 | buildToolsVersion "25.0.3" 11 | defaultConfig { 12 | applicationId "com.sorashiro.chinamapinformation" 13 | minSdkVersion 14 14 | targetSdkVersion 25 15 | versionCode 1 16 | versionName "1.0" 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled true 22 | shrinkResources true 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | 29 | svg { 30 | // 下面四个参数是vector文件生成java渲染类文件的相关配置 31 | // vector资源文件目录,支持多个目录 32 | vectorDirs = ["src/main/svg_debug/drawable"] 33 | // shape资源文件目录 34 | shapeDir = "src/main/svg_release/drawable" 35 | // 生成java渲染类的存放包目录 36 | javaDir = "src/main/java/com/github/megatronking/svg/sample/drawables" 37 | 38 | // 下面参数是svg格式文件生成vector资源文件的配置 39 | svg2vector { 40 | // 这里的test可以随意命名,建议使用文件夹名 41 | test { 42 | // svg格式文件目录 43 | svgDir = "${rootDir}/svg" 44 | // 生成vector资源文件的存放目录 45 | vectorDir = "src/main/svg_debug/drawable" 46 | // 宽高参数指定生成vector文件的宽高,如果省略将默认使用图片原始宽高 47 | // 注意此处宽高比要与图片宽高比一致,否则图片会变形 48 | height = 1141 49 | width = 1369 50 | } 51 | } 52 | } 53 | 54 | dependencies { 55 | compile fileTree(dir: 'libs', include: ['*.jar']) 56 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 57 | exclude group: 'com.android.support', module: 'support-annotations' 58 | }) 59 | compile 'com.android.support:appcompat-v7:25.3.1' 60 | testCompile 'junit:junit:4.12' 61 | 62 | // Library 63 | // compile project(':library') 64 | // ChinaMapInfoView 65 | compile 'com.sorashiro.ChinaMapInfoView:library:1.0.2' 66 | 67 | // ButterKnife 68 | compile 'com.jakewharton:butterknife:8.5.1' 69 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 70 | // SVG-Android 71 | compile project(':library') 72 | } 73 | -------------------------------------------------------------------------------- /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 D:\WORK\Adt5.0-bundle\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/sorashiro/chinamapinformation/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sorashiro.chinamapinformation; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sorashiro.chinamapinformation", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sorashiro/chinamapinformation/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sorashiro.chinamapinformation; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | import com.sorashiro.chinamapinformation.tool.LogAndToastUtil; 8 | import com.sorashiro.chinamapinfoview.ChinaMapInfoView; 9 | import com.sorashiro.chinamapinfoview.CnMap; 10 | import com.sorashiro.chinamapinfoview.CnMapConfig; 11 | 12 | import java.util.HashMap; 13 | 14 | import butterknife.BindView; 15 | import butterknife.ButterKnife; 16 | 17 | public class MainActivity extends AppCompatActivity implements ChinaMapInfoView.ChinaMapViewProvinceListener { 18 | 19 | @BindView(R2.id.imgCnMap) 20 | ChinaMapInfoView imgCnMap; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | 27 | ButterKnife.bind(this); 28 | 29 | initView(); 30 | } 31 | 32 | private void initView() { 33 | imgCnMap.setChinaMapViewProvinceListener(this); 34 | // first get the config map 35 | // 首先获取总设置 map 36 | CnMap cnMap = imgCnMap.getCnMap(); 37 | HashMap cnConfigMap = cnMap.configMap; 38 | // or cnMap.configMap.get("Anhui"); but use cnMap.PROVINCE[0] better 39 | // 或者 cnMap.configMap.get("Anhui"); 但是用 cnMap.PROVINCE[0] 更好些 40 | CnMapConfig configAnhui = cnConfigMap.get(cnMap.PROVINCE[0]); 41 | // support method chaining 42 | // 支持链式调用(方法链) 43 | // configAnhui 44 | // .setFillColor(Color.parseColor("#ee0000")) 45 | // .setClickColor(Color.parseColor("#99ffff")); 46 | } 47 | 48 | @Override 49 | public void onProvinceClick(int i) { 50 | LogAndToastUtil.ToastOut(this, imgCnMap.getCnMap().PROVINCE[i] + " is clicked"); 51 | } 52 | 53 | @Override 54 | public void onProvinceLongClick(int i) { 55 | LogAndToastUtil.ToastOut(this, imgCnMap.getCnMap().PROVINCE[i] + " is long clicked"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sample/src/main/java/com/sorashiro/chinamapinformation/tool/LogAndToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.sorashiro.chinamapinformation.tool; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.widget.Toast; 6 | 7 | /** 8 | * @author Sora 9 | * @since 2016/11/5 10 | *

11 | * A util class about debug and toast. 12 | * Too lazy to produce this class. 13 | * 跟调试和显示Toast相关的工具类,懒到一定境界的产物。 14 | */ 15 | 16 | public class LogAndToastUtil { 17 | 18 | private static final boolean IF_PRINT = true; 19 | 20 | public static void LogV(String s) { 21 | if (IF_PRINT) { 22 | Log.v("aaa", s); 23 | } 24 | } 25 | 26 | private static Toast toast; 27 | 28 | public static void ToastOut(Context context, String s) { 29 | if (toast == null) { 30 | toast = Toast.makeText( 31 | context, 32 | s, 33 | Toast.LENGTH_SHORT); 34 | } else { 35 | toast.setText(s); 36 | } 37 | toast.show(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sora-Shiro/ChinaMapInfoView/88997de7cb7cbd96c0012edb86aef3fd2a5bbaf1/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ChinaMapInformation 3 | 4 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/test/java/com/sorashiro/chinamapinformation/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sorashiro.chinamapinformation; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':library' 2 | -------------------------------------------------------------------------------- /svg/cnsvgbig.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 19 | 23 | 33 | 45 | 74 | 88 | 102 | 115 | 119 | 136 | 159 | 171 | 173 | 187 | 201 | 211 | 224 | 240 | 252 | 253 | 298 | 306 | 330 | 347 | 350 | 363 | 373 | 394 | 399 | 402 | 436 | 468 | 492 | 501 | 502 | --------------------------------------------------------------------------------