├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-mdpi │ │ │ │ ├── cat.jpg │ │ │ │ ├── code1.png │ │ │ │ ├── code2.png │ │ │ │ ├── code3.png │ │ │ │ ├── gif1.gif │ │ │ │ ├── gif2.gif │ │ │ │ ├── gif3.gif │ │ │ │ ├── gif4.gif │ │ │ │ ├── gif5.gif │ │ │ │ ├── gif6.gif │ │ │ │ ├── girl.jpg │ │ │ │ ├── image1.png │ │ │ │ ├── image2.png │ │ │ │ ├── image3.png │ │ │ │ ├── image4.png │ │ │ │ ├── image5.png │ │ │ │ ├── cat_thumbnail.jpg │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── girl_thumbnail.jpg │ │ │ │ ├── cat_thumbnail_high.jpg │ │ │ │ └── girl_thumbnail_high.jpg │ │ │ ├── drawable │ │ │ │ ├── gif_robot_walk.gif │ │ │ │ └── progressbar_orange_selector.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── attrs.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ └── colors.xml │ │ │ ├── layout │ │ │ │ ├── activity_recycle_view.xml │ │ │ │ ├── activity_preview.xml │ │ │ │ ├── item_photoview.xml │ │ │ │ ├── item_recycle_view.xml │ │ │ │ ├── activity_image.xml │ │ │ │ └── activity_main.xml │ │ │ └── values-w820dp │ │ │ │ └── dimens.xml │ │ ├── java │ │ │ └── cy │ │ │ │ └── wcdb │ │ │ │ └── yyh │ │ │ │ └── com │ │ │ │ └── myglidefactoryview │ │ │ │ ├── widget │ │ │ │ ├── NineImageView │ │ │ │ │ ├── ImageAttr.java │ │ │ │ │ ├── NineImageViewAdapter.java │ │ │ │ │ ├── ImageViewWrapper.java │ │ │ │ │ └── NineImageView.java │ │ │ │ ├── DraggableFrameLayout.java │ │ │ │ └── SlideVerticallyLayout.java │ │ │ │ ├── model │ │ │ │ ├── ImageEntity.java │ │ │ │ └── ModelUtil.java │ │ │ │ ├── image │ │ │ │ ├── FixedViewPager.java │ │ │ │ ├── NineImageViewEventAdapter.java │ │ │ │ ├── SingleImageActivity.java │ │ │ │ ├── ImagesAdapter.java │ │ │ │ └── ImagesActivity.java │ │ │ │ ├── util │ │ │ │ ├── ColorUtil.java │ │ │ │ ├── AppUtil.java │ │ │ │ └── StatusBarUtil.java │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── RecycleViewActivity.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── cy │ │ │ └── wcdb │ │ │ └── yyh │ │ │ └── com │ │ │ └── myglidefactoryview │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── cy │ │ └── wcdb │ │ └── yyh │ │ └── com │ │ └── myglidefactoryview │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── GlideImageView ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ ├── strings.xml │ │ │ └── style.xml │ │ ├── java │ │ └── com │ │ │ └── sunfusheng │ │ │ └── glideimageview │ │ │ ├── progress │ │ │ ├── OnGlideImageViewListener.java │ │ │ ├── OnProgressListener.java │ │ │ ├── ProgressAppGlideModule.java │ │ │ ├── ProgressResponseBody.java │ │ │ ├── ProgressManager.java │ │ │ └── CircleProgressView.java │ │ │ ├── transformation │ │ │ └── GlideCircleTransformation.java │ │ │ ├── util │ │ │ └── DisplayUtil.java │ │ │ ├── GlideImageView.java │ │ │ ├── GlideImageLoader.java │ │ │ └── ShapeImageView.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── .idea ├── copyright │ └── profiles_settings.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /GlideImageView/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':GlideImageView' 2 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /GlideImageView/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | GlideImageView 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/cat.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/code1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/code1.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/code2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/code2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/code3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/code3.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/gif1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/gif1.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/gif2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/gif2.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/gif3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/gif3.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/gif4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/gif4.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/gif5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/gif5.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/gif6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/gif6.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/girl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/girl.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/image1.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/image2.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/image3.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/image4.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/image5.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/gif_robot_walk.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/drawable/gif_robot_walk.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/cat_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/cat_thumbnail.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/girl_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/girl_thumbnail.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/cat_thumbnail_high.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/cat_thumbnail_high.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/girl_thumbnail_high.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeGoogler/MyGlideFactoryView/HEAD/app/src/main/res/mipmap-mdpi/girl_thumbnail_high.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | GlideImageView 4 | the image of transition 5 | %1$d/%2$d 6 | 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_recycle_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/progress/OnGlideImageViewListener.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.progress; 2 | 3 | import com.bumptech.glide.load.engine.GlideException; 4 | 5 | /** 6 | * Created by sunfusheng on 2017/6/14. 7 | */ 8 | public interface OnGlideImageViewListener { 9 | 10 | void onProgress(int percent, boolean isDone, GlideException exception); 11 | } 12 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/progress/OnProgressListener.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.progress; 2 | 3 | import com.bumptech.glide.load.engine.GlideException; 4 | 5 | /** 6 | * Created by sunfusheng on 2017/6/14. 7 | */ 8 | public interface OnProgressListener { 9 | 10 | void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/test/java/cy/wcdb/yyh/com/myglidefactoryview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /GlideImageView/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progressbar_orange_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/widget/NineImageView/ImageAttr.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView; 2 | 3 | import java.io.Serializable; 4 | 5 | public class ImageAttr implements Serializable { 6 | 7 | public String url; 8 | public String thumbnailUrl; 9 | 10 | // 显示的宽高 11 | public int width; 12 | public int height; 13 | 14 | // 左上角坐标 15 | public int left; 16 | public int top; 17 | 18 | @Override 19 | public String toString() { 20 | return "ImageAttr{" + 21 | "url='" + url + '\'' + 22 | ", width=" + width + 23 | ", height=" + height + 24 | ", left=" + left + 25 | ", top=" + top + 26 | '}'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/progress/ProgressAppGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.progress; 2 | 3 | import android.content.Context; 4 | 5 | import com.bumptech.glide.Registry; 6 | import com.bumptech.glide.annotation.GlideModule; 7 | import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; 8 | import com.bumptech.glide.load.model.GlideUrl; 9 | import com.bumptech.glide.module.AppGlideModule; 10 | 11 | import java.io.InputStream; 12 | 13 | /** 14 | * Created by sunfusheng on 2017/6/14. 15 | */ 16 | @GlideModule 17 | public class ProgressAppGlideModule extends AppGlideModule { 18 | 19 | @Override 20 | public void registerComponents(Context context, Registry registry) { 21 | registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(ProgressManager.getOkHttpClient())); 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/model/ImageEntity.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | 6 | /** 7 | * Created by sunfusheng on 2017/6/27. 8 | */ 9 | public class ImageEntity implements Serializable { 10 | 11 | private String title; 12 | private ArrayList images; 13 | 14 | public ImageEntity(String title, ArrayList images) { 15 | this.title = title; 16 | this.images = images; 17 | } 18 | 19 | public String getTitle() { 20 | return title; 21 | } 22 | 23 | public void setTitle(String title) { 24 | this.title = title; 25 | } 26 | 27 | public ArrayList getImages() { 28 | return images; 29 | } 30 | 31 | public void setImages(ArrayList images) { 32 | this.images = images; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/androidTest/java/cy/wcdb/yyh/com/myglidefactoryview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("cy.wcdb.yyh.com.myglidefactoryview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GlideImageView/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.3" 7 | 8 | defaultConfig { 9 | minSdkVersion 19 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0.0" 13 | } 14 | 15 | lintOptions { 16 | abortOnError false 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | compile 'com.android.support:support-v4:25.3.1' 30 | compile 'com.github.bumptech.glide:glide:4.0.0-RC0' 31 | compile 'com.github.bumptech.glide:compiler:4.0.0-RC0' 32 | compile "com.github.bumptech.glide:okhttp3-integration:4.0.0-RC0" 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28sp 5 | 26sp 6 | 24sp 7 | 20sp 8 | 17sp 9 | 16sp 10 | 14sp 11 | 12sp 12 | 10sp 13 | 8sp 14 | 15 | 16 | 1dp 17 | 2dp 18 | 4dp 19 | 8dp 20 | 12dp 21 | 16dp 22 | 24dp 23 | 24 | 25 | 73dp 26 | 36dp 27 | 28dp 28 | 22dp 29 | 15dp 30 | 31 | 0dp 32 | 33 | 34 | -------------------------------------------------------------------------------- /GlideImageView/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/sunfusheng/Android/Studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_photoview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/image/FixedViewPager.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.image; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | /** 修复图片在ViewPager控件中缩放报错的BUG */ 9 | public class FixedViewPager extends ViewPager { 10 | 11 | public FixedViewPager(Context context) { 12 | super(context); 13 | } 14 | 15 | public FixedViewPager(Context context, AttributeSet attrs) { 16 | super(context, attrs); 17 | } 18 | 19 | @Override 20 | public boolean onTouchEvent(MotionEvent ev) { 21 | try { 22 | return super.onTouchEvent(ev); 23 | } catch (IllegalArgumentException ex) { 24 | ex.printStackTrace(); 25 | } 26 | return false; 27 | } 28 | 29 | @Override 30 | public boolean onInterceptTouchEvent(MotionEvent ev) { 31 | try { 32 | return super.onInterceptTouchEvent(ev); 33 | } catch (IllegalArgumentException ex) { 34 | ex.printStackTrace(); 35 | } 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/widget/NineImageView/NineImageViewAdapter.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView; 2 | 3 | import android.content.Context; 4 | import android.widget.ImageView; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | public abstract class NineImageViewAdapter implements Serializable { 10 | 11 | protected Context context; 12 | private List imageAttrs; 13 | 14 | public NineImageViewAdapter(Context context, List images) { 15 | this.context = context; 16 | this.imageAttrs = images; 17 | } 18 | 19 | protected void onImageItemClick(Context context, NineImageView nineImageView, int index, List images) { 20 | } 21 | 22 | protected ImageView generateImageView(Context context) { 23 | ImageViewWrapper imageView = new ImageViewWrapper(context); 24 | imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 25 | return imageView; 26 | } 27 | 28 | public List getImageAttrs() { 29 | return imageAttrs; 30 | } 31 | 32 | public void setImageAttrs(List images) { 33 | this.imageAttrs = images; 34 | } 35 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "cy.wcdb.yyh.com.myglidefactoryview" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support:percent:25.3.1' 29 | compile 'com.android.support:design:25.3.1' 30 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 31 | compile 'com.github.chrisbanes:PhotoView:2.0.0' 32 | testCompile 'junit:junit:4.12' 33 | compile project(':GlideImageView') 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recycle_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 28 | 29 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/util/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.util; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by sunfusheng on 16/4/22. 7 | */ 8 | public class ColorUtil { 9 | 10 | // 合成新的颜色值 11 | public static int getNewColorByStartEndColor(Context context, float fraction, int startValue, int endValue) { 12 | return evaluate(fraction, context.getResources().getColor(startValue), context.getResources().getColor(endValue)); 13 | } 14 | /** 15 | * 合成新的颜色值 16 | * @param fraction 颜色取值的级别 (0.0f ~ 1.0f) 17 | * @param startValue 开始显示的颜色 18 | * @param endValue 结束显示的颜色 19 | * @return 返回生成新的颜色值 20 | */ 21 | public static int evaluate(float fraction, int startValue, int endValue) { 22 | int startA = (startValue >> 24) & 0xff; 23 | int startR = (startValue >> 16) & 0xff; 24 | int startG = (startValue >> 8) & 0xff; 25 | int startB = startValue & 0xff; 26 | 27 | int endA = (endValue >> 24) & 0xff; 28 | int endR = (endValue >> 16) & 0xff; 29 | int endG = (endValue >> 8) & 0xff; 30 | int endB = endValue & 0xff; 31 | 32 | return ((startA + (int) (fraction * (endA - startA))) << 24) | 33 | ((startR + (int) (fraction * (endR - startR))) << 16) | 34 | ((startG + (int) (fraction * (endG - startG))) << 8) | 35 | ((startB + (int) (fraction * (endB - startB)))); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/transformation/GlideCircleTransformation.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.transformation; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapShader; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | 8 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 9 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 10 | 11 | import java.security.MessageDigest; 12 | 13 | /** 14 | * Created by sunfusheng on 2017/6/6. 15 | */ 16 | public class GlideCircleTransformation extends BitmapTransformation { 17 | 18 | public GlideCircleTransformation() { 19 | } 20 | 21 | @Override 22 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 23 | return circleCrop(pool, toTransform); 24 | } 25 | 26 | private static Bitmap circleCrop(BitmapPool pool, Bitmap source) { 27 | if (source == null) return null; 28 | 29 | int size = Math.min(source.getWidth(), source.getHeight()); 30 | int x = (source.getWidth() - size) / 2; 31 | int y = (source.getHeight() - size) / 2; 32 | 33 | Bitmap square = Bitmap.createBitmap(source, x, y, size, size); 34 | Bitmap circle = pool.get(size, size, Bitmap.Config.ARGB_8888); 35 | 36 | Canvas canvas = new Canvas(circle); 37 | Paint paint = new Paint(); 38 | paint.setShader(new BitmapShader(square, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 39 | paint.setAntiAlias(true); 40 | float r = size / 2f; 41 | canvas.drawCircle(r, r, r, paint); 42 | return circle; 43 | } 44 | 45 | @Override 46 | public void updateDiskCacheKey(MessageDigest messageDigest) { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 18 | 19 | 28 | 29 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/image/NineImageViewEventAdapter.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.image; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | 9 | 10 | import java.io.Serializable; 11 | import java.util.List; 12 | 13 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.ImageAttr; 14 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.NineImageView; 15 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.NineImageViewAdapter; 16 | 17 | 18 | public class NineImageViewEventAdapter extends NineImageViewAdapter { 19 | 20 | public NineImageViewEventAdapter(Context context, List imageAttr) { 21 | super(context, imageAttr); 22 | } 23 | 24 | @Override 25 | protected void onImageItemClick(Context context, NineImageView nineImageView, int index, List images) { 26 | for (int i = 0; i < images.size(); i++) { 27 | ImageAttr attr = images.get(i); 28 | View imageView = nineImageView.getChildAt(i); 29 | if (i >= nineImageView.getMaxSize()) { 30 | imageView = nineImageView.getChildAt(nineImageView.getMaxSize() - 1); 31 | } 32 | attr.width = imageView.getWidth(); 33 | attr.height = imageView.getHeight(); 34 | int[] points = new int[2]; 35 | imageView.getLocationInWindow(points); 36 | attr.left = points[0]; 37 | attr.top = points[1]; 38 | } 39 | 40 | Intent intent = new Intent(context, ImagesActivity.class); 41 | Bundle bundle = new Bundle(); 42 | bundle.putSerializable(ImagesActivity.IMAGE_ATTR, (Serializable) images); 43 | bundle.putInt(ImagesActivity.CUR_POSITION, index); 44 | intent.putExtras(bundle); 45 | context.startActivity(intent); 46 | ((Activity) context).overridePendingTransition(0, 0); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/progress/ProgressResponseBody.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.progress; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.io.IOException; 6 | 7 | import okhttp3.MediaType; 8 | import okhttp3.ResponseBody; 9 | import okio.Buffer; 10 | import okio.BufferedSource; 11 | import okio.ForwardingSource; 12 | import okio.Okio; 13 | import okio.Source; 14 | 15 | /** 16 | * Created by sunfusheng on 2017/6/14. 17 | */ 18 | public class ProgressResponseBody extends ResponseBody { 19 | 20 | private String imageUrl; 21 | private ResponseBody responseBody; 22 | private OnProgressListener progressListener; 23 | private BufferedSource bufferedSource; 24 | 25 | public ProgressResponseBody(String url, ResponseBody responseBody, OnProgressListener progressListener) { 26 | this.imageUrl = url; 27 | this.responseBody = responseBody; 28 | this.progressListener = progressListener; 29 | } 30 | 31 | @Override 32 | public MediaType contentType() { 33 | return responseBody.contentType(); 34 | } 35 | 36 | @Override 37 | public long contentLength() { 38 | return responseBody.contentLength(); 39 | } 40 | 41 | @Override 42 | public BufferedSource source() { 43 | if (bufferedSource == null) { 44 | bufferedSource = Okio.buffer(source(responseBody.source())); 45 | } 46 | return bufferedSource; 47 | } 48 | 49 | private Source source(Source source) { 50 | return new ForwardingSource(source) { 51 | long totalBytesRead = 0; 52 | 53 | @Override 54 | public long read(@NonNull Buffer sink, long byteCount) throws IOException { 55 | long bytesRead = super.read(sink, byteCount); 56 | totalBytesRead += (bytesRead == -1) ? 0 : bytesRead; 57 | 58 | if (progressListener != null) { 59 | progressListener.onProgress(imageUrl, totalBytesRead, contentLength(), (bytesRead == -1), null); 60 | } 61 | return bytesRead; 62 | } 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GlideImageView/src/main/res/values/style.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 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | 9 | import cy.wcdb.yyh.com.myglidefactoryview.util.StatusBarUtil; 10 | 11 | 12 | /** 13 | * Created by sunfusheng on 2016/12/24. 14 | */ 15 | public abstract class BaseActivity extends Activity { 16 | 17 | protected Activity mActivity; 18 | protected Context mContext; 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | mActivity = this; 24 | mContext = this; 25 | } 26 | 27 | protected void setStatusBarTranslucent(boolean isLightStatusBar) { 28 | StatusBarUtil.setStatusBarTranslucent(this, isLightStatusBar); 29 | } 30 | 31 | @Override 32 | public void startActivity(Intent intent) { 33 | super.startActivity(intent); 34 | if (intent == null) return; 35 | if (intent.getComponent() == null) return; 36 | String className = intent.getComponent().getClassName(); 37 | if (!className.equals(MainActivity.class.getName())) { 38 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 39 | } 40 | } 41 | 42 | @Override 43 | public void startActivityForResult(Intent intent, int requestCode) { 44 | super.startActivityForResult(intent, requestCode); 45 | if (intent == null) return; 46 | if (intent.getComponent() == null) return; 47 | String className = intent.getComponent().getClassName(); 48 | if (!className.equals(MainActivity.class.getName())) { 49 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 50 | } 51 | } 52 | 53 | @Override 54 | public void finish() { 55 | super.finish(); 56 | if (!((Object) this).getClass().equals(MainActivity.class)) { 57 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 58 | } 59 | } 60 | 61 | @Override 62 | protected void onDestroy() { 63 | super.onDestroy(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #00796b 5 | #00796b 6 | #00796b 7 | 8 | 9 | #ff000000 10 | #D9000000 11 | #8C000000 12 | #66000000 13 | #1A000000 14 | #C34A42 15 | #FF9800 16 | #009734 17 | #46AE36 18 | #47B000 19 | #007AFF 20 | #1194F6 21 | #9D1BB2 22 | 23 | #00000000 24 | #0B000000 25 | #1A000000 26 | #33000000 27 | #4D000000 28 | #66000000 29 | #80000000 30 | #9A000000 31 | #B3000000 32 | #CC000000 33 | #E5000000 34 | #FF000000 35 | 36 | #00ffffff 37 | #1Affffff 38 | #33ffffff 39 | #4Dffffff 40 | #66ffffff 41 | #80ffffff 42 | #9Affffff 43 | #B3ffffff 44 | #CCffffff 45 | #E5ffffff 46 | #FFffffff 47 | 48 | #1A000000 49 | 50 | #FFFFFF 51 | #fbc02d 52 | #FFD700 53 | #f57c00 54 | #d01716 55 | #808080 56 | #0a7e07 57 | #455ede 58 | #000000 59 | 60 | 61 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/util/DisplayUtil.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.util; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | import android.view.WindowManager; 6 | 7 | public class DisplayUtil { 8 | 9 | private static WindowManager windowManager; 10 | 11 | private static WindowManager getWindowManager(Context context) { 12 | if (windowManager == null) { 13 | windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 14 | } 15 | return windowManager; 16 | } 17 | 18 | // 根据手机的分辨率将dp的单位转成px(像素) 19 | public static int dip2px(Context context, float dpValue) { 20 | final float scale = context.getResources().getDisplayMetrics().density; 21 | return (int) (dpValue * scale + 0.5f); 22 | } 23 | 24 | // 根据手机的分辨率将px(像素)的单位转成dp 25 | public static int px2dip(Context context, float pxValue) { 26 | final float scale = context.getResources().getDisplayMetrics().density; 27 | return (int) (pxValue / scale + 0.5f); 28 | } 29 | 30 | // 将px值转换为sp值 31 | public static int px2sp(Context context, float pxValue) { 32 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 33 | return (int) (pxValue / fontScale + 0.5f); 34 | } 35 | 36 | // 将sp值转换为px值 37 | public static int sp2px(Context context, float spValue) { 38 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 39 | return (int) (spValue * fontScale + 0.5f); 40 | } 41 | 42 | // 屏幕宽度(像素) 43 | public static int getScreenWidth(Context context) { 44 | DisplayMetrics metric = new DisplayMetrics(); 45 | getWindowManager(context).getDefaultDisplay().getMetrics(metric); 46 | return metric.widthPixels; 47 | } 48 | 49 | // 屏幕高度(像素) 50 | public static int getScreenHeight(Context context) { 51 | DisplayMetrics metric = new DisplayMetrics(); 52 | getWindowManager(context).getDefaultDisplay().getMetrics(metric); 53 | return metric.heightPixels; 54 | } 55 | 56 | public static float getScreenDensity(Context context) { 57 | try { 58 | DisplayMetrics displayMetrics = new DisplayMetrics(); 59 | getWindowManager(context).getDefaultDisplay().getMetrics(displayMetrics); 60 | return displayMetrics.density; 61 | } catch (Throwable e) { 62 | e.printStackTrace(); 63 | return 1.0f; 64 | } 65 | } 66 | 67 | public static int getDensityDpi(Context context) { 68 | try { 69 | DisplayMetrics displayMetrics = new DisplayMetrics(); 70 | getWindowManager(context).getDefaultDisplay().getMetrics(displayMetrics); 71 | return displayMetrics.densityDpi; 72 | } catch (Throwable e) { 73 | e.printStackTrace(); 74 | return 320; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/util/AppUtil.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.util; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.net.ConnectivityManager; 7 | import android.net.NetworkInfo; 8 | import android.text.TextUtils; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Created by sunfusheng on 2017/1/17. 14 | */ 15 | public class AppUtil { 16 | 17 | public static boolean notEmpty(List list) { 18 | return !isEmpty(list); 19 | } 20 | 21 | public static boolean isEmpty(List list) { 22 | if (list == null || list.size() == 0) { 23 | return true; 24 | } 25 | return false; 26 | } 27 | 28 | // 判断网络是否可用 29 | public static boolean isNetworkAvailable(Context context) { 30 | try { 31 | ConnectivityManager connectivity = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); 32 | if (connectivity != null) { 33 | NetworkInfo info = connectivity.getActiveNetworkInfo(); 34 | if (info != null && info.isConnected()) { 35 | if (info.getState() == NetworkInfo.State.CONNECTED) { 36 | return true; 37 | } 38 | } 39 | } 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | return false; 43 | } 44 | return false; 45 | } 46 | 47 | // WiFi是否连接 48 | public static boolean isWiFiAvailable(Context context) { 49 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); 50 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 51 | if (networkInfo != null && networkInfo.isAvailable() && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | // 获取当前应用的版本号 58 | public static String getVersionName(Context context) { 59 | try { 60 | PackageManager packageManager = context.getApplicationContext().getPackageManager(); 61 | PackageInfo packInfo = packageManager.getPackageInfo(context.getApplicationContext().getPackageName(), 0); 62 | String version = packInfo.versionName; 63 | if (!TextUtils.isEmpty(version)) { 64 | return "V" + version; 65 | } 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | } 69 | return "V1.0"; 70 | } 71 | 72 | // 获取当前应用的版本号 73 | public static int getVersionCode(Context context) { 74 | try { 75 | PackageManager packageManager = context.getApplicationContext().getPackageManager(); 76 | PackageInfo packInfo = packageManager.getPackageInfo(context.getApplicationContext().getPackageName(), 0); 77 | return packInfo.versionCode; 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | } 81 | return 1; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/RecycleViewActivity.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import cy.wcdb.yyh.com.myglidefactoryview.image.NineImageViewEventAdapter; 16 | import cy.wcdb.yyh.com.myglidefactoryview.model.ImageEntity; 17 | import cy.wcdb.yyh.com.myglidefactoryview.model.ModelUtil; 18 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.ImageAttr; 19 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.NineImageView; 20 | 21 | public class RecycleViewActivity extends BaseActivity { 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_recycle_view); 27 | 28 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView); 29 | LinearLayoutManager layoutManager = new LinearLayoutManager(this); 30 | recyclerView.setLayoutManager(layoutManager); 31 | RecyclerViewAdapter adapter = new RecyclerViewAdapter(ModelUtil.getImages()); 32 | recyclerView.setAdapter(adapter); 33 | } 34 | 35 | class RecyclerViewAdapter extends RecyclerView.Adapter { 36 | 37 | private List list; 38 | 39 | RecyclerViewAdapter(List list) { 40 | this.list = list; 41 | } 42 | 43 | @Override 44 | public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 45 | View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_recycle_view, viewGroup, false); 46 | return new ViewHolder(view); 47 | } 48 | 49 | @Override 50 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 51 | ImageEntity entity = list.get(position); 52 | 53 | viewHolder.tvTitle.setText(entity.getTitle()); 54 | 55 | ArrayList imageAttrs = new ArrayList<>(); 56 | for (String url : entity.getImages()) { 57 | ImageAttr attr = new ImageAttr(); 58 | attr.url = url; 59 | imageAttrs.add(attr); 60 | } 61 | if (viewHolder.nineImageView.getAdapter() != null) { 62 | viewHolder.nineImageView.setAdapter(viewHolder.nineImageView.getAdapter()); 63 | } else { 64 | viewHolder.nineImageView.setAdapter(new NineImageViewEventAdapter(viewHolder.nineImageView.getContext(), imageAttrs)); 65 | } 66 | } 67 | 68 | @Override 69 | public int getItemCount() { 70 | return list.size(); 71 | } 72 | 73 | class ViewHolder extends RecyclerView.ViewHolder { 74 | TextView tvTitle; 75 | NineImageView nineImageView; 76 | 77 | ViewHolder(View view) { 78 | super(view); 79 | tvTitle = (TextView) view.findViewById(R.id.tv_title); 80 | nineImageView = (NineImageView) view.findViewById(R.id.nineImageView); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 24 | 25 | 26 | 34 | 35 | 47 | 48 | 62 | 63 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/model/ModelUtil.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by sunfusheng on 2017/6/27. 8 | */ 9 | public class ModelUtil { 10 | 11 | public static List getImages() { 12 | List list = new ArrayList<>(); 13 | 14 | ArrayList images = new ArrayList<>(); 15 | images.add("http://pic.58pic.com/58pic/13/62/02/07d58PICcxz_1024.jpg"); 16 | list.add(new ImageEntity("画卷:天道酬勤", images)); 17 | 18 | images = new ArrayList<>(); 19 | images.add("http://img.pconline.com.cn/images/photo2/591572/1100105945280.jpg"); 20 | images.add("http://www.pop-photo.com.cn/data/attachment/forum/myspace/image/2011/07/0/46/76/467693_233590968.jpg"); 21 | images.add("http://www.52design.com/pic/20121/201211313245374.jpg"); 22 | list.add(new ImageEntity("微距离摄影", images)); 23 | 24 | images = new ArrayList<>(); 25 | images.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498629002766&di=6fac924b9c9bc0858074a5eb455e4bd8&imgtype=0&src=http%3A%2F%2Fimg1.3lian.com%2F2015%2Fw21%2F8%2Fd%2F85.jpg"); 26 | list.add(new ImageEntity("又一个小猫咪(450X798)", images)); 27 | 28 | images = new ArrayList<>(); 29 | images.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498628972675&di=329747bae23548dfd988fbc6dcaf76b7&imgtype=0&src=http%3A%2F%2Fwww.17sucai.com%2Fupload%2F534358%2F2016-06-13%2Facdf7f6e040aee07147f9e3508833c8a_big.jpg"); 30 | list.add(new ImageEntity("一个小猫咪(650X406)", images)); 31 | 32 | images = new ArrayList<>(); 33 | images.add("http://img15.3lian.com/2015/h1/308/d/199.jpg"); 34 | images.add("http://pic1.win4000.com/wallpaper/f/566a714a786cc.jpg"); 35 | images.add("http://img15.3lian.com/2015/h1/308/d/204.jpg"); 36 | images.add("http://imglf1.ph.126.net/DIe00tFQ1SZOdKUDG8AO1g==/2068278128970036688.jpg"); 37 | list.add(new ImageEntity("世界著名建筑", images)); 38 | 39 | images = new ArrayList<>(); 40 | images.add("http://wx4.sinaimg.cn/mw690/e59bcb0dly1fgqlue0vtvj20c83htx3d.jpg"); 41 | list.add(new ImageEntity("开发者头条的截图(440X4529)", images)); 42 | 43 | images = new ArrayList<>(); 44 | images.add("http://img2.imgtn.bdimg.com/it/u=2850936076,2080165544&fm=206&gp=0.jpg"); 45 | images.add("http://img3.imgtn.bdimg.com/it/u=524208507,12616758&fm=206&gp=0.jpg"); 46 | images.add("http://img3.imgtn.bdimg.com/it/u=698582197,4250615262&fm=206&gp=0.jpg"); 47 | images.add("http://img5.imgtn.bdimg.com/it/u=1467751238,3257336851&fm=11&gp=0.jpg"); 48 | images.add("http://img5.imgtn.bdimg.com/it/u=3191365283,111438732&fm=21&gp=0.jpg"); 49 | images.add("http://img5.imgtn.bdimg.com/it/u=482494496,1350922497&fm=206&gp=0.jpg"); 50 | images.add("http://img4.imgtn.bdimg.com/it/u=2440866214,1867472386&fm=21&gp=0.jpg"); 51 | images.add("http://img3.imgtn.bdimg.com/it/u=3040385967,1031044866&fm=21&gp=0.jpg"); 52 | images.add("http://img1.imgtn.bdimg.com/it/u=1832737924,144748431&fm=21&gp=0.jpg"); 53 | images.add("http://img5.imgtn.bdimg.com/it/u=2091366266,1524114981&fm=21&gp=0.jpg"); 54 | images.add("http://img5.imgtn.bdimg.com/it/u=2091366266,1524114981&fm=21&gp=0.jpg"); 55 | images.add("http://img5.imgtn.bdimg.com/it/u=1424970962,1243597989&fm=21&gp=0.jpg"); 56 | list.add(new ImageEntity("一共12张图片", images)); 57 | 58 | return list; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/progress/ProgressManager.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.progress; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.bumptech.glide.load.engine.GlideException; 6 | 7 | import java.io.IOException; 8 | import java.lang.ref.WeakReference; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | import okhttp3.Interceptor; 14 | import okhttp3.OkHttpClient; 15 | import okhttp3.Request; 16 | import okhttp3.Response; 17 | 18 | /** 19 | * Created by sunfusheng on 2017/6/14. 20 | */ 21 | public class ProgressManager { 22 | 23 | private static List> listeners = Collections.synchronizedList(new ArrayList>()); 24 | private static OkHttpClient okHttpClient; 25 | 26 | private ProgressManager() { 27 | } 28 | 29 | public static OkHttpClient getOkHttpClient() { 30 | if (okHttpClient == null) { 31 | okHttpClient = new OkHttpClient.Builder() 32 | .addNetworkInterceptor(new Interceptor() { 33 | @Override 34 | public Response intercept(@NonNull Chain chain) throws IOException { 35 | Request request = chain.request(); 36 | Response response = chain.proceed(request); 37 | return response.newBuilder() 38 | .body(new ProgressResponseBody(request.url().toString(), response.body(), LISTENER)) 39 | .build(); 40 | } 41 | }) 42 | .build(); 43 | } 44 | return okHttpClient; 45 | } 46 | 47 | private static final OnProgressListener LISTENER = new OnProgressListener() { 48 | @Override 49 | public void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception) { 50 | if (listeners == null || listeners.size() == 0) return; 51 | 52 | for (int i = 0; i < listeners.size(); i++) { 53 | WeakReference listener = listeners.get(i); 54 | OnProgressListener progressListener = listener.get(); 55 | if (progressListener == null) { 56 | listeners.remove(i); 57 | } else { 58 | progressListener.onProgress(imageUrl, bytesRead, totalBytes, isDone, exception); 59 | } 60 | } 61 | } 62 | }; 63 | 64 | public static void addProgressListener(OnProgressListener progressListener) { 65 | if (progressListener == null) return; 66 | 67 | if (findProgressListener(progressListener) == null) { 68 | listeners.add(new WeakReference<>(progressListener)); 69 | } 70 | } 71 | 72 | public static void removeProgressListener(OnProgressListener progressListener) { 73 | if (progressListener == null) return; 74 | 75 | WeakReference listener = findProgressListener(progressListener); 76 | if (listener != null) { 77 | listeners.remove(listener); 78 | } 79 | } 80 | 81 | private static WeakReference findProgressListener(OnProgressListener listener) { 82 | if (listener == null) return null; 83 | if (listeners == null || listeners.size() == 0) return null; 84 | 85 | for (int i = 0; i < listeners.size(); i++) { 86 | WeakReference progressListener = listeners.get(i); 87 | if (progressListener.get() == listener) { 88 | return progressListener; 89 | } 90 | } 91 | return null; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/util/StatusBarUtil.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.util; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.os.Build; 6 | import android.view.View; 7 | import android.view.Window; 8 | import android.view.WindowManager; 9 | 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * Created by sunfusheng on 16/11/20. 15 | */ 16 | public class StatusBarUtil { 17 | 18 | // 设置状态栏透明与字体颜色 19 | public static void setStatusBarTranslucent(Activity activity, boolean isLightStatusBar) { 20 | if (activity == null) return; 21 | Window window = activity.getWindow(); 22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 23 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 24 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 25 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 26 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 27 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 28 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 29 | window.setStatusBarColor(Color.TRANSPARENT); 30 | } 31 | 32 | if (isXiaomi()) { 33 | setXiaomiStatusBar(window, isLightStatusBar); 34 | } else if (isMeizu()) { 35 | setMeizuStatusBar(window, isLightStatusBar); 36 | } 37 | } 38 | 39 | // 是否是小米手机 40 | public static boolean isXiaomi() { 41 | return "Xiaomi".equals(Build.MANUFACTURER); 42 | } 43 | 44 | // 设置小米状态栏 45 | public static void setXiaomiStatusBar(Window window, boolean isLightStatusBar) { 46 | Class clazz = window.getClass(); 47 | try { 48 | Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); 49 | Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); 50 | int darkModeFlag = field.getInt(layoutParams); 51 | Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); 52 | extraFlagField.invoke(window, isLightStatusBar ? darkModeFlag : 0, darkModeFlag); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | // 是否是魅族手机 59 | public static boolean isMeizu() { 60 | try { 61 | Method method = Build.class.getMethod("hasSmartBar"); 62 | return method != null; 63 | } catch (NoSuchMethodException e) { 64 | } 65 | return false; 66 | } 67 | 68 | // 设置魅族状态栏 69 | public static void setMeizuStatusBar(Window window, boolean isLightStatusBar) { 70 | WindowManager.LayoutParams params = window.getAttributes(); 71 | try { 72 | Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); 73 | Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); 74 | darkFlag.setAccessible(true); 75 | meizuFlags.setAccessible(true); 76 | int bit = darkFlag.getInt(null); 77 | int value = meizuFlags.getInt(params); 78 | if (isLightStatusBar) { 79 | value |= bit; 80 | } else { 81 | value &= ~bit; 82 | } 83 | meizuFlags.setInt(params, value); 84 | window.setAttributes(params); 85 | darkFlag.setAccessible(false); 86 | meizuFlags.setAccessible(false); 87 | } catch (Exception e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/GlideImageView.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.support.annotation.DrawableRes; 6 | import android.util.AttributeSet; 7 | 8 | import com.bumptech.glide.request.RequestOptions; 9 | import com.sunfusheng.glideimageview.progress.OnGlideImageViewListener; 10 | import com.sunfusheng.glideimageview.progress.OnProgressListener; 11 | 12 | /** 13 | * Created by sunfusheng on 2017/6/6. 14 | */ 15 | public class GlideImageView extends ShapeImageView { 16 | 17 | private GlideImageLoader mImageLoader; 18 | 19 | public GlideImageView(Context context) { 20 | this(context, null); 21 | } 22 | 23 | public GlideImageView(Context context, AttributeSet attrs) { 24 | this(context, attrs, 0); 25 | } 26 | 27 | public GlideImageView(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | init(); 30 | } 31 | 32 | private void init() { 33 | mImageLoader = GlideImageLoader.create(this); 34 | } 35 | 36 | public GlideImageLoader getImageLoader() { 37 | if (mImageLoader == null) { 38 | mImageLoader = GlideImageLoader.create(this); 39 | } 40 | return mImageLoader; 41 | } 42 | 43 | public String getImageUrl() { 44 | return getImageLoader().getImageUrl(); 45 | } 46 | 47 | public RequestOptions requestOptions(int placeholderResId) { 48 | return getImageLoader().requestOptions(placeholderResId); 49 | } 50 | 51 | public RequestOptions circleRequestOptions(int placeholderResId) { 52 | return getImageLoader().circleRequestOptions(placeholderResId); 53 | } 54 | 55 | public GlideImageView load(int resId, RequestOptions options) { 56 | getImageLoader().load(resId, options); 57 | return this; 58 | } 59 | 60 | public GlideImageView load(Uri uri, RequestOptions options) { 61 | getImageLoader().load(uri, options); 62 | return this; 63 | } 64 | 65 | public GlideImageView load(String url, RequestOptions options) { 66 | getImageLoader().load(url, options); 67 | return this; 68 | } 69 | 70 | public GlideImageView loadImage(String url, int placeholderResId) { 71 | getImageLoader().loadImage(url, placeholderResId); 72 | return this; 73 | } 74 | 75 | public GlideImageView loadLocalImage(@DrawableRes int resId, int placeholderResId) { 76 | getImageLoader().loadLocalImage(resId, placeholderResId); 77 | return this; 78 | } 79 | 80 | public GlideImageView loadLocalImage(String localPath, int placeholderResId) { 81 | getImageLoader().loadLocalImage(localPath, placeholderResId); 82 | return this; 83 | } 84 | 85 | public GlideImageView loadCircleImage(String url, int placeholderResId) { 86 | setShapeType(ShapeType.CIRCLE); 87 | getImageLoader().loadCircleImage(url, placeholderResId); 88 | return this; 89 | } 90 | 91 | public GlideImageView loadLocalCircleImage(int resId, int placeholderResId) { 92 | setShapeType(ShapeType.CIRCLE); 93 | getImageLoader().loadLocalCircleImage(resId, placeholderResId); 94 | return this; 95 | } 96 | 97 | public GlideImageView loadLocalCircleImage(String localPath, int placeholderResId) { 98 | setShapeType(ShapeType.CIRCLE); 99 | getImageLoader().loadLocalCircleImage(localPath, placeholderResId); 100 | return this; 101 | } 102 | 103 | public GlideImageView listener(OnGlideImageViewListener listener) { 104 | getImageLoader().setOnGlideImageViewListener(getImageUrl(), listener); 105 | return this; 106 | } 107 | 108 | public GlideImageView listener(OnProgressListener listener) { 109 | getImageLoader().setOnProgressListener(getImageUrl(), listener); 110 | return this; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/widget/DraggableFrameLayout.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Rect; 5 | import android.support.annotation.AttrRes; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.widget.ViewDragHelper; 9 | import android.util.AttributeSet; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | import android.widget.FrameLayout; 13 | 14 | import com.sunfusheng.glideimageview.util.DisplayUtil; 15 | 16 | /** 17 | * Created by sunfusheng on 2017/6/27. 18 | */ 19 | public class DraggableFrameLayout extends FrameLayout { 20 | 21 | private ViewDragHelper dragHelper; 22 | private int screenWidth; 23 | private int margin = 10; 24 | 25 | public DraggableFrameLayout(@NonNull Context context) { 26 | this(context, null); 27 | } 28 | 29 | public DraggableFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { 30 | this(context, attrs, 0); 31 | } 32 | 33 | public DraggableFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | init(); 36 | } 37 | 38 | private void init() { 39 | screenWidth = DisplayUtil.getScreenWidth(getContext()); 40 | margin = DisplayUtil.dip2px(getContext(), margin); 41 | 42 | dragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() { 43 | 44 | @Override 45 | public boolean tryCaptureView(View child, int pointerId) { 46 | return true; 47 | } 48 | 49 | @Override 50 | public void onViewCaptured(View capturedChild, int activePointerId) { 51 | super.onViewCaptured(capturedChild, activePointerId); 52 | } 53 | 54 | @Override 55 | public int clampViewPositionHorizontal(View child, int left, int dx) { 56 | return left; 57 | } 58 | 59 | @Override 60 | public int clampViewPositionVertical(View child, int top, int dy) { 61 | return top; 62 | } 63 | 64 | @Override 65 | public int getViewHorizontalDragRange(View child) { 66 | return getMeasuredWidth() - child.getMeasuredWidth(); 67 | } 68 | 69 | @Override 70 | public int getViewVerticalDragRange(View child) { 71 | return getMeasuredHeight() - child.getMeasuredHeight(); 72 | } 73 | 74 | @Override 75 | public void onViewReleased(View releasedChild, float xvel, float yvel) { 76 | super.onViewReleased(releasedChild, xvel, yvel); 77 | int viewWidth = releasedChild.getWidth(); 78 | int viewHeight = releasedChild.getHeight(); 79 | int curLeft = releasedChild.getLeft(); 80 | int curTop = releasedChild.getTop(); 81 | 82 | int finalLeft = margin; 83 | int finalTop = curTop < margin ? margin : curTop; 84 | 85 | if ((curLeft + viewWidth / 2) > screenWidth / 2) { 86 | finalLeft = screenWidth - viewWidth - margin; 87 | } 88 | 89 | if ((finalTop + viewHeight) > getHeight()) { 90 | finalTop = getHeight() - viewHeight - margin; 91 | } 92 | 93 | dragHelper.settleCapturedViewAt(finalLeft, finalTop); 94 | invalidate(); 95 | } 96 | }); 97 | } 98 | 99 | @Override 100 | public boolean onInterceptTouchEvent(MotionEvent ev) { 101 | return dragHelper.shouldInterceptTouchEvent(ev); 102 | } 103 | 104 | @Override 105 | public boolean onTouchEvent(MotionEvent event) { 106 | dragHelper.processTouchEvent(event); 107 | return isTouchChildView(event); 108 | } 109 | 110 | @Override 111 | public void computeScroll() { 112 | super.computeScroll(); 113 | if (dragHelper.continueSettling(true)) { 114 | invalidate(); 115 | } 116 | } 117 | 118 | private boolean isTouchChildView(MotionEvent ev) { 119 | Rect rect = new Rect(); 120 | for (int i = 0; i < getChildCount(); i++) { 121 | View view = getChildAt(i); 122 | rect.set((int) view.getX(), (int) view.getY(), (int) view.getX() + view.getWidth(), (int) view.getY() + view.getHeight()); 123 | if (rect.contains((int) ev.getX(), (int) ev.getY())) { 124 | return true; 125 | } 126 | } 127 | return false; 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/widget/NineImageView/ImageViewWrapper.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.PorterDuff; 8 | import android.graphics.drawable.Drawable; 9 | import android.support.v4.view.ViewCompat; 10 | import android.text.TextPaint; 11 | import android.util.AttributeSet; 12 | import android.util.TypedValue; 13 | import android.view.MotionEvent; 14 | import android.widget.ImageView; 15 | 16 | public class ImageViewWrapper extends ImageView { 17 | 18 | private int moreNum = 0; //显示更多的数量 19 | private int maskColor = 0x88000000; //默认的遮盖颜色 20 | private float textSize = 30; //显示文字的大小单位sp 21 | private int textColor = 0xFFFFFFFF; //显示文字的颜色 22 | 23 | private TextPaint textPaint; //文字的画笔 24 | private String msg = ""; //要绘制的文字 25 | 26 | public ImageViewWrapper(Context context) { 27 | this(context, null); 28 | } 29 | 30 | public ImageViewWrapper(Context context, AttributeSet attrs) { 31 | this(context, attrs, 0); 32 | } 33 | 34 | public ImageViewWrapper(Context context, AttributeSet attrs, int defStyleAttr) { 35 | super(context, attrs, defStyleAttr); 36 | 37 | //转化单位 38 | textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize, getContext().getResources().getDisplayMetrics()); 39 | 40 | textPaint = new TextPaint(); 41 | textPaint.setTextAlign(Paint.Align.CENTER); //文字居中对齐 42 | textPaint.setAntiAlias(true); //抗锯齿 43 | textPaint.setTextSize(textSize); //设置文字大小 44 | textPaint.setColor(textColor); //设置文字颜色 45 | } 46 | 47 | @Override 48 | protected void onDraw(Canvas canvas) { 49 | super.onDraw(canvas); 50 | if (moreNum > 0) { 51 | canvas.drawColor(maskColor); 52 | float baseY = getHeight() / 2 - (textPaint.ascent() + textPaint.descent()) / 2; 53 | canvas.drawText(msg, getWidth() / 2, baseY, textPaint); 54 | } 55 | } 56 | 57 | @Override 58 | public boolean onTouchEvent(MotionEvent event) { 59 | switch (event.getAction()) { 60 | case MotionEvent.ACTION_DOWN: 61 | Drawable drawable = getDrawable(); 62 | if (drawable != null) { 63 | /** 64 | * 默认情况下,所有的从同一资源(R.drawable.XXX)加载来的drawable实例都共享一个共用的状态, 65 | * 如果你更改一个实例的状态,其他所有的实例都会收到相同的通知。 66 | * 使用使 mutate 可以让这个drawable变得状态不定。这个操作不能还原(变为不定后就不能变为原来的状态)。 67 | * 一个状态不定的drawable可以保证它不与其他任何一个drawabe共享它的状态。 68 | * 此处应该是要使用的 mutate(),但是在部分手机上会出现点击后变白的现象,所以没有使用 69 | * 目前这种解决方案没有问题 70 | */ 71 | drawable.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY); 72 | ViewCompat.postInvalidateOnAnimation(this); 73 | } 74 | break; 75 | case MotionEvent.ACTION_MOVE: 76 | break; 77 | case MotionEvent.ACTION_CANCEL: 78 | case MotionEvent.ACTION_UP: 79 | Drawable drawableUp = getDrawable(); 80 | if (drawableUp != null) { 81 | drawableUp.clearColorFilter(); 82 | ViewCompat.postInvalidateOnAnimation(this); 83 | } 84 | break; 85 | } 86 | return super.onTouchEvent(event); 87 | } 88 | 89 | @Override 90 | protected void onDetachedFromWindow() { 91 | super.onDetachedFromWindow(); 92 | setImageDrawable(null); 93 | } 94 | 95 | public int getMoreNum() { 96 | return moreNum; 97 | } 98 | 99 | public void setMoreNum(int moreNum) { 100 | this.moreNum = moreNum; 101 | msg = "+" + moreNum; 102 | invalidate(); 103 | } 104 | 105 | public int getMaskColor() { 106 | return maskColor; 107 | } 108 | 109 | public void setMaskColor(int maskColor) { 110 | this.maskColor = maskColor; 111 | invalidate(); 112 | } 113 | 114 | public float getTextSize() { 115 | return textSize; 116 | } 117 | 118 | public void setTextSize(float textSize) { 119 | this.textSize = textSize; 120 | textPaint.setTextSize(textSize); 121 | invalidate(); 122 | } 123 | 124 | public int getTextColor() { 125 | return textColor; 126 | } 127 | 128 | public void setTextColor(int textColor) { 129 | this.textColor = textColor; 130 | textPaint.setColor(textColor); 131 | invalidate(); 132 | } 133 | } -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/image/SingleImageActivity.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.image; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.ActivityCompat; 7 | import android.text.TextUtils; 8 | import android.view.View; 9 | import android.widget.Toast; 10 | 11 | import com.bumptech.glide.Glide; 12 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 13 | import com.bumptech.glide.load.engine.GlideException; 14 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 15 | import com.bumptech.glide.request.RequestOptions; 16 | import com.sunfusheng.glideimageview.GlideImageLoader; 17 | import com.sunfusheng.glideimageview.GlideImageView; 18 | import com.sunfusheng.glideimageview.progress.CircleProgressView; 19 | import com.sunfusheng.glideimageview.progress.OnGlideImageViewListener; 20 | 21 | import java.util.Random; 22 | 23 | import static cy.wcdb.yyh.com.myglidefactoryview.MainActivity.isLoadAgain; 24 | import cy.wcdb.yyh.com.myglidefactoryview.R; 25 | 26 | /** 27 | * Created by sunfusheng on 2017/6/15. 28 | */ 29 | public class SingleImageActivity extends Activity { 30 | 31 | GlideImageView glideImageView; 32 | CircleProgressView progressView; 33 | 34 | CircleProgressView progressView1; 35 | CircleProgressView progressView2; 36 | CircleProgressView progressView3; 37 | View maskView; 38 | 39 | public static final String KEY_IMAGE_URL = "image_url"; 40 | public static final String KEY_IMAGE_URL_THUMBNAIL = "image_url_thumbnail"; 41 | 42 | String image_url; 43 | String image_url_thumbnail; 44 | 45 | @Override 46 | protected void onCreate(@Nullable Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_image); 49 | 50 | glideImageView = (GlideImageView) findViewById(R.id.glideImageView); 51 | progressView1 = (CircleProgressView) findViewById(R.id.progressView1); 52 | progressView2 = (CircleProgressView) findViewById(R.id.progressView2); 53 | progressView3 = (CircleProgressView) findViewById(R.id.progressView3); 54 | maskView = findViewById(R.id.maskView); 55 | 56 | image_url = getIntent().getStringExtra(KEY_IMAGE_URL); 57 | image_url_thumbnail = getIntent().getStringExtra(KEY_IMAGE_URL_THUMBNAIL); 58 | 59 | initProgressView(); 60 | loadImage(); 61 | } 62 | 63 | private void initProgressView() { 64 | isLoadAgain = new Random().nextInt(3) == 1; 65 | int randomNum = new Random().nextInt(3); 66 | switch (randomNum) { 67 | case 1: 68 | progressView = progressView2; 69 | break; 70 | case 2: 71 | progressView = progressView3; 72 | break; 73 | case 0: 74 | default: 75 | progressView = progressView1; 76 | break; 77 | } 78 | progressView1.setVisibility(View.GONE); 79 | progressView2.setVisibility(View.GONE); 80 | progressView3.setVisibility(View.GONE); 81 | progressView.setVisibility(View.VISIBLE); 82 | } 83 | 84 | private void loadImage() { 85 | glideImageView.setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | ActivityCompat.finishAfterTransition(SingleImageActivity.this); 89 | } 90 | }); 91 | 92 | RequestOptions requestOptions = glideImageView.requestOptions(R.color.black) 93 | .centerCrop(); 94 | RequestOptions requestOptionsWithoutCache = glideImageView.requestOptions(R.color.black) 95 | .centerCrop() 96 | .skipMemoryCache(true) 97 | .diskCacheStrategy(DiskCacheStrategy.NONE); 98 | 99 | GlideImageLoader imageLoader = glideImageView.getImageLoader(); 100 | 101 | imageLoader.setOnGlideImageViewListener(image_url, new OnGlideImageViewListener() { 102 | @Override 103 | public void onProgress(int percent, boolean isDone, GlideException exception) { 104 | if (exception != null && !TextUtils.isEmpty(exception.getMessage())) { 105 | Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show(); 106 | } 107 | progressView.setProgress(percent); 108 | progressView.setVisibility(isDone ? View.GONE : View.VISIBLE); 109 | maskView.setVisibility(isDone ? View.GONE : View.VISIBLE); 110 | } 111 | }); 112 | 113 | imageLoader.requestBuilder(image_url, requestOptionsWithoutCache) 114 | .thumbnail(Glide.with(SingleImageActivity.this) 115 | .load(image_url_thumbnail) 116 | .apply(requestOptions)) 117 | .transition(DrawableTransitionOptions.withCrossFade()) 118 | .into(glideImageView); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/image/ImagesAdapter.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.image; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.annotation.NonNull; 6 | import android.support.v4.view.PagerAdapter; 7 | import android.text.TextUtils; 8 | import android.util.SparseArray; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.ImageView; 13 | 14 | import com.bumptech.glide.RequestBuilder; 15 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 16 | import com.bumptech.glide.load.engine.GlideException; 17 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 18 | import com.bumptech.glide.request.RequestOptions; 19 | import com.bumptech.glide.request.target.SimpleTarget; 20 | import com.bumptech.glide.request.target.Target; 21 | import com.bumptech.glide.request.transition.Transition; 22 | import com.github.chrisbanes.photoview.OnOutsidePhotoTapListener; 23 | import com.github.chrisbanes.photoview.OnPhotoTapListener; 24 | import com.github.chrisbanes.photoview.PhotoView; 25 | import com.sunfusheng.glideimageview.GlideImageLoader; 26 | import com.sunfusheng.glideimageview.progress.CircleProgressView; 27 | import com.sunfusheng.glideimageview.progress.OnGlideImageViewListener; 28 | import com.sunfusheng.glideimageview.util.DisplayUtil; 29 | 30 | import java.util.List; 31 | 32 | import cy.wcdb.yyh.com.myglidefactoryview.R; 33 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.ImageAttr; 34 | 35 | public class ImagesAdapter extends PagerAdapter implements OnPhotoTapListener, OnOutsidePhotoTapListener { 36 | 37 | private Context mContext; 38 | private LayoutInflater mInflater; 39 | private List images; 40 | private SparseArray photoViews = new SparseArray<>(); 41 | 42 | public ImagesAdapter(Context context, @NonNull List images) { 43 | super(); 44 | this.mContext = context; 45 | this.mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 46 | this.images = images; 47 | } 48 | 49 | @Override 50 | public int getCount() { 51 | return images.size(); 52 | } 53 | 54 | @Override 55 | public boolean isViewFromObject(View view, Object object) { 56 | return view == object; 57 | } 58 | 59 | @Override 60 | public void setPrimaryItem(ViewGroup container, int position, Object object) { 61 | super.setPrimaryItem(container, position, object); 62 | } 63 | 64 | public PhotoView getPhotoView(int index) { 65 | return photoViews.get(index); 66 | } 67 | 68 | @Override 69 | public Object instantiateItem(ViewGroup container, int position) { 70 | View view = (ViewGroup) mInflater.inflate(R.layout.item_photoview, container, false); 71 | final CircleProgressView progressView = (CircleProgressView) view.findViewById(R.id.progressView); 72 | final PhotoView photoView = (PhotoView) view.findViewById(R.id.photoView); 73 | photoView.setScaleType(ImageView.ScaleType.FIT_CENTER); 74 | photoView.setOnPhotoTapListener(this); 75 | photoView.setOnOutsidePhotoTapListener(this); 76 | photoViews.put(position, photoView); 77 | 78 | ImageAttr attr = images.get(position); 79 | String url = TextUtils.isEmpty(attr.thumbnailUrl) ? attr.url : attr.thumbnailUrl; 80 | 81 | GlideImageLoader imageLoader = GlideImageLoader.create(photoView); 82 | imageLoader.setOnGlideImageViewListener(url, new OnGlideImageViewListener() { 83 | @Override 84 | public void onProgress(int percent, boolean isDone, GlideException exception) { 85 | progressView.setProgress(percent); 86 | progressView.setVisibility(isDone ? View.GONE : View.VISIBLE); 87 | } 88 | }); 89 | 90 | RequestOptions requestOptions = imageLoader.requestOptions(R.color.placeholder_color) 91 | .centerCrop() 92 | .skipMemoryCache(false) 93 | .diskCacheStrategy(DiskCacheStrategy.RESOURCE) 94 | .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); 95 | final RequestBuilder requestBuilder = imageLoader.requestBuilder(url, requestOptions) 96 | .transition(DrawableTransitionOptions.withCrossFade()); 97 | requestBuilder.into(new SimpleTarget(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) { 98 | @Override 99 | public void onResourceReady(Drawable resource, Transition transition) { 100 | if (resource.getIntrinsicHeight() > DisplayUtil.getScreenHeight(mContext)) { 101 | photoView.setScaleType(ImageView.ScaleType.CENTER_CROP); 102 | } 103 | requestBuilder.into(photoView); 104 | } 105 | }); 106 | 107 | container.addView(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 108 | return view; 109 | } 110 | 111 | @Override 112 | public void destroyItem(ViewGroup container, int position, Object object) { 113 | container.removeView((View) object); 114 | } 115 | 116 | @Override 117 | public void onPhotoTap(ImageView view, float x, float y) { 118 | ((ImagesActivity) mContext).finishWithAnim(); 119 | } 120 | 121 | @Override 122 | public void onOutsidePhotoTap(ImageView imageView) { 123 | ((ImagesActivity) mContext).finishWithAnim(); 124 | } 125 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/widget/SlideVerticallyLayout.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.widget; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ValueAnimator; 6 | import android.content.Context; 7 | import android.os.Build; 8 | import android.support.v4.view.ViewCompat; 9 | import android.util.AttributeSet; 10 | import android.view.MotionEvent; 11 | import android.view.View; 12 | import android.widget.FrameLayout; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class SlideVerticallyLayout extends FrameLayout { 18 | 19 | private int mDownY; 20 | private int mEndY; 21 | private ValueAnimator mSlideAnimator; 22 | private boolean isCanScroll; 23 | private boolean isTouchWithAnimRunning; 24 | private final int MIN_SCROLL_OFFSET = 50; 25 | private final static int ANIM_DURATION = 280; 26 | private final static int THRESHOLD = 4; 27 | private List mSlideView = new ArrayList<>(); 28 | private List slideCblist = new ArrayList<>(); 29 | private List stateCblist = new ArrayList<>(); 30 | private boolean isSlideShown = true; 31 | 32 | public void addSlideCallback(ISlideCallback callback) { 33 | slideCblist.add(callback); 34 | } 35 | 36 | public void addStateCallback(IStateCallback callback) { 37 | stateCblist.add(callback); 38 | } 39 | 40 | public SlideVerticallyLayout(Context context) { 41 | this(context, null); 42 | } 43 | 44 | public SlideVerticallyLayout(Context context, AttributeSet attrs) { 45 | this(context, attrs, 0); 46 | } 47 | 48 | public SlideVerticallyLayout(Context context, AttributeSet attrs, int defStyleAttr) { 49 | super(context, attrs, defStyleAttr); 50 | init(); 51 | } 52 | 53 | void init() { 54 | mSlideAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(ANIM_DURATION); 55 | mSlideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 56 | @Override 57 | public void onAnimationUpdate(ValueAnimator animation) { 58 | float factor = (float) animation.getAnimatedValue(); 59 | int diffY = mEndY - mDownY; 60 | for (ISlideCallback cb : slideCblist) { 61 | cb.onPositionChange((int) (mDownY + diffY * factor)); 62 | } 63 | } 64 | }); 65 | mSlideAnimator.addListener(new AnimatorListenerAdapter() { 66 | @Override 67 | public void onAnimationEnd(Animator animation) { 68 | if (isSlideShown && mEndY == getHeight()) { 69 | isSlideShown = false; 70 | for (IStateCallback cb : stateCblist) { 71 | cb.onHide(); 72 | } 73 | } else if (!isSlideShown && mEndY == 0) { 74 | isSlideShown = true; 75 | for (IStateCallback cb : stateCblist) { 76 | cb.onShow(); 77 | } 78 | } 79 | mDownY = mEndY; 80 | isCanScroll = false; 81 | } 82 | }); 83 | addSlideCallback(new ISlideCallback() { 84 | @Override 85 | public void onPositionChange(int offsetY) { 86 | float factor = (float) (getHeight() - offsetY) / (float) getHeight(); 87 | setBackgroundColor(((int) (factor * 0xc4)) << 24); 88 | } 89 | }); 90 | addSlideCallback(new ISlideCallback() { 91 | @Override 92 | public void onPositionChange(int offsetY) { 93 | for (View view : mSlideView) { 94 | view.setTranslationY(offsetY); 95 | } 96 | } 97 | }); 98 | } 99 | 100 | public void addSlideView(View view) { 101 | mSlideView.add(view); 102 | } 103 | 104 | public void slideDown(boolean anim) { 105 | if (!isSlideShown) return; 106 | if (anim) { 107 | mEndY = getHeight(); 108 | mDownY = 0; 109 | mSlideAnimator.start(); 110 | } else { 111 | for (ISlideCallback cb : slideCblist) { 112 | cb.onPositionChange(getHeight()); 113 | } 114 | isSlideShown = false; 115 | } 116 | } 117 | 118 | public void slideUp(boolean anim) { 119 | if (isSlideShown) return; 120 | if (anim) { 121 | mEndY = 0; 122 | mDownY = getHeight(); 123 | mSlideAnimator.start(); 124 | } else { 125 | for (ISlideCallback cb : slideCblist) { 126 | cb.onPositionChange(0); 127 | } 128 | isSlideShown = true; 129 | } 130 | } 131 | 132 | @Override 133 | public boolean onInterceptTouchEvent(MotionEvent event) { 134 | if (!canChildScrollUp()) { 135 | final int y = (int) event.getY(); 136 | switch (event.getAction() & MotionEvent.ACTION_MASK) { 137 | case MotionEvent.ACTION_DOWN: 138 | isTouchWithAnimRunning = mSlideAnimator.isRunning(); 139 | mDownY = y; 140 | break; 141 | case MotionEvent.ACTION_MOVE: 142 | if (isGreaterThanMinSize(mDownY, y) && !isTouchWithAnimRunning) { 143 | isCanScroll = true; 144 | return true; 145 | } 146 | break; 147 | } 148 | } 149 | return super.onInterceptTouchEvent(event); 150 | } 151 | 152 | @Override 153 | public boolean onTouchEvent(MotionEvent event) { 154 | final int y = (int) event.getY(); 155 | int offsetY = y - mDownY; 156 | switch (event.getAction() & MotionEvent.ACTION_MASK) { 157 | case MotionEvent.ACTION_MOVE: 158 | if (isGreaterThanMinSize(mDownY, y) && isCanScroll) { 159 | for (ISlideCallback cb : slideCblist) { 160 | cb.onPositionChange(getPositionChangeY(offsetY)); 161 | } 162 | } else if (isCanScroll) { 163 | for (ISlideCallback cb : slideCblist) { 164 | cb.onPositionChange(0); 165 | } 166 | } 167 | return true; 168 | case MotionEvent.ACTION_POINTER_DOWN: 169 | case MotionEvent.ACTION_UP: 170 | if (isGreaterThanMinSize(mDownY, y) && isCanScroll) { 171 | mDownY = getPositionChangeY(offsetY); 172 | fixPosition(offsetY); 173 | mSlideAnimator.start(); 174 | } 175 | break; 176 | } 177 | return super.onTouchEvent(event); 178 | } 179 | 180 | private boolean canChildScrollUp() { 181 | for (View view : mSlideView) { 182 | if (canChildScrollUp(view)) return true; 183 | } 184 | return false; 185 | } 186 | 187 | private boolean canChildScrollUp(View view) { 188 | if (Build.VERSION.SDK_INT < 14) { 189 | return ViewCompat.canScrollVertically(view, -1) || view.getScrollY() > 0; 190 | } else { 191 | return ViewCompat.canScrollVertically(view, -1); 192 | } 193 | } 194 | 195 | private int getPositionChangeY(int offsetY) { 196 | if (isSlideShown) { 197 | return Math.abs(offsetY) - MIN_SCROLL_OFFSET; 198 | } else { 199 | return getHeight() - (Math.abs(offsetY)) - MIN_SCROLL_OFFSET; 200 | } 201 | } 202 | 203 | private boolean isGreaterThanMinSize(int y1, int y2) { 204 | if (isSlideShown) { 205 | return y2 - y1 > MIN_SCROLL_OFFSET; 206 | } else { 207 | return y1 - y2 > MIN_SCROLL_OFFSET; 208 | } 209 | } 210 | 211 | private void fixPosition(int offsetY) { 212 | int absOffsetY = Math.abs(offsetY); 213 | if (isSlideShown && absOffsetY > getHeight() / THRESHOLD) { 214 | mEndY = getHeight(); 215 | } else if (!isSlideShown && absOffsetY > getHeight() / THRESHOLD) { 216 | mEndY = 0; 217 | } 218 | } 219 | 220 | public interface ISlideCallback { 221 | void onPositionChange(int offsetY); 222 | } 223 | 224 | public interface IStateCallback { 225 | void onShow(); 226 | 227 | void onHide(); 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/GlideImageLoader.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.net.Uri; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.support.annotation.DrawableRes; 9 | import android.support.annotation.Nullable; 10 | import android.widget.ImageView; 11 | 12 | import com.bumptech.glide.Glide; 13 | import com.bumptech.glide.RequestBuilder; 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.RequestOptions; 18 | import com.bumptech.glide.request.target.Target; 19 | import com.sunfusheng.glideimageview.progress.OnGlideImageViewListener; 20 | import com.sunfusheng.glideimageview.progress.OnProgressListener; 21 | import com.sunfusheng.glideimageview.progress.ProgressManager; 22 | import com.sunfusheng.glideimageview.transformation.GlideCircleTransformation; 23 | 24 | import java.lang.ref.WeakReference; 25 | 26 | /** 27 | * Created by sunfusheng on 2017/6/6. 28 | */ 29 | public class GlideImageLoader { 30 | 31 | private static final String ANDROID_RESOURCE = "android.resource://"; 32 | private static final String FILE = "file://"; 33 | private static final String SEPARATOR = "/"; 34 | private static final String HTTP = "http"; 35 | 36 | private WeakReference mImageView; 37 | private Object mImageUrlObj; 38 | private long mTotalBytes = 0; 39 | private long mLastBytesRead = 0; 40 | private boolean mLastStatus = false; 41 | private Handler mMainThreadHandler; 42 | 43 | private OnProgressListener internalProgressListener; 44 | private OnGlideImageViewListener onGlideImageViewListener; 45 | private OnProgressListener onProgressListener; 46 | 47 | public static GlideImageLoader create(ImageView imageView) { 48 | return new GlideImageLoader(imageView); 49 | } 50 | 51 | private GlideImageLoader(ImageView imageView) { 52 | mImageView = new WeakReference<>(imageView); 53 | mMainThreadHandler = new Handler(Looper.getMainLooper()); 54 | } 55 | 56 | public ImageView getImageView() { 57 | if (mImageView != null) { 58 | return mImageView.get(); 59 | } 60 | return null; 61 | } 62 | 63 | public Context getContext() { 64 | if (getImageView() != null) { 65 | return getImageView().getContext(); 66 | } 67 | return null; 68 | } 69 | 70 | public String getImageUrl() { 71 | if (mImageUrlObj == null) return null; 72 | if (!(mImageUrlObj instanceof String)) return null; 73 | return (String) mImageUrlObj; 74 | } 75 | 76 | public Uri resId2Uri(int resourceId) { 77 | if (getContext() == null) return null; 78 | return Uri.parse(ANDROID_RESOURCE + getContext().getPackageName() + SEPARATOR + resourceId); 79 | } 80 | 81 | public void load(int resId, RequestOptions options) { 82 | load(resId2Uri(resId), options); 83 | } 84 | 85 | public void load(Uri uri, RequestOptions options) { 86 | if (uri == null || getContext() == null) return; 87 | requestBuilder(uri, options).into(getImageView()); 88 | } 89 | 90 | public void load(String url, RequestOptions options) { 91 | if (url == null || getContext() == null) return; 92 | requestBuilder(url, options).into(getImageView()); 93 | } 94 | 95 | public RequestBuilder requestBuilder(Object obj, RequestOptions options) { 96 | this.mImageUrlObj = obj; 97 | return Glide.with(getContext()) 98 | .load(obj) 99 | .apply(options) 100 | .listener(new RequestListener() { 101 | @Override 102 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { 103 | mainThreadCallback(mLastBytesRead, mTotalBytes, true, e); 104 | ProgressManager.removeProgressListener(internalProgressListener); 105 | return false; 106 | } 107 | 108 | @Override 109 | public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { 110 | mainThreadCallback(mLastBytesRead, mTotalBytes, true, null); 111 | ProgressManager.removeProgressListener(internalProgressListener); 112 | return false; 113 | } 114 | }); 115 | } 116 | 117 | public RequestOptions requestOptions(int placeholderResId) { 118 | return requestOptions(placeholderResId, placeholderResId); 119 | } 120 | 121 | public RequestOptions requestOptions(int placeholderResId, int errorResId) { 122 | return new RequestOptions() 123 | .placeholder(placeholderResId) 124 | .error(errorResId); 125 | } 126 | 127 | public RequestOptions circleRequestOptions(int placeholderResId) { 128 | return circleRequestOptions(placeholderResId, placeholderResId); 129 | } 130 | 131 | public RequestOptions circleRequestOptions(int placeholderResId, int errorResId) { 132 | return requestOptions(placeholderResId, errorResId) 133 | .transform(new GlideCircleTransformation()); 134 | } 135 | 136 | public void loadImage(String url, int placeholderResId) { 137 | load(url, requestOptions(placeholderResId)); 138 | } 139 | 140 | public void loadLocalImage(@DrawableRes int resId, int placeholderResId) { 141 | load(resId, requestOptions(placeholderResId)); 142 | } 143 | 144 | public void loadLocalImage(String localPath, int placeholderResId) { 145 | load(FILE + localPath, requestOptions(placeholderResId)); 146 | } 147 | 148 | public void loadCircleImage(String url, int placeholderResId) { 149 | load(url, circleRequestOptions(placeholderResId)); 150 | } 151 | 152 | public void loadLocalCircleImage(int resId, int placeholderResId) { 153 | load(resId, circleRequestOptions(placeholderResId)); 154 | } 155 | 156 | public void loadLocalCircleImage(String localPath, int placeholderResId) { 157 | load(FILE + localPath, circleRequestOptions(placeholderResId)); 158 | } 159 | 160 | private void addProgressListener() { 161 | if (getImageUrl() == null) return; 162 | final String url = getImageUrl(); 163 | if (!url.startsWith(HTTP)) return; 164 | 165 | internalProgressListener = new OnProgressListener() { 166 | @Override 167 | public void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception) { 168 | if (totalBytes == 0) return; 169 | if (!url.equals(imageUrl)) return; 170 | if (mLastBytesRead == bytesRead && mLastStatus == isDone) return; 171 | 172 | mLastBytesRead = bytesRead; 173 | mTotalBytes = totalBytes; 174 | mLastStatus = isDone; 175 | mainThreadCallback(bytesRead, totalBytes, isDone, exception); 176 | 177 | if (isDone) { 178 | ProgressManager.removeProgressListener(this); 179 | } 180 | } 181 | }; 182 | ProgressManager.addProgressListener(internalProgressListener); 183 | } 184 | 185 | private void mainThreadCallback(final long bytesRead, final long totalBytes, final boolean isDone, final GlideException exception) { 186 | mMainThreadHandler.post(new Runnable() { 187 | @Override 188 | public void run() { 189 | final int percent = (int) ((bytesRead * 1.0f / totalBytes) * 100.0f); 190 | if (onProgressListener != null) { 191 | onProgressListener.onProgress((String) mImageUrlObj, bytesRead, totalBytes, isDone, exception); 192 | } 193 | 194 | if (onGlideImageViewListener != null) { 195 | onGlideImageViewListener.onProgress(percent, isDone, exception); 196 | } 197 | } 198 | }); 199 | } 200 | 201 | public void setOnGlideImageViewListener(String imageUrl, OnGlideImageViewListener onGlideImageViewListener) { 202 | this.mImageUrlObj = imageUrl; 203 | this.onGlideImageViewListener = onGlideImageViewListener; 204 | addProgressListener(); 205 | } 206 | 207 | public void setOnProgressListener(String imageUrl, OnProgressListener onProgressListener) { 208 | this.mImageUrlObj = imageUrl; 209 | this.onProgressListener = onProgressListener; 210 | addProgressListener(); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/image/ImagesActivity.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.image; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.app.Activity; 6 | import android.content.Intent; 7 | import android.graphics.Color; 8 | import android.os.Bundle; 9 | import android.support.v4.view.ViewPager; 10 | import android.util.Log; 11 | import android.view.ViewTreeObserver; 12 | import android.widget.RelativeLayout; 13 | import android.widget.TextView; 14 | 15 | import com.github.chrisbanes.photoview.PhotoView; 16 | import com.sunfusheng.glideimageview.util.DisplayUtil; 17 | 18 | import java.util.List; 19 | 20 | import cy.wcdb.yyh.com.myglidefactoryview.R; 21 | import cy.wcdb.yyh.com.myglidefactoryview.util.ColorUtil; 22 | import cy.wcdb.yyh.com.myglidefactoryview.util.StatusBarUtil; 23 | import cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView.ImageAttr; 24 | 25 | public class ImagesActivity extends Activity implements ViewTreeObserver.OnPreDrawListener { 26 | 27 | public static final String IMAGE_ATTR = "image_attr"; 28 | public static final String CUR_POSITION = "cur_position"; 29 | public static final int ANIM_DURATION = 300; // ms 30 | 31 | private RelativeLayout rootView; 32 | private ViewPager viewPager; 33 | private TextView tvTip; 34 | private ImagesAdapter mAdapter; 35 | private List imageAttrs; 36 | 37 | private int curPosition; 38 | private int screenWidth; 39 | private int screenHeight; 40 | private float scaleX; 41 | private float scaleY; 42 | private float translationX; 43 | private float translationY; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_preview); 49 | StatusBarUtil.setStatusBarTranslucent(this, false); 50 | 51 | viewPager = (ViewPager) findViewById(R.id.viewPager); 52 | tvTip = (TextView) findViewById(R.id.tv_tip); 53 | rootView = (RelativeLayout) findViewById(R.id.rootView); 54 | screenWidth = DisplayUtil.getScreenWidth(this); 55 | screenHeight = DisplayUtil.getScreenHeight(this); 56 | 57 | Intent intent = getIntent(); 58 | imageAttrs = (List) intent.getSerializableExtra(IMAGE_ATTR); 59 | curPosition = intent.getIntExtra(CUR_POSITION, 0); 60 | tvTip.setText(String.format(getString(R.string.image_index), (curPosition + 1), imageAttrs.size())); 61 | 62 | mAdapter = new ImagesAdapter(this, imageAttrs); 63 | viewPager.setAdapter(mAdapter); 64 | viewPager.setCurrentItem(curPosition); 65 | viewPager.getViewTreeObserver().addOnPreDrawListener(this); 66 | viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 67 | @Override 68 | public void onPageSelected(int position) { 69 | curPosition = position; 70 | tvTip.setText(String.format(getString(R.string.image_index), (curPosition + 1), imageAttrs.size())); 71 | } 72 | }); 73 | } 74 | 75 | @Override 76 | public void onBackPressed() { 77 | finishWithAnim(); 78 | } 79 | 80 | private void initImageAttr(ImageAttr attr) { 81 | int originalWidth = attr.width; 82 | int originalHeight = attr.height; 83 | int originalCenterX = attr.left + originalWidth / 2; 84 | int originalCenterY = attr.top + originalHeight / 2; 85 | 86 | float widthRatio = screenWidth * 1.0f / originalWidth; 87 | float heightRatio = screenHeight * 1.0f / originalHeight; 88 | float ratio = widthRatio > heightRatio ? heightRatio : widthRatio; 89 | int finalWidth = (int) (originalWidth * ratio); 90 | int finalHeight = (int) (originalHeight * ratio); 91 | 92 | scaleX = originalWidth * 1.0f / finalWidth; 93 | scaleY = originalHeight * 1.0f / finalHeight; 94 | translationX = originalCenterX - screenWidth / 2; 95 | translationY = originalCenterY - screenHeight / 2; 96 | 97 | Log.d("--->", "(left, top): (" + attr.left + ", " + attr.top + ")"); 98 | Log.d("--->", "originalWidth: " + originalWidth + " originalHeight: " + originalHeight); 99 | Log.d("--->", "finalWidth: " + finalWidth + " finalHeight: " + finalHeight); 100 | Log.d("--->", "scaleX: " + scaleX + " scaleY: " + scaleY); 101 | Log.d("--->", "translationX: " + translationX + " translationY: " + translationY); 102 | Log.d("--->", "" + attr.toString()); 103 | Log.d("--->", "----------------------------------------------------------------"); 104 | } 105 | 106 | @Override 107 | public boolean onPreDraw() { 108 | rootView.getViewTreeObserver().removeOnPreDrawListener(this); 109 | PhotoView photoView = mAdapter.getPhotoView(curPosition); 110 | ImageAttr attr = imageAttrs.get(curPosition); 111 | initImageAttr(attr); 112 | 113 | setBackgroundColor(0f, 1f, null); 114 | translateXAnim(photoView, translationX, 0); 115 | translateYAnim(photoView, translationY, 0); 116 | scaleXAnim(photoView, scaleX, 1); 117 | scaleYAnim(photoView, scaleY, 1); 118 | return true; 119 | } 120 | 121 | public void finishWithAnim() { 122 | PhotoView photoView = mAdapter.getPhotoView(curPosition); 123 | photoView.setScale(1f); 124 | ImageAttr attr = imageAttrs.get(curPosition); 125 | initImageAttr(attr); 126 | 127 | translateXAnim(photoView, 0, translationX); 128 | translateYAnim(photoView, 0, translationY); 129 | scaleXAnim(photoView, 1, scaleX); 130 | scaleYAnim(photoView, 1, scaleY); 131 | setBackgroundColor(1f, 0f, new Animator.AnimatorListener() { 132 | @Override 133 | public void onAnimationStart(Animator animation) { 134 | } 135 | 136 | @Override 137 | public void onAnimationEnd(Animator animation) { 138 | finish(); 139 | overridePendingTransition(0, 0); 140 | } 141 | 142 | @Override 143 | public void onAnimationCancel(Animator animation) { 144 | } 145 | 146 | @Override 147 | public void onAnimationRepeat(Animator animation) { 148 | } 149 | }); 150 | } 151 | 152 | private void translateXAnim(final PhotoView photoView, float from, float to) { 153 | ValueAnimator anim = ValueAnimator.ofFloat(from, to); 154 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 155 | @Override 156 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 157 | photoView.setX((Float) valueAnimator.getAnimatedValue()); 158 | } 159 | }); 160 | anim.setDuration(ANIM_DURATION); 161 | anim.start(); 162 | } 163 | 164 | private void translateYAnim(final PhotoView photoView, float from, float to) { 165 | ValueAnimator anim = ValueAnimator.ofFloat(from, to); 166 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 167 | @Override 168 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 169 | photoView.setY((Float) valueAnimator.getAnimatedValue()); 170 | } 171 | }); 172 | anim.setDuration(ANIM_DURATION); 173 | anim.start(); 174 | } 175 | 176 | private void scaleXAnim(final PhotoView photoView, float from, float to) { 177 | ValueAnimator anim = ValueAnimator.ofFloat(from, to); 178 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 179 | @Override 180 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 181 | photoView.setScaleX((Float) valueAnimator.getAnimatedValue()); 182 | } 183 | }); 184 | anim.setDuration(ANIM_DURATION); 185 | anim.start(); 186 | } 187 | 188 | private void scaleYAnim(final PhotoView photoView, float from, float to) { 189 | ValueAnimator anim = ValueAnimator.ofFloat(from, to); 190 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 191 | @Override 192 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 193 | photoView.setScaleY((Float) valueAnimator.getAnimatedValue()); 194 | } 195 | }); 196 | anim.setDuration(ANIM_DURATION); 197 | anim.start(); 198 | } 199 | 200 | private void setBackgroundColor(float from, float to, Animator.AnimatorListener listener) { 201 | ValueAnimator anim = ValueAnimator.ofFloat(from, to); 202 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 203 | @Override 204 | public void onAnimationUpdate(ValueAnimator valueAnimator) { 205 | rootView.setBackgroundColor(ColorUtil.evaluate((Float) valueAnimator.getAnimatedValue(), Color.TRANSPARENT, Color.BLACK)); 206 | } 207 | }); 208 | anim.setDuration(ANIM_DURATION); 209 | if (listener != null) { 210 | anim.addListener(listener); 211 | } 212 | anim.start(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/ShapeImageView.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 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.BitmapDrawable; 13 | import android.graphics.drawable.ColorDrawable; 14 | import android.graphics.drawable.Drawable; 15 | import android.support.annotation.ColorRes; 16 | import android.support.annotation.IntDef; 17 | import android.util.AttributeSet; 18 | import android.view.MotionEvent; 19 | import android.widget.ImageView; 20 | 21 | import com.sunfusheng.glideimageview.util.DisplayUtil; 22 | 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | 26 | /** 27 | * Created by sunfusheng on 2017/6/12. 28 | */ 29 | @SuppressWarnings("deprecation") 30 | public class ShapeImageView extends ImageView { 31 | 32 | // 定义Bitmap的默认配置 33 | private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; 34 | private static final int COLOR_DRAWABLE_DIMENSION = 1; 35 | 36 | // 图片的宽高 37 | private int width; 38 | private int height; 39 | 40 | private int borderColor = 0x1A000000; // 边框颜色 41 | private int borderWidth = 0; // 边框宽度 42 | private int radius = 0; // 圆角弧度 43 | private int shapeType = ShapeType.RECTANGLE; // 图片类型(圆形, 矩形) 44 | 45 | private Paint pressedPaint; // 按下的画笔 46 | private float pressedAlpha = 0.1f; // 按下的透明度 47 | private int pressedColor = 0x1A000000; // 按下的颜色 48 | 49 | @IntDef({ShapeType.RECTANGLE, ShapeType.CIRCLE}) 50 | @Retention(RetentionPolicy.SOURCE) 51 | public @interface ShapeType { 52 | int RECTANGLE = 0; 53 | int CIRCLE = 1; 54 | } 55 | 56 | public ShapeImageView(Context context) { 57 | this(context, null); 58 | } 59 | 60 | public ShapeImageView(Context context, AttributeSet attrs) { 61 | this(context, attrs, 0); 62 | } 63 | 64 | public ShapeImageView(Context context, AttributeSet attrs, int defStyleAttr) { 65 | super(context, attrs, defStyleAttr); 66 | init(context, attrs); 67 | } 68 | 69 | private void init(Context context, AttributeSet attrs) { 70 | if (attrs != null) { 71 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ShapeImageViewStyle); 72 | borderWidth = array.getDimensionPixelOffset(R.styleable.ShapeImageViewStyle_siv_border_width, borderWidth); 73 | borderColor = array.getColor(R.styleable.ShapeImageViewStyle_siv_border_color, borderColor); 74 | radius = array.getDimensionPixelOffset(R.styleable.ShapeImageViewStyle_siv_radius, radius); 75 | pressedAlpha = array.getFloat(R.styleable.ShapeImageViewStyle_siv_pressed_alpha, pressedAlpha); 76 | if (pressedAlpha > 1) pressedAlpha = 1; 77 | pressedColor = array.getColor(R.styleable.ShapeImageViewStyle_siv_pressed_color, pressedColor); 78 | shapeType = array.getInteger(R.styleable.ShapeImageViewStyle_siv_shape_type, shapeType); 79 | array.recycle(); 80 | } 81 | 82 | initPressedPaint(); 83 | setClickable(true); 84 | setDrawingCacheEnabled(true); 85 | setWillNotDraw(false); 86 | } 87 | 88 | // 初始化按下的画笔 89 | private void initPressedPaint() { 90 | pressedPaint = new Paint(); 91 | pressedPaint.setAntiAlias(true); 92 | pressedPaint.setStyle(Paint.Style.FILL); 93 | pressedPaint.setColor(pressedColor); 94 | pressedPaint.setAlpha(0); 95 | pressedPaint.setFlags(Paint.ANTI_ALIAS_FLAG); 96 | } 97 | 98 | @Override 99 | protected void onDraw(Canvas canvas) { 100 | Drawable drawable = getDrawable(); 101 | if (drawable == null) { 102 | return; 103 | } 104 | 105 | if (getWidth() == 0 || getHeight() == 0) { 106 | return; 107 | } 108 | 109 | drawDrawable(canvas, getBitmapFromDrawable(drawable)); 110 | drawBorder(canvas); 111 | drawPressed(canvas); 112 | } 113 | 114 | @Override 115 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 116 | super.onSizeChanged(w, h, oldw, oldh); 117 | width = w; 118 | height = h; 119 | } 120 | 121 | // 绘制圆角 122 | private void drawDrawable(Canvas canvas, Bitmap bitmap) { 123 | Paint paint = new Paint(); 124 | paint.setColor(0xffffffff); 125 | paint.setAntiAlias(true); 126 | 127 | int saveFlags = Canvas.MATRIX_SAVE_FLAG 128 | | Canvas.CLIP_SAVE_FLAG 129 | | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG 130 | | Canvas.FULL_COLOR_LAYER_SAVE_FLAG 131 | | Canvas.CLIP_TO_LAYER_SAVE_FLAG; 132 | 133 | canvas.saveLayer(0, 0, width, height, null, saveFlags); 134 | 135 | if (shapeType == ShapeType.RECTANGLE) { 136 | RectF rectf = new RectF(borderWidth / 2, borderWidth / 2, getWidth() - borderWidth / 2, getHeight() - borderWidth / 2); 137 | canvas.drawRoundRect(rectf, radius, radius, paint); 138 | } else { 139 | canvas.drawCircle(width / 2, height / 2, width / 2 - borderWidth, paint); 140 | } 141 | 142 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // SRC_IN 只显示两层图像交集部分的上层图像 143 | 144 | //Bitmap缩放 145 | float scaleWidth = ((float) getWidth()) / bitmap.getWidth(); 146 | float scaleHeight = ((float) getHeight()) / bitmap.getHeight(); 147 | Matrix matrix = new Matrix(); 148 | matrix.postScale(scaleWidth, scaleHeight); 149 | bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); 150 | canvas.drawBitmap(bitmap, 0, 0, paint); 151 | canvas.restore(); 152 | } 153 | 154 | // 绘制边框 155 | private void drawBorder(Canvas canvas) { 156 | if (borderWidth > 0) { 157 | Paint paint = new Paint(); 158 | paint.setStrokeWidth(borderWidth); 159 | paint.setStyle(Paint.Style.STROKE); 160 | paint.setColor(borderColor); 161 | paint.setAntiAlias(true); 162 | if (shapeType == ShapeType.RECTANGLE) { 163 | RectF rectf = new RectF(borderWidth / 2, borderWidth / 2, getWidth() - borderWidth / 2, getHeight() - borderWidth / 2); 164 | canvas.drawRoundRect(rectf, radius, radius, paint); 165 | } else { 166 | canvas.drawCircle(width / 2, height / 2, (width - borderWidth) / 2, paint); 167 | } 168 | } 169 | } 170 | 171 | // 绘制按下效果 172 | private void drawPressed(Canvas canvas) { 173 | if (shapeType == ShapeType.RECTANGLE) { 174 | RectF rectf = new RectF(1, 1, width - 1, height - 1); 175 | canvas.drawRoundRect(rectf, radius, radius, pressedPaint); 176 | } else { 177 | canvas.drawCircle(width / 2, height / 2, width / 2, pressedPaint); 178 | } 179 | } 180 | 181 | @Override 182 | public boolean onTouchEvent(MotionEvent event) { 183 | switch (event.getAction()) { 184 | case MotionEvent.ACTION_DOWN: 185 | pressedPaint.setAlpha((int) (pressedAlpha * 255)); 186 | invalidate(); 187 | break; 188 | case MotionEvent.ACTION_UP: 189 | pressedPaint.setAlpha(0); 190 | invalidate(); 191 | break; 192 | case MotionEvent.ACTION_MOVE: 193 | break; 194 | default: 195 | pressedPaint.setAlpha(0); 196 | invalidate(); 197 | break; 198 | } 199 | return super.onTouchEvent(event); 200 | } 201 | 202 | // 获取Bitmap内容 203 | private Bitmap getBitmapFromDrawable(Drawable drawable) { 204 | try { 205 | Bitmap bitmap; 206 | if (drawable instanceof BitmapDrawable) { 207 | return ((BitmapDrawable) drawable).getBitmap(); 208 | } else if (drawable instanceof ColorDrawable) { 209 | bitmap = Bitmap.createBitmap(COLOR_DRAWABLE_DIMENSION, COLOR_DRAWABLE_DIMENSION, BITMAP_CONFIG); 210 | } else { 211 | bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); 212 | } 213 | Canvas canvas = new Canvas(bitmap); 214 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 215 | drawable.draw(canvas); 216 | return bitmap; 217 | } catch (OutOfMemoryError e) { 218 | e.printStackTrace(); 219 | return null; 220 | } 221 | } 222 | 223 | // 设置边框颜色 224 | public void setBorderColor(@ColorRes int id) { 225 | this.borderColor = getResources().getColor(id); 226 | invalidate(); 227 | } 228 | 229 | // 设置边框宽度 230 | public void setBorderWidth(int borderWidth) { 231 | this.borderWidth = DisplayUtil.dip2px(getContext(), borderWidth); 232 | invalidate(); 233 | } 234 | 235 | // 设置图片按下颜色透明度 236 | public void setPressedAlpha(float pressAlpha) { 237 | this.pressedAlpha = pressAlpha; 238 | } 239 | 240 | // 设置图片按下的颜色 241 | public void setPressedColor(@ColorRes int id) { 242 | this.pressedColor = getResources().getColor(id); 243 | pressedPaint.setColor(pressedColor); 244 | pressedPaint.setAlpha(0); 245 | invalidate(); 246 | } 247 | 248 | // 设置圆角半径 249 | public void setRadius(int radius) { 250 | this.radius = DisplayUtil.dip2px(getContext(), radius); 251 | invalidate(); 252 | } 253 | 254 | // 设置形状类型 255 | public void setShapeType(@ShapeType int shapeType) { 256 | this.shapeType = shapeType; 257 | invalidate(); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.ActivityCompat; 6 | import android.support.v4.app.ActivityOptionsCompat; 7 | import android.text.TextUtils; 8 | import android.util.Log; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 16 | import com.bumptech.glide.load.engine.GlideException; 17 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 18 | import com.bumptech.glide.request.RequestOptions; 19 | import com.sunfusheng.glideimageview.GlideImageLoader; 20 | import com.sunfusheng.glideimageview.GlideImageView; 21 | import com.sunfusheng.glideimageview.ShapeImageView; 22 | import com.sunfusheng.glideimageview.progress.CircleProgressView; 23 | import com.sunfusheng.glideimageview.progress.OnGlideImageViewListener; 24 | import com.sunfusheng.glideimageview.progress.OnProgressListener; 25 | 26 | import java.util.Random; 27 | 28 | import cy.wcdb.yyh.com.myglidefactoryview.image.SingleImageActivity; 29 | 30 | import static cy.wcdb.yyh.com.myglidefactoryview.image.SingleImageActivity.KEY_IMAGE_URL; 31 | import static cy.wcdb.yyh.com.myglidefactoryview.image.SingleImageActivity.KEY_IMAGE_URL_THUMBNAIL; 32 | 33 | 34 | /** 35 | * Created by sunfusheng on 2017/6/3. 36 | */ 37 | public class MainActivity extends BaseActivity { 38 | 39 | GlideImageView image11; 40 | GlideImageView image12; 41 | GlideImageView image13; 42 | GlideImageView image14; 43 | 44 | GlideImageView image21; 45 | GlideImageView image22; 46 | GlideImageView image23; 47 | GlideImageView image24; 48 | 49 | GlideImageView image31; 50 | GlideImageView image32; 51 | GlideImageView image33; 52 | GlideImageView image34; 53 | 54 | GlideImageView image41; 55 | CircleProgressView progressView1; 56 | GlideImageView image42; 57 | CircleProgressView progressView2; 58 | 59 | TextView draggableView; 60 | 61 | String url1 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1497688355699&di=ea69a930b82ce88561c635089995e124&imgtype=0&src=http%3A%2F%2Fcms-bucket.nosdn.127.net%2Ff84e566bcf654b3698363409fbd676ef20161119091503.jpg"; 62 | String url2 = "http://img1.imgtn.bdimg.com/it/u=4027212837,1228313366&fm=23&gp=0.jpg"; 63 | 64 | public static boolean isLoadAgain = false; // Just for fun when loading images! 65 | 66 | public static final String cat = "https://raw.githubusercontent.com/sfsheng0322/GlideImageView/master/screenshot/cat.jpg"; 67 | public static final String cat_thumbnail = "https://raw.githubusercontent.com/sfsheng0322/GlideImageView/master/screenshot/cat_thumbnail.jpg"; 68 | 69 | public static final String girl = "https://raw.githubusercontent.com/sfsheng0322/GlideImageView/master/screenshot/girl.jpg"; 70 | public static final String girl_thumbnail = "https://raw.githubusercontent.com/sfsheng0322/GlideImageView/master/screenshot/girl_thumbnail.jpg"; 71 | 72 | String gif1 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1496754078616&di=cc68338a66a36de619fa11d0c1b2e6f3&imgtype=0&src=http%3A%2F%2Fapp.576tv.com%2FUploads%2Foltz%2F201609%2F25%2F1474813626468299.gif"; 73 | String gif2 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1497276275707&di=57c8c7917e91afc1bc86b1b57e743425&imgtype=0&src=http%3A%2F%2Fimg.haatoo.com%2Fpics%2F2016%2F05%2F14%2F9%2F4faf3f52b8e8315af7a469731dc7dce5.jpg"; 74 | String gif3 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1497276379533&di=71435f66d66221eb36dab266deb9d6d2&imgtype=0&src=http%3A%2F%2Fatt.bbs.duowan.com%2Fforum%2F201608%2F02%2F190418bmy9zqm94qxlmqf4.gif"; 75 | 76 | @Override 77 | protected void onCreate(Bundle savedInstanceState) { 78 | super.onCreate(savedInstanceState); 79 | setContentView(R.layout.activity_main); 80 | 81 | image11 = (GlideImageView) findViewById(R.id.image11); 82 | image12 = (GlideImageView) findViewById(R.id.image12); 83 | image13 = (GlideImageView) findViewById(R.id.image13); 84 | image14 = (GlideImageView) findViewById(R.id.image14); 85 | 86 | image21 = (GlideImageView) findViewById(R.id.image21); 87 | image22 = (GlideImageView) findViewById(R.id.image22); 88 | image23 = (GlideImageView) findViewById(R.id.image23); 89 | image24 = (GlideImageView) findViewById(R.id.image24); 90 | 91 | image31 = (GlideImageView) findViewById(R.id.image31); 92 | image32 = (GlideImageView) findViewById(R.id.image32); 93 | image33 = (GlideImageView) findViewById(R.id.image33); 94 | image34 = (GlideImageView) findViewById(R.id.image34); 95 | 96 | image41 = (GlideImageView) findViewById(R.id.image41); 97 | progressView1 = (CircleProgressView) findViewById(R.id.progressView1); 98 | image42 = (GlideImageView) findViewById(R.id.image42); 99 | progressView2 = (CircleProgressView) findViewById(R.id.progressView2); 100 | 101 | draggableView = (TextView) findViewById(R.id.draggable_view); 102 | draggableView.setOnClickListener(new View.OnClickListener() { 103 | @Override 104 | public void onClick(View v) { 105 | startActivity(new Intent(mContext, RecycleViewActivity.class)); 106 | } 107 | }); 108 | 109 | isLoadAgain = new Random().nextInt(3) == 1; 110 | 111 | line1(); 112 | line2(); 113 | line3(); 114 | line41(); 115 | line42(); 116 | } 117 | 118 | private void line1() { 119 | image11.loadImage(url1, R.color.placeholder_color).listener(new OnProgressListener() { 120 | @Override 121 | public void onProgress(String imageUrl, long bytesRead, long totalBytes, boolean isDone, GlideException exception) { 122 | Log.d("--->image11", "bytesRead: " + bytesRead + " totalBytes: " + totalBytes + " isDone: " + isDone); 123 | } 124 | }); 125 | 126 | image12.setBorderWidth(3); 127 | image12.setBorderColor(R.color.transparent20); 128 | image12.loadCircleImage(url1, R.color.placeholder_color); 129 | 130 | image13.setRadius(15); 131 | image13.setBorderWidth(3); 132 | image13.setBorderColor(R.color.blue); 133 | image13.setPressedAlpha(0.3f); 134 | image13.setPressedColor(R.color.blue); 135 | image13.loadImage(url1, R.color.placeholder_color); 136 | 137 | image14.setShapeType(ShapeImageView.ShapeType.CIRCLE); 138 | image14.setBorderWidth(3); 139 | image14.setBorderColor(R.color.orange); 140 | image14.setPressedAlpha(0.2f); 141 | image14.setPressedColor(R.color.orange); 142 | image14.loadImage(url1, R.color.placeholder_color); 143 | } 144 | 145 | private void line2() { 146 | image21.loadImage(url2, R.color.placeholder_color); 147 | image22.loadImage(url2, R.color.placeholder_color); 148 | image23.loadImage(url2, R.color.placeholder_color); 149 | image24.loadImage(url2, R.color.placeholder_color); 150 | } 151 | 152 | private void line3() { 153 | image31.loadLocalImage(R.drawable.gif_robot_walk, R.mipmap.ic_launcher); 154 | 155 | image32.loadCircleImage(gif1, R.mipmap.ic_launcher).listener(new OnGlideImageViewListener() { 156 | @Override 157 | public void onProgress(int percent, boolean isDone, GlideException exception) { 158 | Log.d("--->image32", "percent: " + percent + " isDone: " + isDone); 159 | } 160 | }); 161 | 162 | image33.loadImage(gif2, R.mipmap.ic_launcher); 163 | image34.loadImage(gif3, R.mipmap.ic_launcher); 164 | } 165 | 166 | private void line41() { 167 | image41.setOnClickListener(new View.OnClickListener() { 168 | @Override 169 | public void onClick(View v) { 170 | Intent intent = new Intent(MainActivity.this, SingleImageActivity.class); 171 | intent.putExtra(KEY_IMAGE_URL, cat); 172 | intent.putExtra(KEY_IMAGE_URL_THUMBNAIL, cat_thumbnail); 173 | ActivityOptionsCompat compat = ActivityOptionsCompat 174 | .makeSceneTransitionAnimation(MainActivity.this, image41, getString(R.string.transitional_image)); 175 | ActivityCompat.startActivity(MainActivity.this, intent, compat.toBundle()); 176 | } 177 | }); 178 | 179 | RequestOptions requestOptions = image41.requestOptions(R.color.placeholder_color).centerCrop(); 180 | if (isLoadAgain) { 181 | requestOptions.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true); 182 | } 183 | 184 | // 第一种方式加载 185 | image41.load(cat_thumbnail, requestOptions).listener(new OnGlideImageViewListener() { 186 | @Override 187 | public void onProgress(int percent, boolean isDone, GlideException exception) { 188 | if (exception != null && !TextUtils.isEmpty(exception.getMessage())) { 189 | Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show(); 190 | } 191 | progressView1.setProgress(percent); 192 | progressView1.setVisibility(isDone ? View.GONE : View.VISIBLE); 193 | } 194 | }); 195 | } 196 | 197 | private void line42() { 198 | image42.setOnClickListener(new View.OnClickListener() { 199 | @Override 200 | public void onClick(View view) { 201 | Intent intent = new Intent(MainActivity.this, SingleImageActivity.class); 202 | intent.putExtra(KEY_IMAGE_URL, girl); 203 | intent.putExtra(KEY_IMAGE_URL_THUMBNAIL, girl_thumbnail); 204 | ActivityOptionsCompat compat = ActivityOptionsCompat 205 | .makeSceneTransitionAnimation(MainActivity.this, image42, getString(R.string.transitional_image)); 206 | ActivityCompat.startActivity(MainActivity.this, intent, compat.toBundle()); 207 | } 208 | }); 209 | 210 | RequestOptions requestOptions = image42.requestOptions(R.color.placeholder_color).centerCrop(); 211 | if (isLoadAgain) { 212 | requestOptions.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true); 213 | } 214 | 215 | // 第二种方式加载:可以解锁更多功能 216 | GlideImageLoader imageLoader = image42.getImageLoader(); 217 | imageLoader.setOnGlideImageViewListener(girl_thumbnail, new OnGlideImageViewListener() { 218 | @Override 219 | public void onProgress(int percent, boolean isDone, GlideException exception) { 220 | 221 | if (exception != null && !TextUtils.isEmpty(exception.getMessage())) { 222 | Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show(); 223 | } 224 | progressView2.setProgress(percent); 225 | progressView2.setVisibility(isDone ? View.GONE : View.VISIBLE); 226 | } 227 | }); 228 | imageLoader.requestBuilder(girl_thumbnail, requestOptions) 229 | .transition(DrawableTransitionOptions.withCrossFade()) 230 | .into(image42); 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /app/src/main/java/cy/wcdb/yyh/com/myglidefactoryview/widget/NineImageView/NineImageView.java: -------------------------------------------------------------------------------- 1 | package cy.wcdb.yyh.com.myglidefactoryview.widget.NineImageView; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.drawable.Drawable; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.text.TextUtils; 9 | import android.util.AttributeSet; 10 | import android.util.DisplayMetrics; 11 | import android.util.TypedValue; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.ImageView; 15 | 16 | import com.bumptech.glide.load.DataSource; 17 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 18 | import com.bumptech.glide.load.engine.GlideException; 19 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 20 | import com.bumptech.glide.request.RequestListener; 21 | import com.bumptech.glide.request.RequestOptions; 22 | import com.bumptech.glide.request.target.Target; 23 | import com.sunfusheng.glideimageview.GlideImageLoader; 24 | import com.sunfusheng.glideimageview.util.DisplayUtil; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import cy.wcdb.yyh.com.myglidefactoryview.R; 30 | 31 | public class NineImageView extends ViewGroup { 32 | 33 | public static final int MODE_GRID = 0; //网格模式,4张图2X2布局 34 | public static final int MODE_FILL = 1; //填充模式,类似于微信 35 | 36 | private int singleImageWidth; 37 | private int singleImageHeight; 38 | private int singleImageMaxWidth; 39 | private int singleImageMinWidth; 40 | 41 | private int maxImageSize = 9; // 最大显示的图片数 42 | private int gridSpacing = 5; // 宫格间距,单位dp 43 | private int mode = MODE_GRID; // 默认使用GRID模式 44 | 45 | private int columnCount; // 列数 46 | private int rowCount; // 行数 47 | private int gridWidth; // 宫格宽度 48 | private int gridHeight; // 宫格高度 49 | 50 | private List imageViews; 51 | private List imageAttrs; 52 | private NineImageViewAdapter mAdapter; 53 | 54 | public NineImageView(Context context) { 55 | this(context, null); 56 | } 57 | 58 | public NineImageView(Context context, AttributeSet attrs) { 59 | this(context, attrs, 0); 60 | } 61 | 62 | public NineImageView(Context context, AttributeSet attrs, int defStyleAttr) { 63 | super(context, attrs, defStyleAttr); 64 | init(context, attrs); 65 | } 66 | 67 | private void init(Context context, AttributeSet attrs) { 68 | int WIDTH = DisplayUtil.getScreenWidth(context) - DisplayUtil.dip2px(context, 15 * 2); 69 | singleImageMaxWidth = WIDTH * 2 / 3; 70 | singleImageMinWidth = WIDTH / 3; 71 | singleImageWidth = singleImageMinWidth; 72 | singleImageHeight = singleImageMinWidth; 73 | 74 | imageViews = new ArrayList<>(); 75 | 76 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 77 | gridSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gridSpacing, displayMetrics); 78 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NineGridView); 79 | gridSpacing = (int) typedArray.getDimension(R.styleable.NineGridView_ngv_gridSpacing, gridSpacing); 80 | maxImageSize = typedArray.getInt(R.styleable.NineGridView_ngv_maxSize, maxImageSize); 81 | mode = typedArray.getInt(R.styleable.NineGridView_ngv_mode, mode); 82 | typedArray.recycle(); 83 | } 84 | 85 | @Override 86 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 87 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 88 | if (imageAttrs == null) return; 89 | int width = MeasureSpec.getSize(widthMeasureSpec); 90 | int height = 0; 91 | int totalWidth = width - getPaddingLeft() - getPaddingRight(); 92 | if (imageAttrs.size() == 1) { 93 | gridWidth = singleImageWidth; 94 | gridHeight = singleImageHeight; 95 | } else { 96 | gridWidth = gridHeight = (totalWidth - gridSpacing * 2) / 3; 97 | } 98 | width = gridWidth * columnCount + gridSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight(); 99 | height = gridHeight * rowCount + gridSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom(); 100 | setMeasuredDimension(width, height); 101 | } 102 | 103 | @Override 104 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 105 | layoutChildrenView(); 106 | } 107 | 108 | private void layoutChildrenView() { 109 | if (imageAttrs == null) return; 110 | int childrenCount = imageAttrs.size(); 111 | for (int i = 0; i < childrenCount; i++) { 112 | ImageView imageView = (ImageView) getChildAt(i); 113 | ImageAttr imageAttr = imageAttrs.get(i); 114 | if (imageView == null) continue; 115 | 116 | int rowNum = i / columnCount; 117 | int columnNum = i % columnCount; 118 | int left = (gridWidth + gridSpacing) * columnNum + getPaddingLeft(); 119 | int top = (gridHeight + gridSpacing) * rowNum + getPaddingTop(); 120 | int right = left + gridWidth; 121 | int bottom = top + gridHeight; 122 | imageView.layout(left, top, right, bottom); 123 | 124 | loadImage(imageView, imageAttr, childrenCount); 125 | } 126 | } 127 | 128 | private void loadImage(ImageView imageView, ImageAttr attr, final int count) { 129 | String url = TextUtils.isEmpty(attr.thumbnailUrl) ? attr.url : attr.thumbnailUrl; 130 | GlideImageLoader imageLoader = GlideImageLoader.create(imageView); 131 | 132 | RequestOptions requestOptions = imageLoader.requestOptions(R.color.placeholder_color) 133 | .centerCrop() 134 | .skipMemoryCache(false) 135 | .diskCacheStrategy(DiskCacheStrategy.RESOURCE) 136 | .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); 137 | 138 | imageLoader.requestBuilder(url, requestOptions) 139 | .transition(DrawableTransitionOptions.withCrossFade()) 140 | .listener(new RequestListener() { 141 | @Override 142 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { 143 | return false; 144 | } 145 | 146 | @Override 147 | public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { 148 | if (count == 1) { 149 | setSingleImageWidthHeight(resource); 150 | } 151 | return false; 152 | } 153 | }).into(imageView); 154 | } 155 | 156 | private void setSingleImageWidthHeight(Drawable drawable) { 157 | if (drawable == null) return; 158 | int width = drawable.getIntrinsicWidth(); 159 | int height = drawable.getIntrinsicHeight(); 160 | float ratio = width * 1.0f / height; 161 | 162 | if (width > singleImageMaxWidth || ratio > 2.1f) { 163 | singleImageWidth = singleImageMaxWidth; 164 | singleImageHeight = (int) (singleImageMaxWidth * height * 1.0f / width); 165 | } else if (width < singleImageMinWidth || ratio < 0.7f) { 166 | if (ratio < 0.3f) { 167 | singleImageWidth = singleImageMinWidth / 2; 168 | singleImageHeight = (int) (singleImageMinWidth / 2 * height * 1.0f / width); 169 | if (singleImageHeight > singleImageMaxWidth) { 170 | singleImageHeight = singleImageMaxWidth; 171 | } 172 | } else { 173 | singleImageWidth = singleImageMinWidth; 174 | singleImageHeight = (int) (singleImageMinWidth * height * 1.0f / width); 175 | } 176 | } else { 177 | singleImageWidth = width; 178 | singleImageHeight = height; 179 | } 180 | } 181 | 182 | public void setAdapter(@NonNull NineImageViewAdapter adapter) { 183 | this.mAdapter = adapter; 184 | List attrList = adapter.getImageAttrs(); 185 | if (attrList == null || attrList.isEmpty()) { 186 | setVisibility(GONE); 187 | return; 188 | } 189 | setVisibility(VISIBLE); 190 | 191 | int imageCount = attrList.size(); 192 | if (maxImageSize > 0 && imageCount > maxImageSize) { 193 | attrList = attrList.subList(0, maxImageSize); 194 | imageCount = attrList.size(); // 再次获取图片数量 195 | } 196 | 197 | // 默认是3列显示,行数根据图片的数量决定 198 | rowCount = imageCount / 3 + (imageCount % 3 == 0 ? 0 : 1); 199 | columnCount = 3; 200 | // Grid模式下,显示4张使用2X2模式 201 | if (mode == MODE_GRID) { 202 | if (imageCount == 4) { 203 | rowCount = 2; 204 | columnCount = 2; 205 | } 206 | } 207 | 208 | // 保证View的复用,避免重复创建 209 | if (imageAttrs == null) { 210 | for (int i = 0; i < imageCount; i++) { 211 | ImageView imageView = getImageView(i); 212 | addView(imageView, generateDefaultLayoutParams()); 213 | } 214 | } else { 215 | int oldViewCount = imageAttrs.size(); 216 | if (oldViewCount > imageCount) { 217 | removeViews(imageCount, oldViewCount - imageCount); 218 | } else if (oldViewCount < imageCount) { 219 | for (int i = oldViewCount; i < imageCount; i++) { 220 | ImageView imageView = getImageView(i); 221 | addView(imageView, generateDefaultLayoutParams()); 222 | } 223 | } 224 | } 225 | 226 | // 修改最后一个条目,决定是否显示更多 227 | if (adapter.getImageAttrs().size() > maxImageSize) { 228 | View child = getChildAt(maxImageSize - 1); 229 | if (child instanceof ImageViewWrapper) { 230 | ImageViewWrapper imageView = (ImageViewWrapper) child; 231 | imageView.setMoreNum(adapter.getImageAttrs().size() - maxImageSize); 232 | } 233 | } 234 | imageAttrs = attrList; 235 | layoutChildrenView(); 236 | } 237 | 238 | // 获得ImageView,并保证ImageView的重用 239 | private ImageView getImageView(final int position) { 240 | ImageView imageView = null; 241 | if (position < imageViews.size()) { 242 | imageView = imageViews.get(position); 243 | } 244 | if (imageView == null) { 245 | imageView = mAdapter.generateImageView(getContext()); 246 | imageView.setOnClickListener( 247 | new OnClickListener() { 248 | @Override 249 | public void onClick(View view) { 250 | mAdapter.onImageItemClick(getContext(), NineImageView.this, position, mAdapter.getImageAttrs()); 251 | } 252 | }); 253 | imageViews.add(imageView); 254 | } 255 | return imageView; 256 | } 257 | 258 | public NineImageViewAdapter getAdapter() { 259 | return mAdapter; 260 | } 261 | 262 | public void setGridSpacing(int spacing) { 263 | gridSpacing = spacing; 264 | } 265 | 266 | public void setMaxSize(int maxSize) { 267 | maxImageSize = maxSize; 268 | } 269 | 270 | public int getMaxSize() { 271 | return maxImageSize; 272 | } 273 | 274 | } 275 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 19 | 20 | 25 | 26 | 33 | 34 | 41 | 42 | 49 | 50 | 57 | 58 | 59 | 64 | 65 | 77 | 78 | 91 | 92 | 104 | 105 | 117 | 118 | 119 | 124 | 125 | 131 | 132 | 140 | 141 | 148 | 149 | 158 | 159 | 160 | 164 | 165 | 174 | 175 | 184 | 185 | 200 | 201 | 202 | 211 | 212 | 221 | 222 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 248 | 249 | 260 | 261 | -------------------------------------------------------------------------------- /GlideImageView/src/main/java/com/sunfusheng/glideimageview/progress/CircleProgressView.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.glideimageview.progress; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.RectF; 10 | import android.os.Bundle; 11 | import android.os.Parcelable; 12 | import android.support.annotation.IntDef; 13 | import android.util.AttributeSet; 14 | import android.view.animation.AccelerateDecelerateInterpolator; 15 | import android.widget.ProgressBar; 16 | 17 | import com.sunfusheng.glideimageview.R; 18 | import com.sunfusheng.glideimageview.util.DisplayUtil; 19 | 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | 23 | public class CircleProgressView extends ProgressBar { 24 | 25 | private int mReachBarSize = DisplayUtil.dip2px(getContext(), 2); // 未完成进度条大小 26 | private int mNormalBarSize = DisplayUtil.dip2px(getContext(), 2); // 未完成进度条大小 27 | private int mReachBarColor = Color.parseColor("#108ee9"); // 已完成进度颜色 28 | private int mNormalBarColor = Color.parseColor("#FFD3D6DA"); // 未完成进度颜色 29 | private int mTextSize = DisplayUtil.sp2px(getContext(), 14); // 进度值字体大小 30 | private int mTextColor = Color.parseColor("#108ee9"); // 进度的值字体颜色 31 | private float mTextSkewX; // 进度值字体倾斜角度 32 | private String mTextSuffix = "%"; // 进度值前缀 33 | private String mTextPrefix = ""; // 进度值后缀 34 | private boolean mTextVisible = true; // 是否显示进度值 35 | private boolean mReachCapRound; // 画笔是否使用圆角边界,normalStyle下生效 36 | private int mRadius = DisplayUtil.dip2px(getContext(), 20); // 半径 37 | private int mStartArc; // 起始角度 38 | private int mInnerBackgroundColor; // 内部背景填充颜色 39 | private int mProgressStyle = ProgressStyle.NORMAL; // 进度风格 40 | private int mInnerPadding = DisplayUtil.dip2px(getContext(), 1); // 内部圆与外部圆间距 41 | private int mOuterColor; // 外部圆环颜色 42 | private boolean needDrawInnerBackground; // 是否需要绘制内部背景 43 | private RectF rectF; // 外部圆环绘制区域 44 | private RectF rectInner; // 内部圆环绘制区域 45 | private int mOuterSize = DisplayUtil.dip2px(getContext(), 1); // 外层圆环宽度 46 | private Paint mTextPaint; // 绘制进度值字体画笔 47 | private Paint mNormalPaint; // 绘制未完成进度画笔 48 | private Paint mReachPaint; // 绘制已完成进度画笔 49 | private Paint mInnerBackgroundPaint; // 内部背景画笔 50 | private Paint mOutPaint; // 外部圆环画笔 51 | 52 | private int mRealWidth; 53 | private int mRealHeight; 54 | 55 | @IntDef({ProgressStyle.NORMAL, ProgressStyle.FILL_IN, ProgressStyle.FILL_IN_ARC}) 56 | @Retention(RetentionPolicy.SOURCE) 57 | public @interface ProgressStyle { 58 | int NORMAL = 0; 59 | int FILL_IN = 1; 60 | int FILL_IN_ARC = 2; 61 | } 62 | 63 | public CircleProgressView(Context context) { 64 | this(context, null); 65 | } 66 | 67 | public CircleProgressView(Context context, AttributeSet attrs) { 68 | this(context, attrs, 0); 69 | } 70 | 71 | public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) { 72 | super(context, attrs, defStyleAttr); 73 | obtainAttributes(attrs); 74 | initPaint(); 75 | } 76 | 77 | private void initPaint() { 78 | mTextPaint = new Paint(); 79 | mTextPaint.setColor(mTextColor); 80 | mTextPaint.setStyle(Paint.Style.FILL); 81 | mTextPaint.setTextSize(mTextSize); 82 | mTextPaint.setTextSkewX(mTextSkewX); 83 | mTextPaint.setAntiAlias(true); 84 | 85 | mNormalPaint = new Paint(); 86 | mNormalPaint.setColor(mNormalBarColor); 87 | mNormalPaint.setStyle(mProgressStyle == ProgressStyle.FILL_IN_ARC ? Paint.Style.FILL : Paint.Style.STROKE); 88 | mNormalPaint.setAntiAlias(true); 89 | mNormalPaint.setStrokeWidth(mNormalBarSize); 90 | 91 | mReachPaint = new Paint(); 92 | mReachPaint.setColor(mReachBarColor); 93 | mReachPaint.setStyle(mProgressStyle == ProgressStyle.FILL_IN_ARC ? Paint.Style.FILL : Paint.Style.STROKE); 94 | mReachPaint.setAntiAlias(true); 95 | mReachPaint.setStrokeCap(mReachCapRound ? Paint.Cap.ROUND : Paint.Cap.BUTT); 96 | mReachPaint.setStrokeWidth(mReachBarSize); 97 | 98 | if (needDrawInnerBackground) { 99 | mInnerBackgroundPaint = new Paint(); 100 | mInnerBackgroundPaint.setStyle(Paint.Style.FILL); 101 | mInnerBackgroundPaint.setAntiAlias(true); 102 | mInnerBackgroundPaint.setColor(mInnerBackgroundColor); 103 | } 104 | if (mProgressStyle == ProgressStyle.FILL_IN_ARC) { 105 | mOutPaint = new Paint(); 106 | mOutPaint.setStyle(Paint.Style.STROKE); 107 | mOutPaint.setColor(mOuterColor); 108 | mOutPaint.setStrokeWidth(mOuterSize); 109 | mOutPaint.setAntiAlias(true); 110 | } 111 | } 112 | 113 | private void obtainAttributes(AttributeSet attrs) { 114 | TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.CircleProgressView); 115 | mProgressStyle = ta.getInt(R.styleable.CircleProgressView_cpv_progressStyle, ProgressStyle.NORMAL); 116 | // 获取三种风格通用的属性 117 | mNormalBarSize = (int) ta.getDimension(R.styleable.CircleProgressView_cpv_progressNormalSize, mNormalBarSize); 118 | mNormalBarColor = ta.getColor(R.styleable.CircleProgressView_cpv_progressNormalColor, mNormalBarColor); 119 | 120 | mReachBarSize = (int) ta.getDimension(R.styleable.CircleProgressView_cpv_progressReachSize, mReachBarSize); 121 | mReachBarColor = ta.getColor(R.styleable.CircleProgressView_cpv_progressReachColor, mReachBarColor); 122 | 123 | mTextSize = (int) ta.getDimension(R.styleable.CircleProgressView_cpv_progressTextSize, mTextSize); 124 | mTextColor = ta.getColor(R.styleable.CircleProgressView_cpv_progressTextColor, mTextColor); 125 | mTextSkewX = ta.getDimension(R.styleable.CircleProgressView_cpv_progressTextSkewX, 0); 126 | if (ta.hasValue(R.styleable.CircleProgressView_cpv_progressTextSuffix)) { 127 | mTextSuffix = ta.getString(R.styleable.CircleProgressView_cpv_progressTextSuffix); 128 | } 129 | if (ta.hasValue(R.styleable.CircleProgressView_cpv_progressTextPrefix)) { 130 | mTextPrefix = ta.getString(R.styleable.CircleProgressView_cpv_progressTextPrefix); 131 | } 132 | mTextVisible = ta.getBoolean(R.styleable.CircleProgressView_cpv_progressTextVisible, mTextVisible); 133 | 134 | mRadius = (int) ta.getDimension(R.styleable.CircleProgressView_cpv_radius, mRadius); 135 | rectF = new RectF(-mRadius, -mRadius, mRadius, mRadius); 136 | 137 | switch (mProgressStyle) { 138 | case ProgressStyle.FILL_IN: 139 | mReachBarSize = 0; 140 | mNormalBarSize = 0; 141 | mOuterSize = 0; 142 | break; 143 | case ProgressStyle.FILL_IN_ARC: 144 | mStartArc = ta.getInt(R.styleable.CircleProgressView_cpv_progressStartArc, 0) + 270; 145 | mInnerPadding = (int) ta.getDimension(R.styleable.CircleProgressView_cpv_innerPadding, mInnerPadding); 146 | mOuterColor = ta.getColor(R.styleable.CircleProgressView_cpv_outerColor, mReachBarColor); 147 | mOuterSize = (int) ta.getDimension(R.styleable.CircleProgressView_cpv_outerSize, mOuterSize); 148 | mReachBarSize = 0;// 将画笔大小重置为0 149 | mNormalBarSize = 0; 150 | if (!ta.hasValue(R.styleable.CircleProgressView_cpv_progressNormalColor)) { 151 | mNormalBarColor = Color.TRANSPARENT; 152 | } 153 | int mInnerRadius = mRadius - mOuterSize / 2 - mInnerPadding; 154 | rectInner = new RectF(-mInnerRadius, -mInnerRadius, mInnerRadius, mInnerRadius); 155 | 156 | break; 157 | case ProgressStyle.NORMAL: 158 | mReachCapRound = ta.getBoolean(R.styleable.CircleProgressView_cpv_reachCapRound, true); 159 | mStartArc = ta.getInt(R.styleable.CircleProgressView_cpv_progressStartArc, 0) + 270; 160 | if (ta.hasValue(R.styleable.CircleProgressView_cpv_innerBackgroundColor)) { 161 | mInnerBackgroundColor = ta.getColor(R.styleable.CircleProgressView_cpv_innerBackgroundColor, Color.argb(0, 0, 0, 0)); 162 | needDrawInnerBackground = true; 163 | } 164 | break; 165 | } 166 | ta.recycle(); 167 | } 168 | 169 | @Override 170 | protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 171 | int maxBarPaintWidth = Math.max(mReachBarSize, mNormalBarSize); 172 | int maxPaintWidth = Math.max(maxBarPaintWidth, mOuterSize); 173 | int height = 0; 174 | int width = 0; 175 | switch (mProgressStyle) { 176 | case ProgressStyle.FILL_IN: 177 | height = getPaddingTop() + getPaddingBottom() // 边距 178 | + Math.abs(mRadius * 2); // 直径 179 | width = getPaddingLeft() + getPaddingRight() // 边距 180 | + Math.abs(mRadius * 2); // 直径 181 | break; 182 | case ProgressStyle.FILL_IN_ARC: 183 | height = getPaddingTop() + getPaddingBottom() // 边距 184 | + Math.abs(mRadius * 2) // 直径 185 | + maxPaintWidth;// 边框 186 | width = getPaddingLeft() + getPaddingRight() // 边距 187 | + Math.abs(mRadius * 2) // 直径 188 | + maxPaintWidth;// 边框 189 | break; 190 | case ProgressStyle.NORMAL: 191 | height = getPaddingTop() + getPaddingBottom() // 边距 192 | + Math.abs(mRadius * 2) // 直径 193 | + maxBarPaintWidth;// 边框 194 | width = getPaddingLeft() + getPaddingRight() // 边距 195 | + Math.abs(mRadius * 2) // 直径 196 | + maxBarPaintWidth;// 边框 197 | break; 198 | } 199 | 200 | mRealWidth = resolveSize(width, widthMeasureSpec); 201 | mRealHeight = resolveSize(height, heightMeasureSpec); 202 | 203 | setMeasuredDimension(mRealWidth, mRealHeight); 204 | } 205 | 206 | @Override 207 | protected synchronized void onDraw(Canvas canvas) { 208 | switch (mProgressStyle) { 209 | case ProgressStyle.NORMAL: 210 | drawNormalCircle(canvas); 211 | break; 212 | case ProgressStyle.FILL_IN: 213 | drawFillInCircle(canvas); 214 | break; 215 | case ProgressStyle.FILL_IN_ARC: 216 | drawFillInArcCircle(canvas); 217 | break; 218 | } 219 | } 220 | 221 | /** 222 | * 绘制PROGRESS_STYLE_FILL_IN_ARC圆形 223 | */ 224 | private void drawFillInArcCircle(Canvas canvas) { 225 | canvas.save(); 226 | canvas.translate(mRealWidth / 2, mRealHeight / 2); 227 | // 绘制外层圆环 228 | canvas.drawArc(rectF, 0, 360, false, mOutPaint); 229 | // 绘制内层进度实心圆弧 230 | // 内层圆弧半径 231 | float reachArc = getProgress() * 1.0f / getMax() * 360; 232 | canvas.drawArc(rectInner, mStartArc, reachArc, true, mReachPaint); 233 | 234 | // 绘制未到达进度 235 | if (reachArc != 360) { 236 | canvas.drawArc(rectInner, reachArc + mStartArc, 360 - reachArc, true, mNormalPaint); 237 | } 238 | 239 | canvas.restore(); 240 | } 241 | 242 | /** 243 | * 绘制PROGRESS_STYLE_FILL_IN圆形 244 | */ 245 | private void drawFillInCircle(Canvas canvas) { 246 | canvas.save(); 247 | canvas.translate(mRealWidth / 2, mRealHeight / 2); 248 | float progressY = getProgress() * 1.0f / getMax() * (mRadius * 2); 249 | float angle = (float) (Math.acos((mRadius - progressY) / mRadius) * 180 / Math.PI); 250 | float startAngle = 90 + angle; 251 | float sweepAngle = 360 - angle * 2; 252 | // 绘制未到达区域 253 | rectF = new RectF(-mRadius, -mRadius, mRadius, mRadius); 254 | mNormalPaint.setStyle(Paint.Style.FILL); 255 | canvas.drawArc(rectF, startAngle, sweepAngle, false, mNormalPaint); 256 | // 翻转180度绘制已到达区域 257 | canvas.rotate(180); 258 | mReachPaint.setStyle(Paint.Style.FILL); 259 | canvas.drawArc(rectF, 270 - angle, angle * 2, false, mReachPaint); 260 | // 文字显示在最上层最后绘制 261 | canvas.rotate(180); 262 | // 绘制文字 263 | if (mTextVisible) { 264 | String text = mTextPrefix + getProgress() + mTextSuffix; 265 | float textWidth = mTextPaint.measureText(text); 266 | float textHeight = (mTextPaint.descent() + mTextPaint.ascent()); 267 | canvas.drawText(text, -textWidth / 2, -textHeight / 2, mTextPaint); 268 | } 269 | } 270 | 271 | /** 272 | * 绘制PROGRESS_STYLE_NORMAL圆形 273 | */ 274 | private void drawNormalCircle(Canvas canvas) { 275 | canvas.save(); 276 | canvas.translate(mRealWidth / 2, mRealHeight / 2); 277 | // 绘制内部圆形背景色 278 | if (needDrawInnerBackground) { 279 | canvas.drawCircle(0, 0, mRadius - Math.min(mReachBarSize, mNormalBarSize) / 2, 280 | mInnerBackgroundPaint); 281 | } 282 | // 绘制文字 283 | if (mTextVisible) { 284 | String text = mTextPrefix + getProgress() + mTextSuffix; 285 | float textWidth = mTextPaint.measureText(text); 286 | float textHeight = (mTextPaint.descent() + mTextPaint.ascent()); 287 | canvas.drawText(text, -textWidth / 2, -textHeight / 2, mTextPaint); 288 | } 289 | // 计算进度值 290 | float reachArc = getProgress() * 1.0f / getMax() * 360; 291 | // 绘制未到达进度 292 | if (reachArc != 360) { 293 | canvas.drawArc(rectF, reachArc + mStartArc, 360 - reachArc, false, mNormalPaint); 294 | } 295 | // 绘制已到达进度 296 | canvas.drawArc(rectF, mStartArc, reachArc, false, mReachPaint); 297 | canvas.restore(); 298 | } 299 | 300 | /** 301 | * 动画进度(0-当前进度) 302 | * 303 | * @param duration 动画时长 304 | */ 305 | public void runProgressAnim(long duration) { 306 | setProgressInTime(0, duration); 307 | } 308 | 309 | /** 310 | * @param progress 进度值 311 | * @param duration 动画播放时间 312 | */ 313 | public void setProgressInTime(final int progress, final long duration) { 314 | setProgressInTime(progress, getProgress(), duration); 315 | } 316 | 317 | /** 318 | * @param startProgress 起始进度 319 | * @param progress 进度值 320 | * @param duration 动画播放时间 321 | */ 322 | public void setProgressInTime(int startProgress, final int progress, final long duration) { 323 | ValueAnimator valueAnimator = ValueAnimator.ofInt(startProgress, progress); 324 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 325 | 326 | @Override 327 | public void onAnimationUpdate(ValueAnimator animator) { 328 | //获得当前动画的进度值,整型,1-100之间 329 | int currentValue = (Integer) animator.getAnimatedValue(); 330 | setProgress(currentValue); 331 | } 332 | }); 333 | AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator(); 334 | valueAnimator.setInterpolator(interpolator); 335 | valueAnimator.setDuration(duration); 336 | valueAnimator.start(); 337 | } 338 | 339 | public int getReachBarSize() { 340 | return mReachBarSize; 341 | } 342 | 343 | public void setReachBarSize(int reachBarSize) { 344 | mReachBarSize = DisplayUtil.dip2px(getContext(), reachBarSize); 345 | invalidate(); 346 | } 347 | 348 | public int getNormalBarSize() { 349 | return mNormalBarSize; 350 | } 351 | 352 | public void setNormalBarSize(int normalBarSize) { 353 | mNormalBarSize = DisplayUtil.dip2px(getContext(), normalBarSize); 354 | invalidate(); 355 | } 356 | 357 | public int getReachBarColor() { 358 | return mReachBarColor; 359 | } 360 | 361 | public void setReachBarColor(int reachBarColor) { 362 | mReachBarColor = reachBarColor; 363 | invalidate(); 364 | } 365 | 366 | public int getNormalBarColor() { 367 | return mNormalBarColor; 368 | } 369 | 370 | public void setNormalBarColor(int normalBarColor) { 371 | mNormalBarColor = normalBarColor; 372 | invalidate(); 373 | } 374 | 375 | public int getTextSize() { 376 | return mTextSize; 377 | } 378 | 379 | public void setTextSize(int textSize) { 380 | mTextSize = DisplayUtil.sp2px(getContext(), textSize); 381 | invalidate(); 382 | } 383 | 384 | public int getTextColor() { 385 | return mTextColor; 386 | } 387 | 388 | public void setTextColor(int textColor) { 389 | mTextColor = textColor; 390 | invalidate(); 391 | } 392 | 393 | public float getTextSkewX() { 394 | return mTextSkewX; 395 | } 396 | 397 | public void setTextSkewX(float textSkewX) { 398 | mTextSkewX = textSkewX; 399 | invalidate(); 400 | } 401 | 402 | public String getTextSuffix() { 403 | return mTextSuffix; 404 | } 405 | 406 | public void setTextSuffix(String textSuffix) { 407 | mTextSuffix = textSuffix; 408 | invalidate(); 409 | } 410 | 411 | public String getTextPrefix() { 412 | return mTextPrefix; 413 | } 414 | 415 | public void setTextPrefix(String textPrefix) { 416 | mTextPrefix = textPrefix; 417 | invalidate(); 418 | } 419 | 420 | public boolean isTextVisible() { 421 | return mTextVisible; 422 | } 423 | 424 | public void setTextVisible(boolean textVisible) { 425 | mTextVisible = textVisible; 426 | invalidate(); 427 | } 428 | 429 | public boolean isReachCapRound() { 430 | return mReachCapRound; 431 | } 432 | 433 | public void setReachCapRound(boolean reachCapRound) { 434 | mReachCapRound = reachCapRound; 435 | invalidate(); 436 | } 437 | 438 | public int getRadius() { 439 | return mRadius; 440 | } 441 | 442 | public void setRadius(int radius) { 443 | mRadius = DisplayUtil.dip2px(getContext(), radius); 444 | invalidate(); 445 | } 446 | 447 | public int getStartArc() { 448 | return mStartArc; 449 | } 450 | 451 | public void setStartArc(int startArc) { 452 | mStartArc = startArc; 453 | invalidate(); 454 | } 455 | 456 | public int getInnerBackgroundColor() { 457 | return mInnerBackgroundColor; 458 | } 459 | 460 | public void setInnerBackgroundColor(int innerBackgroundColor) { 461 | mInnerBackgroundColor = innerBackgroundColor; 462 | invalidate(); 463 | } 464 | 465 | public int getProgressStyle() { 466 | return mProgressStyle; 467 | } 468 | 469 | public void setProgressStyle(int progressStyle) { 470 | mProgressStyle = progressStyle; 471 | invalidate(); 472 | } 473 | 474 | public int getInnerPadding() { 475 | return mInnerPadding; 476 | } 477 | 478 | public void setInnerPadding(int innerPadding) { 479 | mInnerPadding = DisplayUtil.dip2px(getContext(), innerPadding); 480 | int mInnerRadius = mRadius - mOuterSize / 2 - mInnerPadding; 481 | rectInner = new RectF(-mInnerRadius, -mInnerRadius, mInnerRadius, mInnerRadius); 482 | invalidate(); 483 | } 484 | 485 | public int getOuterColor() { 486 | return mOuterColor; 487 | } 488 | 489 | public void setOuterColor(int outerColor) { 490 | mOuterColor = outerColor; 491 | invalidate(); 492 | } 493 | 494 | public int getOuterSize() { 495 | return mOuterSize; 496 | } 497 | 498 | public void setOuterSize(int outerSize) { 499 | mOuterSize = DisplayUtil.dip2px(getContext(), outerSize); 500 | invalidate(); 501 | } 502 | 503 | private static final String STATE = "state"; 504 | private static final String PROGRESS_STYLE = "progressStyle"; 505 | private static final String TEXT_COLOR = "textColor"; 506 | private static final String TEXT_SIZE = "textSize"; 507 | private static final String TEXT_SKEW_X = "textSkewX"; 508 | private static final String TEXT_VISIBLE = "textVisible"; 509 | private static final String TEXT_SUFFIX = "textSuffix"; 510 | private static final String TEXT_PREFIX = "textPrefix"; 511 | private static final String REACH_BAR_COLOR = "reachBarColor"; 512 | private static final String REACH_BAR_SIZE = "reachBarSize"; 513 | private static final String NORMAL_BAR_COLOR = "normalBarColor"; 514 | private static final String NORMAL_BAR_SIZE = "normalBarSize"; 515 | private static final String IS_REACH_CAP_ROUND = "isReachCapRound"; 516 | private static final String RADIUS = "radius"; 517 | private static final String START_ARC = "startArc"; 518 | private static final String INNER_BG_COLOR = "innerBgColor"; 519 | private static final String INNER_PADDING = "innerPadding"; 520 | private static final String OUTER_COLOR = "outerColor"; 521 | private static final String OUTER_SIZE = "outerSize"; 522 | 523 | @Override 524 | public Parcelable onSaveInstanceState() { 525 | final Bundle bundle = new Bundle(); 526 | bundle.putParcelable(STATE, super.onSaveInstanceState()); 527 | // 保存当前样式 528 | bundle.putInt(PROGRESS_STYLE, getProgressStyle()); 529 | bundle.putInt(RADIUS, getRadius()); 530 | bundle.putBoolean(IS_REACH_CAP_ROUND, isReachCapRound()); 531 | bundle.putInt(START_ARC, getStartArc()); 532 | bundle.putInt(INNER_BG_COLOR, getInnerBackgroundColor()); 533 | bundle.putInt(INNER_PADDING, getInnerPadding()); 534 | bundle.putInt(OUTER_COLOR, getOuterColor()); 535 | bundle.putInt(OUTER_SIZE, getOuterSize()); 536 | // 保存text信息 537 | bundle.putInt(TEXT_COLOR, getTextColor()); 538 | bundle.putInt(TEXT_SIZE, getTextSize()); 539 | bundle.putFloat(TEXT_SKEW_X, getTextSkewX()); 540 | bundle.putBoolean(TEXT_VISIBLE, isTextVisible()); 541 | bundle.putString(TEXT_SUFFIX, getTextSuffix()); 542 | bundle.putString(TEXT_PREFIX, getTextPrefix()); 543 | // 保存已到达进度信息 544 | bundle.putInt(REACH_BAR_COLOR, getReachBarColor()); 545 | bundle.putInt(REACH_BAR_SIZE, getReachBarSize()); 546 | 547 | // 保存未到达进度信息 548 | bundle.putInt(NORMAL_BAR_COLOR, getNormalBarColor()); 549 | bundle.putInt(NORMAL_BAR_SIZE, getNormalBarSize()); 550 | return bundle; 551 | } 552 | 553 | @Override 554 | public void onRestoreInstanceState(Parcelable state) { 555 | if (state instanceof Bundle) { 556 | final Bundle bundle = (Bundle) state; 557 | 558 | mProgressStyle = bundle.getInt(PROGRESS_STYLE); 559 | mRadius = bundle.getInt(RADIUS); 560 | mReachCapRound = bundle.getBoolean(IS_REACH_CAP_ROUND); 561 | mStartArc = bundle.getInt(START_ARC); 562 | mInnerBackgroundColor = bundle.getInt(INNER_BG_COLOR); 563 | mInnerPadding = bundle.getInt(INNER_PADDING); 564 | mOuterColor = bundle.getInt(OUTER_COLOR); 565 | mOuterSize = bundle.getInt(OUTER_SIZE); 566 | 567 | mTextColor = bundle.getInt(TEXT_COLOR); 568 | mTextSize = bundle.getInt(TEXT_SIZE); 569 | mTextSkewX = bundle.getFloat(TEXT_SKEW_X); 570 | mTextVisible = bundle.getBoolean(TEXT_VISIBLE); 571 | mTextSuffix = bundle.getString(TEXT_SUFFIX); 572 | mTextPrefix = bundle.getString(TEXT_PREFIX); 573 | 574 | mReachBarColor = bundle.getInt(REACH_BAR_COLOR); 575 | mReachBarSize = bundle.getInt(REACH_BAR_SIZE); 576 | mNormalBarColor = bundle.getInt(NORMAL_BAR_COLOR); 577 | mNormalBarSize = bundle.getInt(NORMAL_BAR_SIZE); 578 | 579 | initPaint(); 580 | super.onRestoreInstanceState(bundle.getParcelable(STATE)); 581 | return; 582 | } 583 | super.onRestoreInstanceState(state); 584 | } 585 | 586 | @Override 587 | public void invalidate() { 588 | initPaint(); 589 | super.invalidate(); 590 | } 591 | } 592 | --------------------------------------------------------------------------------