├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── ids.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── drawable-xxhdpi │ │ │ ├── ic_rhombus.png │ │ │ └── ic_triangle.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ ├── ic_launcher_round.webp │ │ │ └── img_load_placeholder.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── drawable │ │ │ ├── ic_circle.xml │ │ │ ├── ic_vector_pentagon.xml │ │ │ ├── ic_vector_sector.xml │ │ │ ├── ic_press.xml │ │ │ ├── ic_vector_snowflake.xml │ │ │ ├── ic_vector_flower.xml │ │ │ └── ic_launcher_background.xml │ │ ├── color │ │ │ ├── press.xml │ │ │ ├── press_end.xml │ │ │ └── press_start.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── layout │ │ │ ├── item_listview.xml │ │ │ ├── activity_almighty_list.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_almighty.xml │ │ │ ├── activity_scale_type.xml │ │ │ └── activity_scale_type2.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── raw │ │ │ └── dog_heart.svg │ │ ├── java │ │ └── com │ │ │ └── flyjingfish │ │ │ └── shapeimageview │ │ │ ├── MyApplication.java │ │ │ ├── svg │ │ │ ├── SvgDrawableTranscoder.java │ │ │ └── SvgDecoder.java │ │ │ ├── MainActivity.java │ │ │ ├── MyAppGlideModule.java │ │ │ ├── ActivityCompatHelper.java │ │ │ ├── AlmightyListImageActivity.java │ │ │ ├── AlmightyImageActivity.java │ │ │ ├── ScaleTypeActivity.java │ │ │ └── MyImageLoader.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── library ├── gradle.properties ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ ├── drawable │ │ │ ├── ic_vector_heart.xml │ │ │ └── ic_vector_star.xml │ │ └── values │ │ │ └── values.xml │ │ └── java │ │ └── com │ │ └── flyjingfish │ │ └── shapeimageviewlib │ │ ├── ViewUtils.java │ │ ├── ShapeImageViewAttacher.java │ │ └── AlmightyShapeImageView.java └── build.gradle ├── version.properties ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── publish.gradle ├── screenshot ├── Screenshot_20221011_144810.jpg └── Screenshot_20221031_123252.jpg ├── .gitignore ├── settings.gradle ├── gradle.properties ├── gradlew.bat ├── gradlew ├── LICENSE ├── README-zh.md └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | PROJ_NAME=shapeimageview -------------------------------------------------------------------------------- /version.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 18 19:26:42 CST 2025 2 | PROJ_VERSION=1.5.7 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ShapeImageView 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /screenshot/Screenshot_20221011_144810.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/screenshot/Screenshot_20221011_144810.jpg -------------------------------------------------------------------------------- /screenshot/Screenshot_20221031_123252.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/screenshot/Screenshot_20221031_123252.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_rhombus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/drawable-xxhdpi/ic_rhombus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/drawable-xxhdpi/ic_triangle.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/img_load_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xxhdpi/img_load_placeholder.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlyJingFish/ShapeImageView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 01 17:29:10 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/res/color/press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/press_end.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/press_start.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.app.Application; 4 | 5 | 6 | public class MyApplication extends Application { 7 | public static MyApplication mInstance; 8 | @Override 9 | public void onCreate() { 10 | super.onCreate(); 11 | mInstance = this; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vector_pentagon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_listview.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | build/ 17 | .idea 18 | androidTest/ 19 | test/ 20 | # Project exclude paths 21 | /library/build/ 22 | /library/build/intermediates/javac/debug/classes/ -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: "$rootDir/gradle/publish.gradle" 3 | android { 4 | compileSdkVersion rootProject.ext.sdkVersion 5 | 6 | defaultConfig { 7 | minSdkVersion rootProject.ext.minSdkVersion 8 | targetSdkVersion rootProject.ext.sdkVersion 9 | } 10 | namespace 'com.flyjingfish.shapeimageviewlib' 11 | } 12 | 13 | dependencies { 14 | implementation 'androidx.appcompat:appcompat:1.3.1' 15 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vector_sector.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_almighty_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url 'https://jitpack.io' } 14 | maven { url "https://maven.aliyun.com/repository/public" } 15 | } 16 | 17 | } 18 | rootProject.name = "ShapeImageView" 19 | include ':app' 20 | include ':library' 21 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_vector_heart.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_vector_star.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/svg/SvgDrawableTranscoder.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview.svg; 2 | 3 | import android.graphics.Picture; 4 | import android.graphics.drawable.PictureDrawable; 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | import com.bumptech.glide.load.Options; 8 | import com.bumptech.glide.load.engine.Resource; 9 | import com.bumptech.glide.load.resource.SimpleResource; 10 | import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; 11 | import com.caverock.androidsvg.SVG; 12 | 13 | /** 14 | * Convert the {@link SVG}'s internal representation to an Android-compatible one ({@link Picture}). 15 | */ 16 | public class SvgDrawableTranscoder implements ResourceTranscoder { 17 | @Nullable 18 | @Override 19 | public Resource transcode( 20 | @NonNull Resource toTranscode, @NonNull Options options) { 21 | SVG svg = toTranscode.get(); 22 | Picture picture = svg.renderToPicture(); 23 | PictureDrawable drawable = new PictureDrawable(picture); 24 | return new SimpleResource<>(drawable); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gradle/publish.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'signing' 3 | apply plugin: "com.vanniktech.maven.publish" 4 | group = rootProject.group 5 | version = rootProject.version 6 | 7 | def appVersionName = getAppVersionName() 8 | 9 | mavenPublishing { 10 | coordinates(PROJ_GROUP, PROJ_NAME, appVersionName) 11 | 12 | pom { 13 | name = PROJ_BASENAME 14 | description = PROJ_DESCRIPTION 15 | inceptionYear = "2022" 16 | url = PROJ_WEBSITEURL 17 | licenses { 18 | license { 19 | name = "The Apache License, Version 2.0" 20 | url = "http://www.apache.org/licenses/LICENSE-2.0.txt" 21 | distribution = "http://www.apache.org/licenses/LICENSE-2.0.txt" 22 | } 23 | } 24 | developers { 25 | developer { 26 | id = DEVELOPER_ID 27 | name = DEVELOPER_NAME 28 | email = DEVELOPER_EMAIL 29 | } 30 | } 31 | scm { 32 | connection = PROJ_VCSURL 33 | developerConnection = PROJ_VCSURL 34 | url = PROJ_WEBSITEURL 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.app.AppCompatActivity; 8 | 9 | import com.flyjingfish.shapeimageview.databinding.ActivityMainBinding; 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(@Nullable Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); 17 | setContentView(binding.getRoot()); 18 | binding.AlmightyShapeImageView.setOnClickListener(v -> { 19 | startActivity(new Intent(this,AlmightyImageActivity.class)); 20 | }); 21 | binding.ShapeImageView.setOnClickListener(v -> { 22 | startActivity(new Intent(this,ScaleTypeActivity.class)); 23 | }); 24 | binding.AlmightyShapeImageViewList.setOnClickListener(v -> { 25 | startActivity(new Intent(this,AlmightyListImageActivity.class)); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | 21 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/MyAppGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.PictureDrawable; 5 | import android.util.Log; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | import com.bumptech.glide.Glide; 10 | import com.bumptech.glide.GlideBuilder; 11 | import com.bumptech.glide.Registry; 12 | import com.bumptech.glide.annotation.GlideModule; 13 | import com.bumptech.glide.module.AppGlideModule; 14 | import com.caverock.androidsvg.SVG; 15 | import com.flyjingfish.shapeimageview.svg.SvgDecoder; 16 | import com.flyjingfish.shapeimageview.svg.SvgDrawableTranscoder; 17 | 18 | import java.io.InputStream; 19 | 20 | @GlideModule 21 | public class MyAppGlideModule extends AppGlideModule { 22 | 23 | @Override 24 | public void registerComponents( 25 | @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { 26 | registry 27 | .register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder()) 28 | .append(InputStream.class, SVG.class, new SvgDecoder()); 29 | } 30 | 31 | // Disable manifest parsing to avoid adding similar modules twice. 32 | @Override 33 | public boolean isManifestParsingEnabled() { 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/svg/SvgDecoder.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview.svg; 2 | 3 | import static com.bumptech.glide.request.target.Target.SIZE_ORIGINAL; 4 | 5 | import androidx.annotation.NonNull; 6 | import com.bumptech.glide.load.Options; 7 | import com.bumptech.glide.load.ResourceDecoder; 8 | import com.bumptech.glide.load.engine.Resource; 9 | import com.bumptech.glide.load.resource.SimpleResource; 10 | import com.caverock.androidsvg.SVG; 11 | import com.caverock.androidsvg.SVGParseException; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | 15 | /** Decodes an SVG internal representation from an {@link InputStream}. */ 16 | public class SvgDecoder implements ResourceDecoder { 17 | 18 | @Override 19 | public boolean handles(@NonNull InputStream source, @NonNull Options options) { 20 | // TODO: Can we tell? 21 | return true; 22 | } 23 | 24 | public Resource decode( 25 | @NonNull InputStream source, int width, int height, @NonNull Options options) 26 | throws IOException { 27 | try { 28 | SVG svg = SVG.getFromInputStream(source); 29 | if (width != SIZE_ORIGINAL) { 30 | svg.setDocumentWidth(width); 31 | } 32 | if (height != SIZE_ORIGINAL) { 33 | svg.setDocumentHeight(height); 34 | } 35 | return new SimpleResource<>(svg); 36 | } catch (SVGParseException ex) { 37 | throw new IOException("Cannot load SVG from stream", ex); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | PROJ_GROUP=io.github.flyjingfish 22 | 23 | PROJ_BASENAME=ShapeImageView 24 | PROJ_WEBSITEURL=https://github.com/FlyJingFish/ShapeImageView 25 | PROJ_VCSURL=https://github.com/FlyJingFish/ShapeImageView.git 26 | PROJ_DESCRIPTION=ShapeImageView supports circle or rounded corners, and can draw circle background borders or rounded box background borders. In addition to the built-in properties of ImageView, 4 new display modes are added; 27 | 28 | DEVELOPER_ID=FlyJingFish 29 | DEVELOPER_NAME=FlyJingFish 30 | DEVELOPER_EMAIL=749617782@qq.com 31 | android.defaults.buildfeatures.buildconfig=true 32 | android.nonTransitiveRClass=false 33 | android.nonFinalResIds=false 34 | 35 | TestVersion = 1.5.7 36 | 37 | SONATYPE_HOST=CENTRAL_PORTAL 38 | 39 | RELEASE_SIGNING_ENABLED=true -------------------------------------------------------------------------------- /app/src/main/res/raw/dog_heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdkVersion rootProject.ext.sdkVersion 7 | 8 | defaultConfig { 9 | applicationId "com.flyjingfish.shapeimageview" 10 | minSdkVersion 21 11 | targetSdkVersion rootProject.ext.sdkVersion 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildFeatures { 18 | viewBinding true 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | namespace 'com.flyjingfish.shapeimageview' 32 | } 33 | dependencies { 34 | 35 | implementation 'androidx.appcompat:appcompat:1.3.1' 36 | implementation 'com.google.android.material:material:1.3.0' 37 | implementation 'androidx.constraintlayout:constraintlayout:2.1.2' 38 | implementation project(path: ':library') 39 | testImplementation 'junit:junit:4.+' 40 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 42 | 43 | 44 | implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1' 45 | 46 | //glide 47 | implementation 'com.github.bumptech.glide:glide:4.12.0' 48 | implementation 'com.github.bumptech.glide:annotations:4.12.0' 49 | implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0' 50 | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' 51 | implementation 'jp.wasabeef:glide-transformations:4.3.0' 52 | 53 | implementation 'com.caverock:androidsvg-aar:1.4' 54 | 55 | implementation 'org.apache.lucene:lucene-core:4.0.0' 56 | } -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/shapeimageviewlib/ViewUtils.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageviewlib; 2 | 3 | import android.util.LayoutDirection; 4 | import android.view.View; 5 | 6 | import androidx.core.text.TextUtilsCompat; 7 | 8 | import java.util.Locale; 9 | 10 | class ViewUtils { 11 | 12 | public static int getViewPaddingLeft(View view){ 13 | boolean isRtl = false; 14 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 15 | isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == LayoutDirection.RTL; 16 | } 17 | int paddingStart = view.getPaddingStart(); 18 | int paddingEnd = view.getPaddingEnd(); 19 | int paddingLeft = view.getPaddingLeft(); 20 | int paddingLeftMax; 21 | 22 | if (isRtl){ 23 | if (paddingEnd != 0){ 24 | paddingLeftMax = paddingEnd; 25 | }else { 26 | paddingLeftMax = paddingLeft; 27 | } 28 | }else { 29 | if (paddingStart != 0){ 30 | paddingLeftMax = paddingStart; 31 | }else { 32 | paddingLeftMax = paddingLeft; 33 | } 34 | 35 | } 36 | 37 | return paddingLeftMax; 38 | } 39 | 40 | public static int getViewPaddingRight(View view){ 41 | boolean isRtl = false; 42 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 43 | isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == LayoutDirection.RTL; 44 | } 45 | int paddingStart = view.getPaddingStart(); 46 | int paddingEnd = view.getPaddingEnd(); 47 | int paddingRight = view.getPaddingRight(); 48 | int paddingRightMax; 49 | if (isRtl){ 50 | if (paddingStart != 0){ 51 | paddingRightMax = paddingStart; 52 | }else { 53 | paddingRightMax = paddingRight; 54 | } 55 | }else { 56 | if (paddingEnd != 0){ 57 | paddingRightMax = paddingEnd; 58 | }else { 59 | paddingRightMax = paddingRight; 60 | } 61 | 62 | } 63 | return paddingRightMax; 64 | } 65 | 66 | public static float getRtlValue(float startEndValue,float leftRightValue){ 67 | float value; 68 | if (startEndValue != 0){ 69 | value = startEndValue; 70 | }else { 71 | value = leftRightValue; 72 | } 73 | return value; 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/ActivityCompatHelper.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.ContextWrapper; 7 | import android.view.Window; 8 | 9 | import androidx.fragment.app.Fragment; 10 | import androidx.fragment.app.FragmentActivity; 11 | 12 | 13 | public class ActivityCompatHelper { 14 | private static final int MIN_FRAGMENT_COUNT = 1; 15 | 16 | public static boolean isDestroy(Activity activity) { 17 | if (activity == null) { 18 | return true; 19 | } 20 | return activity.isFinishing() || activity.isDestroyed(); 21 | } 22 | 23 | 24 | /** 25 | * 验证Fragment是否已存在 26 | * 27 | * @param fragmentTag Fragment标签 28 | * @return 29 | */ 30 | public static boolean checkFragmentNonExits(FragmentActivity activity, String fragmentTag) { 31 | if (isDestroy(activity)) { 32 | return false; 33 | } 34 | Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(fragmentTag); 35 | return fragment == null; 36 | } 37 | 38 | 39 | public static boolean assertValidRequest(Context context) { 40 | if (context instanceof Activity) { 41 | Activity activity = (Activity) context; 42 | return !isDestroy(activity); 43 | } else if (context instanceof ContextWrapper) { 44 | ContextWrapper contextWrapper = (ContextWrapper) context; 45 | if (contextWrapper.getBaseContext() instanceof Activity) { 46 | Activity activity = (Activity) contextWrapper.getBaseContext(); 47 | return !isDestroy(activity); 48 | } 49 | } 50 | return true; 51 | } 52 | 53 | /** 54 | * 验证当前是否是根Fragment 55 | * 56 | * @param activity 57 | * @return 58 | */ 59 | public static boolean checkRootFragment(FragmentActivity activity) { 60 | if (ActivityCompatHelper.isDestroy(activity)) { 61 | return false; 62 | } 63 | return activity.getSupportFragmentManager().getBackStackEntryCount() == MIN_FRAGMENT_COUNT; 64 | } 65 | 66 | public static Window getWindow(Context context) { 67 | Activity activity = getActivity(context); 68 | if (activity != null){ 69 | return activity.getWindow(); 70 | } 71 | return null; 72 | } 73 | 74 | public static Activity getActivity(Context context) { 75 | return (Activity) context; 76 | } 77 | 78 | public static FragmentActivity getFragmentActivity(Context context) { 79 | if (context instanceof FragmentActivity) { 80 | return (FragmentActivity) context; 81 | } 82 | return null; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/AlmightyListImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.annotation.Nullable; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.recyclerview.widget.GridLayoutManager; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.flyjingfish.shapeimageview.databinding.ActivityAlmightyListBinding; 15 | import com.flyjingfish.shapeimageviewlib.AlmightyShapeImageView; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class AlmightyListImageActivity extends AppCompatActivity { 21 | 22 | private ActivityAlmightyListBinding binding; 23 | 24 | @Override 25 | protected void onCreate(@Nullable Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | binding = ActivityAlmightyListBinding.inflate(getLayoutInflater()); 28 | setContentView(binding.getRoot()); 29 | List list = new ArrayList<>(); 30 | for (int i = 0; i < 1000; i++) { 31 | list.add("https://img2.baidu.com/it/u=2415498875,118078114&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889"); 32 | } 33 | RvAdapter rvAdapter = new RvAdapter(list); 34 | binding.recyclerView.setAdapter(rvAdapter); 35 | binding.recyclerView.setLayoutManager(new GridLayoutManager(this,2)); 36 | } 37 | 38 | 39 | private static class RvAdapter extends RecyclerView.Adapter { 40 | List datas; 41 | int[] srcs = new int[]{ 42 | R.drawable.ic_vector_flower, 43 | R.drawable.ic_vector_pentagon, 44 | R.drawable.ic_vector_sector, 45 | R.drawable.ic_vector_snowflake, 46 | R.drawable.ic_vector_heart, 47 | R.drawable.ic_vector_star, 48 | }; 49 | 50 | public RvAdapter(List datas) { 51 | this.datas = datas; 52 | } 53 | 54 | @NonNull 55 | @Override 56 | public MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 57 | return new RvAdapter.MyHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_listview, parent, false)); 58 | } 59 | 60 | @Override 61 | public void onBindViewHolder(@NonNull MyHolder holder, int position) { 62 | holder.ivImage.setShapeResource(srcs[position%srcs.length]); 63 | MyImageLoader.getInstance().load(holder.ivImage, datas.get(position), R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 64 | } 65 | 66 | @Override 67 | public int getItemCount() { 68 | return datas.size(); 69 | } 70 | 71 | class MyHolder extends RecyclerView.ViewHolder { 72 | AlmightyShapeImageView ivImage; 73 | 74 | public MyHolder(@NonNull View itemView) { 75 | super(itemView); 76 | ivImage = itemView.findViewById(R.id.iv_image); 77 | } 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vector_snowflake.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/AlmightyImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.content.ContentResolver; 4 | import android.graphics.drawable.Drawable; 5 | import android.graphics.drawable.PictureDrawable; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | import androidx.appcompat.app.AppCompatActivity; 13 | 14 | import com.bumptech.glide.load.DataSource; 15 | import com.bumptech.glide.load.engine.GlideException; 16 | import com.bumptech.glide.request.RequestListener; 17 | import com.bumptech.glide.request.target.CustomTarget; 18 | import com.bumptech.glide.request.target.Target; 19 | import com.bumptech.glide.request.transition.Transition; 20 | import com.flyjingfish.shapeimageview.databinding.ActivityAlmightyBinding; 21 | import com.flyjingfish.shapeimageviewlib.AlmightyShapeImageView; 22 | 23 | import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; 24 | 25 | public class AlmightyImageActivity extends AppCompatActivity { 26 | 27 | private ActivityAlmightyBinding binding; 28 | private String itemData; 29 | 30 | @Override 31 | protected void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | binding = ActivityAlmightyBinding.inflate(getLayoutInflater()); 34 | setContentView(binding.getRoot()); 35 | itemData = "https://img2.baidu.com/it/u=2415498875,118078114&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889"; 36 | // itemData = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F9%2F57c4f3db0ff7a_120_80.jpg&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1662179613&t=105ec8c89e77a853ba57e2a2dc056eab"; 37 | setData(); 38 | binding.iv9.setOnClickListener(v -> { 39 | }); 40 | binding.ivPress.setOnClickListener(v -> { 41 | }); 42 | 43 | loadSvgRes(); 44 | } 45 | 46 | private void loadSvgRes() { 47 | Uri uri = 48 | Uri.parse( 49 | ContentResolver.SCHEME_ANDROID_RESOURCE 50 | + "://" 51 | + getPackageName() 52 | + "/" 53 | + R.raw.dog_heart); 54 | GlideApp.with(this) 55 | .as(PictureDrawable.class) 56 | .transition(withCrossFade()) 57 | .load(uri).into(new CustomTarget() { 58 | @Override 59 | public void onResourceReady(@NonNull PictureDrawable resource, @Nullable Transition transition) { 60 | binding.iv10.setShapeResource(resource); 61 | } 62 | 63 | @Override 64 | public void onLoadCleared(@Nullable Drawable placeholder) { 65 | 66 | } 67 | }); 68 | } 69 | 70 | 71 | private void setData() { 72 | MyImageLoader.getInstance().load(binding.iv1, itemData, Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 73 | MyImageLoader.getInstance().load(binding.iv2, itemData, Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 74 | MyImageLoader.getInstance().load(binding.iv3, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 75 | MyImageLoader.getInstance().load(binding.iv4, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 76 | MyImageLoader.getInstance().load(binding.iv5, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 77 | MyImageLoader.getInstance().load(binding.iv6, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 78 | MyImageLoader.getInstance().load(binding.iv7, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 79 | MyImageLoader.getInstance().load(binding.iv8, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 80 | MyImageLoader.getInstance().load(binding.iv9, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 81 | MyImageLoader.getInstance().load(binding.ivPress, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 82 | MyImageLoader.getInstance().load(binding.iv10, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 83 | } 84 | 85 | public void onChangeClick(View view) { 86 | binding.iv1.setShapeResource(((AlmightyShapeImageView) view).getShapeDrawable()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/ScaleTypeActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.SeekBar; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import com.bumptech.glide.request.target.Target; 12 | import com.flyjingfish.shapeimageview.databinding.ActivityScaleTypeBinding; 13 | 14 | 15 | public class ScaleTypeActivity extends AppCompatActivity { 16 | 17 | private ActivityScaleTypeBinding binding; 18 | private String itemData; 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | binding = ActivityScaleTypeBinding.inflate(getLayoutInflater()); 24 | setContentView(binding.getRoot()); 25 | itemData = "https://pics4.baidu.com/feed/50da81cb39dbb6fd95aa0c599b8d0d1e962b3708.jpeg?token=bf17224f51a6f4bb389e787f9c487940"; 26 | setData(); 27 | binding.seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 28 | @Override 29 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 30 | float radio = progress *1f/2 +1; 31 | binding.tvRadioValue.setText("autoCrop_height_width_ratio = " + radio); 32 | binding.ivAutoStartCrop.setAutoCropHeightWidthRatio(radio); 33 | binding.ivAutoEndCrop.setAutoCropHeightWidthRatio(radio); 34 | } 35 | 36 | @Override 37 | public void onStartTrackingTouch(SeekBar seekBar) { 38 | 39 | } 40 | 41 | @Override 42 | public void onStopTrackingTouch(SeekBar seekBar) { 43 | 44 | } 45 | }); 46 | 47 | //自定义渐变色 48 | binding.ivCenterInside.setGradientColors(new int[]{getResources().getColor(R.color.purple_700),getResources().getColor(R.color.white),getResources().getColor(R.color.purple_200)}); 49 | //渐变色分布 50 | binding.ivCenterInside.setGradientPositions(new float[]{0f,0.5f,1}); 51 | 52 | binding.ivFitStart.setOnClickListener(v -> {}); 53 | binding.ivFitCenter.setOnClickListener(v -> {}); 54 | binding.ivPress.setOnClickListener(v -> {}); 55 | } 56 | 57 | 58 | private void setData() { 59 | MyImageLoader.getInstance().load(binding.ivCenter, itemData, Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 60 | MyImageLoader.getInstance().load(binding.ivCenterCrop, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 61 | MyImageLoader.getInstance().load(binding.ivCenterInside, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 62 | MyImageLoader.getInstance().load(binding.ivFitStart, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 63 | MyImageLoader.getInstance().load(binding.ivFitCenter, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 64 | MyImageLoader.getInstance().load(binding.ivFitEnd, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 65 | MyImageLoader.getInstance().load(binding.ivStartCrop, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 66 | MyImageLoader.getInstance().load(binding.ivEndCrop, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 67 | MyImageLoader.getInstance().load(binding.ivAutoStartCrop, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 68 | MyImageLoader.getInstance().load(binding.ivAutoEndCrop, itemData, R.mipmap.img_load_placeholder, R.mipmap.img_load_placeholder); 69 | } 70 | 71 | public void onPicClick(View view) { 72 | switch (view.getId()) { 73 | case R.id.tv_pic1: 74 | binding.tvPic1.setBackgroundColor(getResources().getColor(R.color.teal_200)); 75 | binding.tvPic2.setBackgroundColor(Color.TRANSPARENT); 76 | binding.tvPic3.setBackgroundColor(Color.TRANSPARENT); 77 | itemData = "https://pics4.baidu.com/feed/50da81cb39dbb6fd95aa0c599b8d0d1e962b3708.jpeg?token=bf17224f51a6f4bb389e787f9c487940"; 78 | break; 79 | case R.id.tv_pic2: 80 | binding.tvPic2.setBackgroundColor(getResources().getColor(R.color.teal_200)); 81 | binding.tvPic1.setBackgroundColor(Color.TRANSPARENT); 82 | binding.tvPic3.setBackgroundColor(Color.TRANSPARENT); 83 | itemData = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp0.itc.cn%2Fq_70%2Fimages03%2F20210227%2F6687c969b58d486fa2f23d8488b96ae4.jpeg&refer=http%3A%2F%2Fp0.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1661701773&t=19043990158a1d11c2a334146020e2ce"; 84 | break; 85 | case R.id.tv_pic3: 86 | binding.tvPic3.setBackgroundColor(getResources().getColor(R.color.teal_200)); 87 | binding.tvPic1.setBackgroundColor(Color.TRANSPARENT); 88 | binding.tvPic2.setBackgroundColor(Color.TRANSPARENT); 89 | itemData = "https://img1.baidu.com/it/u=3124924291,3476865151&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=4841"; 90 | break; 91 | } 92 | setData(); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_vector_flower.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyjingfish/shapeimageview/MyImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageview; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.util.TypedValue; 5 | import android.widget.ImageView; 6 | 7 | import androidx.annotation.DrawableRes; 8 | import androidx.annotation.Nullable; 9 | 10 | import com.bumptech.glide.RequestBuilder; 11 | import com.bumptech.glide.load.DataSource; 12 | import com.bumptech.glide.load.Transformation; 13 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 14 | import com.bumptech.glide.load.engine.GlideException; 15 | import com.bumptech.glide.load.resource.bitmap.CenterCrop; 16 | import com.bumptech.glide.load.resource.bitmap.CircleCrop; 17 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners; 18 | import com.bumptech.glide.request.RequestListener; 19 | import com.bumptech.glide.request.RequestOptions; 20 | import com.bumptech.glide.request.target.Target; 21 | 22 | import jp.wasabeef.glide.transformations.BlurTransformation; 23 | 24 | public class MyImageLoader { 25 | private MyImageLoader() { 26 | } 27 | 28 | private static MyImageLoader mInstance; 29 | 30 | public static MyImageLoader getInstance() { 31 | if (null == mInstance) { 32 | synchronized (MyImageLoader.class) { 33 | if (null == mInstance) { 34 | mInstance = new MyImageLoader(); 35 | } 36 | } 37 | } 38 | return mInstance; 39 | } 40 | 41 | public void load(ImageView iv, String url, @DrawableRes int p, @DrawableRes int err) { 42 | if (!ActivityCompatHelper.assertValidRequest(iv.getContext())) 43 | return; 44 | into(url, iv, 0, 0, p, err, false, -1, false); 45 | } 46 | public void load(ImageView iv, String url, int w, int h, @DrawableRes int p, @DrawableRes int err) { 47 | if (!ActivityCompatHelper.assertValidRequest(iv.getContext())) 48 | return; 49 | into(url, iv, w, h, p, err, false, -1, false,null); 50 | } 51 | public void load(ImageView iv, String url, int w, int h, @DrawableRes int p, @DrawableRes int err,OnImageLoadListener requestListener) { 52 | if (!ActivityCompatHelper.assertValidRequest(iv.getContext())) 53 | return; 54 | into(url, iv, w, h, p, err, false, -1, false,requestListener); 55 | } 56 | 57 | public void loadRoundCorner(ImageView iv, String url, float radiusDp, @DrawableRes int p, @DrawableRes int err) { 58 | if (!ActivityCompatHelper.assertValidRequest(iv.getContext())) 59 | return; 60 | into(url, iv, 0, 0, p, err, false, radiusDp, false); 61 | } 62 | 63 | public void loadRoundCorner(ImageView iv, String url, float radiusDp, int w, int h, @DrawableRes int p, @DrawableRes int err,OnImageLoadListener requestListener) { 64 | if (!ActivityCompatHelper.assertValidRequest(iv.getContext())) 65 | return; 66 | into(url, iv, w, h, p, err, false, radiusDp, false,requestListener); 67 | } 68 | 69 | private void into(String url, ImageView iv, int w, int h, @DrawableRes int p, @DrawableRes int err, boolean isCircle, float radiusDp, boolean isBlur) { 70 | into(url, iv, w, h, p, err, isCircle, radiusDp, isBlur,null); 71 | } 72 | 73 | private void into(String url,ImageView iv, int w, int h, @DrawableRes int p, @DrawableRes int err, boolean isCircle, float radiusDp, boolean isBlur, OnImageLoadListener requestListener) { 74 | RequestBuilder requestBuilder = GlideApp.with(iv).load(url); 75 | if (isBlur || isCircle || radiusDp != -1) { 76 | Transformation[] transformations = new Transformation[0]; 77 | if (isBlur && !isCircle && radiusDp == -1) { 78 | transformations = new Transformation[]{new CenterCrop(), new BlurTransformation()}; 79 | } else if (isBlur && isCircle && radiusDp == -1) { 80 | transformations = new Transformation[]{new CenterCrop(), new BlurTransformation(), new CircleCrop()}; 81 | } else if (isBlur && !isCircle && radiusDp != -1) { 82 | transformations = new Transformation[]{new CenterCrop(), new BlurTransformation(), new RoundedCorners(dp2px(radiusDp))}; 83 | } else if (!isBlur && isCircle && radiusDp == -1) { 84 | transformations = new Transformation[]{new CenterCrop(), new CircleCrop()}; 85 | } else if (!isBlur && !isCircle && radiusDp != -1) { 86 | transformations = new Transformation[]{new CenterCrop(), new RoundedCorners(dp2px(radiusDp))}; 87 | } 88 | RequestOptions mRequestOptions = new RequestOptions().transform(transformations); 89 | 90 | requestBuilder.apply(mRequestOptions); 91 | if (w > 0 && h > 0) 92 | mRequestOptions.override(w, h); 93 | } else if (w > 0 && h > 0) { 94 | requestBuilder.override(w, h); 95 | } else if (w == Target.SIZE_ORIGINAL && h == Target.SIZE_ORIGINAL) { 96 | requestBuilder.override(w, h); 97 | } 98 | if (p != -1) 99 | requestBuilder.placeholder(p); 100 | if (err != -1) 101 | requestBuilder.error(err); 102 | if (requestListener != null){ 103 | requestBuilder.addListener(new RequestListener() { 104 | @Override 105 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { 106 | requestListener.onFailed(); 107 | return false; 108 | } 109 | 110 | @Override 111 | public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { 112 | requestListener.onSuccess(); 113 | return false; 114 | } 115 | }); 116 | } 117 | requestBuilder.into(iv); 118 | } 119 | 120 | public interface OnImageLoadListener{ 121 | void onSuccess(); 122 | void onFailed(); 123 | } 124 | 125 | 126 | public static int dp2px(float dp){ 127 | return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,MyApplication.mInstance.getResources().getDisplayMetrics()) + 0.5f); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_almighty.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 25 | 26 | 35 | 36 | 45 | 46 | 55 | 56 | 57 | 62 | 63 | 72 | 73 | 82 | 83 | 92 | 93 | 101 | 106 | 107 | 116 | 117 | 126 | 127 | 128 | 136 | 141 | 142 | 151 | 161 | 162 | 163 | 171 | 176 | 177 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/shapeimageviewlib/ShapeImageViewAttacher.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageviewlib; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Matrix; 5 | import android.graphics.Rect; 6 | import android.graphics.RectF; 7 | import android.graphics.drawable.Drawable; 8 | import android.graphics.drawable.PictureDrawable; 9 | import android.view.View; 10 | import android.widget.ImageView; 11 | 12 | 13 | class ShapeImageViewAttacher implements View.OnLayoutChangeListener { 14 | private final ShapeImageView mImageView; 15 | private final Matrix mBaseMatrix = new Matrix(); 16 | private final Matrix mDisplayMatrix = new Matrix(); 17 | private final Matrix mDrawMatrix = new Matrix(); 18 | private ShapeImageView.ShapeScaleType mScaleType; 19 | private float mAutoCropHeightWidthRatio; 20 | 21 | public ShapeImageViewAttacher(ShapeImageView imageView) { 22 | this.mImageView = imageView; 23 | imageView.addOnLayoutChangeListener(this); 24 | } 25 | 26 | @Override 27 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 28 | if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) { 29 | update(); 30 | } 31 | } 32 | 33 | public void update() { 34 | Drawable drawable = mImageView.getDrawable(); 35 | updateBaseMatrix(drawable); 36 | } 37 | 38 | private void updateBaseMatrix(Drawable drawable) { 39 | if (drawable == null) { 40 | return; 41 | } 42 | final float viewWidth = getImageViewWidth(mImageView); 43 | final float viewHeight = getImageViewHeight(mImageView); 44 | final int drawableWidth = drawable.getIntrinsicWidth(); 45 | final int drawableHeight = drawable.getIntrinsicHeight(); 46 | final float widthScale = viewWidth / drawableWidth; 47 | final float heightScale = viewHeight / drawableHeight; 48 | if (mScaleType == ShapeImageView.ShapeScaleType.START_CROP) { 49 | mBaseMatrix.reset(); 50 | float scale = Math.max(widthScale, heightScale); 51 | mBaseMatrix.postScale(scale, scale); 52 | mBaseMatrix.postTranslate(0, 0); 53 | resetMatrix(); 54 | } else if (mScaleType == ShapeImageView.ShapeScaleType.END_CROP) { 55 | mBaseMatrix.reset(); 56 | float scale = Math.max(widthScale, heightScale); 57 | mBaseMatrix.postScale(scale, scale); 58 | mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale), 59 | (viewHeight - drawableHeight * scale)); 60 | resetMatrix(); 61 | } else if (mScaleType == ShapeImageView.ShapeScaleType.AUTO_START_CENTER_CROP) { 62 | float imageHeightWidthRatio = drawableHeight * 1f / drawableWidth; 63 | float viewHeightWidthRatio = viewHeight / viewWidth; 64 | float ratio = imageHeightWidthRatio/viewHeightWidthRatio; 65 | mBaseMatrix.reset(); 66 | float scale = Math.max(widthScale, heightScale); 67 | mBaseMatrix.postScale(scale, scale); 68 | if (ratio >= mAutoCropHeightWidthRatio) { 69 | mBaseMatrix.postTranslate(0, 0); 70 | } else { 71 | mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2, 72 | (viewHeight - drawableHeight * scale) / 2); 73 | } 74 | resetMatrix(); 75 | } else if (mScaleType == ShapeImageView.ShapeScaleType.AUTO_END_CENTER_CROP) { 76 | float imageHeightWidthRatio = drawableHeight * 1f / drawableWidth; 77 | float viewHeightWidthRatio = viewHeight / viewWidth; 78 | float ratio = imageHeightWidthRatio/viewHeightWidthRatio; 79 | mBaseMatrix.reset(); 80 | float scale = Math.max(widthScale, heightScale); 81 | mBaseMatrix.postScale(scale, scale); 82 | if (ratio >= mAutoCropHeightWidthRatio) { 83 | mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale), 84 | (viewHeight - drawableHeight * scale)); 85 | } else { 86 | mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2, 87 | (viewHeight - drawableHeight * scale) / 2); 88 | } 89 | resetMatrix(); 90 | } else { 91 | ImageView.ScaleType scaleType = ShapeImageView.ShapeScaleType.getScaleType(mScaleType); 92 | if (scaleType != null){ 93 | mImageView.setScaleType(scaleType); 94 | } 95 | } 96 | 97 | 98 | } 99 | 100 | private void resetMatrix() { 101 | setImageViewMatrix(getDrawMatrix()); 102 | } 103 | 104 | private Matrix getDrawMatrix() { 105 | mDrawMatrix.set(mBaseMatrix); 106 | return mDrawMatrix; 107 | } 108 | 109 | private void setImageViewMatrix(Matrix matrix) { 110 | mImageView.setImageMatrix(matrix); 111 | } 112 | 113 | public void setScaleType(ShapeImageView.ShapeScaleType scaleType) { 114 | if (scaleType != mScaleType) { 115 | mScaleType = scaleType; 116 | update(); 117 | } 118 | } 119 | 120 | public ShapeImageView.ShapeScaleType getShapeScaleType() { 121 | return mScaleType; 122 | } 123 | 124 | private int getImageViewWidth(ImageView imageView) { 125 | return imageView.getWidth() - ViewUtils.getViewPaddingLeft(imageView) - ViewUtils.getViewPaddingRight(imageView); 126 | } 127 | 128 | private int getImageViewHeight(ImageView imageView) { 129 | return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); 130 | } 131 | 132 | public Matrix getImageMatrix() { 133 | return mDrawMatrix; 134 | } 135 | 136 | public void setAutoCropHeightWidthRatio(float autoCropHeightWidthRatio) { 137 | this.mAutoCropHeightWidthRatio = autoCropHeightWidthRatio; 138 | } 139 | 140 | public void updateDisplayMatrix() { 141 | Drawable drawable = mImageView.getDrawable(); 142 | if (drawable == null) { 143 | return; 144 | } 145 | final float viewWidth = getImageViewWidth(mImageView); 146 | final float viewHeight = getImageViewHeight(mImageView); 147 | final int drawableWidth = drawable.getIntrinsicWidth(); 148 | final int drawableHeight = drawable.getIntrinsicHeight(); 149 | mDisplayMatrix.reset(); 150 | final float widthScale = viewWidth / drawableWidth; 151 | final float heightScale = viewHeight / drawableHeight; 152 | if (mScaleType == ShapeImageView.ShapeScaleType.CENTER) { 153 | mDisplayMatrix.postTranslate((viewWidth - drawableWidth) / 2F, 154 | (viewHeight - drawableHeight) / 2F); 155 | 156 | } else if (mScaleType == ShapeImageView.ShapeScaleType.CENTER_CROP) { 157 | float scale = Math.max(widthScale, heightScale); 158 | mDisplayMatrix.postScale(scale, scale); 159 | mDisplayMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, 160 | (viewHeight - drawableHeight * scale) / 2F); 161 | 162 | } else if (mScaleType == ShapeImageView.ShapeScaleType.CENTER_INSIDE) { 163 | float scale = Math.min(1.0f, Math.min(widthScale, heightScale)); 164 | mDisplayMatrix.postScale(scale, scale); 165 | mDisplayMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, 166 | (viewHeight - drawableHeight * scale) / 2F); 167 | 168 | } else if (this.mScaleType == ShapeImageView.ShapeScaleType.START_CROP|| 169 | this.mScaleType == ShapeImageView.ShapeScaleType.END_CROP|| 170 | this.mScaleType == ShapeImageView.ShapeScaleType.AUTO_START_CENTER_CROP|| 171 | this.mScaleType == ShapeImageView.ShapeScaleType.AUTO_END_CENTER_CROP) { 172 | float scale = Math.max(widthScale, heightScale); 173 | mDisplayMatrix.postScale(scale, scale); 174 | mDisplayMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, 175 | (viewHeight - drawableHeight * scale) / 2F); 176 | } else { 177 | RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight); 178 | RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight); 179 | switch (mScaleType) { 180 | case FIT_CENTER: 181 | mDisplayMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER); 182 | break; 183 | case FIT_START: 184 | mDisplayMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.START); 185 | break; 186 | case FIT_END: 187 | mDisplayMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.END); 188 | break; 189 | case FIT_XY: 190 | mDisplayMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.FILL); 191 | break; 192 | default: 193 | break; 194 | } 195 | } 196 | getDisplayRect(mDisplayMatrix); 197 | } 198 | 199 | private final RectF mDisplayRectF = new RectF(); 200 | 201 | private void getDisplayRect(Matrix matrix) { 202 | Drawable drawable = mImageView.getDrawable(); 203 | if (drawable == null) { 204 | return; 205 | } 206 | mDisplayRectF.set(0, 0, drawable.getIntrinsicWidth(), 207 | drawable.getIntrinsicHeight()); 208 | matrix.mapRect(mDisplayRectF); 209 | final float viewWidth = getImageViewWidth(mImageView); 210 | final float viewHeight = getImageViewHeight(mImageView); 211 | float paddingLeft = ViewUtils.getViewPaddingLeft(mImageView); 212 | float paddingTop = mImageView.getPaddingTop(); 213 | float left = Math.max(mDisplayRectF.left,0)+paddingLeft; 214 | float top = Math.max(mDisplayRectF.top,0)+paddingTop; 215 | float right = Math.min(mDisplayRectF.right,viewWidth)+paddingLeft; 216 | float bottom = Math.min(mDisplayRectF.bottom,viewHeight)+paddingTop; 217 | mDisplayRectF.set((int) left, (int) top, (int) right, (int) bottom); 218 | } 219 | 220 | public RectF getDisplayRectF() { 221 | return mDisplayRectF; 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /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 [2022] [FlyJingFish] 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 | -------------------------------------------------------------------------------- /library/src/main/java/com/flyjingfish/shapeimageviewlib/AlmightyShapeImageView.java: -------------------------------------------------------------------------------- 1 | package com.flyjingfish.shapeimageviewlib; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Matrix; 8 | import android.graphics.Paint; 9 | import android.graphics.PorterDuff; 10 | import android.graphics.PorterDuffXfermode; 11 | import android.graphics.RectF; 12 | import android.graphics.drawable.Drawable; 13 | import android.graphics.drawable.PictureDrawable; 14 | import android.util.AttributeSet; 15 | 16 | import androidx.annotation.DrawableRes; 17 | import androidx.annotation.NonNull; 18 | import androidx.annotation.Nullable; 19 | import androidx.appcompat.widget.AppCompatImageView; 20 | import androidx.core.content.ContextCompat; 21 | 22 | public class AlmightyShapeImageView extends AppCompatImageView { 23 | private Drawable mShapeResource; 24 | private final Paint mShapePaint; 25 | private ShapeScaleType mShapeScaleType; 26 | private boolean isDrawShapeClear; 27 | private final RectF mLayerRectF = new RectF(); 28 | private final PorterDuffXfermode SRC_IN = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN); 29 | 30 | public AlmightyShapeImageView(@NonNull Context context) { 31 | this(context, null); 32 | } 33 | 34 | public AlmightyShapeImageView(@NonNull Context context, @Nullable AttributeSet attrs) { 35 | this(context, attrs, 0); 36 | } 37 | 38 | public AlmightyShapeImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 39 | super(context, attrs, defStyleAttr); 40 | 41 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AlmightyShapeImageView); 42 | mShapeResource = a.getDrawable(R.styleable.AlmightyShapeImageView_FlyJFish_almighty_shape_resource); 43 | mShapeScaleType = ShapeScaleType.getType(a.getInt(R.styleable.AlmightyShapeImageView_FlyJFish_almighty_shape_scaleType, 0)); 44 | a.recycle(); 45 | mShapePaint = new Paint(); 46 | mShapePaint.setColor(Color.WHITE); 47 | mShapePaint.setAntiAlias(true); 48 | mShapePaint.setStyle(Paint.Style.FILL); 49 | } 50 | 51 | @Override 52 | protected void drawableStateChanged() { 53 | super.drawableStateChanged(); 54 | if (mShapeResource != null){ 55 | final int[] drawableState = getDrawableState(); 56 | boolean inval = mShapeResource.setState(drawableState); 57 | if (inval){ 58 | invalidate(); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | protected void onDraw(Canvas canvas) { 65 | if (mShapeResource != null) { 66 | mLayerRectF.set(0, 0, canvas.getWidth(), canvas.getHeight()); 67 | preDrawShaper(canvas); 68 | drawShape(canvas); 69 | super.onDraw(canvas); 70 | canvas.restore(); 71 | } else { 72 | super.onDraw(canvas); 73 | } 74 | 75 | } 76 | 77 | private void preDrawShaper(Canvas canvas) { 78 | if (mShapeScaleType != ShapeScaleType.ALWAYS_FIX_XY){ 79 | return; 80 | } 81 | int height = getHeight(); 82 | int width = getWidth(); 83 | int paddingLeft = ViewUtils.getViewPaddingLeft(this); 84 | int paddingRight = ViewUtils.getViewPaddingRight(this); 85 | int paddingTop = getPaddingTop(); 86 | int paddingBottom = getPaddingBottom(); 87 | Drawable drawable = getDrawable(); 88 | Matrix matrix = getImageMatrix(); 89 | if (drawable == null || matrix == null) { 90 | return; 91 | } 92 | 93 | float[] matrixValues = new float[9]; 94 | matrix.getValues(matrixValues); 95 | int drawableWidth = drawable.getIntrinsicWidth(); 96 | int drawableHeight = drawable.getIntrinsicHeight(); 97 | 98 | 99 | float pictureWidth; 100 | float pictureHeight; 101 | 102 | 103 | float transX; 104 | float transY; 105 | 106 | ScaleType scaleType = getScaleType(); 107 | if (scaleType == ScaleType.FIT_XY || scaleType == ScaleType.CENTER_CROP) { 108 | transX = paddingLeft; 109 | transY = paddingTop; 110 | pictureWidth = width - paddingLeft - paddingRight; 111 | pictureHeight = height - paddingTop - paddingBottom; 112 | } else if (scaleType == ScaleType.CENTER) { 113 | if (drawableWidth < width || drawableHeight < height) { 114 | pictureWidth = Math.min(drawableWidth, width); 115 | pictureHeight = Math.min(drawableHeight, height); 116 | transX = (width - pictureWidth) / 2; 117 | transY = (height - pictureHeight) / 2; 118 | } else { 119 | transX = paddingLeft; 120 | transY = paddingTop; 121 | pictureWidth = width - paddingLeft - paddingRight; 122 | pictureHeight = height - paddingTop - paddingBottom; 123 | } 124 | } else { 125 | transX = (int) matrixValues[2] + paddingLeft; 126 | transY = (int) matrixValues[5] + paddingTop; 127 | 128 | pictureWidth = drawableWidth * matrixValues[0]; 129 | pictureHeight = drawableHeight * matrixValues[4]; 130 | } 131 | int left = (int) (transX ); 132 | int top = (int) (transY); 133 | int right = ((int) (pictureWidth + transX )); 134 | int bottom = ((int) (pictureHeight + transY)); 135 | 136 | mShapePaint.setXfermode(null); 137 | canvas.saveLayer(mLayerRectF, mShapePaint, Canvas.ALL_SAVE_FLAG); 138 | canvas.drawRect(left,top,right,bottom,mShapePaint); 139 | 140 | mShapePaint.setXfermode(SRC_IN); 141 | canvas.saveLayer(mLayerRectF, mShapePaint, Canvas.ALL_SAVE_FLAG); 142 | mShapeResource.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom); 143 | if (mShapeResource instanceof PictureDrawable){ 144 | float scaleX = mShapeResource.getBounds().width() *1f/mShapeResource.getIntrinsicWidth(); 145 | float scaleY = mShapeResource.getBounds().height() *1f/mShapeResource.getIntrinsicHeight(); 146 | canvas.translate(mShapeResource.getBounds().left, mShapeResource.getBounds().top); 147 | canvas.scale(scaleX,scaleY); 148 | canvas.drawPicture(((PictureDrawable) mShapeResource).getPicture()); 149 | canvas.scale(1/scaleX,1/scaleY); 150 | canvas.translate(-mShapeResource.getBounds().left,- mShapeResource.getBounds().top); 151 | }else { 152 | mShapeResource.draw(canvas); 153 | } 154 | isDrawShapeClear = true; 155 | } 156 | 157 | private void drawShape(Canvas canvas) { 158 | int height = getHeight(); 159 | int width = getWidth(); 160 | int paddingLeft = ViewUtils.getViewPaddingLeft(this); 161 | int paddingRight = ViewUtils.getViewPaddingRight(this); 162 | int paddingTop = getPaddingTop(); 163 | int paddingBottom = getPaddingBottom(); 164 | Drawable drawable = getDrawable(); 165 | if (drawable == null) { 166 | return; 167 | } 168 | 169 | Matrix matrix = getImageMatrix(); 170 | int left; 171 | int top; 172 | int right; 173 | int bottom; 174 | if (matrix == null || mShapeScaleType == ShapeScaleType.ALWAYS_FIX_XY) { 175 | left = paddingLeft; 176 | top = paddingTop; 177 | right = width - paddingRight; 178 | bottom = height - paddingBottom; 179 | } else { 180 | float[] matrixValues = new float[9]; 181 | matrix.getValues(matrixValues); 182 | int drawableWidth = drawable.getIntrinsicWidth(); 183 | int drawableHeight = drawable.getIntrinsicHeight(); 184 | float drawHWScale = drawableHeight * 1f / drawableWidth; 185 | 186 | int shapeResourceWidth = mShapeResource.getIntrinsicWidth(); 187 | int shapeResourceHeight = mShapeResource.getIntrinsicHeight(); 188 | 189 | float shapeHWScale = shapeResourceHeight * 1f / shapeResourceWidth; 190 | 191 | float viewHWScale = height * 1f / width; 192 | 193 | float pictureWidth; 194 | float pictureHeight; 195 | float shapeWidth; 196 | float shapeHeight; 197 | 198 | 199 | float transX; 200 | float transY; 201 | 202 | ScaleType scaleType = getScaleType(); 203 | if (scaleType == ScaleType.FIT_XY || scaleType == ScaleType.CENTER_CROP) { 204 | transX = paddingLeft; 205 | transY = paddingTop; 206 | pictureWidth = width - paddingLeft - paddingRight; 207 | pictureHeight = height - paddingTop - paddingBottom; 208 | if (viewHWScale > shapeHWScale) {//按drawable宽走 209 | shapeWidth = pictureWidth; 210 | shapeHeight = shapeWidth * shapeHWScale; 211 | } else {//按drawable高走 212 | shapeHeight = pictureHeight; 213 | shapeWidth = shapeHeight / shapeHWScale; 214 | } 215 | } else if (scaleType == ScaleType.CENTER) { 216 | if (drawableWidth < width || drawableHeight < height) { 217 | pictureWidth = Math.min(drawableWidth, width); 218 | pictureHeight = Math.min(drawableHeight, height); 219 | float pictureHWScale = pictureHeight / pictureWidth; 220 | if (pictureHWScale > shapeHWScale) {//按drawable宽走 221 | shapeWidth = pictureWidth; 222 | shapeHeight = shapeWidth * shapeHWScale; 223 | } else {//按drawable高走 224 | shapeHeight = pictureHeight; 225 | shapeWidth = shapeHeight / shapeHWScale; 226 | } 227 | transX = (width - pictureWidth) / 2; 228 | transY = (height - pictureHeight) / 2; 229 | } else { 230 | transX = paddingLeft; 231 | transY = paddingTop; 232 | pictureWidth = width - paddingLeft - paddingRight; 233 | pictureHeight = height - paddingTop - paddingBottom; 234 | if (viewHWScale > shapeHWScale) {//按drawable宽走 235 | shapeWidth = pictureWidth; 236 | shapeHeight = shapeWidth * shapeHWScale; 237 | } else {//按drawable高走 238 | shapeHeight = pictureHeight; 239 | shapeWidth = shapeHeight / shapeHWScale; 240 | } 241 | } 242 | } else { 243 | transX = (int) matrixValues[2] + paddingLeft; 244 | transY = (int) matrixValues[5] + paddingTop; 245 | 246 | pictureWidth = drawableWidth * matrixValues[0]; 247 | pictureHeight = drawableHeight * matrixValues[4]; 248 | if (drawHWScale > shapeHWScale) {//按drawable宽走 249 | shapeWidth = pictureWidth; 250 | shapeHeight = shapeWidth * shapeHWScale; 251 | } else {//按drawable高走 252 | shapeHeight = pictureHeight; 253 | shapeWidth = shapeHeight / shapeHWScale; 254 | } 255 | } 256 | if (mShapeScaleType == ShapeScaleType.FOLLOW_IMAGEVIEW_FULL_IMAGE){ 257 | shapeWidth = pictureWidth; 258 | shapeHeight = pictureHeight; 259 | } 260 | left = ((int) (transX + (pictureWidth - shapeWidth) / 2)); 261 | top = ((int) (transY + (pictureHeight - shapeHeight) / 2)); 262 | right = ((int) (shapeWidth + transX + (pictureWidth - shapeWidth) / 2)); 263 | bottom = ((int) (shapeHeight + transY + (pictureHeight - shapeHeight) / 2)); 264 | } 265 | 266 | if (!isDrawShapeClear){ 267 | mShapePaint.setXfermode(null); 268 | canvas.saveLayer(mLayerRectF, mShapePaint, Canvas.ALL_SAVE_FLAG); 269 | mShapeResource.setBounds(left, top, right, bottom); 270 | if (mShapeResource instanceof PictureDrawable){ 271 | float scaleX = mShapeResource.getBounds().width() *1f/mShapeResource.getIntrinsicWidth(); 272 | float scaleY = mShapeResource.getBounds().height() *1f/mShapeResource.getIntrinsicHeight(); 273 | canvas.translate(mShapeResource.getBounds().left, mShapeResource.getBounds().top); 274 | canvas.scale(scaleX,scaleY); 275 | canvas.drawPicture(((PictureDrawable) mShapeResource).getPicture()); 276 | canvas.scale(1/scaleX,1/scaleY); 277 | canvas.translate(-mShapeResource.getBounds().left,- mShapeResource.getBounds().top); 278 | }else { 279 | mShapeResource.draw(canvas); 280 | } 281 | } 282 | mShapePaint.setXfermode(SRC_IN); 283 | canvas.saveLayer(mLayerRectF, mShapePaint, Canvas.ALL_SAVE_FLAG); 284 | 285 | isDrawShapeClear = false; 286 | } 287 | 288 | public Drawable getShapeDrawable() { 289 | return mShapeResource; 290 | } 291 | 292 | public void setShapeResource(Drawable shapeResource) { 293 | this.mShapeResource = shapeResource; 294 | invalidate(); 295 | } 296 | 297 | public void setShapeResource(@DrawableRes int shapeResourceRes) { 298 | setShapeResource(ContextCompat.getDrawable(getContext(),shapeResourceRes)); 299 | } 300 | 301 | public ShapeScaleType getShapeScaleType() { 302 | return mShapeScaleType; 303 | } 304 | 305 | public void setShapeScaleType(ShapeScaleType shapeScaleType) { 306 | this.mShapeScaleType = shapeScaleType; 307 | invalidate(); 308 | } 309 | 310 | public enum ShapeScaleType { 311 | FOLLOW_IMAGEVIEW_KEEP_RESOURCE_SCALE(0),FOLLOW_IMAGEVIEW_FULL_IMAGE(1), ALWAYS_FIX_XY(2); 312 | 313 | ShapeScaleType(int type) { 314 | this.type = type; 315 | } 316 | 317 | final int type; 318 | 319 | public int getType() { 320 | return type; 321 | } 322 | 323 | public static ShapeScaleType getType(int type) { 324 | if (type == 2) { 325 | return ALWAYS_FIX_XY; 326 | } else if (type == 1) { 327 | return FOLLOW_IMAGEVIEW_FULL_IMAGE; 328 | } else { 329 | return FOLLOW_IMAGEVIEW_KEEP_RESOURCE_SCALE; 330 | } 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # ShapeImageView 2 | 3 | [![Maven central](https://img.shields.io/maven-central/v/io.github.flyjingfish/shapeimageview)](https://central.sonatype.com/artifact/io.github.flyjingfish/shapeimageview/) 4 | [![GitHub stars](https://img.shields.io/github/stars/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/stargazers) 5 | [![GitHub forks](https://img.shields.io/github/forks/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/network) 6 | [![GitHub issues](https://img.shields.io/github/issues/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/issues) 7 | [![GitHub license](https://img.shields.io/github/license/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/blob/master/LICENSE) 8 | 9 | 10 | 11 | ## ShapeImageView支持圆图或圆角图,可绘制圆环背景边框或圆角框背景边框,除ImageView自带属性外新增4种显示模式; 12 | ## AlmightyShapeImageView支持显示任意图形,只有你想不到,没有它做不到; 13 | ## 本库中的ImageView没有直接操作Bitmap,而是Drawable,只要系统的ImageView支持的都可显示(例如Glide加载的gif图),可以放心使用!!! 14 | 15 | ShapeImageView|AlmightyShapeImageView 16 | ----- | ----- 17 | show|show 18 | 19 | 20 | 21 | ## 特色功能 22 | 1、**ShapeImageView** 支持渐变色圆角边框或圆形边框,不限图片设置方式,完美兼容所有的图片加载库 23 | 24 | 2、**ShapeImageView** 矩形图片支持四个角独立设置角度值,矩形边框也支持四个角独立设置角度值 25 | 26 | 3、**ShapeImageView** 新增支持startCrop、endCrop、autoStartCenterCrop、autoEndCenterCrop四种显示模式 27 | 28 | (**autoStartCenterCrop和autoEndCenterCrop显示模式可通过设置autoCrop_height_width_ratio之后,自动在startCrop和centerCrop(endCrop和centerCrop)之间切换**) 29 | 30 | 4、**AlmightyShapeImageView** 完美兼容所有的图片加载库,支持定义任意图形,只有你想不到没有它做不到 31 | 32 | 33 | 34 | ## 第一步,需要引用的build.gradle 35 | 36 | ```gradle 37 | dependencies { 38 | implementation 'io.github.flyjingfish:shapeimageview:1.5.7' 39 | } 40 | ``` 41 | ## 第二步,使用说明 42 | 43 | # 一、ShapeImageView 使用说明 44 | 45 | ### ShapeImageView 示例 46 | ```xml 47 | 69 | ``` 70 | 71 | ### 外框使用 72 | 73 | **1,需要特别设置一下padding,否则图片有些ScaleType默认显示充满ImageView** 74 | 75 | 2,渐变色外框颜色分布可通过**setGradientPositions**设置,默认null值均匀分布 76 | 77 | 3,渐变色想自定义更多颜色可通过**setGradientColors**设置 78 | 79 | ### 属性一览 80 | 81 | | attr | format | description | 82 | |-------------------------------------------|:---------------:|:------------------------------------:| 83 | | FlyJFish_shape | enum | 图片是 rectangle矩形/oval圆形 | 84 | | FlyJFish_shape_radius | dimension | 图片四个角圆角 | 85 | | FlyJFish_shape_left_top_radius | dimension | 图片左上角圆角 | 86 | | FlyJFish_shape_right_top_radius | dimension | 图片右上角圆角 | 87 | | FlyJFish_shape_right_bottom_radius | dimension | 图片右下角圆角 | 88 | | FlyJFish_shape_left_bottom_radius | dimension | 图片左下角圆角 | 89 | | FlyJFish_shape_start_top_radius | dimension | 图片左上(Rtl:右上)角圆角 | 90 | | FlyJFish_shape_end_top_radius | dimension | 图片右上(Rtl:左上)角圆角 | 91 | | FlyJFish_shape_end_bottom_radius | dimension | 图片右下(Rtl:左下)角圆角 | 92 | | FlyJFish_shape_start_bottom_radius | dimension | 图片左下(Rtl:右下)角圆角 | 93 | | FlyJFish_shape_border | enum | 背景边框绘制形状是 none不绘制/rectangle矩形/oval圆形 | 94 | | FlyJFish_shape_border_radius | dimension | 背景边框四个角圆角 | 95 | | FlyJFish_shape_border_left_top_radius | dimension | 背景边框左上角圆角 | 96 | | FlyJFish_shape_border_right_top_radius | dimension | 背景边框右上角圆角 | 97 | | FlyJFish_shape_border_right_bottom_radius | dimension | 背景边框右下角圆角 | 98 | | FlyJFish_shape_border_left_bottom_radius | dimension | 背景边框左下角圆角 | 99 | | FlyJFish_shape_border_start_top_radius | dimension | 背景边框左上(Rtl:右上)角圆角 | 100 | | FlyJFish_shape_border_end_top_radius | dimension | 背景边框右上(Rtl:左上)角圆角 | 101 | | FlyJFish_shape_border_end_bottom_radius | dimension | 背景边框右下(Rtl:左下)角圆角 | 102 | | FlyJFish_shape_border_start_bottom_radius | dimension | 背景边框左下(Rtl:右下)角圆角 | 103 | | FlyJFish_shape_border_color | color/reference | 背景边框绘制颜色 | 104 | | FlyJFish_shape_border_gradient | boolean | 背景边框绘制是否渐变色 | 105 | | FlyJFish_shape_border_startColor | color/reference | 背景边框绘制渐变色开始颜色 | 106 | | FlyJFish_shape_border_centerColor | color/reference | 背景边框绘制渐变色中间颜色 | 107 | | FlyJFish_shape_border_endColor | color/reference | 背景边框绘制渐变色结束颜色 | 108 | | FlyJFish_shape_border_angle | float | 背景边框绘制渐变色开始角度 | 109 | | FlyJFish_shape_border_rtl_angle | boolean | 背景边框绘制渐变色开始角度是否支持镜像Rtl适配 | 110 | | FlyJFish_shape_border_strokeWidth | dimension | 背景边框绘制画笔宽度 | 111 | | FlyJFish_autoCrop_height_width_ratio | float | 图像高宽比是View高宽比的倍数 | 112 | | FlyJFish_shapeScaleType | enum | 如果设置新增显示模式设置这个,详情如下: | 113 | 114 | | FlyJFish_shapeScaleType | description | 115 | |-------------------------|:-------------------------:| 116 | | startCrop | 裁剪开始左上 | 117 | | endCrop | 裁剪开始右下 | 118 | | autoStartCenterCrop | 自动在startCrop和centerCrop切换 | 119 | | autoEndCenterCrop | 自动在endCrop和centerCrop切换 | 120 | 121 | # 二、AlmightyShapeImageView 使用说明 122 | 123 | ### AlmightyShapeImageView 示例(库内内置 ❤️ [ic_vector_heart](https://github.com/FlyJingFish/ShapeImageView/tree/master/library/src/main/res/drawable) 和 ⭐️ [ic_vector_star](https://github.com/FlyJingFish/ShapeImageView/tree/master/library/src/main/res/drawable)) 124 | 125 | **使用的关键在于设置一个图形资源图(即 FlyJFish_almighty_shape_resource),想做显示什么形状的图片只要设置一个资源图,就都可以实现** 126 | 127 | ```xml 128 | 136 | ``` 137 | 138 | ### 属性一览 139 | 140 | | attr | format | description | 141 | |-----------------------------------|:---------:|:-----------:| 142 | | FlyJFish_almighty_shape_resource | reference | 图形资源 | 143 | | FlyJFish_almighty_shape_scaleType | enum | 绘制图形资源的显示类型 | 144 | 145 | 146 | | FlyJFish_almighty_shape_scaleType | description | 147 | |-----------------------------------|:-----------------------------------------------------------------------:| 148 | | followImageViewKeepResourceScale | 图形资源跟随ImageView的ScaleType类型并且保持图形资源的宽高比 | 149 | | followImageViewFullImage | 图形资源跟随ImageView的ScaleType类型但是充满图片的宽高(图形相对可能会拉伸) | 150 | | alwaysFixXY | 图形资源不会跟随ImageView的ScaleType,总是充满ImageView容器(设置这个属性有些ScaleType会导致图形显示不全) | 151 | 152 | ### 方法 153 | 154 | | method | type | description | 155 | |-------------------|:--------------:|:-------------:| 156 | | setShapeResource | Drawable | 图形资源Drawable | 157 | | setShapeResource | DrawableRes | 图形资源id | 158 | | setShapeScaleType | ShapeScaleType | 设置绘制图形资源的显示类型 | 159 | 160 | ### 图形资源设置提示 161 | 162 | **FlyJFish_almighty_shape_resource** 就是让UI提前将图形导出的图片资源,可以是shape,可以是vector,可以是png图片,但是**强烈建议使用shape或vector矢量图形效果更佳** 163 | 164 | 如果使用是png或svg资源可以将其转化为vector,详情可以看我的博客: 165 | 166 | - [博客使用说明](https://blog.csdn.net/u013077428/article/details/127613904) 167 | 168 | ### 番外:如果您的图形资源图想要用网络图片(即不打包到apk中) 169 | 170 | 可以下载网络图片然后调用 **setShapeResource** 设置图形即可 171 | 172 | 例如,通过Glide下载并设置: 173 | 174 | ```java 175 | Glide.with(context).load("网络链接").into(new CustomTarget() { 176 | @Override 177 | public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { 178 | imageView.setShapeResource(resource); 179 | } 180 | 181 | @Override 182 | public void onLoadCleared(@Nullable Drawable placeholder) { 183 | 184 | } 185 | }); 186 | ``` 187 | 188 | #### 如果想直接使用svg格式图可以这样做(不建议这样做,因为 svg 图可以直接转化为 vector 图,[点此查看转化说明](https://blog.csdn.net/u013077428/article/details/127613904)) 189 | 190 | 引用三方解析包 191 | 192 | ```gradle 193 | dependencies { 194 | implementation 'com.caverock:androidsvg-aar:1.4' 195 | } 196 | ``` 197 | 198 | **新增如下两个类** 199 | 200 | - [SvgDecoder](https://github.com/FlyJingFish/ShapeImageView/tree/master/app/src/main/java/com/flyjingfish/shapeimageview/svg/SvgDecoder.java) 201 | 202 | ```java 203 | public class SvgDecoder implements ResourceDecoder { 204 | 205 | @Override 206 | public boolean handles(@NonNull InputStream source, @NonNull Options options) { 207 | // TODO: Can we tell? 208 | return true; 209 | } 210 | 211 | public Resource decode( 212 | @NonNull InputStream source, int width, int height, @NonNull Options options) 213 | throws IOException { 214 | try { 215 | SVG svg = SVG.getFromInputStream(source); 216 | if (width != SIZE_ORIGINAL) { 217 | svg.setDocumentWidth(width); 218 | } 219 | if (height != SIZE_ORIGINAL) { 220 | svg.setDocumentHeight(height); 221 | } 222 | return new SimpleResource<>(svg); 223 | } catch (SVGParseException ex) { 224 | throw new IOException("Cannot load SVG from stream", ex); 225 | } 226 | } 227 | } 228 | ``` 229 | 230 | - [SvgDrawableTranscoder](https://github.com/FlyJingFish/ShapeImageView/tree/master/app/src/main/java/com/flyjingfish/shapeimageview/svg/SvgDrawableTranscoder.java) 231 | 232 | ```java 233 | public class SvgDrawableTranscoder implements ResourceTranscoder { 234 | @Nullable 235 | @Override 236 | public Resource transcode( 237 | @NonNull Resource toTranscode, @NonNull Options options) { 238 | SVG svg = toTranscode.get(); 239 | Picture picture = svg.renderToPicture(); 240 | PictureDrawable drawable = new PictureDrawable(picture); 241 | return new SimpleResource<>(drawable); 242 | } 243 | } 244 | ``` 245 | 246 | **新增glide配置** 247 | 248 | [MyAppGlideModule](https://github.com/FlyJingFish/ShapeImageView/tree/master/app/src/main/java/com/flyjingfish/shapeimageview/svg/MyAppGlideModule.java) 249 | 250 | ```java 251 | 252 | @GlideModule 253 | public class MyAppGlideModule extends AppGlideModule { 254 | 255 | @Override 256 | public void registerComponents( 257 | @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { 258 | registry 259 | .register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder()) 260 | .append(InputStream.class, SVG.class, new SvgDecoder()); 261 | } 262 | 263 | @Override 264 | public boolean isManifestParsingEnabled() { 265 | return false; 266 | } 267 | } 268 | 269 | ``` 270 | 271 | **开始使用svg** 272 | 273 | - 本地资源 274 | 275 | ```java 276 | Uri uri = 277 | Uri.parse( 278 | ContentResolver.SCHEME_ANDROID_RESOURCE 279 | + "://" 280 | + getPackageName() 281 | + "/" 282 | + R.raw.dog_heart); 283 | GlideApp.with(this) 284 | .as(PictureDrawable.class) 285 | .transition(withCrossFade()) 286 | .load(uri).into(new CustomTarget() { 287 | @Override 288 | public void onResourceReady(@NonNull PictureDrawable resource, @Nullable Transition transition) { 289 | imageView.setShapeResource(resource); 290 | } 291 | 292 | @Override 293 | public void onLoadCleared(@Nullable Drawable placeholder) { 294 | 295 | } 296 | }); 297 | ``` 298 | 299 | - 网络资源 300 | 301 | ```java 302 | Uri uri = Uri.parse("http://www.clker.com/cliparts/u/Z/2/b/a/6/android-toy-h.svg"); 303 | GlideApp.with(this) 304 | .as(PictureDrawable.class) 305 | .transition(withCrossFade()) 306 | .load(uri).into(new CustomTarget() { 307 | @Override 308 | public void onResourceReady(@NonNull PictureDrawable resource, @Nullable Transition transition) { 309 | imageView.setShapeResource(resource); 310 | } 311 | 312 | @Override 313 | public void onLoadCleared(@Nullable Drawable placeholder) { 314 | 315 | } 316 | }); 317 | ``` 318 | 319 | 320 | ## 常见问题 321 | 1、有的图形设置rotation、rotationX、rotationY等变化属性之后图片显示不全 322 | 323 | - 解决方案:设置margin属性即可解决 324 | 325 | 326 | ## 最后推荐我写的另外一些库 327 | 328 | - [OpenImage 轻松实现在应用内点击小图查看大图的动画放大效果](https://github.com/FlyJingFish/OpenImage)(已内置当前库) 329 | 330 | - [AndroidAOP 一个注解就可请求权限,禁止多点,切换线程等等,更可定制出属于你的 Aop 代码](https://github.com/FlyJingFish/AndroidAOP) 331 | 332 | - [主页查看更多开源库](https://github.com/FlyJingFish) 333 | 334 | 335 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_scale_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 27 | 28 | 36 | 37 | 45 | 46 | 47 | 52 | 53 | 59 | 60 | 66 | 67 | 73 | 74 | 75 | 79 | 80 | 90 | 91 | 104 | 105 | 114 | 115 | 116 | 121 | 122 | 128 | 129 | 135 | 136 | 142 | 143 | 144 | 148 | 149 | 166 | 167 | 189 | 190 | 206 | 207 | 216 | 221 | 222 | 229 | 230 | 237 | 238 | 239 | 240 | 244 | 245 | 246 | 263 | 264 | 280 | 281 | 294 | 295 | 296 | 301 | 302 | 309 | 310 | 317 | 318 | 319 | 323 | 324 | 339 | 340 | 353 | 354 | 355 | 363 | 364 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_scale_type2.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 27 | 28 | 36 | 37 | 45 | 46 | 47 | 52 | 53 | 59 | 60 | 66 | 67 | 73 | 74 | 75 | 79 | 80 | 96 | 97 | 119 | 120 | 136 | 137 | 138 | 143 | 144 | 150 | 151 | 157 | 158 | 164 | 165 | 166 | 170 | 171 | 186 | 187 | 202 | 203 | 218 | 219 | 220 | 225 | 226 | 232 | 233 | 240 | 241 | 248 | 249 | 250 | 254 | 255 | 278 | 279 | 295 | 296 | 311 | 312 | 313 | 318 | 319 | 326 | 327 | 334 | 335 | 336 | 340 | 341 | 355 | 356 | 368 | 369 | 370 | 378 | 379 | 386 | 387 | 388 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShapeImageView 2 | 3 | [![Maven central](https://img.shields.io/maven-central/v/io.github.flyjingfish/shapeimageview)](https://central.sonatype.com/artifact/io.github.flyjingfish/shapeimageview/) 4 | [![GitHub stars](https://img.shields.io/github/stars/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/stargazers) 5 | [![GitHub forks](https://img.shields.io/github/forks/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/network) 6 | [![GitHub issues](https://img.shields.io/github/issues/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/issues) 7 | [![GitHub license](https://img.shields.io/github/license/FlyJingFish/ShapeImageView.svg)](https://github.com/FlyJingFish/ShapeImageView/blob/master/LICENSE) 8 | 9 | 10 | # [中文版使用说明](https://github.com/FlyJingFish/ShapeImageView/blob/master/README-zh.md) 11 | 12 | ## ShapeImageView supports circle or rounded corners, and can draw circle background borders or rounded box background borders. In addition to the built-in properties of ImageView, 4 new display modes are added; 13 | ## AlmightyShapeImageView supports the display of arbitrary graphics, only you can't think of it, you can't do it without it; 14 | ## The ImageView in this library does not directly operate Bitmap, but Drawable, as long as the system's ImageView supports it, it can be displayed (such as the gif image loaded by Glide), you can use it with confidence! ! ! 15 | 16 | 17 | ShapeImageView|AlmightyShapeImageView 18 | ----- | ----- 19 | show|show 20 | 21 | 22 | 23 | ## Special function 24 | 25 | 1、**ShapeImageView** supports gradient rounded borders or rounded borders, unlimited image settings, perfectly compatible with all image loading libraries 26 | 27 | 2、 The **ShapeImageView** rectangular image supports the independent setting of the angle value of the four corners, and the rectangular border also supports the independent setting of the angle value of the four corners. 28 | 29 | 3、**ShapeImageView** supports four display modes: startCrop, endCrop, autoStartCenterCrop, autoEndCenterCrop 30 | 31 | (**AutoStartCenterCrop and autoEndCenterCrop display mode can be automatically switched between startCrop and centerCrop (endCrop and centerCrop) after setting autoCrop_height_width_ratio**) 32 | 33 | 4、**AlmightyShapeImageView** is perfectly compatible with all image loading libraries, supports defining arbitrary graphics, only you can't think of it without it 34 | 35 | 36 | 37 | ## The first step, the root directory build.gradle 38 | 39 | ```gradle 40 | dependencies { 41 | implementation 'io.github.flyjingfish:shapeimageview:1.5.7' 42 | } 43 | ``` 44 | ## The second step, instructions for use 45 | 46 | # 一、ShapeImageView Instructions for Use 47 | 48 | ### ShapeImageView example 49 | ```xml 50 | 72 | ``` 73 | 74 | ### Outer frame use 75 | 76 | **1,You need to set padding specially, otherwise some ScaleType of the picture will be filled with ImageView by default.** 77 | 78 | 2,The color distribution of the gradient frame can be set through **setGradientPositions**, and the default null value is evenly distributed 79 | 80 | 3,Gradient color If you want to customize more colors, you can set it through **setGradientColors** 81 | 82 | ### List of properties 83 | 84 | | attr | format | description | 85 | |-------------------------------------------|:---------------:|:-------------------------------------------------------------------------------------------:| 86 | | FlyJFish_shape | enum | The picture is a rectangle/oval circle | 87 | | FlyJFish_shape_radius | dimension | The four corners of the image are rounded | 88 | | FlyJFish_shape_left_top_radius | dimension | The upper left corner of the image is rounded | 89 | | FlyJFish_shape_right_top_radius | dimension | Round corners in the upper right corner of the image | 90 | | FlyJFish_shape_right_bottom_radius | dimension | The bottom right corner of the image is rounded | 91 | | FlyJFish_shape_left_bottom_radius | dimension | The bottom left corner of the image is rounded | 92 | | FlyJFish_shape_start_top_radius | dimension | image top left (Rtl: top right) corner rounded | 93 | | FlyJFish_shape_end_top_radius | dimension | image top right (Rtl: top left) corner rounded | 94 | | FlyJFish_shape_end_bottom_radius | dimension | picture bottom right (Rtl: bottom left) corner rounded | 95 | | FlyJFish_shape_start_bottom_radius | dimension | picture bottom left (Rtl: bottom right) corner rounded | 96 | | FlyJFish_shape_border | enum | The background border drawing shape is none without drawing/rectangle rectangle/oval circle | 97 | | FlyJFish_shape_border_radius | dimension | The four corners of the background border are rounded | 98 | | FlyJFish_shape_border_left_top_radius | dimension | The top left corner of the background border is rounded | 99 | | FlyJFish_shape_border_right_top_radius | dimension | rounded corners in the upper right corner of the background border | 100 | | FlyJFish_shape_border_right_bottom_radius | dimension | The bottom right corner of the background border is rounded | 101 | | FlyJFish_shape_border_left_bottom_radius | dimension | The bottom left corner of the background border is rounded | 102 | | FlyJFish_shape_border_start_top_radius | dimension | background border top left (Rtl: top right) corner rounded | 103 | | FlyJFish_shape_border_end_top_radius | dimension | background border top right (Rtl: top left) corner rounded | 104 | | FlyJFish_shape_border_end_bottom_radius | dimension | background border bottom right (Rtl: bottom left) corner rounded | 105 | | FlyJFish_shape_border_start_bottom_radius | dimension | The bottom left (Rtl: bottom right) corner of the background border is rounded | 106 | | FlyJFish_shape_border_color | color/reference | Background border drawing color | 107 | | FlyJFish_shape_border_gradient | boolean | Whether the background border is drawn in gradient color | 108 | | FlyJFish_shape_border_startColor | color/reference | The background border draw gradient color start color | 109 | | FlyJFish_shape_border_centerColor | color/reference | The background border draws the gradient middle color | 110 | | FlyJFish_shape_border_endColor | color/reference | The background border draws the gradient color end color | 111 | | FlyJFish_shape_border_angle | float | Start angle of background border drawing gradient color | 112 | | FlyJFish_shape_border_rtl_angle | boolean | Does the gradient start angle of the background border support mirror Rtl adaptation | 113 | | FlyJFish_shape_border_strokeWidth | dimension | width of background border drawing brush | 114 | | FlyJFish_autoCrop_height_width_ratio | float | The image aspect ratio is a multiple of the View aspect ratio | 115 | | FlyJFish_shapeScaleType | enum | If you set the new display mode to set this, the details are as follows: | 116 | 117 | | FlyJFish_shapeScaleType | description | 118 | |-------------------------|:-----------------------------------------------------:| 119 | | startCrop | Crop start top left | 120 | | endCrop | Crop start bottom right | 121 | | autoStartCenterCrop | Automatically switch between startCrop and centerCrop | 122 | | autoEndCenterCrop | Automatically switch between endCrop and centerCrop | 123 | 124 | # 二、AlmightyShapeImageView Instructions for Use 125 | 126 | ### AlmightyShapeImageView example(Built-in library ❤️ [ic_vector_heart](https://github.com/FlyJingFish/ShapeImageView/tree/master/library/src/main/res/drawable) 和 ⭐️ [ic_vector_star](https://github.com/FlyJingFish/ShapeImageView/tree/master/library/src/main/res/drawable)) 127 | 128 | **The key to use is to set a graphic resource map (that is, FlyJFish_almighty_shape_resource). If you want to display a picture of any shape, you can achieve it as long as you set a resource map.** 129 | 130 | ```xml 131 | 139 | ``` 140 | 141 | ### List of properties 142 | 143 | | attr | format | description | 144 | |-----------------------------------|:---------:|:----------------------------------------:| 145 | | FlyJFish_almighty_shape_resource | reference | Shape Resource | 146 | | FlyJFish_almighty_shape_scaleType | enum | The display type of the drawing resource | 147 | 148 | 149 | | FlyJFish_almighty_shape_scaleType | description | 150 | |-----------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| 151 | | followImageViewKeepResourceScale | The graphics resource follows the ImageView's ScaleType type and maintains the aspect ratio of the graphics resource | 152 | | followImageViewFullImage | The graphics resource follows the ImageView's ScaleType but fills the width and height of the image (the graphics may be stretched relatively) | 153 | | alwaysFixXY | The graphics resource will not follow the ScaleType of the ImageView, and always fill the ImageView container (setting this property to some ScaleType will cause the graphics to be incomplete) | 154 | 155 | ### Method 156 | 157 | | method | type | description | 158 | |------------------|:--------------:|:----------------------------------------------------:| 159 | | setShapeResource | Drawable | Shape Resource Drawable | 160 | | setShapeResource | DrawableRes | Shape resource id | 161 | | setShapeScaleType | ShapeScaleType | Sets the display type for drawing graphics resources | 162 | 163 | ### Graphic resource setting prompt 164 | 165 | **FlyJFish_almighty_shape_resource** is the image resource that allows the UI to export the graphics in advance. It can be a shape, a vector, or a png image, but **It is strongly recommended to use shape or vector vector graphics for better results** 166 | 167 | If you use png or svg resources, you can convert them to vector. For details, see my blog: 168 | 169 | - [Blog Instructions](https://blog.csdn.net/u013077428/article/details/127613904) 170 | 171 | ### Extra episode: If you want to use network images for your graphics resource map (that is, not packaged into apk) 172 | 173 | You can download network pictures through Glide and then call **setShapeResource** to set the graphics 174 | 175 | For example, download and set via Glide: 176 | 177 | ```java 178 | Glide.with(context).load("Internet connection").into(new CustomTarget() { 179 | @Override 180 | public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { 181 | imageView.setShapeResource(resource); 182 | } 183 | 184 | @Override 185 | public void onLoadCleared(@Nullable Drawable placeholder) { 186 | 187 | } 188 | }); 189 | ``` 190 | 191 | #### If you want to use the svg format map directly, you can do this(This is not recommended, because svg images can be directly converted into vector images, [click here to view the conversion instructions](https://blog.csdn.net/u013077428/article/details/127613904)) 192 | 193 | Refer to the three-party analysis package 194 | 195 | ```gradle 196 | dependencies { 197 | implementation 'com.caverock:androidsvg-aar:1.4' 198 | } 199 | ``` 200 | 201 | **Add the following two classes** 202 | 203 | - [SvgDecoder](https://github.com/FlyJingFish/ShapeImageView/tree/master/app/src/main/java/com/flyjingfish/shapeimageview/svg/SvgDecoder.java) 204 | 205 | ```java 206 | public class SvgDecoder implements ResourceDecoder { 207 | 208 | @Override 209 | public boolean handles(@NonNull InputStream source, @NonNull Options options) { 210 | // TODO: Can we tell? 211 | return true; 212 | } 213 | 214 | public Resource decode( 215 | @NonNull InputStream source, int width, int height, @NonNull Options options) 216 | throws IOException { 217 | try { 218 | SVG svg = SVG. getFromInputStream(source); 219 | if (width != SIZE_ORIGINAL) { 220 | svg.setDocumentWidth(width); 221 | } 222 | if (height != SIZE_ORIGINAL) { 223 | svg.setDocumentHeight(height); 224 | } 225 | return new SimpleResource<>(svg); 226 | } catch (SVGParseException ex) { 227 | throw new IOException("Cannot load SVG from stream", ex); 228 | } 229 | } 230 | } 231 | ``` 232 | 233 | - [SvgDrawableTranscoder](https://github.com/FlyJingFish/ShapeImageView/tree/master/app/src/main/java/com/flyjingfish/shapeimageview/svg/SvgDrawableTranscoder.java) 234 | 235 | ```java 236 | public class SvgDrawableTranscoder implements ResourceTranscoder { 237 | @Nullable 238 | @Override 239 | public Resource transcode( 240 | @NonNull Resource toTranscode, @NonNull Options options) { 241 | SVG svg = toTranscode. get(); 242 | Picture picture = svg. renderToPicture(); 243 | PictureDrawable drawable = new PictureDrawable(picture); 244 | return new SimpleResource<>(drawable); 245 | } 246 | } 247 | ``` 248 | 249 | **New glide configuration** 250 | 251 | [MyAppGlideModule](https://github.com/FlyJingFish/ShapeImageView/tree/master/app/src/main/java/com/flyjingfish/shapeimageview/svg/MyAppGlideModule.java) 252 | 253 | ```java 254 | 255 | @GlideModule 256 | public class MyAppGlideModule extends AppGlideModule { 257 | 258 | @Override 259 | public void registerComponents( 260 | @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { 261 | registry 262 | .register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder()) 263 | .append(InputStream.class, SVG.class, new SvgDecoder()); 264 | } 265 | 266 | @Override 267 | public boolean isManifestParsingEnabled() { 268 | return false; 269 | } 270 | } 271 | 272 | ``` 273 | 274 | **Start using svg** 275 | 276 | - local resources 277 | 278 | ```java 279 | Uri uri = 280 | Uri. parse( 281 | ContentResolver.SCHEME_ANDROID_RESOURCE 282 | + "://" 283 | + getPackageName() 284 | + "/" 285 | + R.raw.dog_heart); 286 | GlideApp.with(this) 287 | .as(PictureDrawable.class) 288 | .transition(withCrossFade()) 289 | .load(uri).into(new CustomTarget() { 290 | @Override 291 | public void onResourceReady(@NonNull PictureDrawable resource, @Nullable Transition transition) { 292 | imageView.setShapeResource(resource); 293 | } 294 | 295 | @Override 296 | public void onLoadCleared(@Nullable Drawable placeholder) { 297 | 298 | } 299 | }); 300 | ``` 301 | 302 | - Internet resources 303 | 304 | ```java 305 | Uri uri = Uri.parse("http://www.clker.com/cliparts/u/Z/2/b/a/6/android-toy-h.svg"); 306 | GlideApp.with(this) 307 | .as(PictureDrawable.class) 308 | .transition(withCrossFade()) 309 | .load(uri).into(new CustomTarget() { 310 | @Override 311 | public void onResourceReady(@NonNull PictureDrawable resource, @Nullable Transition transition) { 312 | imageView.setShapeResource(resource); 313 | } 314 | 315 | @Override 316 | public void onLoadCleared(@Nullable Drawable placeholder) { 317 | 318 | } 319 | }); 320 | ``` 321 | 322 | ## Common problem 323 | 324 | 1. In some graphics, the pictures are not displayed completely after changing attributes such as rotation, rotationX, rotationY, etc. 325 | 326 | - Solution: Set the margin property to solve 327 | 328 | 329 | 330 | ## Finally, I recommend some other libraries I wrote 331 | 332 | - [OpenImage makes it easy to click on a small image in the application to view the animated enlargement effect of the large image](https://github.com/FlyJingFish/OpenImage) 333 | 334 | - [AndroidAOP can request permissions, prohibit multiple points, switch threads, etc. with just one annotation. You can also customize your own Aop code](https://github.com/FlyJingFish/AndroidAOP) 335 | 336 | - [View more open source libraries on the homepage](https://github.com/FlyJingFish) 337 | 338 | 339 | --------------------------------------------------------------------------------