├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hotbitmapgg │ │ └── nineimagesample │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hotbitmapgg │ │ │ └── nineimagesample │ │ │ ├── ImageDetailsActivity.java │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ ├── activity_image_details.xml │ │ └── activity_main.xml │ │ ├── menu │ │ └── menu_number.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hotbitmapgg │ └── nineimagesample │ └── ExampleUnitTest.java ├── art ├── pic1.png ├── pic2.png └── pic3.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── nineimagelibrary ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hotbitmapgg │ │ └── nineimagelibrary │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hotbitmapgg │ │ │ └── nineimagelibrary │ │ │ ├── DisplayUtil.java │ │ │ └── NineImageView.java │ └── res │ │ ├── drawable │ │ └── ic_placeholder_image.png │ │ └── values │ │ ├── nine_image_style.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── hotbitmapgg │ └── nineimagelibrary │ └── ExampleUnitTest.java └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.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 | .idea/compiler.xml 11 | .idea/copyright/profiles_settings.xml 12 | .idea/gradle.xml 13 | .idea/misc.xml 14 | .idea/modules.xml 15 | .idea/runConfigurations.xml 16 | .idea/workspace.xml___jb_tmp___ 17 | .idea/vcs.xml 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | 3 | Everyone is permitted to copy and distribute verbatim or modified 4 | copies of this license document, and changing it is allowed as long 5 | as the name is changed. 6 | 7 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 8 | 9 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 10 | 11 | 0. You just DO WHAT THE FUCK YOU WANT TO. 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NineImageView 2 | 3 | [![](https://jitpack.io/v/HotBitmapGG/NineImageView.svg)](https://jitpack.io/#HotBitmapGG/NineImageView) 4 | 5 | A simple use of nine pictures show the control 6 | 7 | ## Screenshots 8 | 9 | 10 | 11 | 12 | ## Import 13 | 14 | ```java 15 | 16 | Step 1. Add the JitPack repository to your build file 17 | Add it in your root build.gradle at the end of repositories: 18 | 19 | allprojects { 20 | repositories { 21 | ... 22 | maven { url "https://jitpack.io" } 23 | } 24 | } 25 | 26 | 27 | 28 | Step 2. Add the dependency 29 | 30 | dependencies { 31 | compile 'com.github.HotBitmapGG:NineImageView:V1.1' 32 | } 33 | 34 | ``` 35 | 36 | ## Directions 37 | 38 | * custom properties 39 | 40 | name | format | instructions 41 | -----|------|---- 42 | nineImageHorizontalSpace | dimension | Horizontal Space 43 | nineImageVerticalSpace | dimension | Vertical Space 44 | nineImageRadius | dimension | View Radius 45 | nineImageRatio | float | View Ratio 46 | nineImageColumns | integer | View Columns Number 47 | 48 | ## Usage 49 | 50 | * 1.In the XML 51 | 52 | ```java 53 | 54 | 62 | 63 | ``` 64 | 65 | * 2.In the code 66 | 67 | ```java 68 | 69 | NineImageView nineImageView = (NineImageView) findViewById(R.id.multi_image); 70 | //set the picture url set 71 | nineImageView.setImageUrls(imageUrls); 72 | nineImageView.setOnClickItemListener(new NineImageView.OnClickItemListener() { 73 | @Override 74 | public void onClick(int i, ArrayList url) { 75 | //set the picture click event,then do you have to do operation 76 | } 77 | }); 78 | 79 | ``` 80 | 81 | ## instructions 82 | 83 | The control of the image to load depends on the glide load library if you need to use other image You can directly copy under the lib files to your project, the framework for the image to replace it. 84 | 85 | 86 | ## About me 87 | 88 | [![Wercker](https://img.shields.io/badge/weibo-HotBitmapGG-blue.svg)](http://weibo.com/3223089177/profile?topnav=1&wvr=6&is_all=1) 89 | 90 | An android developer in Wuhan. 91 | 92 | If you want to make friends with me, You can focus on my weibo. 93 | 94 | 95 | ## License 96 | 97 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 98 | 99 | Everyone is permitted to copy and distribute verbatim or modified 100 | copies of this license document, and changing it is allowed as long 101 | as the name is changed. 102 | 103 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 104 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 105 | 106 | You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.hotbitmapgg.nineimagesample" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.1.0' 28 | compile 'com.android.support:design:25.1.0' 29 | testCompile 'junit:junit:4.12' 30 | compile project(':nineimagelibrary') 31 | } 32 | -------------------------------------------------------------------------------- /app/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:\Android\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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hotbitmapgg/nineimagesample/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagesample; 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.hotbitmapgg.nineimagesample", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/hotbitmapgg/nineimagesample/ImageDetailsActivity.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagesample; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.View; 8 | import android.widget.ImageView; 9 | 10 | import com.bumptech.glide.Glide; 11 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 12 | import com.bumptech.glide.load.resource.drawable.GlideDrawable; 13 | import com.bumptech.glide.request.RequestListener; 14 | import com.bumptech.glide.request.target.Target; 15 | 16 | /** 17 | * Created by cnsunrun on 2017/7/7. 18 | */ 19 | 20 | public class ImageDetailsActivity extends AppCompatActivity { 21 | 22 | private ImageView imageView; 23 | 24 | @Override 25 | protected void onCreate(@Nullable Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_image_details); 28 | String image = getIntent().getStringExtra("image"); 29 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 30 | toolbar.setTitle("详情"); 31 | setSupportActionBar(toolbar); 32 | imageView = (ImageView) findViewById(R.id.image); 33 | Glide.with(this).load(image) 34 | .diskCacheStrategy(DiskCacheStrategy.ALL) 35 | .crossFade(0) 36 | .listener(new RequestListener() { 37 | @Override 38 | public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { 39 | return false; 40 | } 41 | 42 | @Override 43 | public boolean onResourceReady(GlideDrawable resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { 44 | imageView.setImageDrawable(resource); 45 | setImageOnClick(); 46 | return false; 47 | } 48 | }) 49 | .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); 50 | } 51 | 52 | private void setImageOnClick() { 53 | imageView.setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | onBackPressed(); 57 | } 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/hotbitmapgg/nineimagesample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagesample; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | 10 | import com.hotbitmapgg.nineimagelibrary.NineImageView; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | private List imageUrls = Arrays.asList( 19 | "https://ws1.sinaimg.cn/large/610dc034ly1fgllsthvu1j20u011in1p.jpg", 20 | "https://ws1.sinaimg.cn/large/610dc034ly1fgj7jho031j20u011itci.jpg", 21 | "https://ws1.sinaimg.cn/large/610dc034ly1fgi3vd6irmj20u011i439.jpg", 22 | "https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg", 23 | "https://ws1.sinaimg.cn/large/610dc034ly1fgdmpxi7erj20qy0qyjtr.jpg", 24 | "https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg", 25 | "https://ws1.sinaimg.cn/large/610dc034ly1fgbbp94y9zj20u011idkf.jpg", 26 | "https://ws1.sinaimg.cn/large/610dc034ly1fga6auw8ycj20u00u00uw.jpg", 27 | "https://ws1.sinaimg.cn/large/d23c7564ly1fg7ow5jtl9j20pb0pb4gw.jpg"); 28 | private NineImageView nineImageView; 29 | 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_main); 35 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 36 | toolbar.setTitle(getResources().getString(R.string.app_name)); 37 | setSupportActionBar(toolbar); 38 | nineImageView = (NineImageView) findViewById(R.id.multi_image); 39 | //set the picture url set 40 | nineImageView.setImageUrls(imageUrls); 41 | nineImageView.setOnClickItemListener(new NineImageView.OnClickItemListener() { 42 | @Override 43 | public void onClick(int i, ArrayList url) { 44 | //set the picture click event,then do you have to do operation 45 | Intent intent = new Intent(MainActivity.this, ImageDetailsActivity.class); 46 | intent.putExtra("image", url.get(i)); 47 | startActivity(intent); 48 | } 49 | }); 50 | } 51 | 52 | @Override 53 | public boolean onCreateOptionsMenu(Menu menu) { 54 | getMenuInflater().inflate(R.menu.menu_number, menu); 55 | return true; 56 | } 57 | 58 | @Override 59 | public boolean onOptionsItemSelected(MenuItem item) { 60 | switch (item.getItemId()) { 61 | case R.id.menu_number_1: 62 | nineImageView.setImageUrls(imageUrls.subList(0, 1)); 63 | break; 64 | case R.id.menu_number_2: 65 | nineImageView.setImageUrls(imageUrls.subList(0, 2)); 66 | break; 67 | case R.id.menu_number_3: 68 | nineImageView.setImageUrls(imageUrls.subList(0, 3)); 69 | break; 70 | case R.id.menu_number_4: 71 | nineImageView.setImageUrls(imageUrls.subList(0, 4)); 72 | break; 73 | case R.id.menu_number_5: 74 | nineImageView.setImageUrls(imageUrls.subList(0, 5)); 75 | break; 76 | case R.id.menu_number_6: 77 | nineImageView.setImageUrls(imageUrls.subList(0, 6)); 78 | break; 79 | case R.id.menu_number_7: 80 | nineImageView.setImageUrls(imageUrls.subList(0, 7)); 81 | break; 82 | case R.id.menu_number_8: 83 | nineImageView.setImageUrls(imageUrls.subList(0, 8)); 84 | break; 85 | case R.id.menu_number_9: 86 | nineImageView.setImageUrls(imageUrls.subList(0, 9)); 87 | break; 88 | } 89 | return super.onOptionsItemSelected(item); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_image_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 24 | 25 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 24 | 25 | 26 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_number.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | 34 | 35 | 40 | 41 | 46 | 47 | 52 | 53 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NineImageSample 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/hotbitmapgg/nineimagesample/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagesample; 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 | } -------------------------------------------------------------------------------- /art/pic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/art/pic1.png -------------------------------------------------------------------------------- /art/pic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/art/pic2.png -------------------------------------------------------------------------------- /art/pic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/art/pic3.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /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/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 28 14:30:57 CST 2017 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-3.3-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 | -------------------------------------------------------------------------------- /nineimagelibrary/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /nineimagelibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 27 | exclude group: 'com.android.support', module: 'support-annotations' 28 | }) 29 | compile 'com.android.support:appcompat-v7:25.1.0' 30 | testCompile 'junit:junit:4.12' 31 | compile 'com.github.bumptech.glide:glide:3.7.0' 32 | } 33 | -------------------------------------------------------------------------------- /nineimagelibrary/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:\Android\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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /nineimagelibrary/src/androidTest/java/com/hotbitmapgg/nineimagelibrary/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagelibrary; 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.hotbitmapgg.nineimagelibrary.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nineimagelibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /nineimagelibrary/src/main/java/com/hotbitmapgg/nineimagelibrary/DisplayUtil.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagelibrary; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.util.DisplayMetrics; 6 | import android.util.TypedValue; 7 | import android.view.Display; 8 | import android.view.WindowManager; 9 | 10 | import java.lang.reflect.Field; 11 | 12 | import static android.util.Log.w; 13 | 14 | /** 15 | * Created by hcc on 17/06/28. 16 | *

17 | * Utils for getting display metrics such as screen size, dp, sp and px 18 | */ 19 | public class DisplayUtil { 20 | 21 | private static final String DEBUG_TAG = DisplayUtil.class.getSimpleName(); 22 | 23 | private static Context appContext; 24 | 25 | private static int screenWidth; 26 | private static int screenHeight; 27 | 28 | private static int statusBarHeight; 29 | 30 | private DisplayUtil() { 31 | } 32 | 33 | /** 34 | * If want to use the more convenient method like {@link #getScreenWidth()}, 35 | * {@link #getScreenHeight()} and {@link #getStatusBarHeight()}, should call this 36 | * with application context before. it's good to initialize in the application's 37 | * onCreate call back. 38 | * 39 | * @param appContext must be application context for best practise 40 | */ 41 | public synchronized static void init(Context appContext) { 42 | DisplayUtil.appContext = appContext; 43 | int[] size = getScreenSize(appContext); 44 | screenWidth = size[0]; 45 | screenHeight = size[1]; 46 | statusBarHeight = getStatusBarHeight(appContext); 47 | } 48 | 49 | /** 50 | * This method is guaranteed to get the proper screen width and height. 51 | * 52 | * @param context any activity or application context 53 | * @return the size with width and height pixels of screen 54 | */ 55 | public synchronized static int[] getScreenSize(Context context) { 56 | final String name = Context.WINDOW_SERVICE; 57 | final Object service = context.getSystemService(name); 58 | final WindowManager wm = (WindowManager) service; 59 | final Display display = wm.getDefaultDisplay(); 60 | final DisplayMetrics dm = new DisplayMetrics(); 61 | display.getMetrics(dm); 62 | screenWidth = dm.widthPixels; 63 | screenHeight = dm.heightPixels; 64 | int[] size = new int[2]; 65 | size[0] = screenWidth; 66 | size[1] = screenHeight; 67 | // Log.d("screenWidth of size[0]", String.valueOf(size[0])); 68 | // Log.d("screenHeight of size[1]", String.valueOf(size[1])); 69 | return size; 70 | } 71 | 72 | private static final String DIMEN_CLASS_NAME = "com.android.internal.R$dimen"; 73 | private static final String SB_HEIGHT_FIELD_NAME = "status_bar_height"; 74 | 75 | /** 76 | * Get the proper system status bar's height using reflection. 77 | * 78 | * @param context any activity or application context 79 | * @return the height in pixel of system status bar 80 | */ 81 | public synchronized static int getStatusBarHeight(Context context) { 82 | try { 83 | final Class theClass = Class.forName(DIMEN_CLASS_NAME); 84 | final Object classObj = theClass.newInstance(); 85 | final Field field = theClass.getField(SB_HEIGHT_FIELD_NAME); 86 | final Object idObj = field.get(classObj); 87 | final String idStr = idObj.toString(); 88 | final int id = Integer.parseInt(idStr); 89 | final Resources resources = context.getResources(); 90 | statusBarHeight = resources.getDimensionPixelSize(id); 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | } 94 | // Log.d("statusBarHeight", String.valueOf(statusBarHeight)); 95 | return statusBarHeight; 96 | } 97 | 98 | /** 99 | * Get the user device's screen width, the correctness is not guaranteed, 100 | * because it's depend on the calling sequence. 101 | * WARNING: Should calling {@link #init(Context)} or {@link #getScreenSize(Context)} before, 102 | * or you may get 0 as result. 103 | * 104 | * @return the pixel width of screen, return 0 if the call order is wrong 105 | */ 106 | public synchronized static int getScreenWidth() { 107 | if (screenWidth == 0 && appContext != null) { 108 | int[] size = getScreenSize(appContext); 109 | screenWidth = size[0]; 110 | screenHeight = size[1]; 111 | } 112 | return screenWidth; 113 | } 114 | 115 | /** 116 | * Get the user device's screen height, the correctness is not guaranteed, 117 | * because it's depend on the calling sequence. 118 | * WARNING: Should calling {@link #init(Context)} or {@link #getScreenSize(Context)} before, 119 | * or you may get 0 as result. 120 | * 121 | * @return the pixel height of screen, return 0 if the call order is wrong 122 | */ 123 | public synchronized static int getScreenHeight() { 124 | if (screenHeight == 0 && appContext != null) { 125 | int[] size = getScreenSize(appContext); 126 | screenWidth = size[0]; 127 | screenHeight = size[1]; 128 | } 129 | return screenHeight; 130 | } 131 | 132 | public synchronized static float getScreenRate() { 133 | if ((screenWidth == 0 || screenHeight == 0) && appContext != null) { 134 | int[] size = getScreenSize(appContext); 135 | screenWidth = size[0]; 136 | screenHeight = size[1]; 137 | } 138 | return (float) screenHeight / (float) screenWidth; 139 | } 140 | 141 | /** 142 | * Get the system status bar's height, the correctness is not guaranteed, 143 | * because it's depend on the calling sequence. 144 | * WARNING: Should calling {@link #init(Context)} or {@link #getStatusBarHeight(Context)} before, 145 | * or you may get 0 as result. 146 | * 147 | * @return the pixel height of system status bar, return 0 if the call order is wrong 148 | */ 149 | public synchronized static int getStatusBarHeight() { 150 | if (statusBarHeight == 0 && appContext != null) { 151 | statusBarHeight = getStatusBarHeight(appContext); 152 | } 153 | return statusBarHeight; 154 | } 155 | 156 | 157 | private static final String WARNING_MSG0 = "Should call init(Context appContext) before this!"; 158 | private static final String WARNING_MSG1 = "The result from this method is WRONG basically!"; 159 | 160 | /** 161 | * Another way to convert dp(density-independent pixels) unit to equivalent px(pixels), 162 | * depending on device {@link DisplayMetrics}, using {@link TypedValue}. 163 | * This method is more recommended compared to {@link #convertDpToPx(float, Context)}. 164 | * 165 | * @param dp value in dp unit which will be converted into px 166 | * @param context context to get resources and device specific display metrics 167 | * @return a float value to represent px equivalent to dp 168 | */ 169 | public synchronized static float dpToPx(float dp, Context context) { 170 | final Resources resources = context.getResources(); 171 | final DisplayMetrics metrics = resources.getDisplayMetrics(); 172 | final int unit = TypedValue.COMPLEX_UNIT_DIP; 173 | return TypedValue.applyDimension(unit, dp, metrics); 174 | } 175 | 176 | /** 177 | * Calling this instead of {@link #dpToPx(float, Context)} for convenience, ONLY 178 | * IF you have {@link #init(Context)} with the application context before. 179 | * WARNING: Will return the original value passed in by @param dp when calling 180 | * sequence is wrong! 181 | * 182 | * @param dp value in dp unit which will be converted into px 183 | * @return a float value to represent px equivalent to dp 184 | */ 185 | public synchronized static float dpToPx(float dp) { 186 | if (appContext != null) { 187 | return dpToPx(dp, appContext); 188 | } else { 189 | w(DEBUG_TAG + ".dpToPx()", WARNING_MSG0); 190 | w(DEBUG_TAG + ".dpToPx()", WARNING_MSG1); 191 | return dp; 192 | } 193 | } 194 | 195 | /** 196 | * Convert dp(density-independent pixels) unit to equivalent px(pixels), depending on 197 | * device densityDpi of {@link DisplayMetrics}. 198 | * 199 | * @param dp value in dp unit which will be converted into px 200 | * @param context context to get resources and device specific display metrics 201 | * @return a float value to represent px equivalent to dp 202 | */ 203 | public synchronized static float convertDpToPx(float dp, Context context) { 204 | Resources resources = context.getResources(); 205 | DisplayMetrics metrics = resources.getDisplayMetrics(); 206 | final int densityDpi = metrics.densityDpi; 207 | return dp * (densityDpi / 160f); 208 | } 209 | 210 | /** 211 | * Calling this instead of {@link #convertDpToPx(float, Context)} for convenience, 212 | * ONLY IF you have {@link #init(Context)} with the application context before. 213 | * WARNING: Will return the original value passed in by @param dp when calling 214 | * sequence is wrong! 215 | * 216 | * @param dp value in dp unit which will be converted into px 217 | * @return a float value to represent px equivalent to dp 218 | */ 219 | public synchronized static float convertDpToPx(float dp) { 220 | if (appContext != null) { 221 | return convertDpToPx(dp, appContext); 222 | } else { 223 | w(DEBUG_TAG + ".convertDpToPixel()", WARNING_MSG0); 224 | w(DEBUG_TAG + ".convertDpToPixel()", WARNING_MSG1); 225 | return dp; 226 | } 227 | } 228 | 229 | /** 230 | * Convert px(pixels) unit to equivalent dp(density-independent pixels), depending on 231 | * device densityDpi of {@link DisplayMetrics}. 232 | * 233 | * @param px value in px unit which will be converted into dp 234 | * @param context context to get resources and device specific display metrics 235 | * @return a float value to represent dp equivalent to px 236 | */ 237 | public synchronized static float convertPxToDp(float px, Context context) { 238 | Resources resources = context.getResources(); 239 | DisplayMetrics metrics = resources.getDisplayMetrics(); 240 | final int densityDpi = metrics.densityDpi; 241 | return px / (densityDpi / 160f); 242 | } 243 | 244 | /** 245 | * Calling this instead of {@link #convertPxToDp(float, Context)} for convenience, 246 | * ONLY IF you have {@link #init(Context)} with the application context before. 247 | * WARNING: Will return the original value passed in by @param px when calling 248 | * sequence is wrong! 249 | * 250 | * @param px value in px unit which will be converted into dp 251 | * @return a float value to represent dp equivalent to px 252 | */ 253 | public synchronized static float convertPxToDp(float px) { 254 | if (appContext != null) { 255 | return convertPxToDp(px, appContext); 256 | } else { 257 | w(DEBUG_TAG + ".convertPixelsToDp()", WARNING_MSG0); 258 | w(DEBUG_TAG + ".convertPixelsToDp()", WARNING_MSG1); 259 | return px; 260 | } 261 | } 262 | 263 | /** 264 | * Convert sp(scale-independent pixels) unit to equivalent px(pixels), depending on 265 | * device scaledDensity of {@link DisplayMetrics}. 266 | * 267 | * @param sp value in sp unit which will be converted into px 268 | * @param context context to get resources and device specific display metrics 269 | * @return a float value to represent px equivalent to sp 270 | */ 271 | public synchronized static float convertSpToPx(float sp, Context context) { 272 | final Resources resources = context.getResources(); 273 | final DisplayMetrics metrics = resources.getDisplayMetrics(); 274 | final float fontScale = metrics.scaledDensity; 275 | return sp * fontScale; 276 | } 277 | 278 | /** 279 | * Calling this instead of {@link #convertSpToPx(float, Context)} for convenience, 280 | * ONLY IF you have {@link #init(Context)} with the application context before. 281 | * WARNING: Will return the original value passed in by @param px when calling 282 | * sequence is wrong! 283 | * 284 | * @param sp value in sp unit which will be converted into px 285 | * @return a float value to represent px equivalent to sp 286 | */ 287 | public synchronized static float convertSpToPx(float sp) { 288 | if (appContext != null) { 289 | return convertSpToPx(sp, appContext); 290 | } else { 291 | w(DEBUG_TAG + ".spToPx()", WARNING_MSG0); 292 | w(DEBUG_TAG + ".spToPx()", WARNING_MSG1); 293 | return sp; 294 | } 295 | } 296 | 297 | /** 298 | * Convert px(pixels) unit to equivalent sp(scale-independent pixels), depending on 299 | * device scaledDensity of {@link DisplayMetrics}. 300 | * 301 | * @param px value in px unit which will be converted into sp 302 | * @param context context to get resources and device specific display metrics 303 | * @return a float value to represent sp equivalent to px 304 | */ 305 | public synchronized static float convertPxToSp(float px, Context context) { 306 | final Resources resources = context.getResources(); 307 | final DisplayMetrics metrics = resources.getDisplayMetrics(); 308 | final float fontScale = metrics.scaledDensity; 309 | return px / fontScale; 310 | } 311 | 312 | /** 313 | * Calling this instead of {@link #convertPxToSp(float, Context)} for convenience, 314 | * ONLY IF you have {@link #init(Context)} with the application context before. 315 | * WARNING: Will return the original value passed in by @param px when calling 316 | * sequence is wrong! 317 | * 318 | * @param px value in px unit which will be converted into sp 319 | * @return a float value to represent sp equivalent to px 320 | */ 321 | public synchronized static float convertPxToSp(float px) { 322 | if (appContext != null) { 323 | return convertPxToSp(px, appContext); 324 | } else { 325 | w(DEBUG_TAG + ".pxToSp()", WARNING_MSG0); 326 | w(DEBUG_TAG + ".pxToSp()", WARNING_MSG1); 327 | return px; 328 | } 329 | } 330 | 331 | } 332 | -------------------------------------------------------------------------------- /nineimagelibrary/src/main/java/com/hotbitmapgg/nineimagelibrary/NineImageView.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagelibrary; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.graphics.BitmapShader; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Matrix; 11 | import android.graphics.Paint; 12 | import android.graphics.Rect; 13 | import android.graphics.RectF; 14 | import android.graphics.Shader; 15 | import android.util.AttributeSet; 16 | import android.view.MotionEvent; 17 | import android.view.View; 18 | import android.view.ViewConfiguration; 19 | 20 | import com.bumptech.glide.Glide; 21 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 22 | import com.bumptech.glide.request.animation.GlideAnimation; 23 | import com.bumptech.glide.request.target.SimpleTarget; 24 | import com.bumptech.glide.request.target.Target; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.Collection; 29 | import java.util.Collections; 30 | 31 | 32 | /** 33 | * Created by hcc on 17/06/28. 34 | *

35 | * A nine pictures controls 36 | */ 37 | public class NineImageView extends View { 38 | 39 | private String[] mImageUrls; 40 | private Bitmap[] mBitmaps; 41 | private Bitmap mPlaceHolder; 42 | private int mHorizontalSpace = 10; 43 | private int mVerticalSpace = 10; 44 | private int mRadius = 0; 45 | private int mImageWidth = 0; 46 | private int mImageHeight = 0; 47 | private float mRatio = 1f; 48 | private int mColumns; 49 | private int mRows; 50 | private Matrix matrix = new Matrix(); 51 | private final Paint mPaint = new Paint(); 52 | private final Paint mTextPaint = new Paint(); 53 | private final Paint mTextBgPaint = new Paint(); 54 | private OnClickItemListener onClickItemListener; 55 | private MotionEvent mEventDown; 56 | private int mDown; 57 | private RectF[] mDrawRects; 58 | private boolean haveRule; 59 | private Target[] mTargets = new Target[0]; 60 | 61 | 62 | public NineImageView(Context context) { 63 | this(context, null); 64 | } 65 | 66 | public NineImageView(Context context, AttributeSet attrs) { 67 | this(context, attrs, 0); 68 | } 69 | 70 | public NineImageView(Context context, AttributeSet attrs, int defStyleAttr) { 71 | super(context, attrs, defStyleAttr); 72 | if (attrs != null) { 73 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NineImageView); 74 | try { 75 | for (int i = 0; i < a.getIndexCount(); i++) { 76 | int attr = a.getIndex(i); 77 | if (attr == R.styleable.NineImageView_nineImageHorizontalSpace) { 78 | mHorizontalSpace = a.getDimensionPixelSize(attr, mHorizontalSpace); 79 | 80 | } else if (attr == R.styleable.NineImageView_nineImageVerticalSpace) { 81 | mVerticalSpace = a.getDimensionPixelSize(attr, mVerticalSpace); 82 | 83 | } else if (attr == R.styleable.NineImageView_nineImageRadius) { 84 | mRadius = a.getDimensionPixelSize(attr, mRadius); 85 | 86 | } else if (attr == R.styleable.NineImageView_nineImageRatio) { 87 | mRatio = a.getFloat(attr, mRatio); 88 | 89 | } 90 | } 91 | } finally { 92 | a.recycle(); 93 | } 94 | } 95 | 96 | mPaint.setAntiAlias(true); 97 | mTextPaint.setAntiAlias(true); 98 | mPlaceHolder = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_placeholder_image); 99 | 100 | setImageUrls(null); 101 | } 102 | 103 | @Override 104 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 105 | super.onLayout(changed, left, top, right, bottom); 106 | for (int i = 0; i < mImageUrls.length; i++) { 107 | loadBitmap(i, mImageUrls[i]); 108 | } 109 | } 110 | 111 | @Override 112 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 113 | if (mImageUrls.length != 0) { 114 | int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); 115 | mImageWidth = (width - (mColumns - 1) * mHorizontalSpace - getPaddingLeft() - getPaddingRight()) / mColumns; 116 | mImageHeight = (int) (mImageWidth * mRatio); 117 | mRows = (int) Math.ceil(mImageUrls.length * 1f / mColumns); 118 | int height = mImageHeight * mRows + (mRows - 1) * mVerticalSpace + getPaddingTop() + getPaddingBottom(); 119 | setMeasuredDimension(width, height); 120 | } else { 121 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 122 | } 123 | } 124 | 125 | 126 | @Override 127 | protected void onDraw(Canvas canvas) { 128 | super.onDraw(canvas); 129 | if (mBitmaps.length != 0) { 130 | for (int row = 0; row < mRows; row++) { 131 | for (int column = 0; column < mColumns; column++) { 132 | int i = row * mColumns + column; 133 | boolean isGif; 134 | if (i >= mImageUrls.length) { 135 | break; 136 | } else { 137 | isGif = mImageUrls[i].toLowerCase().contains(".gif"); 138 | } 139 | Bitmap bitmap = mBitmaps[i]; 140 | if (bitmap == null) { 141 | bitmap = mPlaceHolder; 142 | } 143 | 144 | 145 | float left = getPaddingLeft() + column * mHorizontalSpace + column * mImageWidth; 146 | float top = getPaddingTop() + row * mVerticalSpace + row * mImageHeight; 147 | float scale; 148 | float dx = 0, dy = 0; 149 | 150 | int dwidth = bitmap.getWidth(); 151 | int dheight = bitmap.getHeight(); 152 | int vwidth = mImageWidth; 153 | int vheight = mImageHeight; 154 | if (dwidth * vheight > vwidth * dheight) { 155 | scale = (float) vheight / (float) dheight; 156 | dx = (vwidth - dwidth * scale) * 0.5f; 157 | } else { 158 | scale = (float) vwidth / (float) dwidth; 159 | dy = (vheight - dheight * scale) * 0.5f; 160 | } 161 | 162 | matrix.setScale(scale, scale); 163 | matrix.postTranslate(left + Math.round(dx), top + Math.round(dy)); 164 | 165 | BitmapShader mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 166 | mBitmapShader.setLocalMatrix(matrix); 167 | mPaint.setShader(mBitmapShader); 168 | RectF rectF = new RectF(); 169 | rectF.set(left, top, left + mImageWidth, top + mImageHeight); 170 | mDrawRects[i] = rectF; 171 | canvas.drawRoundRect(rectF, mRadius, mRadius, mPaint); 172 | if (isGif) { 173 | String gifString = "GIF"; 174 | mTextPaint.setTextSize(DisplayUtil.dpToPx(12)); 175 | mTextPaint.setColor(Color.WHITE); 176 | Rect bounds = new Rect(); 177 | mTextPaint.getTextBounds(gifString, 0, gifString.length(), bounds); 178 | mTextBgPaint.setColor(Color.BLACK); 179 | mTextBgPaint.setAlpha(60); 180 | RectF rectB = new RectF(); 181 | rectB.set(left + DisplayUtil.dpToPx(6), top + DisplayUtil.dpToPx(6), 182 | left + DisplayUtil.dpToPx(6) * 2 + bounds.width(), top + DisplayUtil.dpToPx(6) * 2 + bounds.height()); 183 | canvas.drawRoundRect(rectB, mRadius, mRadius, mTextBgPaint); 184 | canvas.drawText(gifString, left + DisplayUtil.dpToPx(9), top + DisplayUtil.dpToPx(9) + bounds.height(), mTextPaint); 185 | } 186 | } 187 | } 188 | } 189 | } 190 | 191 | 192 | @Override 193 | public boolean onTouchEvent(MotionEvent event) { 194 | boolean isClickItem = false; 195 | int action = event.getActionMasked(); 196 | switch (action) { 197 | case MotionEvent.ACTION_DOWN: 198 | mEventDown = MotionEvent.obtain(event); 199 | mDown = getClickItem(mEventDown); 200 | isClickItem = mDown > -1; 201 | break; 202 | case MotionEvent.ACTION_UP: 203 | if (mEventDown != null) { 204 | float distance = (float) Math.sqrt(Math.pow((event.getX() - mEventDown.getX()), 2) + Math.pow((event.getY() - mEventDown.getY()), 2)); 205 | if (distance < ViewConfiguration.getTouchSlop()) { 206 | int iUp = getClickItem(event); 207 | if (mDown == iUp && iUp > -1) { 208 | isClickItem = true; 209 | if (onClickItemListener != null) { 210 | onClickItemListener.onClick(iUp, new ArrayList<>(Arrays.asList(mImageUrls))); 211 | } 212 | } 213 | } 214 | } 215 | break; 216 | } 217 | return isClickItem || super.onTouchEvent(event); 218 | } 219 | 220 | private int getClickItem(MotionEvent event) { 221 | for (int i = 0; i < mDrawRects.length; i++) { 222 | if (mDrawRects[i] != null && mDrawRects[i].contains(event.getX(), event.getY())) { 223 | return i; 224 | } 225 | } 226 | return -1; 227 | } 228 | 229 | 230 | public interface OnClickItemListener { 231 | void onClick(int i, ArrayList url); 232 | } 233 | 234 | private void loadBitmap(final int i, final String url) { 235 | Glide.with(getContext()) 236 | .load(url) 237 | .asBitmap() 238 | .diskCacheStrategy(DiskCacheStrategy.ALL) 239 | .override(mImageWidth, mImageHeight) 240 | .into(mTargets[i]); 241 | } 242 | 243 | 244 | public void setImageUrls(Collection imageUrls) { 245 | for (Target target : mTargets) { 246 | Glide.clear(target); 247 | } 248 | if (imageUrls == null) imageUrls = Collections.emptyList(); 249 | int newSize = imageUrls.size(); 250 | mImageUrls = imageUrls.toArray(new String[newSize]); 251 | mBitmaps = new Bitmap[newSize]; 252 | mDrawRects = new RectF[newSize]; 253 | mTargets = new Target[newSize]; 254 | if (!haveRule) { 255 | mColumns = imageUrls.size() <= 4 ? 2 : 3; 256 | } 257 | for (int i = 0; i < imageUrls.size(); i++) { 258 | mTargets[i] = new PositionTarget(i); 259 | } 260 | requestLayout(); 261 | } 262 | 263 | 264 | public void setColumns(int columns) { 265 | mColumns = columns; 266 | haveRule = true; 267 | } 268 | 269 | 270 | public void setOnClickItemListener(OnClickItemListener onClickItemListener) { 271 | this.onClickItemListener = onClickItemListener; 272 | } 273 | 274 | private void setImageRect(int pos) { 275 | int column = pos / mColumns; 276 | int row = pos % mColumns; 277 | 278 | int left = getPaddingLeft() + column * mHorizontalSpace + column * mImageWidth; 279 | int top = getPaddingTop() + row * mVerticalSpace + mImageHeight; 280 | invalidate(left, top, left + mImageWidth, top + mImageHeight); 281 | } 282 | 283 | private class PositionTarget extends SimpleTarget { 284 | private final int i; 285 | 286 | PositionTarget(int i) { 287 | this.i = i; 288 | } 289 | 290 | @Override 291 | public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { 292 | mBitmaps[i] = resource; 293 | setImageRect(i); 294 | } 295 | 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /nineimagelibrary/src/main/res/drawable/ic_placeholder_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HotBitmapGG/NineImageView/988c4fb2743ee4505c3ab1f3c8afcb2b196fc17a/nineimagelibrary/src/main/res/drawable/ic_placeholder_image.png -------------------------------------------------------------------------------- /nineimagelibrary/src/main/res/values/nine_image_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /nineimagelibrary/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NineImageLibrary 3 | 4 | -------------------------------------------------------------------------------- /nineimagelibrary/src/test/java/com/hotbitmapgg/nineimagelibrary/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.hotbitmapgg.nineimagelibrary; 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 ':app', ':nineimagelibrary' 2 | --------------------------------------------------------------------------------