├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── Larger ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── starot │ │ └── larger │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── starot │ │ │ └── larger │ │ │ ├── Larger.kt │ │ │ ├── act │ │ │ ├── CustomLargerAct.kt │ │ │ └── LargerAct.kt │ │ │ ├── adapter │ │ │ └── FgPageAdapter.kt │ │ │ ├── anim │ │ │ ├── AnimBgHelper.kt │ │ │ ├── AnimDragHelper.kt │ │ │ ├── AnimEnterHelper.kt │ │ │ ├── AnimExitHelper.kt │ │ │ ├── AnimParentHelper.kt │ │ │ └── impl │ │ │ │ ├── AnimListener.kt │ │ │ │ ├── OnAnimatorIntercept.kt │ │ │ │ └── OnDragAnimListener.kt │ │ │ ├── bean │ │ │ └── LargerBean.kt │ │ │ ├── builder │ │ │ ├── LargerBuilder.kt │ │ │ ├── config │ │ │ │ ├── ImageMultiConfig.kt │ │ │ │ ├── ImageSingleConfig.kt │ │ │ │ ├── VideoMultiConfig.kt │ │ │ │ └── VideoSingleConfig.kt │ │ │ └── impl │ │ │ │ ├── CommandConfig.kt │ │ │ │ ├── ImageConfig.kt │ │ │ │ ├── MultiConfig.kt │ │ │ │ ├── SingleConfig.kt │ │ │ │ └── VideoConfig.kt │ │ │ ├── config │ │ │ └── Config.kt │ │ │ ├── enums │ │ │ ├── AnimStatus.kt │ │ │ ├── AnimType.kt │ │ │ ├── BackEnum.kt │ │ │ ├── LargerDataEnum.kt │ │ │ ├── LargerEnum.kt │ │ │ ├── LoadImageStatus.kt │ │ │ └── Orientation.kt │ │ │ ├── fragment │ │ │ ├── BaseLargerFragment.kt │ │ │ ├── ImageFg.kt │ │ │ └── VideoFg.kt │ │ │ ├── impl │ │ │ ├── OnImageLoadListener.kt │ │ │ ├── OnLargerConfigListener.kt │ │ │ ├── OnLargerListener.kt │ │ │ ├── OnLargerType.kt │ │ │ ├── OnLifecycleListener.kt │ │ │ ├── OnLoadProgressListener.kt │ │ │ └── OnVideoLoadListener.kt │ │ │ ├── livedata │ │ │ └── UnViscousLiveData.kt │ │ │ ├── status │ │ │ └── LargerStatus.kt │ │ │ ├── utils │ │ │ ├── ColorTool.kt │ │ │ ├── LogUtils.kt │ │ │ ├── PageChange.kt │ │ │ └── StatusBarTools.kt │ │ │ └── view │ │ │ ├── image │ │ │ ├── Compat.java │ │ │ ├── CustomGestureDetector.java │ │ │ ├── LargerImageView.java │ │ │ ├── OnGestureListener.java │ │ │ ├── OnLargerDragListener.kt │ │ │ ├── OnLargerScaleListener.kt │ │ │ ├── OnMatrixChangedListener.java │ │ │ ├── OnOutsidePhotoTapListener.java │ │ │ ├── OnPhotoTapListener.java │ │ │ ├── OnScaleChangedListener.java │ │ │ ├── OnSingleFlingListener.java │ │ │ ├── OnViewDragListener.java │ │ │ ├── OnViewTapListener.java │ │ │ ├── PhotoViewAttacher.java │ │ │ └── Util.java │ │ │ └── progress │ │ │ ├── CircleProgressView.kt │ │ │ ├── ImageProgressLoader.kt │ │ │ └── ProgresssLoader.kt │ └── res │ │ ├── layout │ │ ├── activity_larger_base.xml │ │ └── fg_image.xml │ │ └── values │ │ ├── attrs.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── starot │ └── larger │ └── ExampleUnitTest.kt ├── LargerGlide ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── allens │ │ └── largerglide │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── allens │ │ └── largerglide │ │ ├── GlideImageLoader.kt │ │ ├── body │ │ └── ProgressResponseBody.kt │ │ ├── impl │ │ ├── CustomRequestListener.kt │ │ └── ProgressListener.kt │ │ ├── interceptor │ │ └── ProgressInterceptor.kt │ │ └── module │ │ └── MyGlideModule.kt │ └── test │ └── java │ └── com │ └── allens │ └── largerglide │ └── ExampleUnitTest.kt ├── LargerLoadVideo ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── largerloadvideo │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── largerloadvideo │ │ │ ├── LargerDrag.kt │ │ │ ├── LargerVideoLoad.kt │ │ │ └── MyVideoView.java │ └── res │ │ ├── layout │ │ ├── item_larger_video.xml │ │ └── layout_jzstd_notitle.xml │ │ └── values │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── largerloadvideo │ └── ExampleUnitTest.kt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── starot │ │ └── wechat │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── starot │ │ │ └── wechat │ │ │ ├── MainActivity.kt │ │ │ ├── activity │ │ │ ├── BaseAct.kt │ │ │ ├── HybridListAct.kt │ │ │ ├── ImageListAct.kt │ │ │ ├── SingleAudioAct.kt │ │ │ ├── SingleImageAct.kt │ │ │ └── VideoListAct.kt │ │ │ ├── adapter │ │ │ ├── HybridListAdapter.kt │ │ │ ├── ImageListAdapter.kt │ │ │ └── VideoListAdapter.kt │ │ │ ├── bean │ │ │ ├── ImageBean.kt │ │ │ └── VideoBean.kt │ │ │ └── utils │ │ │ └── Urls.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_image_list.xml │ │ ├── activity_image_single.xml │ │ ├── activity_main.xml │ │ ├── activity_single.xml │ │ ├── item_custom_image.xml │ │ └── item_image.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── com │ └── starot │ └── wechat │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | WeChat -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 22 | 23 | 24 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | xmlns:android 33 | 34 | ^$ 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | xmlns:.* 44 | 45 | ^$ 46 | 47 | 48 | BY_NAME 49 | 50 |
51 |
52 | 53 | 54 | 55 | .*:id 56 | 57 | http://schemas.android.com/apk/res/android 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | .*:name 67 | 68 | http://schemas.android.com/apk/res/android 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | name 78 | 79 | ^$ 80 | 81 | 82 | 83 |
84 |
85 | 86 | 87 | 88 | style 89 | 90 | ^$ 91 | 92 | 93 | 94 |
95 |
96 | 97 | 98 | 99 | .* 100 | 101 | ^$ 102 | 103 | 104 | BY_NAME 105 | 106 |
107 |
108 | 109 | 110 | 111 | .* 112 | 113 | http://schemas.android.com/apk/res/android 114 | 115 | 116 | ANDROID_ATTRIBUTE_ORDER 117 | 118 |
119 |
120 | 121 | 122 | 123 | .* 124 | 125 | .* 126 | 127 | 128 | BY_NAME 129 | 130 |
131 |
132 |
133 |
134 | 135 | 137 |
138 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Larger/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /Larger/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | 6 | android { 7 | compileSdkVersion 30 8 | buildToolsVersion "30.0.2" 9 | 10 | defaultConfig { 11 | minSdkVersion 19 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | consumerProguardFiles "consumer-rules.pro" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: "libs", include: ["*.jar"]) 30 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 31 | implementation 'androidx.core:core-ktx:1.3.1' 32 | implementation 'androidx.appcompat:appcompat:1.2.0' 33 | testImplementation 'junit:junit:4.13' 34 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 35 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 36 | 37 | 38 | 39 | implementation 'androidx.viewpager2:viewpager2:1.0.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 41 | 42 | 43 | 44 | 45 | 46 | } -------------------------------------------------------------------------------- /Larger/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/Larger/consumer-rules.pro -------------------------------------------------------------------------------- /Larger/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /Larger/src/androidTest/java/com/starot/larger/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.starot.larger.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /Larger/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/Larger.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger 2 | 3 | import com.starot.larger.builder.LargerBuilder 4 | import com.starot.larger.builder.config.ImageMultiConfig 5 | import com.starot.larger.builder.config.ImageSingleConfig 6 | import com.starot.larger.builder.config.VideoMultiConfig 7 | import com.starot.larger.builder.config.VideoSingleConfig 8 | import com.starot.larger.config.LargerConfig 9 | import com.starot.larger.enums.LargerEnum 10 | 11 | object Larger { 12 | 13 | var largerConfig: LargerConfig? = null 14 | 15 | fun create(): Builder { 16 | return Builder() 17 | } 18 | 19 | class Builder() { 20 | 21 | 22 | fun withImageMulti(): ImageMultiConfig { 23 | largerConfig = LargerConfig() 24 | return ImageMultiConfig(largerConfig) 25 | } 26 | 27 | fun withImageSingle(): ImageSingleConfig { 28 | largerConfig = LargerConfig() 29 | return ImageSingleConfig(largerConfig) 30 | } 31 | 32 | 33 | fun withVideoMulti(): VideoMultiConfig { 34 | largerConfig = LargerConfig() 35 | return VideoMultiConfig(largerConfig) 36 | } 37 | 38 | fun withVideoSingle(): VideoSingleConfig { 39 | largerConfig = LargerConfig() 40 | return VideoSingleConfig(largerConfig) 41 | } 42 | 43 | 44 | //列表类型 45 | fun withListType(): LargerBuilder { 46 | largerConfig = LargerConfig() 47 | return LargerBuilder(largerConfig?.apply { 48 | largerType = LargerEnum.LISTS 49 | }) 50 | } 51 | 52 | 53 | } 54 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/act/CustomLargerAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.act 2 | 3 | import androidx.fragment.app.Fragment 4 | import com.starot.larger.bean.LargerBean 5 | import com.starot.larger.enums.LargerDataEnum 6 | import com.starot.larger.fragment.ImageFg 7 | import com.starot.larger.fragment.VideoFg 8 | 9 | class CustomLargerAct : LargerAct() { 10 | override fun createFragment(position: Int, data: LargerBean): Fragment { 11 | if (data.getType() == LargerDataEnum.IMAGE) { 12 | return ImageFg().newInstance(data, position) 13 | } else if (data.getType() == LargerDataEnum.Video) { 14 | return VideoFg().newInstance(data, position) 15 | } 16 | return Fragment() 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/act/LargerAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.act 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.starot.larger.Larger 6 | import com.starot.larger.R 7 | import com.starot.larger.adapter.FgPageAdapter 8 | import com.starot.larger.enums.AnimStatus 9 | import com.starot.larger.enums.BackEnum 10 | import com.starot.larger.impl.OnLargerConfigListener 11 | import com.starot.larger.impl.OnLargerType 12 | import com.starot.larger.status.LargerStatus 13 | import com.starot.larger.utils.LogUtils 14 | import com.starot.larger.utils.PageChange 15 | import com.starot.larger.utils.StatusBarTools 16 | import kotlinx.android.synthetic.main.activity_larger_base.* 17 | 18 | abstract class LargerAct : AppCompatActivity(), 19 | PageChange.PageChangeListener, 20 | OnLargerConfigListener, 21 | FgPageAdapter.OnCreateFragmentListener { 22 | 23 | //当前的index 24 | private var mCurrentIndex = 0 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | //取消动画 28 | overridePendingTransition(0, 0) 29 | super.onCreate(savedInstanceState) 30 | setContentView(R.layout.activity_larger_base) 31 | //沉寂式 32 | StatusBarTools.setStatusBar(this) 33 | 34 | //当前的index 35 | mCurrentIndex = getIndex() 36 | 37 | //添加到了lifecycle 38 | val videoLoad = Larger.largerConfig?.videoLoad 39 | val imageLoad = Larger.largerConfig?.imageLoad 40 | if (imageLoad != null) { 41 | lifecycle.addObserver(imageLoad) 42 | } 43 | if (videoLoad != null) { 44 | lifecycle.addObserver(videoLoad) 45 | } 46 | lifecycle.addObserver(LargerStatus) 47 | 48 | //设置适配器 49 | larger_viewpager.adapter = 50 | FgPageAdapter(supportFragmentManager, lifecycle, getData(), this) 51 | //不需要平滑过渡了 52 | larger_viewpager.setCurrentItem(mCurrentIndex, false) 53 | //viewpager 滑动 index 更改 54 | PageChange().register(viewPager2 = larger_viewpager, listener = this) 55 | 56 | 57 | //竖着滑动 58 | larger_viewpager.orientation = getOrientation() 59 | 60 | 61 | //viewpager2 默认没有懒加载 这里给设置 1 提高滑动时候的体验 62 | larger_viewpager.offscreenPageLimit = 1 63 | 64 | //检查状态判断是否可以滑动viewpager 65 | LargerStatus.status.observe(this, { 66 | LogUtils.i("动画状态 $it") 67 | when (it) { 68 | AnimStatus.ENTER_START, AnimStatus.EXIT_START, AnimStatus.DRAG_START, AnimStatus.SCALE_START -> { 69 | larger_viewpager.isUserInputEnabled = false //true:滑动,false:禁止滑动 70 | LogUtils.i("viewPager--------禁止滑动") 71 | } 72 | AnimStatus.ENTER_END, AnimStatus.SCALE_END -> { 73 | larger_viewpager.isUserInputEnabled = true //true:滑动,false:禁止滑动 74 | LogUtils.i("viewPager-------滑动") 75 | } 76 | AnimStatus.EXIT_END -> { 77 | LogUtils.i("viewPager-------滑动") 78 | larger_viewpager.isUserInputEnabled = true //true:滑动,false:禁止滑动 79 | finish() 80 | overridePendingTransition(0, 0) 81 | } 82 | } 83 | }) 84 | 85 | } 86 | 87 | //数据源 88 | private fun getData(): List? { 89 | return Larger.largerConfig?.data as List? 90 | } 91 | 92 | 93 | //当前的图片index 94 | private fun getIndex(): Int { 95 | return Larger.largerConfig?.position ?: 0 96 | } 97 | 98 | 99 | //viewpager 滑动监听 100 | override fun onPageChange(pos: Int) { 101 | mCurrentIndex = pos 102 | LogUtils.i("viewPager change:$pos") 103 | LargerStatus.pos.value = pos 104 | } 105 | 106 | 107 | override fun onDestroy() { 108 | super.onDestroy() 109 | //清理资源 110 | Larger.largerConfig = null 111 | } 112 | 113 | override fun onBackPressed() { 114 | if (LargerStatus.back.value == BackEnum.BACK_NOME) { 115 | LargerStatus.back.value = BackEnum.BACK_PREPARE 116 | } 117 | } 118 | 119 | 120 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/adapter/FgPageAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.adapter 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentManager 5 | import androidx.lifecycle.Lifecycle 6 | import androidx.viewpager2.adapter.FragmentStateAdapter; 7 | 8 | class FgPageAdapter( 9 | fg: FragmentManager, 10 | lifecycle: Lifecycle, 11 | private val data: List?, 12 | private val listener: OnCreateFragmentListener 13 | ) : 14 | FragmentStateAdapter(fg, lifecycle) { 15 | override fun getItemCount(): Int { 16 | return data?.size ?: 0 17 | } 18 | 19 | override fun createFragment(position: Int): Fragment { 20 | if(data?.get(position) == null){ 21 | return Fragment() 22 | } 23 | return listener.createFragment(position, data[position]) 24 | } 25 | 26 | 27 | interface OnCreateFragmentListener { 28 | fun createFragment(position: Int, data: T): Fragment 29 | } 30 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/AnimBgHelper.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim 2 | 3 | import android.animation.ValueAnimator 4 | import android.graphics.Color 5 | import android.view.View 6 | import com.starot.larger.impl.OnLargerConfigListener 7 | import com.starot.larger.utils.ColorTool 8 | 9 | object AnimBgHelper : OnLargerConfigListener { 10 | //修改进入的时候背景 渐变 黑色 11 | fun enter( 12 | parent: View, 13 | originalScale: Float, 14 | duration: Long 15 | ) { 16 | val valueAnimator = ValueAnimator() 17 | valueAnimator.duration = duration 18 | valueAnimator.setFloatValues(originalScale, 1f) 19 | valueAnimator.addUpdateListener { animation -> 20 | parent.setBackgroundColor( 21 | ColorTool.getColorWithAlpha(getBackGroundColor(), (animation.animatedValue as Float)) 22 | ) 23 | } 24 | valueAnimator.start() 25 | } 26 | 27 | 28 | //修改退出的时候背景 渐变 黑色 29 | fun exit( 30 | parent: View, 31 | start: Float, 32 | duration: Long 33 | ) { 34 | startWithRange(start, 0f, parent, duration) 35 | } 36 | 37 | 38 | private fun startWithRange( 39 | start: Float, 40 | end: Float, 41 | parent: View, 42 | duration: Long 43 | ) { 44 | val valueAnimator = ValueAnimator() 45 | valueAnimator.duration = duration 46 | valueAnimator.setFloatValues(start, end) 47 | valueAnimator.addUpdateListener { animation -> 48 | parent.setBackgroundColor( 49 | ColorTool.getColorWithAlpha(getBackGroundColor(), (animation.animatedValue as Float)) 50 | ) 51 | } 52 | valueAnimator.start() 53 | } 54 | 55 | 56 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/AnimDragHelper.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim 2 | 3 | import android.os.Build 4 | import android.transition.* 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.view.animation.DecelerateInterpolator 8 | import android.widget.ImageView 9 | import androidx.recyclerview.widget.RecyclerView 10 | import com.starot.larger.anim.impl.OnAnimatorIntercept 11 | import com.starot.larger.anim.impl.OnAnimatorListener 12 | import com.starot.larger.enums.AnimType 13 | import com.starot.larger.impl.OnImageLoadReadyListener 14 | import com.starot.larger.utils.LogUtils 15 | 16 | object AnimDragHelper : OnAnimatorIntercept { 17 | 18 | var currentScale: Float = 0f 19 | 20 | private var thumbnailView: View? = null 21 | 22 | // override fun beforeTransition( 23 | // type: AnimType, 24 | // itemView: View, 25 | // fullView: View, 26 | // thumbnailView: View?, 27 | // listener: OnAnimatorListener 28 | // ) { 29 | // this.thumbnailView = thumbnailView 30 | // if (thumbnailView == null) { 31 | // LogUtils.i("beforeTransition thumbnailView is null") 32 | // return 33 | // } 34 | // listener.onTranslatorBefore(type, fullView, thumbnailView) 35 | // 36 | // } 37 | 38 | override fun beforeTransition( 39 | type: AnimType, 40 | itemView: View, 41 | fullView: View, 42 | thumbnailView: View?, 43 | listener: OnAnimatorListener, 44 | onImageLoadReadyListener: OnImageLoadReadyListener? 45 | ) { 46 | this.thumbnailView = thumbnailView 47 | if (thumbnailView == null) { 48 | LogUtils.i("beforeTransition thumbnailView is null") 49 | return 50 | } 51 | listener.onTranslatorBefore(type, fullView, thumbnailView) 52 | } 53 | 54 | 55 | override fun startTransition( 56 | type: AnimType, 57 | fullView: View, 58 | thumbnailView: View?, 59 | listener: OnAnimatorListener 60 | ) { 61 | if (thumbnailView == null) { 62 | LogUtils.i("startTransition thumbnailView is null") 63 | return 64 | } 65 | listener.onTranslatorStart(type, fullView, thumbnailView) 66 | fullView.translationX = (0f) 67 | fullView.translationY = (0f) 68 | fullView.scaleX = (1f) 69 | fullView.scaleY = (1f) 70 | } 71 | 72 | override fun transitionSet(durationTime: Long): Transition { 73 | return TransitionSet().apply { 74 | if (thumbnailView == null) { 75 | addTransition(AutoTransition()) 76 | } else { 77 | addTransition(ChangeBounds()) 78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 79 | addTransition(ChangeImageTransform()) 80 | addTransition(ChangeTransform()) 81 | } 82 | } 83 | duration = durationTime 84 | interpolator = DecelerateInterpolator() 85 | } 86 | } 87 | 88 | 89 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/AnimEnterHelper.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim 2 | 3 | import android.os.Build 4 | import android.transition.* 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.view.animation.DecelerateInterpolator 8 | import com.starot.larger.anim.impl.OnAnimatorIntercept 9 | import com.starot.larger.anim.impl.OnAnimatorListener 10 | import com.starot.larger.enums.AnimType 11 | import com.starot.larger.impl.OnImageLoadReadyListener 12 | import com.starot.larger.utils.LogUtils 13 | 14 | object AnimEnterHelper : OnAnimatorIntercept { 15 | 16 | 17 | override fun start( 18 | type: AnimType, 19 | duration: Long, 20 | fullView: View, 21 | thumbnailView: View?, 22 | listener: OnAnimatorListener 23 | ) { 24 | beforeTransition( 25 | type, 26 | fullView, 27 | fullView, 28 | thumbnailView, 29 | listener, 30 | object : OnImageLoadReadyListener { 31 | override fun onLoadFailed() { 32 | 33 | } 34 | 35 | override fun onReady() { 36 | fullView.post { 37 | TransitionManager.beginDelayedTransition( 38 | fullView.parent as ViewGroup, 39 | getTransition(type, duration, listener) 40 | ) 41 | startTransition(type, fullView, thumbnailView, listener) 42 | } 43 | } 44 | }) 45 | 46 | 47 | } 48 | 49 | // override fun beforeTransition( 50 | // type: AnimType, 51 | // itemView: View, 52 | // fullView: View, 53 | // thumbnailView: View?, 54 | // listener: OnAnimatorListener 55 | // ) { 56 | // if (thumbnailView == null) { 57 | // LogUtils.i("beforeTransition thumbnailView is null") 58 | // return 59 | // } 60 | // listener.onTranslatorBefore(type, fullView, thumbnailView) 61 | // fullView.layoutParams = fullView.layoutParams.apply { 62 | // width = thumbnailView.width 63 | // height = thumbnailView.height 64 | // AnimParentHelper.parentAnim(this, thumbnailView, fullView) 65 | // } 66 | // 67 | // } 68 | 69 | override fun beforeTransition( 70 | type: AnimType, 71 | itemView: View, 72 | fullView: View, 73 | thumbnailView: View?, 74 | listener: OnAnimatorListener, 75 | onImageLoadReadyListener: OnImageLoadReadyListener? 76 | ) { 77 | if (thumbnailView == null) { 78 | LogUtils.i("beforeTransition thumbnailView is null") 79 | return 80 | } 81 | listener.onTranslatorBefore(type, fullView, thumbnailView, onImageLoadReadyListener) 82 | fullView.layoutParams = fullView.layoutParams.apply { 83 | width = thumbnailView.width 84 | height = thumbnailView.height 85 | AnimParentHelper.parentAnim(this, thumbnailView, fullView) 86 | } 87 | } 88 | 89 | override fun startTransition( 90 | type: AnimType, 91 | fullView: View, 92 | thumbnailView: View?, 93 | listener: OnAnimatorListener 94 | ) { 95 | if (thumbnailView == null) { 96 | LogUtils.i("startTransition thumbnailView is null") 97 | return 98 | } 99 | listener.onTranslatorStart(type, fullView, thumbnailView) 100 | fullView.layoutParams = fullView.layoutParams.apply { 101 | width = ViewGroup.LayoutParams.MATCH_PARENT 102 | height = ViewGroup.LayoutParams.MATCH_PARENT 103 | if (this is ViewGroup.MarginLayoutParams) { 104 | marginStart = 0 105 | topMargin = 0 106 | } 107 | } 108 | } 109 | 110 | override fun transitionSet(durationTime: Long): Transition { 111 | return TransitionSet().apply { 112 | addTransition(ChangeBounds()) 113 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 114 | addTransition(ChangeImageTransform()) 115 | } 116 | duration = durationTime 117 | interpolator = DecelerateInterpolator() 118 | } 119 | } 120 | 121 | 122 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/AnimExitHelper.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim 2 | 3 | import android.os.Build 4 | import android.transition.* 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.view.animation.DecelerateInterpolator 8 | import android.widget.ImageView 9 | import androidx.recyclerview.widget.RecyclerView 10 | import com.starot.larger.anim.impl.OnAnimatorIntercept 11 | import com.starot.larger.anim.impl.OnAnimatorListener 12 | import com.starot.larger.enums.AnimType 13 | import com.starot.larger.impl.OnImageLoadReadyListener 14 | import com.starot.larger.utils.LogUtils 15 | 16 | object AnimExitHelper : OnAnimatorIntercept { 17 | 18 | private var thumbnailView: View? = null 19 | 20 | // override fun beforeTransition( 21 | // type: AnimType, 22 | // itemView: View, 23 | // fullView: View, 24 | // thumbnailView: View?, 25 | // listener: OnAnimatorListener 26 | // ) { 27 | // this.thumbnailView = thumbnailView 28 | // if (thumbnailView == null) { 29 | // LogUtils.i("beforeTransition thumbnailView is null") 30 | // return 31 | // } 32 | // listener.onTranslatorBefore(type, fullView, thumbnailView) 33 | // 34 | // } 35 | 36 | override fun beforeTransition( 37 | type: AnimType, 38 | itemView: View, 39 | fullView: View, 40 | thumbnailView: View?, 41 | listener: OnAnimatorListener, 42 | onImageLoadReadyListener: OnImageLoadReadyListener? 43 | ) { 44 | this.thumbnailView = thumbnailView 45 | if (thumbnailView == null) { 46 | LogUtils.i("beforeTransition thumbnailView is null") 47 | return 48 | } 49 | listener.onTranslatorBefore(type, fullView, thumbnailView) 50 | } 51 | 52 | 53 | override fun startTransition( 54 | type: AnimType, 55 | fullView: View, 56 | thumbnailView: View?, 57 | listener: OnAnimatorListener 58 | ) { 59 | if (thumbnailView == null) { 60 | LogUtils.i("startTransition thumbnailView is null") 61 | fullView.visibility = View.GONE 62 | return 63 | } 64 | listener.onTranslatorStart(type, fullView, thumbnailView) 65 | fullView.translationX = 0f 66 | fullView.translationY = 0f 67 | fullView.scaleX = 1f 68 | fullView.scaleY = 1f 69 | fullView.layoutParams = fullView.layoutParams.apply { 70 | width = thumbnailView.width 71 | height = thumbnailView.height 72 | AnimParentHelper.parentAnim(this, thumbnailView, fullView) 73 | } 74 | } 75 | 76 | override fun transitionSet(durationTime: Long): Transition { 77 | return TransitionSet().apply { 78 | if (thumbnailView == null) { 79 | addTransition(AutoTransition()) 80 | } else { 81 | addTransition(ChangeBounds()) 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 83 | addTransition(ChangeImageTransform()) 84 | addTransition(ChangeTransform()) 85 | } 86 | } 87 | duration = durationTime 88 | interpolator = DecelerateInterpolator() 89 | } 90 | } 91 | 92 | 93 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/AnimParentHelper.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim 2 | 3 | import android.view.View 4 | import android.view.ViewGroup 5 | import android.widget.ImageView 6 | import androidx.constraintlayout.widget.ConstraintLayout 7 | import androidx.constraintlayout.widget.ConstraintSet 8 | 9 | object AnimParentHelper { 10 | 11 | fun parentAnim( 12 | parent: ViewGroup.LayoutParams, 13 | thumbnailView: View, 14 | fullView: View 15 | ) { 16 | val location = AnimEnterHelper.getLocationOnScreen(thumbnailView) 17 | when (fullView.parent) { 18 | is ConstraintLayout -> { 19 | val constraintSet = ConstraintSet().apply { 20 | clone(fullView.parent as ConstraintLayout) 21 | clear(fullView.id, ConstraintSet.START) 22 | clear(fullView.id, ConstraintSet.TOP) 23 | clear(fullView.id, ConstraintSet.BOTTOM) 24 | clear(fullView.id, ConstraintSet.RIGHT) 25 | //重新建立约束 26 | connect( 27 | fullView.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, 28 | ConstraintSet.TOP, location[1] 29 | ) 30 | connect( 31 | fullView.id, ConstraintSet.START, ConstraintSet.PARENT_ID, 32 | ConstraintSet.START, location[0] 33 | ) 34 | } 35 | constraintSet.applyTo(fullView.parent as ConstraintLayout) 36 | } 37 | else -> { 38 | if (parent is ViewGroup.MarginLayoutParams) { 39 | parent.marginStart = location[0] 40 | parent.topMargin = location[1] 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/impl/AnimListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim.impl 2 | 3 | import android.view.View 4 | import com.starot.larger.anim.AnimBgHelper 5 | import com.starot.larger.anim.AnimDragHelper 6 | import com.starot.larger.anim.AnimEnterHelper 7 | import com.starot.larger.anim.AnimExitHelper 8 | import com.starot.larger.enums.AnimType 9 | import com.starot.larger.utils.LogUtils 10 | 11 | //动画的逻辑 12 | interface AnimListener : OnAnimatorListener, OnDragAnimListener { 13 | 14 | 15 | fun enterAnimStart( 16 | parentView: View, 17 | duration: Long, 18 | fullView: View?, 19 | thumbnailView: View? 20 | ) { 21 | LogUtils.i("入场动画 start") 22 | if (fullView == null) { 23 | return 24 | } 25 | AnimEnterHelper.start( 26 | AnimType.ENTER, 27 | duration, 28 | fullView, 29 | thumbnailView, 30 | this, 31 | ) 32 | //背景颜色变化 33 | AnimBgHelper.enter(parentView, 0f, duration) 34 | } 35 | 36 | fun dragResumeAnimStart( 37 | start: Float, 38 | parentView: View, 39 | duration: Long, 40 | fullView: View?, 41 | thumbnailView: View? 42 | ) { 43 | LogUtils.i("drag动画 start") 44 | if (fullView == null) { 45 | return 46 | } 47 | AnimDragHelper.start( 48 | AnimType.DRAG_RESUME, 49 | duration, 50 | fullView, 51 | thumbnailView, 52 | this, 53 | ) 54 | //背景颜色变化 55 | AnimBgHelper.enter(parentView, start, duration) 56 | } 57 | 58 | 59 | fun exitAnimStart( 60 | parentView: View, 61 | duration: Long, 62 | fullView: View?, 63 | thumbnailView: View?, 64 | ) { 65 | exitAnimStart(AnimType.EXIT, 1.0f, parentView, duration, fullView, thumbnailView) 66 | } 67 | 68 | fun exitAnimStart( 69 | type: AnimType, 70 | start: Float, 71 | parentView: View, 72 | duration: Long, 73 | fullView: View?, 74 | thumbnailView: View?, 75 | ) { 76 | LogUtils.i("退场动画 start") 77 | if (fullView == null) { 78 | LogUtils.i("退场动画 start fullView == null") 79 | return 80 | } 81 | AnimExitHelper.start( 82 | type, 83 | duration, 84 | fullView, 85 | thumbnailView, 86 | this 87 | ) 88 | //背景颜色变化 89 | AnimBgHelper.exit(parentView, start, duration) 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/impl/OnAnimatorIntercept.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim.impl 2 | 3 | 4 | import android.transition.Transition 5 | import android.transition.TransitionManager 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import com.starot.larger.enums.AnimType 9 | import com.starot.larger.impl.OnImageLoadReadyListener 10 | 11 | 12 | interface OnAnimatorListener { 13 | 14 | 15 | //变化开始之前 16 | fun onTranslatorBefore( 17 | type: AnimType, 18 | fullView: View, 19 | thumbnailView: View, 20 | onImageLoadReadyListener: OnImageLoadReadyListener? = null 21 | ) 22 | 23 | // fun onTranslatorBefore( 24 | // type: AnimType, 25 | // fullView: View, 26 | // thumbnailView: View 27 | // ) 28 | 29 | //开始变化 30 | fun onTranslatorStart( 31 | type: AnimType, 32 | fullView: View, 33 | thumbnailView: View 34 | ) 35 | 36 | //动画开始 37 | fun onAnimatorStart(type: AnimType) 38 | 39 | //动画结束 40 | fun onAnimatorEnd(type: AnimType) 41 | 42 | //动画取消 43 | fun onAnimatorCancel(type: AnimType) 44 | 45 | 46 | } 47 | 48 | 49 | interface OnAnimatorIntercept { 50 | 51 | 52 | fun start( 53 | type: AnimType, 54 | duration: Long, 55 | fullView: View, 56 | thumbnailView: View?, 57 | listener: OnAnimatorListener 58 | ) { 59 | 60 | beforeTransition(type, fullView, fullView, thumbnailView, listener) 61 | 62 | fullView.post { 63 | TransitionManager.beginDelayedTransition( 64 | fullView.parent as ViewGroup, 65 | getTransition(type, duration, listener) 66 | ) 67 | startTransition(type, fullView, thumbnailView, listener) 68 | } 69 | } 70 | 71 | 72 | fun getTransition( 73 | type: AnimType, 74 | duration: Long, 75 | listener: OnAnimatorListener 76 | ): Transition { 77 | return transitionSet(duration).apply { 78 | addListener(object : Transition.TransitionListener { 79 | override fun onTransitionEnd(transition: Transition?) { 80 | listener.onAnimatorEnd(type) 81 | } 82 | 83 | override fun onTransitionResume(transition: Transition?) { 84 | } 85 | 86 | override fun onTransitionPause(transition: Transition?) { 87 | } 88 | 89 | override fun onTransitionCancel(transition: Transition?) { 90 | listener.onAnimatorCancel(type) 91 | } 92 | 93 | override fun onTransitionStart(transition: Transition?) { 94 | listener.onAnimatorStart(type) 95 | } 96 | }) 97 | } 98 | } 99 | 100 | // fun beforeTransition( 101 | // type: AnimType, 102 | // itemView: View, 103 | // fullView: View, 104 | // thumbnailView: View?, 105 | // listener: OnAnimatorListener 106 | // ) 107 | 108 | fun beforeTransition( 109 | type: AnimType, 110 | itemView: View, 111 | fullView: View, 112 | thumbnailView: View?, 113 | listener: OnAnimatorListener, 114 | onImageLoadReadyListener: OnImageLoadReadyListener? = null 115 | ) 116 | 117 | 118 | fun startTransition( 119 | type: AnimType, 120 | fullView: View, 121 | thumbnailView: View?, 122 | listener: OnAnimatorListener 123 | ) 124 | 125 | fun transitionSet(durationTime: Long): Transition 126 | 127 | 128 | fun getLocationOnScreen(thumbnailView: View): IntArray { 129 | val location = IntArray(2) 130 | thumbnailView.getLocationOnScreen(location) 131 | return location 132 | } 133 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/anim/impl/OnDragAnimListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.anim.impl 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.view.View 6 | import com.starot.larger.Larger 7 | import com.starot.larger.anim.AnimBgHelper 8 | import com.starot.larger.anim.AnimDragHelper 9 | import com.starot.larger.anim.AnimExitHelper 10 | import com.starot.larger.enums.AnimType 11 | import com.starot.larger.impl.OnLargerConfigListener 12 | import com.starot.larger.utils.ColorTool 13 | import com.starot.larger.utils.LogUtils 14 | import kotlin.math.abs 15 | import kotlin.math.max 16 | import kotlin.math.min 17 | 18 | interface OnDragAnimListener : OnLargerConfigListener { 19 | 20 | //开始移动 21 | fun startDrag(parent: View, view: View?, x: Float, y: Float) { 22 | if (view == null) { 23 | return 24 | } 25 | 26 | //图片的缩放 和位置变化 27 | val fixedOffsetY = y - 0 28 | val fraction = abs(max(-1f, min(1f, fixedOffsetY / view.height))) 29 | val fakeScale = 1 - min(0.4f, fraction) 30 | view.scaleX = fakeScale 31 | view.scaleY = fakeScale 32 | view.translationY = fixedOffsetY 33 | view.translationX = x 34 | 35 | 36 | //如果将upCanMove 设置成true 就给他改变颜色 37 | val upCanMove = Larger.largerConfig?.upCanMove 38 | if (upCanMove == null || upCanMove == false) { 39 | //已经向上了 就黑色背景 不需要改动了 40 | if (y > 0) { 41 | //背景的颜色 变化 42 | val scale: Float = abs(y) / getWindowHeight(view.context) 43 | AnimDragHelper.currentScale = 1 - scale 44 | parent.setBackgroundColor( 45 | ColorTool.getColorWithAlpha(getBackGroundColor(), 1 - scale) 46 | ) 47 | } 48 | } else { 49 | val scale: Float = abs(y) / getWindowHeight(view.context) 50 | AnimDragHelper.currentScale = 1 - scale 51 | parent.setBackgroundColor( 52 | ColorTool.getColorWithAlpha(getBackGroundColor(), 1 - scale) 53 | ) 54 | } 55 | 56 | } 57 | 58 | fun endDrag(view: View?) { 59 | if (view == null) { 60 | LogUtils.i("endDrag view is null") 61 | return 62 | } 63 | if (abs(view.translationY) < view.height * 0.4f) { 64 | onDragResume(AnimDragHelper.currentScale, view) 65 | } else { 66 | onDragExit(AnimDragHelper.currentScale, view) 67 | 68 | } 69 | } 70 | 71 | fun onDragExit(scale: Float, fullView: View) 72 | 73 | fun onDragResume(scale: Float, fullView: View) 74 | 75 | 76 | //手机屏幕 高度 77 | fun getWindowHeight(context: Context): Int { 78 | return context.applicationContext.resources.displayMetrics.heightPixels 79 | } 80 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/bean/LargerBean.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.bean 2 | 3 | import com.starot.larger.enums.LargerDataEnum 4 | import com.starot.larger.impl.OnLargerType 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | abstract class LargerBean : OnLargerType { 8 | var thumbnailsUrl: String? = null 9 | var fullUrl: String? = null 10 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/LargerBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.widget.ImageView 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.starot.larger.Larger 8 | import com.starot.larger.act.CustomLargerAct 9 | import com.starot.larger.config.LargerConfig 10 | import com.starot.larger.enums.Orientation 11 | import com.starot.larger.impl.* 12 | import com.starot.larger.view.progress.CircleProgressView 13 | import com.starot.larger.view.progress.ImageProgressLoader 14 | 15 | class LargerBuilder(private val listConfig: LargerConfig?) { 16 | 17 | //pos 18 | fun setIndex(pos: Int): LargerBuilder { 19 | listConfig?.position = pos 20 | return this 21 | } 22 | 23 | //设置持续时间 24 | fun setDuration(duration: Long): LargerBuilder { 25 | listConfig?.duration = duration 26 | return this 27 | } 28 | 29 | //是否自动加载 30 | fun setAutomatic(automatic: Boolean): LargerBuilder { 31 | listConfig?.automatic = automatic 32 | return this 33 | } 34 | 35 | //withSingle 模式下 设置imageView 36 | fun setImagesWithSingle(images: List): LargerBuilder { 37 | listConfig?.images = images 38 | return this 39 | } 40 | 41 | //设置数据源 42 | fun setData(data: List): LargerBuilder { 43 | listConfig?.data = data 44 | return this 45 | } 46 | 47 | 48 | //开始 49 | fun start(context: Context) { 50 | val intent = Intent(context, CustomLargerAct::class.java) 51 | context.startActivity(intent) 52 | } 53 | 54 | //跳转到自定义的act 55 | fun start(context: Context, cls: Class<*>) { 56 | val intent = Intent(context, cls) 57 | context.startActivity(intent) 58 | } 59 | 60 | 61 | //列表的情况需要将 recyclerview 也传入 62 | fun setRecyclerView(recyclerView: RecyclerView): LargerBuilder { 63 | listConfig?.recyclerView = recyclerView 64 | return this 65 | } 66 | 67 | 68 | //是否直接向上就能够拖动,微信直接向上不可以拖动,这里默认false 69 | fun setUpCanMove(canMove: Boolean): LargerBuilder { 70 | listConfig?.upCanMove = canMove 71 | return this 72 | } 73 | 74 | //是否自动加载下一页大图,默认不自动加载下一页 75 | fun setLoadNextFragment(auto:Boolean): LargerBuilder { 76 | listConfig?.loadNextFragment = auto 77 | return this 78 | } 79 | 80 | //是否打印日志 81 | fun setDebug(debug: Boolean): LargerBuilder { 82 | listConfig?.debug = debug 83 | return this 84 | } 85 | 86 | //设置背景颜色 87 | fun setBackgroundColor(color: Int): LargerBuilder { 88 | listConfig?.backgroundColor = color 89 | return this 90 | } 91 | 92 | //设置图片加载器 93 | fun setImageLoad(imageLoad: OnImageLoadListener): LargerBuilder { 94 | listConfig?.imageLoad = imageLoad 95 | return this 96 | } 97 | 98 | //设置加载的进度 99 | fun setProgressType(progressType: ImageProgressLoader.ProgressType): LargerBuilder { 100 | listConfig?.progressType = progressType 101 | return this 102 | } 103 | 104 | //设置是否显示加载框 105 | fun setProgressLoaderUse(use: Boolean): LargerBuilder { 106 | listConfig?.progressLoaderUse = use 107 | return this 108 | } 109 | 110 | //设置加载方向 111 | fun setOrientation(orientation: Orientation): LargerBuilder { 112 | listConfig?.orientation = orientation 113 | return this 114 | } 115 | 116 | //设置视屏加载器 117 | fun setVideoLoad(videoLoad: OnVideoLoadListener): LargerBuilder { 118 | listConfig?.videoLoad = videoLoad 119 | return this 120 | } 121 | 122 | //自定义布局 123 | fun setCustomListener( 124 | layoutId: Int, 125 | fullViewId: Int, 126 | listener: OnCustomImageLoadListener? = null 127 | ): LargerBuilder { 128 | listConfig?.layoutId = layoutId 129 | listConfig?.fullViewId = fullViewId 130 | listConfig?.customImageLoadListener = listener 131 | return this 132 | } 133 | 134 | fun setCustomListener( 135 | layoutId: Int, 136 | fullViewId: Int, 137 | progressId: Int, 138 | listener: OnCustomImageLoadListener? = null 139 | ): LargerBuilder { 140 | listConfig?.layoutId = layoutId 141 | listConfig?.progressId = progressId 142 | listConfig?.fullViewId = fullViewId 143 | listConfig?.customImageLoadListener = listener 144 | return this 145 | } 146 | 147 | //最大的比例 148 | fun setMaxScale(maxScale: Float): LargerBuilder { 149 | listConfig?.maxScale = maxScale 150 | return this 151 | } 152 | 153 | //设置双击中间的比例 可以和 max 相同 但是不能大于max 154 | fun setMediumScale(mediumScale: Float): LargerBuilder { 155 | listConfig?.mediumScale = mediumScale 156 | return this 157 | } 158 | 159 | 160 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/config/ImageMultiConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.config 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.starot.larger.act.CustomLargerAct 7 | import com.starot.larger.act.LargerAct 8 | import com.starot.larger.builder.impl.ImageConfig 9 | import com.starot.larger.builder.impl.MultiConfig 10 | import com.starot.larger.config.LargerConfig 11 | import com.starot.larger.enums.LargerEnum 12 | import com.starot.larger.enums.Orientation 13 | import com.starot.larger.impl.OnCustomImageLoadListener 14 | import com.starot.larger.impl.OnImageLoadListener 15 | import com.starot.larger.impl.OnLargerType 16 | import com.starot.larger.view.progress.ImageProgressLoader 17 | 18 | class ImageMultiConfig(private val largerConfig: LargerConfig?) : 19 | MultiConfig, ImageConfig { 20 | 21 | init { 22 | largerConfig?.largerType = LargerEnum.LISTS 23 | } 24 | 25 | override fun setRecyclerView(recyclerView: RecyclerView): ImageMultiConfig { 26 | largerConfig?.recyclerView = recyclerView 27 | return this 28 | } 29 | 30 | override fun setData(data: List): ImageMultiConfig { 31 | largerConfig?.data = data 32 | return this 33 | } 34 | 35 | override fun setAutomatic(automatic: Boolean): ImageMultiConfig { 36 | largerConfig?.automatic = automatic 37 | return this 38 | } 39 | 40 | override fun setMaxScale(maxScale: Float): ImageMultiConfig { 41 | largerConfig?.maxScale = maxScale 42 | return this 43 | } 44 | 45 | override fun setMediumScale(mediumScale: Float): ImageMultiConfig { 46 | largerConfig?.mediumScale = mediumScale 47 | return this 48 | } 49 | 50 | override fun setImageLoad(imageLoad: OnImageLoadListener): ImageMultiConfig { 51 | largerConfig?.imageLoad = imageLoad 52 | return this 53 | } 54 | 55 | override fun setProgressType(progressType: ImageProgressLoader.ProgressType): ImageMultiConfig { 56 | largerConfig?.progressType = progressType 57 | return this 58 | } 59 | 60 | override fun setProgressLoaderUse(use: Boolean): ImageMultiConfig { 61 | largerConfig?.progressLoaderUse = use 62 | return this 63 | } 64 | 65 | override fun setCustomListener( 66 | layoutId: Int, 67 | fullViewId: Int, 68 | listener: OnCustomImageLoadListener? 69 | ): ImageMultiConfig { 70 | largerConfig?.layoutId = layoutId 71 | largerConfig?.fullViewId = fullViewId 72 | largerConfig?.customImageLoadListener = listener 73 | return this 74 | } 75 | 76 | override fun setCustomListener( 77 | layoutId: Int, 78 | fullViewId: Int, 79 | progressId: Int, 80 | listener: OnCustomImageLoadListener? 81 | ): ImageMultiConfig { 82 | largerConfig?.progressId = progressId 83 | largerConfig?.layoutId = layoutId 84 | largerConfig?.fullViewId = fullViewId 85 | largerConfig?.customImageLoadListener = listener 86 | return this 87 | } 88 | 89 | override fun setUpCanMove(canMove: Boolean): ImageMultiConfig { 90 | largerConfig?.upCanMove = canMove 91 | return this 92 | } 93 | 94 | override fun setLoadNextFragment(auto: Boolean): ImageMultiConfig { 95 | largerConfig?.loadNextFragment = auto 96 | return this 97 | } 98 | 99 | override fun setDebug(debug: Boolean): ImageMultiConfig { 100 | largerConfig?.debug = debug 101 | return this 102 | } 103 | 104 | override fun setBackgroundColor(color: Int): ImageMultiConfig { 105 | largerConfig?.backgroundColor = color 106 | return this 107 | } 108 | 109 | override fun setOrientation(orientation: Orientation): ImageMultiConfig { 110 | largerConfig?.orientation = orientation 111 | return this 112 | } 113 | 114 | override fun setIndex(pos: Int): ImageMultiConfig { 115 | largerConfig?.position = pos 116 | return this 117 | } 118 | 119 | override fun setDuration(duration: Long): ImageMultiConfig { 120 | largerConfig?.duration = duration 121 | return this 122 | } 123 | 124 | 125 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/config/ImageSingleConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.config 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.widget.ImageView 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.starot.larger.act.CustomLargerAct 8 | import com.starot.larger.act.LargerAct 9 | import com.starot.larger.builder.impl.ImageConfig 10 | import com.starot.larger.builder.impl.MultiConfig 11 | import com.starot.larger.builder.impl.SingleConfig 12 | import com.starot.larger.config.LargerConfig 13 | import com.starot.larger.enums.LargerEnum 14 | import com.starot.larger.enums.Orientation 15 | import com.starot.larger.impl.OnCustomImageLoadListener 16 | import com.starot.larger.impl.OnImageLoadListener 17 | import com.starot.larger.impl.OnLargerType 18 | import com.starot.larger.view.progress.ImageProgressLoader 19 | 20 | class ImageSingleConfig(private val largerConfig: LargerConfig?) : 21 | SingleConfig, ImageConfig { 22 | 23 | init { 24 | largerConfig?.largerType = LargerEnum.SINGLES 25 | } 26 | 27 | override fun setAutomatic(automatic: Boolean): ImageSingleConfig { 28 | largerConfig?.automatic = automatic 29 | return this 30 | } 31 | 32 | override fun setMaxScale(maxScale: Float): ImageSingleConfig { 33 | largerConfig?.maxScale = maxScale 34 | return this 35 | } 36 | 37 | override fun setMediumScale(mediumScale: Float): ImageSingleConfig { 38 | largerConfig?.mediumScale = mediumScale 39 | return this 40 | } 41 | 42 | override fun setImageLoad(imageLoad: OnImageLoadListener): ImageSingleConfig { 43 | largerConfig?.imageLoad = imageLoad 44 | return this 45 | } 46 | 47 | override fun setProgressType(progressType: ImageProgressLoader.ProgressType): ImageSingleConfig { 48 | largerConfig?.progressType = progressType 49 | return this 50 | } 51 | 52 | override fun setProgressLoaderUse(use: Boolean): ImageSingleConfig { 53 | largerConfig?.progressLoaderUse = use 54 | return this 55 | } 56 | 57 | override fun setCustomListener( 58 | layoutId: Int, 59 | fullViewId: Int, 60 | listener: OnCustomImageLoadListener? 61 | ): ImageSingleConfig { 62 | largerConfig?.layoutId = layoutId 63 | largerConfig?.fullViewId = fullViewId 64 | largerConfig?.customImageLoadListener = listener 65 | return this 66 | } 67 | 68 | override fun setCustomListener( 69 | layoutId: Int, 70 | fullViewId: Int, 71 | progressId: Int, 72 | listener: OnCustomImageLoadListener? 73 | ): ImageSingleConfig { 74 | largerConfig?.progressId = progressId 75 | largerConfig?.layoutId = layoutId 76 | largerConfig?.fullViewId = fullViewId 77 | largerConfig?.customImageLoadListener = listener 78 | return this 79 | } 80 | 81 | override fun setUpCanMove(canMove: Boolean): ImageSingleConfig { 82 | largerConfig?.upCanMove = canMove 83 | return this 84 | } 85 | 86 | override fun setLoadNextFragment(auto: Boolean): ImageSingleConfig { 87 | largerConfig?.loadNextFragment = auto 88 | return this 89 | } 90 | 91 | override fun setDebug(debug: Boolean): ImageSingleConfig { 92 | largerConfig?.debug = debug 93 | return this 94 | } 95 | 96 | override fun setBackgroundColor(color: Int): ImageSingleConfig { 97 | largerConfig?.backgroundColor = color 98 | return this 99 | } 100 | 101 | override fun setOrientation(orientation: Orientation): ImageSingleConfig { 102 | largerConfig?.orientation = orientation 103 | return this 104 | } 105 | 106 | 107 | override fun setDuration(duration: Long): ImageSingleConfig { 108 | largerConfig?.duration = duration 109 | return this 110 | } 111 | 112 | override fun setImage(image: ImageView): ImageSingleConfig { 113 | largerConfig?.images = arrayListOf(image) 114 | return this 115 | } 116 | 117 | override fun setData(data: OnLargerType): ImageSingleConfig { 118 | largerConfig?.data = arrayListOf(data) 119 | return this 120 | } 121 | 122 | 123 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/config/VideoMultiConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.config 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.starot.larger.act.CustomLargerAct 7 | import com.starot.larger.act.LargerAct 8 | import com.starot.larger.builder.impl.ImageConfig 9 | import com.starot.larger.builder.impl.MultiConfig 10 | import com.starot.larger.builder.impl.VideoConfig 11 | import com.starot.larger.config.LargerConfig 12 | import com.starot.larger.enums.LargerEnum 13 | import com.starot.larger.enums.Orientation 14 | import com.starot.larger.impl.OnCustomImageLoadListener 15 | import com.starot.larger.impl.OnImageLoadListener 16 | import com.starot.larger.impl.OnLargerType 17 | import com.starot.larger.impl.OnVideoLoadListener 18 | import com.starot.larger.view.progress.ImageProgressLoader 19 | 20 | class VideoMultiConfig(private val largerConfig: LargerConfig?) : 21 | MultiConfig, VideoConfig { 22 | 23 | init { 24 | largerConfig?.largerType = LargerEnum.LISTS 25 | } 26 | 27 | override fun setRecyclerView(recyclerView: RecyclerView): VideoMultiConfig { 28 | largerConfig?.recyclerView = recyclerView 29 | return this 30 | } 31 | 32 | override fun setData(data: List): VideoMultiConfig { 33 | largerConfig?.data = data 34 | return this 35 | } 36 | 37 | 38 | override fun setImageLoad(imageLoad: OnImageLoadListener): VideoMultiConfig { 39 | largerConfig?.imageLoad = imageLoad 40 | return this 41 | } 42 | 43 | 44 | override fun setUpCanMove(canMove: Boolean): VideoMultiConfig { 45 | largerConfig?.upCanMove = canMove 46 | return this 47 | } 48 | 49 | override fun setLoadNextFragment(auto: Boolean): VideoMultiConfig { 50 | largerConfig?.loadNextFragment = auto 51 | return this 52 | } 53 | 54 | override fun setDebug(debug: Boolean): VideoMultiConfig { 55 | largerConfig?.debug = debug 56 | return this 57 | } 58 | 59 | override fun setBackgroundColor(color: Int): VideoMultiConfig { 60 | largerConfig?.backgroundColor = color 61 | return this 62 | } 63 | 64 | override fun setOrientation(orientation: Orientation): VideoMultiConfig { 65 | largerConfig?.orientation = orientation 66 | return this 67 | } 68 | 69 | override fun setIndex(pos: Int): VideoMultiConfig { 70 | largerConfig?.position = pos 71 | return this 72 | } 73 | 74 | override fun setDuration(duration: Long): VideoMultiConfig { 75 | largerConfig?.duration = duration 76 | return this 77 | } 78 | 79 | override fun setVideoLoad(videoLoader: OnVideoLoadListener): VideoMultiConfig { 80 | largerConfig?.videoLoad = videoLoader 81 | return this 82 | } 83 | 84 | 85 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/config/VideoSingleConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.config 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.widget.ImageView 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.starot.larger.act.CustomLargerAct 8 | import com.starot.larger.act.LargerAct 9 | import com.starot.larger.builder.impl.ImageConfig 10 | import com.starot.larger.builder.impl.MultiConfig 11 | import com.starot.larger.builder.impl.SingleConfig 12 | import com.starot.larger.builder.impl.VideoConfig 13 | import com.starot.larger.config.LargerConfig 14 | import com.starot.larger.enums.LargerEnum 15 | import com.starot.larger.enums.Orientation 16 | import com.starot.larger.impl.OnCustomImageLoadListener 17 | import com.starot.larger.impl.OnImageLoadListener 18 | import com.starot.larger.impl.OnLargerType 19 | import com.starot.larger.impl.OnVideoLoadListener 20 | import com.starot.larger.view.progress.ImageProgressLoader 21 | 22 | class VideoSingleConfig(private val largerConfig: LargerConfig?) : 23 | SingleConfig, VideoConfig { 24 | 25 | init { 26 | largerConfig?.largerType = LargerEnum.SINGLES 27 | } 28 | 29 | 30 | override fun setImageLoad(imageLoad: OnImageLoadListener): VideoSingleConfig { 31 | largerConfig?.imageLoad = imageLoad 32 | return this 33 | } 34 | 35 | 36 | override fun setUpCanMove(canMove: Boolean): VideoSingleConfig { 37 | largerConfig?.upCanMove = canMove 38 | return this 39 | } 40 | 41 | override fun setLoadNextFragment(auto: Boolean): VideoSingleConfig { 42 | largerConfig?.loadNextFragment = auto 43 | return this 44 | } 45 | 46 | override fun setDebug(debug: Boolean): VideoSingleConfig { 47 | largerConfig?.debug = debug 48 | return this 49 | } 50 | 51 | override fun setBackgroundColor(color: Int): VideoSingleConfig { 52 | largerConfig?.backgroundColor = color 53 | return this 54 | } 55 | 56 | override fun setOrientation(orientation: Orientation): VideoSingleConfig { 57 | largerConfig?.orientation = orientation 58 | return this 59 | } 60 | 61 | 62 | override fun setDuration(duration: Long): VideoSingleConfig { 63 | largerConfig?.duration = duration 64 | return this 65 | } 66 | 67 | override fun setImage(image: ImageView): VideoSingleConfig { 68 | largerConfig?.images = arrayListOf(image) 69 | return this 70 | } 71 | 72 | override fun setData(data: OnLargerType): VideoSingleConfig { 73 | largerConfig?.data = arrayListOf(data) 74 | return this 75 | } 76 | 77 | override fun setVideoLoad(videoLoader: OnVideoLoadListener): VideoSingleConfig { 78 | largerConfig?.videoLoad = videoLoader 79 | return this 80 | } 81 | 82 | 83 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/impl/CommandConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.impl 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.starot.larger.act.CustomLargerAct 6 | import com.starot.larger.config.LargerConfig 7 | import com.starot.larger.enums.Orientation 8 | import com.starot.larger.impl.OnLargerType 9 | 10 | 11 | interface CommandConfig { 12 | 13 | //是否直接向上就能够拖动,微信直接向上不可以拖动,这里默认false 14 | fun setUpCanMove(canMove: Boolean): T 15 | 16 | //是否自动加载下一页大图,默认不自动加载下一页 17 | fun setLoadNextFragment(auto: Boolean): T 18 | 19 | //是否打印日志 20 | fun setDebug(debug: Boolean): T 21 | 22 | 23 | //设置背景颜色 24 | fun setBackgroundColor(color: Int): T 25 | 26 | //设置加载方向 27 | fun setOrientation(orientation: Orientation): T 28 | 29 | //设置持续时间 30 | fun setDuration(duration: Long): T 31 | 32 | //开始 33 | fun start(context: Context) { 34 | val intent = Intent(context, CustomLargerAct::class.java) 35 | context.startActivity(intent) 36 | } 37 | 38 | //跳转到自定义的act 39 | fun start(context: Context, cls: Class<*>) { 40 | val intent = Intent(context, cls) 41 | context.startActivity(intent) 42 | } 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/impl/ImageConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.impl 2 | 3 | import com.starot.larger.config.LargerConfig 4 | import com.starot.larger.impl.OnCustomImageLoadListener 5 | import com.starot.larger.impl.OnImageLoadListener 6 | import com.starot.larger.view.progress.ImageProgressLoader 7 | 8 | 9 | interface ImageConfig : CommandConfig { 10 | //是否自动加载 11 | fun setAutomatic(automatic: Boolean): T 12 | 13 | //最大的比例 14 | fun setMaxScale(maxScale: Float): T 15 | 16 | //设置双击中间的比例 可以和 max 相同 但是不能大于max 17 | fun setMediumScale(mediumScale: Float): T 18 | 19 | //设置图片加载器 20 | fun setImageLoad(imageLoad: OnImageLoadListener): T 21 | 22 | //设置加载的进度 23 | fun setProgressType(progressType: ImageProgressLoader.ProgressType): T 24 | 25 | //设置是否显示加载框 26 | fun setProgressLoaderUse(use: Boolean): T 27 | 28 | //自定义布局 29 | fun setCustomListener( 30 | layoutId: Int, 31 | fullViewId: Int, 32 | listener: OnCustomImageLoadListener? = null 33 | ): T 34 | 35 | //自定义布局 36 | fun setCustomListener( 37 | layoutId: Int, 38 | fullViewId: Int, 39 | progressId: Int, 40 | listener: OnCustomImageLoadListener? = null 41 | ): T 42 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/impl/MultiConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.impl 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.starot.larger.config.LargerConfig 5 | import com.starot.larger.impl.OnLargerType 6 | 7 | interface MultiConfig { 8 | //列表的情况需要将 recyclerview 也传入 9 | fun setRecyclerView(recyclerView: RecyclerView): T 10 | 11 | //pos 12 | fun setIndex(pos: Int): T 13 | 14 | //设置数据源 15 | fun setData(data: List): T 16 | 17 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/impl/SingleConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.impl 2 | 3 | import android.widget.ImageView 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.starot.larger.builder.config.ImageMultiConfig 6 | import com.starot.larger.impl.OnLargerType 7 | 8 | interface SingleConfig { 9 | 10 | //withSingle 模式下 设置imageView 11 | fun setImage(image: ImageView): T 12 | 13 | //设置数据源 14 | fun setData(data: OnLargerType): T 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/builder/impl/VideoConfig.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.builder.impl 2 | 3 | import com.starot.larger.config.LargerConfig 4 | import com.starot.larger.impl.OnCustomImageLoadListener 5 | import com.starot.larger.impl.OnImageLoadListener 6 | import com.starot.larger.impl.OnVideoLoadListener 7 | import com.starot.larger.view.progress.ImageProgressLoader 8 | 9 | 10 | interface VideoConfig : CommandConfig { 11 | 12 | 13 | //设置图片加载器 14 | fun setImageLoad(imageLoad: OnImageLoadListener): T 15 | 16 | fun setVideoLoad(videoLoader: OnVideoLoadListener): T 17 | 18 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/config/Config.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.config 2 | 3 | import android.graphics.Color 4 | import android.view.View 5 | import android.widget.ImageView 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.starot.larger.enums.LargerEnum 8 | import com.starot.larger.enums.Orientation 9 | import com.starot.larger.impl.* 10 | import com.starot.larger.view.progress.ImageProgressLoader 11 | 12 | 13 | class DefConfig() { 14 | 15 | companion object { 16 | var def_duration = 300L 17 | var def_max_scale = 3.0f 18 | var def_max_scale_last_size = 1.0f 19 | var def_medium_scale = 1.5f 20 | var def_min_scale = 0.2f 21 | var def_back_color = Color.BLACK 22 | var def_automatic = true 23 | var def_debug = false 24 | var def_progress_type = ImageProgressLoader.ProgressType.FULL 25 | var def_up_can_move = false 26 | var def_loadNextFragment = false 27 | var def_progress_use = true 28 | var orientation = Orientation.ORIENTATION_HORIZONTAL 29 | } 30 | } 31 | 32 | 33 | data class LargerConfig( 34 | 35 | //是否直接向上就能够拖动,微信直接向上不可以拖动,这里默认false 36 | var upCanMove: Boolean = DefConfig.def_up_can_move, 37 | 38 | //是否自动加载下一页,默认不自动加载下一页 39 | var loadNextFragment :Boolean = DefConfig.def_loadNextFragment, 40 | 41 | //是否打印日志 42 | var debug: Boolean = DefConfig.def_debug, 43 | 44 | //单个或者多个图片 45 | var images: List? = null, 46 | 47 | //列表 48 | var recyclerView: RecyclerView? = null, 49 | 50 | //使用的类型 51 | var largerType: LargerEnum = LargerEnum.LISTS, 52 | 53 | //持续时间 54 | var duration: Long = DefConfig.def_duration, 55 | 56 | //是否自动加载大图 57 | var automatic: Boolean = DefConfig.def_automatic, 58 | 59 | //最大缩放比例 (2 - f) 60 | var maxScale: Float = DefConfig.def_max_scale, 61 | 62 | //双击中间的大小 需要小于等于 max 63 | var mediumScale: Float = DefConfig.def_medium_scale, 64 | 65 | //最小缩放比例 (0.1f-0.7f) 66 | var minScale: Float = DefConfig.def_min_scale, 67 | 68 | //数据集合 69 | var data: List? = null, 70 | 71 | //当前的下标 72 | var position: Int = 0, 73 | 74 | //列表的布局 75 | var layoutId: Int? = null, 76 | 77 | //大图的ImageViewID 78 | var fullViewId: Int? = null, 79 | 80 | //获取加载框的id 81 | var progressId: Int? = null, 82 | 83 | //默认的背景颜色 84 | var backgroundColor: Int = DefConfig.def_back_color, 85 | 86 | //自定义 87 | var customImageLoadListener: OnCustomImageLoadListener? = null, 88 | 89 | //图片加载器 90 | var imageLoad: OnImageLoadListener? = null, 91 | 92 | //加载y样式 93 | var progressType: ImageProgressLoader.ProgressType = DefConfig.def_progress_type, 94 | 95 | //是否使用加载框 96 | var progressLoaderUse: Boolean = DefConfig.def_progress_use, 97 | 98 | //设置滑动方向 99 | var orientation: Orientation = DefConfig.orientation, 100 | 101 | //视屏加载器 102 | var videoLoad: OnVideoLoadListener? = null 103 | ) -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/AnimStatus.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class AnimStatus { 4 | 5 | NOME, 6 | 7 | ENTER_START, 8 | ENTER_END, 9 | 10 | EXIT_START, 11 | EXIT_END, 12 | 13 | DRAG_START, 14 | DRAG_END, 15 | 16 | SCALE_START, 17 | SCALE_END 18 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/AnimType.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class AnimType { 4 | ENTER, 5 | EXIT, 6 | DRAG_RESUME, 7 | DRAG_EXIT, 8 | 9 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/BackEnum.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class BackEnum { 4 | BACK_NOME, 5 | BACK_PREPARE, 6 | BACK_START, 7 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/LargerDataEnum.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class LargerDataEnum { 4 | IMAGE, 5 | Video 6 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/LargerEnum.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class LargerEnum { 4 | //列表类型 5 | LISTS, 6 | //多个或者单个图片类型 7 | SINGLES 8 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/LoadImageStatus.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class LoadImageStatus { 4 | 5 | //显示出加载框状态 6 | LOAD_SHOW, 7 | //开始下载大图 8 | LOAD_START, 9 | //大图下载成功 10 | LOAD_SUCCESS, 11 | //大图加载失败 12 | LOAD_FAILED 13 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/enums/Orientation.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.enums 2 | 3 | enum class Orientation { 4 | ORIENTATION_VERTICAL, 5 | ORIENTATION_HORIZONTAL 6 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/fragment/VideoFg.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.fragment 2 | 3 | import android.view.View 4 | import android.widget.ImageView 5 | import androidx.viewpager2.widget.ViewPager2 6 | import com.starot.larger.Larger 7 | import com.starot.larger.bean.LargerBean 8 | import com.starot.larger.enums.AnimStatus 9 | import com.starot.larger.enums.AnimType 10 | import com.starot.larger.view.image.OnLargerDragListener 11 | import com.starot.larger.impl.OnImageLoadReadyListener 12 | import com.starot.larger.status.LargerStatus 13 | import com.starot.larger.utils.LogUtils 14 | import kotlin.math.abs 15 | 16 | class VideoFg : BaseLargerFragment(), OnLargerDragListener { 17 | 18 | private lateinit var videoView: View 19 | 20 | 21 | private var isLoadVideo = false 22 | 23 | override fun getLayoutId(): Int { 24 | return Larger.largerConfig?.layoutId ?: Larger.largerConfig?.videoLoad?.getVideoLayoutId() 25 | ?: -1 26 | } 27 | 28 | 29 | // override fun onTranslatorBefore(type: AnimType, fullView: View, thumbnailView: View) { 30 | // val imageView = getPoster(fragmentView) 31 | // if (imageView != null && thumbnailView is ImageView && type == AnimType.ENTER) { 32 | // val data = getData() ?: return 33 | // val thumbnailsUrl = data.thumbnailsUrl 34 | // if (thumbnailsUrl.isNullOrEmpty()) { 35 | // return 36 | // } 37 | // Larger.largerConfig?.imageLoad?.load(thumbnailsUrl, position, false, imageView) 38 | // imageView.scaleType = thumbnailView.scaleType 39 | // } 40 | // } 41 | 42 | override fun onTranslatorBefore( 43 | type: AnimType, 44 | fullView: View, 45 | thumbnailView: View, 46 | onImageLoadReadyListener: OnImageLoadReadyListener? 47 | ) { 48 | val imageView = getPoster(fragmentView) 49 | if (imageView != null && thumbnailView is ImageView && type == AnimType.ENTER) { 50 | val data = getData() ?: return 51 | val thumbnailsUrl = data.thumbnailsUrl 52 | if (thumbnailsUrl.isNullOrEmpty()) { 53 | return 54 | } 55 | Larger.largerConfig?.imageLoad?.load( 56 | thumbnailsUrl, 57 | position, 58 | imageView, 59 | onImageLoadReadyListener 60 | ) 61 | imageView.scaleType = thumbnailView.scaleType 62 | } 63 | } 64 | 65 | override fun onTranslatorStart(type: AnimType, fullView: View, thumbnailView: View) { 66 | val imageView = getPoster(fragmentView) 67 | if (imageView is ImageView && type == AnimType.ENTER) { 68 | imageView.scaleType = ImageView.ScaleType.FIT_CENTER 69 | } else if (imageView is ImageView && thumbnailView is ImageView && (type == AnimType.EXIT || type == AnimType.DRAG_EXIT)) { 70 | imageView.scaleType = thumbnailView.scaleType 71 | } 72 | } 73 | 74 | 75 | override fun getFullViewId(): Int { 76 | return Larger.largerConfig?.fullViewId ?: Larger.largerConfig?.videoLoad?.getVideoViewId() 77 | ?: -1 78 | } 79 | 80 | override fun onDoBefore( 81 | data: LargerBean?, 82 | fullView: View?, 83 | thumbnailView: View?, 84 | position: Int, 85 | view: View 86 | ) { 87 | if (fullView != null) { 88 | this.videoView = fullView 89 | } 90 | 91 | val imageView = getPoster(view) 92 | if (imageView != null && data != null) { 93 | val thumbnailsUrl = data.thumbnailsUrl 94 | if (thumbnailsUrl.isNullOrEmpty()) { 95 | return 96 | } 97 | imageView.scaleType = ImageView.ScaleType.FIT_CENTER 98 | // Larger.largerConfig?.imageLoad?.load(thumbnailsUrl, false, imageView) 99 | } 100 | } 101 | 102 | private fun getPoster(view: View): ImageView? { 103 | return Larger.largerConfig?.videoLoad?.getPoster(view) 104 | } 105 | 106 | override fun onDoAfter( 107 | data: LargerBean?, 108 | fullView: View?, 109 | thumbnailView: View?, 110 | position: Int, 111 | view: View 112 | ) { 113 | LogUtils.i("video onDoAfter") 114 | if (data != null) { 115 | val poster = getPoster(view) 116 | if (poster != null) { 117 | poster.scaleType = ImageView.ScaleType.FIT_CENTER 118 | } 119 | if (isLoadVideo) { 120 | LogUtils.i("isLoadVideo ") 121 | return 122 | } 123 | isLoadVideo = true 124 | Larger.largerConfig?.videoLoad?.loadVideo(data, view, this) 125 | } 126 | 127 | } 128 | 129 | 130 | override fun onDrag(x: Float, y: Float) { 131 | if (isAnimIng()) return 132 | LogUtils.i("video drag------------------") 133 | startDrag(fragmentView, videoView, x = x, y = y) 134 | } 135 | 136 | override fun onDragEnd() { 137 | LargerStatus.status.value = (AnimStatus.DRAG_END) 138 | endDrag(videoView) 139 | } 140 | 141 | override fun onDragPrepare(dx: Float, dy: Float): Boolean { 142 | //动画过程中不能触发drag 143 | if (isAnimIng()) { 144 | return false 145 | } 146 | 147 | if (getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) { 148 | //一开始向上无效 149 | if (abs(dy) > abs(dx)) { 150 | //默认是可以不可以直接向上滑的 151 | val upCanMove = Larger.largerConfig?.upCanMove 152 | if (upCanMove == null || upCanMove == false) { 153 | if (dy < 0) { 154 | return false 155 | } 156 | } 157 | return true 158 | } 159 | } else { 160 | if (abs(dx) > abs(dy)) { 161 | return true 162 | } 163 | } 164 | 165 | return false 166 | } 167 | 168 | override fun onDragStart() { 169 | LargerStatus.status.value = (AnimStatus.DRAG_START) 170 | } 171 | 172 | override fun onAlreadyLoad( 173 | data: LargerBean?, 174 | fullView: View?, 175 | thumbnailView: View?, 176 | position: Int, 177 | view: View, 178 | success: (LargerBean) -> Unit 179 | ) { 180 | val imageView = getPoster(view) 181 | if (imageView != null && data != null) { 182 | val thumbnailsUrl = data.thumbnailsUrl 183 | if (thumbnailsUrl.isNullOrEmpty()) { 184 | return 185 | } 186 | Larger.largerConfig?.imageLoad?.load( 187 | thumbnailsUrl, 188 | position, 189 | imageView, 190 | object : OnImageLoadReadyListener { 191 | override fun onLoadFailed() { 192 | 193 | } 194 | 195 | override fun onReady() { 196 | LogUtils.i("onReady") 197 | success(data) 198 | } 199 | }) 200 | } 201 | } 202 | 203 | 204 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnImageLoadListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import android.view.View 4 | import android.widget.ImageView 5 | import androidx.lifecycle.MutableLiveData 6 | import com.starot.larger.bean.LargerBean 7 | import com.starot.larger.enums.LoadImageStatus 8 | 9 | //加载图片接口 10 | interface OnImageLoadListener : OnLoadProgressPrepareListener, OnLifecycleListener { 11 | 12 | 13 | //加载图片 14 | fun load(url: String, position: Int, isLoadFull: Boolean, imageView: ImageView) 15 | 16 | fun load(url: String, position: Int, imageView: ImageView, listener: OnImageLoadReadyListener?) 17 | 18 | //判断是有有缓存 19 | fun checkCache(url: String, listener: OnImageCacheListener) 20 | 21 | //清理缓存 22 | fun clearCache() 23 | } 24 | 25 | //是否有缓存 26 | interface OnImageCacheListener { 27 | fun onCache(hasCache: Boolean) 28 | } 29 | 30 | 31 | interface OnImageLoadReadyListener { 32 | fun onLoadFailed(); 33 | fun onReady() 34 | } 35 | 36 | //自行处理view 37 | interface OnCustomImageLoadListener : OnLoadProgressListener { 38 | 39 | 40 | //动画开始以前做啥事情 41 | fun onDoBefore( 42 | progressLoadChangeLiveData:MutableLiveData, 43 | imageLoader: OnImageLoadListener?, 44 | view: View, 45 | position: Int, 46 | data: LargerBean 47 | ) 48 | 49 | //动画结束以后做啥事情 50 | fun onDoAfter( 51 | progressLoadChangeLiveData:MutableLiveData, 52 | imageLoader: OnImageLoadListener?, 53 | view: View, 54 | position: Int, 55 | data: LargerBean 56 | ) 57 | 58 | } 59 | 60 | 61 | interface OnLoadProgressPrepareListener { 62 | 63 | // 将记录是否显示加载的进度框 的 liveData 传入 64 | fun onPrepareProgressView(status: MutableLiveData, position: Int) {} 65 | 66 | // 将记录变化的 liveData 传入 67 | fun onPrepareLoadProgress(progressLiveData: MutableLiveData, position: Int) {} 68 | 69 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnLargerConfigListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.GridLayoutManager 5 | import androidx.recyclerview.widget.LinearLayoutManager 6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 7 | import androidx.viewpager2.widget.ViewPager2 8 | import com.starot.larger.Larger 9 | import com.starot.larger.config.DefConfig 10 | import com.starot.larger.enums.LargerEnum 11 | import com.starot.larger.enums.Orientation 12 | import com.starot.larger.view.progress.ImageProgressLoader 13 | 14 | //处理返回一些 框架需要的数据 15 | interface OnLargerConfigListener { 16 | 17 | 18 | //动画时长 19 | fun getDuration(): Long { 20 | return Larger.largerConfig?.duration ?: DefConfig.def_duration 21 | } 22 | 23 | //背景颜色 24 | fun getBackGroundColor(): Int { 25 | return Larger.largerConfig?.backgroundColor ?: DefConfig.def_back_color 26 | } 27 | 28 | //加载方向 29 | fun getOrientation(): Int { 30 | return if (Larger.largerConfig?.orientation == null) { 31 | ViewPager2.ORIENTATION_HORIZONTAL 32 | } else { 33 | if (Larger.largerConfig?.orientation == Orientation.ORIENTATION_HORIZONTAL) { 34 | ViewPager2.ORIENTATION_HORIZONTAL 35 | } else { 36 | ViewPager2.ORIENTATION_VERTICAL 37 | } 38 | } 39 | 40 | } 41 | 42 | 43 | //是否自动加载下一页大图数据 44 | fun isAutoLoadNextFragment(): Boolean { 45 | return Larger.largerConfig?.loadNextFragment ?: DefConfig.def_loadNextFragment 46 | } 47 | 48 | 49 | //缩放最大 50 | fun getMaxScale(): Float { 51 | val maxScale = Larger.largerConfig?.maxScale ?: return DefConfig.def_max_scale 52 | if (maxScale < DefConfig.def_max_scale_last_size) { 53 | return DefConfig.def_max_scale_last_size 54 | } 55 | return maxScale 56 | } 57 | 58 | //双击 中间的大小 59 | fun getMediumScale(): Float { 60 | val mediumScale = Larger.largerConfig?.mediumScale ?: return DefConfig.def_medium_scale 61 | if (getMaxScale() < mediumScale) { 62 | throw Throwable("mediumScale must <= maxScale") 63 | } 64 | return mediumScale 65 | } 66 | 67 | //缩放最小 68 | fun getMinScale(): Float { 69 | val minScale = Larger.largerConfig?.minScale ?: return DefConfig.def_min_scale 70 | if (minScale > 0.7f) { 71 | return 0.7f 72 | } 73 | return minScale 74 | } 75 | 76 | //获取小图 77 | fun getThumbnailView(position: Int): View? { 78 | return when (Larger.largerConfig?.largerType) { 79 | LargerEnum.LISTS -> { 80 | getViewInRecycler(position) 81 | } 82 | LargerEnum.SINGLES -> { 83 | getViewInSingles(position) 84 | } 85 | else -> { 86 | null 87 | } 88 | } 89 | } 90 | 91 | //是否自动加载 92 | fun isAutomatic(): Boolean { 93 | return Larger.largerConfig?.automatic ?: DefConfig.def_automatic 94 | } 95 | 96 | //获取singles 小图 97 | private fun getViewInSingles(position: Int): View? { 98 | val images = Larger.largerConfig?.images 99 | return images?.get(position) 100 | } 101 | 102 | //图片加载框的样式 103 | fun getProgressLoaderType(): ImageProgressLoader.ProgressType { 104 | return Larger.largerConfig?.progressType ?: DefConfig.def_progress_type 105 | } 106 | 107 | private fun getViewInRecycler(position: Int): View? { 108 | val recyclerView = Larger.largerConfig?.recyclerView 109 | val layoutManager = recyclerView?.layoutManager 110 | 111 | var pos = position 112 | when (layoutManager) { 113 | is GridLayoutManager -> { 114 | pos = getRecyclerViewId(layoutManager, pos) 115 | } 116 | is LinearLayoutManager -> { 117 | pos = getRecyclerViewId(layoutManager, pos) 118 | } 119 | is StaggeredGridLayoutManager -> { 120 | } 121 | } 122 | return recyclerView?.getChildAt(pos) ?: return null 123 | } 124 | 125 | 126 | //获取recyclerView Id 127 | private fun getRecyclerViewId( 128 | layoutManager: LinearLayoutManager, 129 | pos: Int 130 | ): Int { 131 | var pos1 = pos 132 | pos1 -= layoutManager.findFirstVisibleItemPosition() 133 | return pos1 134 | } 135 | 136 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnLargerListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.GridLayoutManager 5 | import androidx.recyclerview.widget.LinearLayoutManager 6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 7 | import com.starot.larger.Larger 8 | import com.starot.larger.config.DefConfig 9 | import com.starot.larger.enums.LargerEnum 10 | 11 | //处理返回一些 框架需要的数据 12 | interface OnLargerListener : OnLargerConfigListener { 13 | 14 | //返回larger 界面的 需要加载的 大图 id 15 | fun getFullViewId(): Int 16 | 17 | //获取layout id 18 | fun getLayoutId(): Int 19 | 20 | //获取加载框的id 21 | fun getProgressId(): Int{ 22 | return -1 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnLargerType.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import android.os.Parcelable 4 | import com.starot.larger.enums.LargerDataEnum 5 | 6 | /** 当前数据源的类型 **/ 7 | interface OnLargerType : Parcelable { 8 | 9 | fun getType(): LargerDataEnum 10 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnLifecycleListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import androidx.lifecycle.LifecycleObserver 5 | import androidx.lifecycle.OnLifecycleEvent 6 | 7 | 8 | interface OnLifecycleListener : LifecycleObserver { 9 | 10 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) 11 | fun onCreate() { 12 | } 13 | 14 | @OnLifecycleEvent(Lifecycle.Event.ON_START) 15 | fun onStart() { 16 | } 17 | 18 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 19 | fun onResume() { 20 | } 21 | 22 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 23 | fun onPause() { 24 | } 25 | 26 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 27 | fun onStop() { 28 | } 29 | 30 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 31 | fun onDestroy() { 32 | } 33 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnLoadProgressListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import com.starot.larger.enums.LoadImageStatus 6 | 7 | interface OnLoadProgressListener : OnLifecycleListener { 8 | 9 | //进度条的变化 10 | fun onProgressChange( 11 | context: Context, 12 | view: View, 13 | progressId: Int, 14 | status: LoadImageStatus, 15 | position: Int 16 | ) 17 | 18 | //图片加载进度 19 | fun onLoadProgress(view: View, progressId: Int, progress: Int, position: Int) 20 | 21 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/impl/OnVideoLoadListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.impl 2 | 3 | import android.view.View 4 | import android.widget.ImageView 5 | import com.starot.larger.bean.LargerBean 6 | import com.starot.larger.view.image.OnLargerDragListener 7 | 8 | //加载图片接口 9 | interface OnVideoLoadListener : OnLoadProgressPrepareListener, 10 | OnVideoViewIdListener, OnLifecycleListener { 11 | 12 | //返回预览图 13 | fun getPoster(view: View): ImageView 14 | 15 | //加载视屏 16 | fun loadVideo(data: LargerBean, view: View, listener: OnLargerDragListener) 17 | 18 | 19 | //清理资源 20 | fun onRelease() 21 | 22 | } 23 | 24 | 25 | interface OnVideoViewIdListener { 26 | //返回视屏的id 27 | fun getVideoViewId(): Int 28 | 29 | //返回布局id 30 | fun getVideoLayoutId(): Int 31 | 32 | 33 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/livedata/UnViscousLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.livedata 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Observer 7 | import java.lang.reflect.Field 8 | import java.lang.reflect.Method 9 | 10 | 11 | class UnViscousLiveData : MutableLiveData() { 12 | //目的:使得在observe被调用的时候,能够保证 if (observer.mLastVersion >= mVersion) (livedata源码里面的)成立 13 | override fun observe(owner: LifecycleOwner, observer: Observer) { 14 | super.observe(owner, observer) 15 | try { 16 | hook(observer) 17 | } catch (t: Throwable) { 18 | 19 | } 20 | } 21 | 22 | /**要修改observer.mLastVersion的值那么思考:(逆向思维) 23 | * mLastVersion-》observer-》iterator.next().getValue()-》mObservers 24 | * 反射使用的时候,正好相反 25 | * 26 | * mObservers-》函数(iterator.next().getValue())-》observer-》mLastVersion 27 | * 通过hook,将observer.mLastVersion = mVersion 28 | * @param observer 29 | * @throws Exception 30 | */ 31 | @Throws(Exception::class) 32 | private fun hook(observer: Observer) { 33 | val liveDataClass = LiveData::class.java 34 | val fieldObservers: Field = liveDataClass.getDeclaredField("mObservers") 35 | fieldObservers.isAccessible = true 36 | val mObservers = fieldObservers.get(this) 37 | val mObserversClass: Class<*> = mObservers.javaClass 38 | val method: Method = mObserversClass.getDeclaredMethod("get", Any::class.java) 39 | method.isAccessible = true 40 | val entry = method.invoke(mObservers, observer) 41 | val observerWrapper = (entry as Map.Entry<*, *>).value!! 42 | val mObserver: Class<*> = observerWrapper.javaClass.superclass //observer 43 | val mLastVersion: Field = mObserver.getDeclaredField("mLastVersion") 44 | mLastVersion.isAccessible = true 45 | val mVersion: Field = liveDataClass.getDeclaredField("mVersion") 46 | mVersion.isAccessible = true 47 | val mVersionObject = mVersion.get(this) 48 | mLastVersion.set(observerWrapper, mVersionObject) 49 | } 50 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/status/LargerStatus.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.status 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import com.starot.larger.enums.AnimStatus 5 | import com.starot.larger.enums.BackEnum 6 | import com.starot.larger.impl.OnLifecycleListener 7 | import com.starot.larger.livedata.UnViscousLiveData 8 | 9 | object LargerStatus : OnLifecycleListener { 10 | 11 | var status = MutableLiveData().apply { 12 | value = (AnimStatus.NOME) 13 | } 14 | 15 | //是否已经加载过了 16 | var isLoad = MutableLiveData().apply { 17 | value = (false) 18 | } 19 | 20 | var pos = MutableLiveData() 21 | 22 | var back = MutableLiveData().apply { 23 | value = BackEnum.BACK_NOME 24 | } 25 | 26 | override fun onDestroy() { 27 | super.onDestroy() 28 | isLoad.value = (false) 29 | back.value = BackEnum.BACK_NOME 30 | status.value = (AnimStatus.NOME) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/utils/ColorTool.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.utils 2 | 3 | object ColorTool { 4 | 5 | /** 6 | * 对rgb色彩加入透明度 7 | * @param alpha 透明度,取值范围 0.0f -- 1.0f. 8 | * @param baseColor 9 | * @return a color with alpha made from base color 10 | */ 11 | fun getColorWithAlpha(baseColor: Int, alpha: Float): Int { 12 | val a = 255.coerceAtMost(0.coerceAtLeast((alpha * 255).toInt())) shl 24 13 | val rgb = 0x00ffffff and baseColor 14 | return a + rgb 15 | } 16 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/utils/LogUtils.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.utils 2 | 3 | import android.util.Log 4 | import com.starot.larger.Larger 5 | 6 | object LogUtils { 7 | 8 | 9 | private const val TAG = "allens_tag" 10 | 11 | @JvmStatic 12 | fun i(info: String) { 13 | if (Larger.largerConfig?.debug == true) 14 | Log.e(TAG, info) 15 | } 16 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/utils/PageChange.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.utils 2 | 3 | import androidx.viewpager2.widget.ViewPager2 4 | 5 | class PageChange { 6 | 7 | 8 | fun register(viewPager2: ViewPager2, listener: PageChangeListener) { 9 | viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { 10 | override fun onPageSelected(position: Int) { 11 | super.onPageSelected(position) 12 | listener.onPageChange(position) 13 | } 14 | }) 15 | } 16 | 17 | interface PageChangeListener { 18 | fun onPageChange(pos: Int) 19 | } 20 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/utils/StatusBarTools.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.utils 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.graphics.Color 6 | import android.os.Build 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.Window 10 | import android.view.WindowManager 11 | import android.widget.FrameLayout 12 | import androidx.core.view.ViewCompat 13 | 14 | //将电池兰取消 沉寂式 的布局 15 | object StatusBarTools { 16 | 17 | 18 | fun setStatusBar(activity: Activity) { 19 | translucentStatusBar(activity, true) 20 | changeToLightStatusBar(activity) 21 | } 22 | 23 | //SDK >= 21时, 取消状态栏的阴影 24 | private fun translucentStatusBar( 25 | activity: Activity, 26 | hideStatusBarBackground: Boolean 27 | ) { 28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 29 | translucentStatusBarLOLLIPOP(activity, hideStatusBarBackground) 30 | } else 31 | translucentStatusBar(activity) 32 | } 33 | 34 | //SDK> = 23,将状态栏改为浅色模式(状态栏图标和字体会变成深色) 35 | private fun changeToLightStatusBar(activity: Activity) { 36 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 37 | return 38 | } 39 | val window = activity.window ?: return 40 | val decorView = window.decorView 41 | decorView.systemUiVisibility = 42 | decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 43 | } 44 | 45 | 46 | private fun translucentStatusBarLOLLIPOP( 47 | activity: Activity, 48 | hideStatusBarBackground: Boolean 49 | ) { 50 | val window = activity.window 51 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) 52 | if (hideStatusBarBackground) { 53 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 54 | window.statusBarColor = Color.TRANSPARENT 55 | window.decorView.systemUiVisibility = 56 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 57 | } else { 58 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 59 | window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE 60 | } 61 | val mContentView = 62 | window.findViewById(Window.ID_ANDROID_CONTENT) as ViewGroup 63 | val mChildView = mContentView.getChildAt(0) 64 | if (mChildView != null) { 65 | ViewCompat.setFitsSystemWindows(mChildView, false) 66 | ViewCompat.requestApplyInsets(mChildView) 67 | } 68 | } 69 | 70 | private fun translucentStatusBar(activity: Activity) { 71 | val window = activity.window 72 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 73 | val mContentView = 74 | activity.findViewById(Window.ID_ANDROID_CONTENT) as ViewGroup 75 | val mContentChild = mContentView.getChildAt(0) 76 | removeFakeStatusBarViewIfExist(activity) 77 | removeMarginTopOfContentChild( 78 | mContentChild, 79 | getStatusBarHeight(activity) 80 | ) 81 | if (mContentChild != null) { 82 | ViewCompat.setFitsSystemWindows(mContentChild, false) 83 | } 84 | } 85 | 86 | /** 87 | * return statusBar's Height in pixels 88 | */ 89 | private fun getStatusBarHeight(context: Context): Int { 90 | var result = 0 91 | val resId = 92 | context.resources.getIdentifier("status_bar_height", "dimen", "android") 93 | if (resId > 0) { 94 | result = context.resources.getDimensionPixelOffset(resId) 95 | } 96 | return result 97 | } 98 | 99 | 100 | /** 101 | * remove marginTop to simulate set FitsSystemWindow false 102 | */ 103 | private fun removeMarginTopOfContentChild( 104 | mContentChild: View?, 105 | statusBarHeight: Int 106 | ) { 107 | if (mContentChild == null) { 108 | return 109 | } 110 | if ("marginAdded" == mContentChild.tag) { 111 | val lp = 112 | mContentChild.layoutParams as FrameLayout.LayoutParams 113 | lp.topMargin -= statusBarHeight 114 | mContentChild.layoutParams = lp 115 | mContentChild.tag = null 116 | } 117 | } 118 | 119 | 120 | /** 121 | * use reserved order to remove is more quickly. 122 | */ 123 | private fun removeFakeStatusBarViewIfExist(activity: Activity) { 124 | val window = activity.window 125 | val mDecorView = window.decorView as ViewGroup 126 | val fakeView = 127 | mDecorView.findViewWithTag("statusBarView") 128 | if (fakeView != null) { 129 | mDecorView.removeView(fakeView) 130 | } 131 | } 132 | 133 | 134 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/Compat.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2011, 2012 Chris Banes. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.starot.larger.view.image; 17 | 18 | import android.annotation.TargetApi; 19 | import android.os.Build.VERSION; 20 | import android.os.Build.VERSION_CODES; 21 | import android.view.View; 22 | 23 | class Compat { 24 | 25 | private static final int SIXTY_FPS_INTERVAL = 1000 / 60; 26 | 27 | public static void postOnAnimation(View view, Runnable runnable) { 28 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { 29 | postOnAnimationJellyBean(view, runnable); 30 | } else { 31 | view.postDelayed(runnable, SIXTY_FPS_INTERVAL); 32 | } 33 | } 34 | 35 | @TargetApi(16) 36 | private static void postOnAnimationJellyBean(View view, Runnable runnable) { 37 | view.postOnAnimation(runnable); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnGestureListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2011, 2012 Chris Banes. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package com.starot.larger.view.image; 17 | 18 | interface OnGestureListener { 19 | 20 | void onDrag(float dx, float dy); 21 | 22 | void onFling(float startX, float startY, float velocityX, 23 | float velocityY); 24 | 25 | void onScale(float scaleFactor, float focusX, float focusY); 26 | 27 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnLargerDragListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image 2 | 3 | interface OnLargerDragListener { 4 | 5 | 6 | fun onDrag(x: Float, y: Float) {} 7 | 8 | fun onDragEnd() {} 9 | 10 | fun onDragStart() {} 11 | 12 | fun onDragPrepare(dx: Float, dy: Float): Boolean { 13 | return true 14 | } 15 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnLargerScaleListener.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image 2 | 3 | interface OnLargerScaleListener { 4 | 5 | 6 | fun onScaleEnd() {} 7 | 8 | fun onScaleStart() {} 9 | 10 | fun onScale(scaleFactor: Float, focusX: Float, focusY: Float) {} 11 | 12 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnMatrixChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | import android.graphics.RectF; 4 | 5 | /** 6 | * Interface definition for a callback to be invoked when the internal Matrix has changed for 7 | * this View. 8 | */ 9 | public interface OnMatrixChangedListener { 10 | 11 | /** 12 | * Callback for when the Matrix displaying the Drawable has changed. This could be because 13 | * the View's bounds have changed, or the user has zoomed. 14 | * 15 | * @param rect - Rectangle displaying the Drawable's new bounds. 16 | */ 17 | void onMatrixChanged(RectF rect); 18 | } 19 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnOutsidePhotoTapListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | import android.widget.ImageView; 4 | 5 | /** 6 | * Callback when the user tapped outside of the photo 7 | */ 8 | public interface OnOutsidePhotoTapListener { 9 | 10 | /** 11 | * The outside of the photo has been tapped 12 | */ 13 | void onOutsidePhotoTap(ImageView imageView); 14 | } 15 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnPhotoTapListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | import android.widget.ImageView; 4 | 5 | /** 6 | * A callback to be invoked when the Photo is tapped with a single 7 | * tap. 8 | */ 9 | public interface OnPhotoTapListener { 10 | 11 | /** 12 | * A callback to receive where the user taps on a photo. You will only receive a callback if 13 | * the user taps on the actual photo, tapping on 'whitespace' will be ignored. 14 | * 15 | * @param view ImageView the user tapped. 16 | * @param x where the user tapped from the of the Drawable, as percentage of the 17 | * Drawable width. 18 | * @param y where the user tapped from the top of the Drawable, as percentage of the 19 | * Drawable height. 20 | */ 21 | void onPhotoTap(ImageView view, float x, float y); 22 | } 23 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnScaleChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | 4 | /** 5 | * Interface definition for callback to be invoked when attached ImageView scale changes 6 | */ 7 | public interface OnScaleChangedListener { 8 | 9 | /** 10 | * Callback for when the scale changes 11 | * 12 | * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in) 13 | * @param focusX focal point X position 14 | * @param focusY focal point Y position 15 | */ 16 | void onScaleChange(float scaleFactor, float focusX, float focusY); 17 | } 18 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnSingleFlingListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | import android.view.MotionEvent; 4 | 5 | /** 6 | * A callback to be invoked when the ImageView is flung with a single 7 | * touch 8 | */ 9 | public interface OnSingleFlingListener { 10 | 11 | /** 12 | * A callback to receive where the user flings on a ImageView. You will receive a callback if 13 | * the user flings anywhere on the view. 14 | * 15 | * @param e1 MotionEvent the user first touch. 16 | * @param e2 MotionEvent the user last touch. 17 | * @param velocityX distance of user's horizontal fling. 18 | * @param velocityY distance of user's vertical fling. 19 | */ 20 | boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); 21 | } 22 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnViewDragListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | /** 4 | * Interface definition for a callback to be invoked when the photo is experiencing a drag event 5 | */ 6 | public interface OnViewDragListener { 7 | 8 | /** 9 | * Callback for when the photo is experiencing a drag event. This cannot be invoked when the 10 | * user is scaling. 11 | * 12 | * @param dx The change of the coordinates in the x-direction 13 | * @param dy The change of the coordinates in the y-direction 14 | */ 15 | void onDrag(float dx, float dy); 16 | } 17 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/OnViewTapListener.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | import android.view.View; 4 | 5 | public interface OnViewTapListener { 6 | 7 | /** 8 | * A callback to receive where the user taps on a ImageView. You will receive a callback if 9 | * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored. 10 | * 11 | * @param view - View the user tapped. 12 | * @param x - where the user tapped from the left of the View. 13 | * @param y - where the user tapped from the top of the View. 14 | */ 15 | void onViewTap(View view, float x, float y); 16 | } 17 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/image/Util.java: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.image; 2 | 3 | import android.view.MotionEvent; 4 | import android.widget.ImageView; 5 | 6 | class Util { 7 | 8 | static void checkZoomLevels(float minZoom, float midZoom, 9 | float maxZoom) { 10 | if (minZoom >= midZoom) { 11 | throw new IllegalArgumentException( 12 | "Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value"); 13 | } else if (midZoom > maxZoom) { 14 | throw new IllegalArgumentException( 15 | "Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value"); 16 | } 17 | } 18 | 19 | static boolean hasDrawable(ImageView imageView) { 20 | return imageView.getDrawable() != null; 21 | } 22 | 23 | static boolean isSupportedScaleType(final ImageView.ScaleType scaleType) { 24 | if (scaleType == null) { 25 | return false; 26 | } 27 | switch (scaleType) { 28 | case MATRIX: 29 | throw new IllegalStateException("Matrix scale type is not supported"); 30 | } 31 | return true; 32 | } 33 | 34 | static int getPointerIndex(int action) { 35 | return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/progress/ImageProgressLoader.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.progress 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.view.View 6 | import com.starot.larger.enums.LoadImageStatus 7 | import com.starot.larger.utils.LogUtils 8 | 9 | class ImageProgressLoader(private val type: ProgressType) : ProgressLoader() { 10 | 11 | 12 | override fun onProgressChange( 13 | context: Context, 14 | view: View, 15 | progressId: Int, 16 | status: LoadImageStatus, 17 | position: Int 18 | ) { 19 | LogUtils.i("进度弹窗$position 是否不显示: $status") 20 | if (progressId == -1) { 21 | return 22 | } 23 | val progressView = view.findViewById(progressId) ?: return 24 | when (status) { 25 | LoadImageStatus.LOAD_SHOW -> { 26 | progressView.visibility = View.VISIBLE 27 | } 28 | LoadImageStatus.LOAD_FAILED -> { 29 | progressView.visibility = View.INVISIBLE 30 | } 31 | LoadImageStatus.LOAD_SUCCESS -> { 32 | progressView.visibility = View.INVISIBLE 33 | } 34 | else -> { 35 | 36 | } 37 | } 38 | if (progressView is CircleProgressView) { 39 | setStyle( 40 | progressView, 41 | if (type == ProgressType.FULL) { 42 | 1 43 | } else { 44 | 0 45 | } 46 | ) 47 | } 48 | 49 | } 50 | 51 | override fun onLoadProgress(view: View, progressId: Int, progress: Int, position: Int) { 52 | 53 | LogUtils.i("进度弹窗$position progress:$progress") 54 | if (progressId == -1) { 55 | return 56 | } 57 | val progressView = view.findViewById(progressId) 58 | if (progressView is CircleProgressView) { 59 | progressView.progress = progress 60 | } 61 | 62 | } 63 | 64 | 65 | enum class ProgressType { 66 | NONE, 67 | FULL 68 | } 69 | 70 | 71 | private fun setStyle(progressView: CircleProgressView?, style: Int) { 72 | when (style) { 73 | 0 -> { 74 | progressView?.progressStyle = CircleProgressView.ProgressStyle.NORMAL 75 | progressView?.textColor = Color.parseColor("#ffffff") 76 | progressView?.normalBarColor = Color.parseColor("#00000000") 77 | progressView?.reachBarColor = Color.parseColor("#ffffff") 78 | progressView?.isTextVisible = true 79 | progressView?.textSuffix = "%" 80 | progressView?.reachBarSize = 3 81 | progressView?.textSize = 11 82 | } 83 | 1 -> { 84 | progressView?.progressStyle = CircleProgressView.ProgressStyle.FILL_IN_ARC 85 | progressView?.outerColor = Color.parseColor("#ffffff") 86 | progressView?.normalBarColor = Color.parseColor("#00000000") 87 | progressView?.reachBarColor = Color.parseColor("#ffffff") 88 | progressView?.outerSize = 2 89 | progressView?.innerPadding = 2 90 | } 91 | else -> { 92 | progressView?.progressStyle = CircleProgressView.ProgressStyle.FILL_IN_ARC 93 | progressView?.outerColor = Color.parseColor("#ffffff") 94 | progressView?.normalBarColor = Color.parseColor("#00000000") 95 | progressView?.reachBarColor = Color.parseColor("#ffffff") 96 | progressView?.outerSize = 2 97 | progressView?.innerPadding = 2 98 | } 99 | } 100 | 101 | } 102 | } -------------------------------------------------------------------------------- /Larger/src/main/java/com/starot/larger/view/progress/ProgresssLoader.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger.view.progress 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import com.starot.larger.enums.LoadImageStatus 6 | import com.starot.larger.impl.OnLoadProgressListener 7 | 8 | open class ProgressLoader : OnLoadProgressListener { 9 | override fun onProgressChange( 10 | context: Context, 11 | view: View, 12 | progressId: Int, 13 | status: LoadImageStatus, 14 | position: Int 15 | ) { 16 | 17 | } 18 | 19 | override fun onLoadProgress(view: View, progressId: Int, progress: Int, position: Int) { 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Larger/src/main/res/layout/activity_larger_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /Larger/src/main/res/layout/fg_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /Larger/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Larger/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Larger/src/test/java/com/starot/larger/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.starot.larger 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /LargerGlide/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /LargerGlide/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 30 8 | buildToolsVersion "30.0.2" 9 | 10 | defaultConfig { 11 | minSdkVersion 19 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | consumerProguardFiles "consumer-rules.pro" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: "libs", include: ["*.jar"]) 30 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 31 | implementation 'androidx.core:core-ktx:1.3.1' 32 | implementation 'androidx.appcompat:appcompat:1.2.0' 33 | testImplementation 'junit:junit:4.13' 34 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 35 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 36 | 37 | 38 | 39 | implementation project(":Larger") 40 | 41 | kapt 'com.github.bumptech.glide:glide:4.11.0' 42 | kapt 'com.github.bumptech.glide:compiler:4.11.0' 43 | implementation "com.github.bumptech.glide:okhttp3-integration:4.11.0" 44 | 45 | } -------------------------------------------------------------------------------- /LargerGlide/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/LargerGlide/consumer-rules.pro -------------------------------------------------------------------------------- /LargerGlide/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /LargerGlide/src/androidTest/java/com/allens/largerglide/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.allens.largerglide.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /LargerGlide/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | -------------------------------------------------------------------------------- /LargerGlide/src/main/java/com/allens/largerglide/GlideImageLoader.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.graphics.drawable.Drawable 6 | import android.os.Handler 7 | import android.os.Looper 8 | import android.widget.ImageView 9 | import androidx.lifecycle.MutableLiveData 10 | import com.allens.largerglide.impl.CustomRequestListener 11 | import com.allens.largerglide.impl.ProgressListener 12 | import com.allens.largerglide.interceptor.ProgressInterceptor 13 | import com.bumptech.glide.Glide 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.starot.larger.enums.LoadImageStatus 20 | import com.starot.larger.impl.OnImageCacheListener 21 | import com.starot.larger.impl.OnImageLoadListener 22 | import com.starot.larger.impl.OnImageLoadReadyListener 23 | import com.starot.larger.utils.LogUtils 24 | import java.io.File 25 | 26 | //Glide 加载图片 这里记录了 加载的进度 和 加载状态 后续考虑 移植到其他地方 以便于拓展 27 | class GlideImageLoader(private val context: Context) : OnImageLoadListener { 28 | 29 | 30 | private var handler: Handler = Handler(Looper.getMainLooper()) 31 | 32 | private val statusMap = HashMap>() 33 | private val progressMap = HashMap>() 34 | 35 | private var thread: Thread? = null 36 | 37 | @SuppressLint("CheckResult") 38 | override fun load(url: String, position: Int, isLoadFull: Boolean, imageView: ImageView) { 39 | 40 | val progressViewLiveData = statusMap[position] 41 | val progressLiveData = progressMap[position] 42 | val options = RequestOptions() 43 | if (isLoadFull) { 44 | ProgressInterceptor.addListener(url, object : ProgressListener { 45 | override fun onProgress(progress: Int) { 46 | 47 | val value = progressViewLiveData?.value 48 | if (value == null || value == LoadImageStatus.LOAD_SHOW) { 49 | progressViewLiveData?.value = LoadImageStatus.LOAD_START 50 | } 51 | 52 | //进度 53 | progressLiveData?.value = (progress) 54 | } 55 | }) 56 | options 57 | .placeholder(imageView.drawable) 58 | .override(imageView.width, imageView.height) 59 | } 60 | LogUtils.i("load image isLoadFull:$isLoadFull, url:$url") 61 | Glide.with(context) 62 | .load(url) 63 | .apply(options) 64 | .listener(CustomRequestListener(url, progressViewLiveData, isLoadFull)) 65 | .into(imageView) 66 | } 67 | 68 | override fun load( 69 | url: String, 70 | position: Int, 71 | imageView: ImageView, 72 | listener: OnImageLoadReadyListener? 73 | ) { 74 | Glide.with(context) 75 | .load(url) 76 | .listener(object : RequestListener { 77 | override fun onLoadFailed( 78 | e: GlideException?, 79 | model: Any?, 80 | target: Target?, 81 | isFirstResource: Boolean 82 | ): Boolean { 83 | listener?.onLoadFailed() 84 | return false 85 | } 86 | 87 | override fun onResourceReady( 88 | resource: Drawable?, 89 | model: Any?, 90 | target: Target?, 91 | dataSource: DataSource?, 92 | isFirstResource: Boolean 93 | ): Boolean { 94 | listener?.onReady() 95 | return false 96 | } 97 | }) 98 | .into(imageView) 99 | 100 | } 101 | 102 | override fun checkCache(url: String, listener: OnImageCacheListener) { 103 | thread = Thread { 104 | val file: File? = try { 105 | Glide.with(context).downloadOnly() 106 | .load(url) 107 | .apply( 108 | RequestOptions().onlyRetrieveFromCache(true) 109 | ).submit() 110 | .get() 111 | } catch (e: Exception) { 112 | null 113 | } 114 | if (file == null) { 115 | handler.post { 116 | listener.onCache(false) 117 | } 118 | } else { 119 | handler.post { 120 | listener.onCache(true) 121 | } 122 | } 123 | } 124 | thread?.start() 125 | } 126 | 127 | 128 | override fun clearCache() { 129 | Glide.get(context).clearMemory() 130 | Thread { Glide.get(context).clearDiskCache() }.start() 131 | } 132 | 133 | override fun onPrepareLoadProgress(progressLiveData: MutableLiveData, position: Int) { 134 | progressMap[position] = progressLiveData 135 | } 136 | 137 | override fun onPrepareProgressView( 138 | status: MutableLiveData, 139 | position: Int 140 | ) { 141 | statusMap[position] = status 142 | } 143 | 144 | 145 | override fun onDestroy() { 146 | super.onDestroy() 147 | statusMap.clear() 148 | progressMap.clear() 149 | handler.removeCallbacksAndMessages(null) 150 | thread?.interrupt() 151 | thread = null 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /LargerGlide/src/main/java/com/allens/largerglide/body/ProgressResponseBody.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide.body 2 | 3 | 4 | import android.os.Handler 5 | import android.util.Log 6 | import com.allens.largerglide.impl.ProgressListener 7 | import com.allens.largerglide.interceptor.ProgressInterceptor 8 | import okhttp3.MediaType 9 | import okhttp3.ResponseBody 10 | import okio.* 11 | 12 | 13 | class ProgressResponseBody( 14 | url: String, 15 | private val handler: Handler, 16 | private var responseBody: ResponseBody 17 | ) : ResponseBody() { 18 | 19 | private var listener: ProgressListener? = ProgressInterceptor.map[url] 20 | 21 | override fun contentLength(): Long { 22 | return responseBody.contentLength() 23 | } 24 | 25 | override fun contentType(): MediaType? { 26 | return responseBody.contentType() 27 | } 28 | 29 | override fun source(): BufferedSource { 30 | return Okio.buffer(ProgressSource(responseBody.source())) 31 | } 32 | 33 | inner class ProgressSource(source: Source) : ForwardingSource(source) { 34 | 35 | private var totalBytesRead: Long = 0 36 | 37 | private var currentProgress = 0 38 | override fun read(sink: Buffer, byteCount: Long): Long { 39 | val bytesRead = super.read(sink, byteCount) 40 | val fullLength = responseBody.contentLength() 41 | if (bytesRead == -1L) { 42 | totalBytesRead = fullLength; 43 | } else { 44 | totalBytesRead += bytesRead; 45 | } 46 | val progress = (100f * totalBytesRead / fullLength).toInt() 47 | if (progress != currentProgress) { 48 | handler.post { 49 | listener?.onProgress(progress) 50 | } 51 | } 52 | currentProgress = progress; 53 | return bytesRead 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LargerGlide/src/main/java/com/allens/largerglide/impl/CustomRequestListener.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide.impl 2 | 3 | import android.graphics.drawable.Drawable 4 | import androidx.lifecycle.MutableLiveData 5 | import com.allens.largerglide.interceptor.ProgressInterceptor 6 | import com.bumptech.glide.load.DataSource 7 | import com.bumptech.glide.load.engine.GlideException 8 | import com.bumptech.glide.request.RequestListener 9 | import com.bumptech.glide.request.target.Target 10 | import com.starot.larger.enums.LoadImageStatus 11 | 12 | class CustomRequestListener( 13 | private val url: String, 14 | private val progressViewLiveData: MutableLiveData?, 15 | private val loadFull: Boolean 16 | ) : 17 | RequestListener { 18 | override fun onLoadFailed( 19 | e: GlideException?, 20 | model: Any?, 21 | target: Target?, 22 | isFirstResource: Boolean 23 | ): Boolean { 24 | ProgressInterceptor.removeListener(url = url) 25 | 26 | if (loadFull) { 27 | progressViewLiveData?.value = LoadImageStatus.LOAD_FAILED 28 | } 29 | return false 30 | } 31 | 32 | override fun onResourceReady( 33 | resource: Drawable?, 34 | model: Any?, 35 | target: Target?, 36 | dataSource: DataSource?, 37 | isFirstResource: Boolean 38 | ): Boolean { 39 | ProgressInterceptor.removeListener(url = url) 40 | 41 | if (loadFull) { 42 | progressViewLiveData?.value = LoadImageStatus.LOAD_SUCCESS 43 | } 44 | return false 45 | } 46 | 47 | 48 | } -------------------------------------------------------------------------------- /LargerGlide/src/main/java/com/allens/largerglide/impl/ProgressListener.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide.impl 2 | 3 | 4 | interface ProgressListener { 5 | fun onProgress(progress: Int) 6 | } 7 | -------------------------------------------------------------------------------- /LargerGlide/src/main/java/com/allens/largerglide/interceptor/ProgressInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide.interceptor 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import com.allens.largerglide.impl.ProgressListener 6 | import com.allens.largerglide.body.ProgressResponseBody 7 | import okhttp3.Interceptor 8 | import okhttp3.Request 9 | import okhttp3.Response 10 | import java.util.concurrent.ConcurrentHashMap 11 | 12 | 13 | object ProgressInterceptor : Interceptor { 14 | override fun intercept(chain: Interceptor.Chain): Response { 15 | val request: Request = chain.request() 16 | val response = chain.proceed(request) 17 | val url: String = request.url().toString() 18 | val body = response.body() 19 | val handler = Handler(Looper.getMainLooper()) 20 | return response.newBuilder().body(body?.let { 21 | ProgressResponseBody( 22 | url, 23 | handler, 24 | it 25 | ) 26 | }).build() 27 | } 28 | 29 | val map = ConcurrentHashMap() 30 | 31 | //入注册下载监听 32 | fun addListener(url: String, listener: ProgressListener) { 33 | map[url] = listener 34 | } 35 | 36 | //取消注册下载监听 37 | fun removeListener(url: String?) { 38 | map.remove(url) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LargerGlide/src/main/java/com/allens/largerglide/module/MyGlideModule.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide.module 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import com.bumptech.glide.Glide 6 | import com.bumptech.glide.Registry 7 | import com.bumptech.glide.annotation.GlideModule 8 | import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader 9 | import com.bumptech.glide.load.model.GlideUrl 10 | import com.bumptech.glide.module.AppGlideModule 11 | import com.allens.largerglide.interceptor.ProgressInterceptor 12 | import okhttp3.OkHttpClient 13 | import java.io.InputStream 14 | 15 | @GlideModule 16 | class MyGlideModule : AppGlideModule() { 17 | override fun registerComponents( 18 | context: Context, 19 | glide: Glide, 20 | registry: Registry 21 | ) { 22 | //添加拦截器到Glide 23 | val builder = OkHttpClient.Builder() 24 | builder.addInterceptor(ProgressInterceptor) 25 | val okHttpClient = builder.build() 26 | 27 | registry.replace( 28 | GlideUrl::class.java, 29 | InputStream::class.java, 30 | OkHttpUrlLoader.Factory(okHttpClient) 31 | ) 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /LargerGlide/src/test/java/com/allens/largerglide/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.allens.largerglide 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /LargerLoadVideo/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /LargerLoadVideo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 30 7 | buildToolsVersion "30.0.2" 8 | 9 | defaultConfig { 10 | minSdkVersion 21 11 | targetSdkVersion 30 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility 1.8 27 | targetCompatibility 1.8 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: "libs", include: ["*.jar"]) 33 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 34 | implementation 'androidx.core:core-ktx:1.3.1' 35 | implementation 'androidx.appcompat:appcompat:1.2.0' 36 | testImplementation 'junit:junit:4.13' 37 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 39 | 40 | 41 | implementation project(":Larger") 42 | 43 | implementation 'cn.jzvd:jiaozivideoplayer:7.4.2' 44 | } -------------------------------------------------------------------------------- /LargerLoadVideo/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/LargerLoadVideo/consumer-rules.pro -------------------------------------------------------------------------------- /LargerLoadVideo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /LargerLoadVideo/src/androidTest/java/com/example/largerloadvideo/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.largerloadvideo 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.largerloadvideo.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /LargerLoadVideo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | -------------------------------------------------------------------------------- /LargerLoadVideo/src/main/java/com/example/largerloadvideo/LargerDrag.kt: -------------------------------------------------------------------------------- 1 | package com.example.largerloadvideo 2 | 3 | import android.content.Context 4 | import android.view.GestureDetector 5 | import android.view.MotionEvent 6 | import android.view.ViewConfiguration 7 | import com.starot.larger.view.image.OnLargerDragListener 8 | import java.util.concurrent.atomic.AtomicBoolean 9 | import kotlin.math.sqrt 10 | 11 | 12 | class LargerDrag(private val listener: OnLargerDragListener, context: Context) : 13 | GestureDetector.SimpleOnGestureListener() { 14 | 15 | val isDragging = AtomicBoolean(false) 16 | 17 | private val mTouchSlop = ViewConfiguration.get(context).scaledEdgeSlop 18 | override fun onScroll( 19 | e1: MotionEvent?, 20 | e2: MotionEvent?, 21 | distanceX: Float, 22 | distanceY: Float 23 | ): Boolean { 24 | if (e1 != null && e2 != null) { 25 | 26 | if (!isDragging.get()) { 27 | val dx = e2.rawX - e1.rawX 28 | val dy = e2.rawY - e1.rawY 29 | if (listener.onDragPrepare(dx, dy)) { 30 | isDragging.set(sqrt(dx * dx + (dy * dy).toDouble()) >= mTouchSlop) 31 | if (isDragging.get()) { 32 | listener.onDragStart() 33 | } 34 | } 35 | } else { 36 | listener.onDrag( 37 | e2.rawX - e1.rawX, 38 | e2.rawY - e1.rawY 39 | ) 40 | } 41 | } 42 | return super.onScroll(e1, e2, distanceX, distanceY) 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /LargerLoadVideo/src/main/java/com/example/largerloadvideo/LargerVideoLoad.kt: -------------------------------------------------------------------------------- 1 | package com.example.largerloadvideo 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.widget.ImageView 6 | import androidx.lifecycle.MutableLiveData 7 | import cn.jzvd.JZUtils 8 | import cn.jzvd.Jzvd 9 | import cn.jzvd.JzvdStd 10 | import com.starot.larger.bean.LargerBean 11 | import com.starot.larger.view.image.OnLargerDragListener 12 | import com.starot.larger.impl.OnVideoLoadListener 13 | import com.starot.larger.utils.LogUtils 14 | 15 | 16 | //视屏加载器 17 | class LargerVideoLoad(private val context: Context) : OnVideoLoadListener { 18 | 19 | 20 | override fun getPoster(view: View): ImageView { 21 | val video = view.findViewById(getVideoViewId()) 22 | return video.posterImageView 23 | } 24 | 25 | 26 | override fun loadVideo(data: LargerBean, view: View, listener: OnLargerDragListener) { 27 | LogUtils.i("loadVideo") 28 | val video = view.findViewById(getVideoViewId())?:return 29 | video.setDragListener(object : OnLargerDragListener { 30 | override fun onDrag(x: Float, y: Float) { 31 | listener.onDrag(x, y = y) 32 | } 33 | 34 | override fun onDragEnd() { 35 | listener.onDragEnd() 36 | Jzvd.goOnPlayOnResume() 37 | } 38 | 39 | override fun onDragPrepare(dx: Float, dy: Float): Boolean { 40 | return listener.onDragPrepare(dx, dy) 41 | } 42 | 43 | override fun onDragStart() { 44 | listener.onDragStart() 45 | Jzvd.goOnPlayOnPause() 46 | video.setButtonProgressStatus(false) 47 | } 48 | }) 49 | video.setUp(data.fullUrl, "", Jzvd.SCREEN_NORMAL) 50 | video.startVideoAfterPreloading() 51 | } 52 | 53 | 54 | override fun onRelease() { 55 | LogUtils.i("onRelease") 56 | Jzvd.releaseAllVideos() 57 | } 58 | 59 | override fun onPause() { 60 | LogUtils.i("onPause") 61 | JZUtils.clearSavedProgress(context.applicationContext, null) 62 | Jzvd.goOnPlayOnPause() 63 | } 64 | 65 | override fun onResume() { 66 | LogUtils.i("onResume") 67 | Jzvd.goOnPlayOnResume() 68 | } 69 | 70 | override fun onDestroy() { 71 | super.onDestroy() 72 | Jzvd.releaseAllVideos() 73 | } 74 | 75 | 76 | override fun getVideoViewId(): Int { 77 | return R.id.audio_video 78 | } 79 | 80 | 81 | override fun getVideoLayoutId(): Int { 82 | return R.layout.item_larger_video 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /LargerLoadVideo/src/main/java/com/example/largerloadvideo/MyVideoView.java: -------------------------------------------------------------------------------- 1 | package com.example.largerloadvideo; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.GestureDetector; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | import com.starot.larger.view.image.OnLargerDragListener; 10 | 11 | import cn.jzvd.JzvdStd; 12 | 13 | public class MyVideoView extends JzvdStd { 14 | 15 | 16 | private GestureDetector gestureDetector; 17 | 18 | private LargerDrag largerDrag; 19 | 20 | private OnLargerDragListener listener; 21 | 22 | public MyVideoView(Context context) { 23 | super(context); 24 | } 25 | 26 | public MyVideoView(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | } 29 | 30 | @Override 31 | public void onCompletion() { 32 | super.onCompletion(); 33 | posterImageView.setVisibility(View.GONE); 34 | } 35 | 36 | @Override 37 | public int getLayoutId() { 38 | return R.layout.layout_jzstd_notitle; 39 | } 40 | 41 | @Override 42 | public boolean dispatchTouchEvent(MotionEvent ev) { 43 | if (gestureDetector != null && largerDrag != null) { 44 | gestureDetector.onTouchEvent(ev); 45 | if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { 46 | if (listener != null && largerDrag.isDragging().get()) { 47 | largerDrag.isDragging().set(false); 48 | listener.onDragEnd(); 49 | } 50 | } 51 | } 52 | 53 | return super.dispatchTouchEvent(ev); 54 | } 55 | 56 | 57 | public void setDragListener(OnLargerDragListener listener) { 58 | this.listener = listener; 59 | largerDrag = new LargerDrag(listener, getContext()); 60 | gestureDetector = new GestureDetector(getContext(), largerDrag); 61 | } 62 | 63 | public void setButtonProgressStatus(boolean isVisibility) { 64 | bottomProgressBar.setVisibility(isVisibility ? VISIBLE : GONE); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /LargerLoadVideo/src/main/res/layout/item_larger_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | -------------------------------------------------------------------------------- /LargerLoadVideo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LargerLoadVideo/src/test/java/com/example/largerloadvideo/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.largerloadvideo 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 高仿微信朋友圈,点击查看大图,放大 缩小,可自定义 5 | 6 | 7 | 8 | 9 | # 前言 10 | 11 | 之前写过一个 [高仿微信查看大图 放大缩小](https://allens.icu/posts/30acc017/) ,开源以后,有小伙伴提出疑问,想要加入视屏的功能,反思了一下,自己当初写这个轮子时候,为什么没有考虑到这个问题,后来经过修改,吧兼容视频放入轮子中 12 | 13 | > 举一反三 14 | 15 | - 除了保留当初设计的图片功能,还需要保留拓展,给以后的其他功能加入 16 | - 能够兼容各种模式在一起 17 | - 开发者只需要图片功能,不需要视屏,那么就不需要视屏的轮子, 18 | - 可以自行拓展想要的布局,和可修改的拓展性 19 | 20 | 21 | 22 | 23 | # 下载 24 | 25 | 这里将每个部分模块化,可以单独依赖,减少体积 26 | 27 | ```java 28 | allprojects { 29 | repositories { 30 | ... 31 | maven { url 'https://www.jitpack.io' } 32 | } 33 | } 34 | ``` 35 | 36 | 必须添加的模块,也是基础模块 37 | 38 | ```java 39 | dependencies { 40 | implementation 'com.github.JiangHaiYang01.WeChatPhoto:Larger:0.0.5' 41 | } 42 | ``` 43 | 44 | 最新版本 [![](https://www.jitpack.io/v/JiangHaiYang01/WeChatPhoto.svg)](https://www.jitpack.io/#JiangHaiYang01/WeChatPhoto) 45 | 46 | 47 | 提供默认的图片加载器 48 | 49 | ```java 50 | implementation 'com.github.JiangHaiYang01.WeChatPhoto:LargerGlide:0.0.5' 51 | ``` 52 | 53 | 提供默认的视屏加载器 54 | ```java 55 | implementation 'com.github.JiangHaiYang01.WeChatPhoto:LargerLoadVideo:0.0.5' 56 | ``` 57 | 58 | 59 | 60 | 61 | # 使用介绍 62 | 63 | ## 加载列表类型的图片 64 | 65 | 66 | 自定义数据类型,需要继承 ``LargerBean`` 并且设置这个类型的是想要改成哪种模式,当前支持的模式 图片 和 视屏模式 67 | 68 | ```java 69 | @Parcelize 70 | class ImageBean : LargerBean() { 71 | override fun getType(): LargerDataEnum { 72 | return LargerDataEnum.IMAGE 73 | } 74 | 75 | } 76 | ``` 77 | 78 | > 需要导入 图片加载器 79 | 80 | 在需要的地方启动 下面是最基础的用法, 就能实现之前版本的需求 81 | 82 | ```java 83 | Larger.create() 84 | .withImageMulti()//这里展示的是列表类型的 85 | .setImageLoad(GlideImageLoader(context)) //图片加载器 86 | .setIndex(position)//下标 87 | .setDuration(3000)//动画持续时间 88 | .setRecyclerView(recyclerView)//recyclerview 89 | .setData(data) //添加默认的数据源 90 | .start(context) 91 | ``` 92 | 93 | 94 | 95 | 96 | ![](https://gitee.com/_Allens/BlogImage/raw/master/image/20200911091816.gif) 97 | 98 | 99 | ## 加载视屏 100 | 101 | 和图片一样需要自定义数据类型 102 | 103 | ``` 104 | @Parcelize 105 | class VideoBean : LargerBean() { 106 | override fun getType(): LargerDataEnum { 107 | return LargerDataEnum.Video 108 | } 109 | 110 | } 111 | ``` 112 | 113 | 导入图片加载器和视屏加载器 114 | 115 | ```java 116 | Larger.create() 117 | .withVideoMulti()//这里展示的是列表类型的 118 | .setImageLoad(GlideImageLoader(context)) //图片加载器 119 | .setVideoLoad(LargerVideoLoad(context))//视屏加载器 120 | .setIndex(position)//下标 121 | .setRecyclerView(recyclerView)//recyclerview 122 | .setData(data) //添加默认的数据源 123 | .start(context)//跳转 124 | 125 | ``` 126 | 127 | 128 | 129 | 130 | ![](https://gitee.com/_Allens/BlogImage/raw/master/image/20200911092159.gif) 131 | 132 | 133 | # 更多属性 134 | 135 | ``` 136 | 137 | //是否直接向上就能够拖动,微信直接向上不可以拖动,这里默认false 138 | var upCanMove: Boolean = DefConfig.def_up_can_move, 139 | 140 | //是否自动加载下一页,默认不自动加载下一页 141 | var loadNextFragment :Boolean = DefConfig.def_loadNextFragment, 142 | 143 | //是否打印日志 144 | var debug: Boolean = DefConfig.def_debug, 145 | 146 | //单个或者多个图片 147 | var images: List? = null, 148 | 149 | //列表 150 | var recyclerView: RecyclerView? = null, 151 | 152 | //使用的类型 153 | var largerType: LargerEnum = LargerEnum.LISTS, 154 | 155 | //持续时间 156 | var duration: Long = DefConfig.def_duration, 157 | 158 | //是否自动加载大图 159 | var automatic: Boolean = DefConfig.def_automatic, 160 | 161 | //最大缩放比例 (2 - f) 162 | var maxScale: Float = DefConfig.def_max_scale, 163 | 164 | //双击中间的大小 需要小于等于 max 165 | var mediumScale: Float = DefConfig.def_medium_scale, 166 | 167 | //最小缩放比例 (0.1f-0.7f) 168 | var minScale: Float = DefConfig.def_min_scale, 169 | 170 | //数据集合 171 | var data: List? = null, 172 | 173 | //当前的下标 174 | var position: Int = 0, 175 | 176 | //列表的布局 177 | var layoutId: Int? = null, 178 | 179 | //大图的ImageViewID 180 | var fullViewId: Int? = null, 181 | 182 | //获取加载框的id 183 | var progressId: Int? = null, 184 | 185 | //默认的背景颜色 186 | var backgroundColor: Int = DefConfig.def_back_color, 187 | 188 | //自定义 189 | var customImageLoadListener: OnCustomImageLoadListener? = null, 190 | 191 | //图片加载器 192 | var imageLoad: OnImageLoadListener? = null, 193 | 194 | //加载y样式 195 | var progressType: ImageProgressLoader.ProgressType = DefConfig.def_progress_type, 196 | 197 | //是否使用加载框 198 | var progressLoaderUse: Boolean = DefConfig.def_progress_use, 199 | 200 | //设置滑动方向 201 | var orientation: Orientation = DefConfig.orientation, 202 | 203 | //视屏加载器 204 | var videoLoad: OnVideoLoadListener? = null 205 | ``` 206 | 207 | 208 | 209 | # 最后 210 | 211 | 整个架构用fragment 的方式去处理,方便后续的拓展,视屏部分使用的是 [JiaoZiVideoPlayer](https://github.com/Jzvd/JiaoZiVideoPlayer) 作为播放器,eumm 可以替换自己喜欢的播放器去处理, 212 | 213 | 214 | 215 | # github 216 | 217 | [Github](https://github.com/JiangHaiYang01/WeChatPhoto) 218 | [博客说明](https://allens.icu/posts/4a05dc12/#more) 219 | 220 | 221 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 30 8 | buildToolsVersion "30.0.2" 9 | 10 | defaultConfig { 11 | applicationId "com.starot.wechat_debug" 12 | minSdkVersion 21 13 | targetSdkVersion 30 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility 1.8 28 | targetCompatibility 1.8 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: "libs", include: ["*.jar"]) 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 35 | implementation 'androidx.core:core-ktx:1.3.1' 36 | implementation 'androidx.appcompat:appcompat:1.2.0' 37 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 38 | testImplementation 'junit:junit:4.13' 39 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 41 | 42 | implementation project(":Larger") 43 | //加载器 44 | implementation project(":LargerGlide") 45 | //进度 46 | // implementation project(":LargerProgress") 47 | //加载视屏模式 48 | implementation project(":LargerLoadVideo") 49 | 50 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 51 | 52 | implementation 'com.github.bumptech.glide:glide:4.11.0' 53 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' 54 | 55 | 56 | // debugImplementation because LeakCanary should only run in debug builds. 57 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4' 58 | 59 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/starot/wechat/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.starot.wechat", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.widget.Button 6 | import androidx.appcompat.app.AppCompatActivity 7 | import com.bumptech.glide.Glide 8 | import com.starot.wechat.activity.* 9 | import kotlinx.android.synthetic.main.activity_main.* 10 | 11 | class MainActivity : BaseAct() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_main) 15 | 16 | val arrayListOf = arrayListOf( 17 | "图片列表布局", 18 | "加载原图自定义处理", 19 | "加载框的样式1", 20 | "加载框的样式2", 21 | "竖着滑动", 22 | "设置缩放大小", 23 | "设置背景颜色", 24 | "加载列表视屏", 25 | "图片视屏混合", 26 | "单个图片", 27 | "单个视屏", 28 | "清理缓存" 29 | ) 30 | 31 | for (index in arrayListOf.indices) { 32 | val button = Button(this) 33 | button.text = arrayListOf[index] 34 | main_ll.addView(button) 35 | button.setOnClickListener { 36 | 37 | 38 | when (index) { 39 | 0 -> { 40 | val intent = Intent(this, ImageListAct::class.java) 41 | intent.putExtra("name", arrayListOf[index]) 42 | intent.putExtra("type", 0) 43 | startActivity(intent) 44 | } 45 | 1 -> { 46 | val intent = Intent(this, ImageListAct::class.java) 47 | intent.putExtra("name", arrayListOf[index]) 48 | intent.putExtra("type", 1) 49 | startActivity(intent) 50 | } 51 | 52 | 2 -> { 53 | 54 | Glide.get(this).clearMemory() 55 | Thread(Runnable { Glide.get(this).clearDiskCache() }).start() 56 | 57 | val intent = Intent(this, ImageListAct::class.java) 58 | intent.putExtra("name", arrayListOf[index]) 59 | intent.putExtra("type", 2) 60 | startActivity(intent) 61 | } 62 | 3 -> { 63 | 64 | Glide.get(this).clearMemory() 65 | Thread(Runnable { Glide.get(this).clearDiskCache() }).start() 66 | 67 | val intent = Intent(this, ImageListAct::class.java) 68 | intent.putExtra("name", arrayListOf[index]) 69 | intent.putExtra("type", 3) 70 | startActivity(intent) 71 | } 72 | 4 -> { 73 | val intent = Intent(this, ImageListAct::class.java) 74 | intent.putExtra("name", arrayListOf[index]) 75 | intent.putExtra("type", 4) 76 | startActivity(intent) 77 | } 78 | 5 -> { 79 | val intent = Intent(this, ImageListAct::class.java) 80 | intent.putExtra("name", arrayListOf[index]) 81 | intent.putExtra("type", 5) 82 | startActivity(intent) 83 | } 84 | 6 -> { 85 | val intent = Intent(this, ImageListAct::class.java) 86 | intent.putExtra("name", arrayListOf[index]) 87 | intent.putExtra("type", 6) 88 | startActivity(intent) 89 | } 90 | 7 -> { 91 | val intent = Intent(this, VideoListAct::class.java) 92 | intent.putExtra("name", arrayListOf[index]) 93 | intent.putExtra("type", 0) 94 | startActivity(intent) 95 | } 96 | 97 | 8 -> { 98 | val intent = Intent(this, HybridListAct::class.java) 99 | intent.putExtra("name", arrayListOf[index]) 100 | intent.putExtra("type", 0) 101 | startActivity(intent) 102 | } 103 | 104 | 9 -> { 105 | val intent = Intent(this, SingleImageAct::class.java) 106 | intent.putExtra("name", arrayListOf[index]) 107 | intent.putExtra("type", 0) 108 | startActivity(intent) 109 | } 110 | 10 -> { 111 | val intent = Intent(this, SingleAudioAct::class.java) 112 | intent.putExtra("name", arrayListOf[index]) 113 | intent.putExtra("type", 0) 114 | startActivity(intent) 115 | } 116 | 117 | arrayListOf.size - 1 -> { 118 | Glide.get(this).clearMemory() 119 | Thread(Runnable { Glide.get(this).clearDiskCache() }).start() 120 | return@setOnClickListener 121 | } 122 | } 123 | 124 | 125 | } 126 | } 127 | 128 | } 129 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/activity/BaseAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.starot.larger.utils.StatusBarTools 6 | import com.starot.wechat.R 7 | 8 | open class BaseAct :AppCompatActivity(){ 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | StatusBarTools.setStatusBar(this) 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/activity/HybridListAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.recyclerview.widget.GridLayoutManager 6 | import com.starot.larger.bean.LargerBean 7 | import com.starot.wechat.R 8 | import com.starot.wechat.adapter.HybridListAdapter 9 | import com.starot.wechat.adapter.ImageListAdapter 10 | import com.starot.wechat.bean.ImageBean 11 | import com.starot.wechat.bean.VideoBean 12 | import com.starot.wechat.utils.Urls 13 | import kotlinx.android.synthetic.main.activity_image_list.* 14 | 15 | 16 | class HybridListAct : BaseAct() { 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setContentView(R.layout.activity_image_list) 21 | title = intent.getStringExtra("name") 22 | val type = intent.getIntExtra("type", 0) 23 | 24 | val list = arrayListOf() 25 | 26 | val targetButtonSmall = Urls().getTargetButtonSmall() 27 | val targetButtonTarget = Urls().getTargetButtonTarget() 28 | for (index in targetButtonSmall.indices) { 29 | val element = ImageBean() 30 | element.fullUrl = targetButtonTarget[index] 31 | element.thumbnailsUrl = targetButtonSmall[index] 32 | list.add(element) 33 | } 34 | 35 | 36 | val audioImage = Urls().getAudioImage() 37 | val audio = Urls().getAudio() 38 | for (index in audioImage.indices) { 39 | val element = VideoBean() 40 | element.fullUrl = audio[index] 41 | element.thumbnailsUrl = audioImage[index] 42 | list.add(element) 43 | } 44 | 45 | 46 | 47 | act_list_ry.layoutManager = GridLayoutManager(this, 2) 48 | act_list_ry.adapter = HybridListAdapter(list, act_list_ry, type) 49 | } 50 | 51 | 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/activity/ImageListAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.recyclerview.widget.GridLayoutManager 6 | import com.starot.wechat.R 7 | import com.starot.wechat.adapter.ImageListAdapter 8 | import com.starot.wechat.bean.ImageBean 9 | import com.starot.wechat.utils.Urls 10 | import kotlinx.android.synthetic.main.activity_image_list.* 11 | 12 | 13 | class ImageListAct : BaseAct() { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_image_list) 18 | title = intent.getStringExtra("name") 19 | val type = intent.getIntExtra("type", 0) 20 | 21 | val list = arrayListOf() 22 | 23 | val targetButtonSmall = Urls().getTargetButtonSmall() 24 | val targetButtonTarget = Urls().getTargetButtonTarget() 25 | for (index in targetButtonSmall.indices) { 26 | val element = ImageBean() 27 | element.fullUrl = targetButtonTarget[index] 28 | element.thumbnailsUrl = targetButtonSmall[index] 29 | list.add(element) 30 | } 31 | act_list_ry.layoutManager = GridLayoutManager(this, 2) 32 | act_list_ry.adapter = ImageListAdapter(list, act_list_ry, type) 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/activity/SingleAudioAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.activity 2 | 3 | import android.os.Bundle 4 | import com.allens.largerglide.GlideImageLoader 5 | import com.bumptech.glide.Glide 6 | import com.example.largerloadvideo.LargerVideoLoad 7 | import com.starot.larger.Larger 8 | import com.starot.wechat.R 9 | import com.starot.wechat.bean.VideoBean 10 | import com.starot.wechat.utils.Urls 11 | import kotlinx.android.synthetic.main.activity_image_single.* 12 | 13 | 14 | class SingleAudioAct : BaseAct() { 15 | 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_image_single) 20 | title = intent.getStringExtra("name") 21 | 22 | 23 | val list = arrayListOf() 24 | 25 | val targetButtonSmall = Urls().getAudioImage() 26 | val targetButtonTarget = Urls().getAudio() 27 | for (index in targetButtonSmall.indices) { 28 | if (index == 2) { 29 | val element = VideoBean() 30 | element.fullUrl = targetButtonTarget[index] 31 | element.thumbnailsUrl = targetButtonSmall[index] 32 | list.add(element) 33 | 34 | } 35 | } 36 | 37 | Glide.with(this).load(list[0].thumbnailsUrl).into(src_image) 38 | 39 | src_image.setOnClickListener { 40 | Larger.create() 41 | .withVideoSingle()//这里展示的是列表类型的 42 | .setImageLoad(GlideImageLoader(this)) //图片加载器 43 | .setDuration(3000)//动画持续时间 44 | .setVideoLoad(LargerVideoLoad(this)) 45 | .setImage(src_image)//设置imageView 46 | .setData(list[0]) //添加默认的数据源 47 | .start(this) 48 | } 49 | } 50 | 51 | 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/activity/SingleImageAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.activity 2 | 3 | import android.os.Bundle 4 | import com.allens.largerglide.GlideImageLoader 5 | import com.bumptech.glide.Glide 6 | import com.starot.larger.Larger 7 | import com.starot.wechat.R 8 | import com.starot.wechat.bean.ImageBean 9 | import com.starot.wechat.utils.Urls 10 | import kotlinx.android.synthetic.main.activity_image_single.* 11 | 12 | 13 | class SingleImageAct : BaseAct() { 14 | 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.activity_image_single) 19 | title = intent.getStringExtra("name") 20 | 21 | 22 | val list = arrayListOf() 23 | 24 | val targetButtonSmall = Urls().getTargetButtonSmall() 25 | val targetButtonTarget = Urls().getTargetButtonTarget() 26 | for (index in targetButtonSmall.indices) { 27 | if (index == 1) { 28 | break 29 | } 30 | val element = ImageBean() 31 | element.fullUrl = targetButtonTarget[index] 32 | element.thumbnailsUrl = targetButtonSmall[index] 33 | list.add(element) 34 | 35 | } 36 | 37 | Glide.with(this).load(list[0].thumbnailsUrl).into(src_image) 38 | 39 | src_image.setOnClickListener { 40 | Larger.create() 41 | .withImageSingle()//这里展示的是单个图片 42 | .setImageLoad(GlideImageLoader(this)) //图片加载器 43 | .setDuration(300)//动画持续时间 44 | .setImage(src_image)//设置imageView 45 | .setData(list[0]) //添加默认的数据源 46 | .start(this) 47 | } 48 | } 49 | 50 | 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/activity/VideoListAct.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.recyclerview.widget.GridLayoutManager 6 | import com.starot.wechat.R 7 | import com.starot.wechat.adapter.VideoListAdapter 8 | import com.starot.wechat.bean.VideoBean 9 | import com.starot.wechat.utils.Urls 10 | import kotlinx.android.synthetic.main.activity_image_list.* 11 | 12 | 13 | class VideoListAct : BaseAct() { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_image_list) 18 | title = intent.getStringExtra("name") 19 | val type = intent.getIntExtra("type", 0) 20 | val list = arrayListOf() 21 | 22 | val targetButtonSmall = Urls().getAudioImage() 23 | val targetButtonTarget = Urls().getAudio() 24 | for (index in targetButtonSmall.indices) { 25 | val element = VideoBean() 26 | element.fullUrl = targetButtonTarget[index] 27 | element.thumbnailsUrl = targetButtonSmall[index] 28 | list.add(element) 29 | } 30 | act_list_ry.layoutManager = GridLayoutManager(this, 2) 31 | act_list_ry.adapter = VideoListAdapter(list, act_list_ry,type) 32 | } 33 | 34 | 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/adapter/HybridListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.adapter 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.ImageView 9 | import android.widget.TextView 10 | import androidx.recyclerview.widget.RecyclerView 11 | import com.allens.largerglide.GlideImageLoader 12 | import com.bumptech.glide.Glide 13 | import com.example.largerloadvideo.LargerVideoLoad 14 | import com.starot.larger.Larger 15 | import com.starot.larger.bean.LargerBean 16 | import com.starot.larger.enums.Orientation 17 | import com.starot.larger.impl.OnCustomImageLoadListener 18 | import com.starot.larger.impl.OnImageCacheListener 19 | import com.starot.larger.impl.OnImageLoadListener 20 | import com.starot.wechat.R 21 | import com.starot.wechat.bean.ImageBean 22 | import kotlin.collections.ArrayList 23 | 24 | 25 | class HybridListAdapter( 26 | private val data: ArrayList, 27 | private val recyclerView: RecyclerView, 28 | private val type: Int, 29 | ) : 30 | RecyclerView.Adapter() { 31 | 32 | private lateinit var context: Context 33 | 34 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 35 | var image: ImageView = itemView.findViewById(R.id.item_image) 36 | } 37 | 38 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 39 | val v: View = 40 | LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false) 41 | context = parent.context 42 | return ViewHolder(v) 43 | } 44 | 45 | override fun getItemCount(): Int { 46 | return data.size 47 | } 48 | 49 | 50 | 51 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 52 | Glide.with(context) 53 | .load(data[position].thumbnailsUrl) 54 | .into(holder.image) 55 | 56 | 57 | holder.itemView.setOnClickListener { 58 | val withListType = Larger.create() 59 | .withListType()//这里展示的是列表类型的 60 | .setImageLoad(GlideImageLoader(context)) //图片加载器 61 | .setVideoLoad(LargerVideoLoad(context)) 62 | .setIndex(position)//下标 63 | .setDuration(300)//动画持续时间 64 | // .setProgress(ProgressLoader(ProgressLoader.ProgressType.FULL)) //添加进度显示 65 | .setRecyclerView(recyclerView)//recyclerview 66 | .setData(data) //添加默认的数据源 67 | withListType.start(context) 68 | } 69 | 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/adapter/VideoListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.adapter 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.allens.largerglide.GlideImageLoader 10 | import com.bumptech.glide.Glide 11 | import com.example.largerloadvideo.LargerVideoLoad 12 | import com.starot.larger.Larger 13 | import com.starot.larger.enums.Orientation 14 | import com.starot.wechat.R 15 | import com.starot.wechat.bean.VideoBean 16 | import kotlin.collections.ArrayList 17 | 18 | 19 | class VideoListAdapter( 20 | private val data: ArrayList, 21 | private val recyclerView: RecyclerView, 22 | private val type: Int, 23 | ) : 24 | RecyclerView.Adapter() { 25 | 26 | private lateinit var context: Context 27 | 28 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 29 | var image: ImageView = itemView.findViewById(R.id.item_image) 30 | } 31 | 32 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 33 | val v: View = 34 | LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false) 35 | context = parent.context 36 | return ViewHolder(v) 37 | } 38 | 39 | override fun getItemCount(): Int { 40 | return data.size 41 | } 42 | 43 | 44 | 45 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 46 | Glide.with(context) 47 | .load(data[position].thumbnailsUrl) 48 | .into(holder.image) 49 | 50 | 51 | 52 | holder.itemView.setOnClickListener { 53 | val withListType = Larger.create() 54 | .withVideoMulti()//这里展示的是列表类型的 55 | .setImageLoad(GlideImageLoader(context)) //图片加载器 56 | .setVideoLoad(LargerVideoLoad(context))//视屏加载器 57 | .setIndex(position)//下标 58 | .setDebug(true) 59 | .setUpCanMove(true) 60 | .setDuration(3000)//动画持续时间 61 | .setRecyclerView(recyclerView)//recyclerview 62 | .setData(data) //添加默认的数据源 63 | when (type) { 64 | 0 -> { 65 | } 66 | } 67 | withListType.start(context) 68 | } 69 | 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/bean/ImageBean.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.bean 2 | 3 | import com.starot.larger.bean.LargerBean 4 | import com.starot.larger.enums.LargerDataEnum 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | class ImageBean : LargerBean() { 9 | override fun getType(): LargerDataEnum { 10 | return LargerDataEnum.IMAGE 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/bean/VideoBean.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.bean 2 | 3 | import com.starot.larger.bean.LargerBean 4 | import com.starot.larger.enums.LargerDataEnum 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | class VideoBean : LargerBean() { 9 | override fun getType(): LargerDataEnum { 10 | return LargerDataEnum.Video 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/starot/wechat/utils/Urls.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat.utils 2 | 3 | import java.util.ArrayList 4 | 5 | open class Urls { 6 | fun getTargetButtonTarget(): List { 7 | val list: MutableList = 8 | ArrayList() 9 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643441654-assets/web-upload/771e09b0-aaf9-4308-bae0-cd5b3cb98817.jpeg") 10 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643441557-assets/web-upload/94ed7774-2bed-4dbe-be54-080c2f8939a1.jpeg") 11 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643441632-assets/web-upload/84d01b3f-7f28-4125-b3c7-8e5b5b15c0cb.jpeg") 12 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4k2flh3j31c00u0gs8.jpg") 13 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4m99esij30u01hcn1x.jpg") 14 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4onuhofj30sg1ek43l.jpg") 15 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4pi32qcj30qe110gob.jpg") 16 | 17 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643441654-assets/web-upload/771e09b0-aaf9-4308-bae0-cd5b3cb98817.jpeg") 18 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643441557-assets/web-upload/94ed7774-2bed-4dbe-be54-080c2f8939a1.jpeg") 19 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643441632-assets/web-upload/84d01b3f-7f28-4125-b3c7-8e5b5b15c0cb.jpeg") 20 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4k2flh3j31c00u0gs8.jpg") 21 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4m99esij30u01hcn1x.jpg") 22 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4onuhofj30sg1ek43l.jpg") 23 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4pi32qcj30qe110gob.jpg") 24 | return list 25 | } 26 | 27 | fun getTargetButtonSmall(): List { 28 | val list: MutableList = 29 | ArrayList() 30 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643585276-assets/web-upload/6e2520de-544d-45b6-9892-ff228d14e175.jpeg") 31 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643585330-assets/web-upload/85629ce4-bacf-4ab4-af6f-5a0a67931b68.jpeg") 32 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643585344-assets/web-upload/3b178609-8aef-48f3-9ebc-41537149f13b.jpeg") 33 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4l9gekoj305c03c3yf.jpg") 34 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4mwvc60j303005c0sl.jpg") 35 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4onuhofj30sg1ek43l.jpg") 36 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4pxkzaxj302n03p743.jpg") 37 | 38 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643585276-assets/web-upload/6e2520de-544d-45b6-9892-ff228d14e175.jpeg") 39 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643585330-assets/web-upload/85629ce4-bacf-4ab4-af6f-5a0a67931b68.jpeg") 40 | list.add("https://cdn.nlark.com/yuque/0/2020/jpeg/252337/1592643585344-assets/web-upload/3b178609-8aef-48f3-9ebc-41537149f13b.jpeg") 41 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4l9gekoj305c03c3yf.jpg") 42 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4mwvc60j303005c0sl.jpg") 43 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4onuhofj30sg1ek43l.jpg") 44 | list.add("https://tva1.sinaimg.cn/large/007S8ZIlly1giy4pxkzaxj302n03p743.jpg") 45 | return list 46 | } 47 | 48 | fun getAudioImage(): List { 49 | val list: MutableList = 50 | ArrayList() 51 | list.add("https://gitee.com/_Allens/BlogImage/raw/master/image/20200901152310.png") 52 | list.add("https://gitee.com/_Allens/BlogImage/raw/master/image/20200901152900.png") 53 | list.add("https://gitee.com/_Allens/BlogImage/raw/master/image/20200901153024.png") 54 | list.add("http://jzvd-pic.nathen.cn/jzvd-pic/00b026e7-b830-4994-bc87-38f4033806a6.jpg") 55 | list.add("http://jzvd-pic.nathen.cn/jzvd-pic/1d935cc5-a1e7-4779-bdfa-20fd7a60724c.jpg") 56 | list.add("http://jzvd-pic.nathen.cn/jzvd-pic/a019ffc1-556c-4a85-b70c-b1b49811d577.jpg") 57 | list.add("http://jzvd-pic.nathen.cn/jzvd-pic/6fc2ae91-36e2-44c5-bb10-29ae5d5c678c.png") 58 | list.add("http://jzvd-pic.nathen.cn/jzvd-pic/f03cee95-9b78-4dd5-986f-d162c06c385c.png") 59 | list.add("http://jzvd-pic.nathen.cn/jzvd-pic/e7ea659f-c3d2-4979-9ea5-f993b05e5930.png") 60 | 61 | return list 62 | } 63 | 64 | 65 | fun getAudio(): List { 66 | val list: MutableList = 67 | ArrayList() 68 | list.add("https://mp4.vjshi.com/2018-12-22/f4de0fcda0cf34707cf89d8d38825692.mp4") 69 | list.add("https://mp4.vjshi.com/2020-08-28/f17ccf7a47b0d96d8a033397f6eac7f5.mp4") 70 | list.add("https://mp4.vjshi.com/2020-03-17/6cf7d4f0ad7a573bf6d684515c4ee4e7.mp4") 71 | list.add("http://jzvd.nathen.cn/c494b340ff704015bb6682ffde3cd302/64929c369124497593205a4190d7d128-5287d2089db37e62345123a1be272f8b.mp4") 72 | list.add("http://jzvd.nathen.cn/63f3f73712544394be981d9e4f56b612/69c5767bb9e54156b5b60a1b6edeb3b5-5287d2089db37e62345123a1be272f8b.mp4") 73 | list.add("http://jzvd.nathen.cn/b201be3093814908bf987320361c5a73/2f6d913ea25941ffa78cc53a59025383-5287d2089db37e62345123a1be272f8b.mp4") 74 | list.add("http://jzvd.nathen.cn/d2438fd1c37c4618a704513ad38d68c5/68626a9d53ca421c896ac8010f172b68-5287d2089db37e62345123a1be272f8b.mp4") 75 | list.add("http://jzvd.nathen.cn/25a8d119cfa94b49a7a4117257d8ebd7/f733e65a22394abeab963908f3c336db-5287d2089db37e62345123a1be272f8b.mp4") 76 | list.add("http://jzvd.nathen.cn/7512edd1ad834d40bb5b978402274b1a/9691c7f2d7b74b5e811965350a0e5772-5287d2089db37e62345123a1be272f8b.mp4") 77 | list.add("http://jzvd.nathen.cn/c6e3dc12a1154626b3476d9bf3bd7266/6b56c5f0dc31428083757a45764763b0-5287d2089db37e62345123a1be272f8b.mp4") 78 | return list 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_image_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_image_single.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_single.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_custom_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | WeChat 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/test/java/com/starot/wechat/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.starot.wechat 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "1.4.0" 4 | repositories { 5 | // maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'} 6 | google() 7 | jcenter() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:4.0.1' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | jcenter() 22 | // maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'} 23 | maven { url 'https://www.jitpack.io' } 24 | google() 25 | mavenCentral() 26 | } 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiangHaiYang01/WeChatPhoto/e1f5739ae1ba02d22344073e81ef1e5c51511c61/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 02 09:35:20 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':LargerGlide' 2 | include ':LargerLoadVideo' 3 | include ':Larger' 4 | include ':app' 5 | rootProject.name = "WeChat" --------------------------------------------------------------------------------