├── .env
├── .gitignore
├── .gitlab-ci.yml
├── .idea
├── .gitignore
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── gradle.xml
├── jarRepositories.xml
├── markdown-navigator-enh.xml
├── markdown-navigator.xml
├── misc.xml
├── modules
│ └── native
│ │ └── src
│ │ └── main
│ │ └── cpp
│ │ ├── com_example_anative_NativeColorRenderer.h
│ │ ├── com_example_anative_NativeColorRenderer_Companion.h
│ │ └── com_example_anative_Test.h
└── vcs.xml
├── OpenGL
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── opengl
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── sample
│ │ │ └── demo1.glsl
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── opengl
│ │ │ ├── GLMainActivity.kt
│ │ │ ├── camera
│ │ │ ├── CameraSize.kt
│ │ │ └── ICamera.kt
│ │ │ └── sample
│ │ │ └── ColorGLActivity.kt
│ └── res
│ │ ├── layout
│ │ ├── activity_color_g_l.xml
│ │ └── activity_g_l_main.xml
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── opengl
│ └── ExampleUnitTest.kt
├── app
├── .gitignore
├── allwins.jks
├── build.gradle
├── proguard-rules.pro
├── release
│ ├── app-release.apk
│ └── output.json
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── cj
│ │ └── customwidget
│ │ ├── Aes.java
│ │ ├── Charset.kt
│ │ ├── EncryptUtils.kt
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── cj
│ │ │ └── customwidget
│ │ │ ├── APP.kt
│ │ │ ├── ASMActivity.java
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainActivity2.kt
│ │ │ ├── ScrollingActivity.kt
│ │ │ ├── ext
│ │ │ ├── CharSequenceExt.kt
│ │ │ └── Ext.kt
│ │ │ ├── helper
│ │ │ ├── IAudioPlay.kt
│ │ │ ├── MediaPlayerAudioPlay.kt
│ │ │ └── MusicCropWarp.kt
│ │ │ ├── page
│ │ │ ├── CropMusicActivity.kt
│ │ │ ├── LooperRecyclerViewActivity.kt
│ │ │ ├── ShadowActivity.kt
│ │ │ ├── VideoCropSeekActivity.kt
│ │ │ ├── falling
│ │ │ │ ├── FallingActivity.kt
│ │ │ │ ├── FallingAdapter.kt
│ │ │ │ └── RedPackAnim.kt
│ │ │ ├── opengl
│ │ │ │ ├── CameraReviewActivity.kt
│ │ │ │ └── OpenGLActivity.kt
│ │ │ ├── viewmodel
│ │ │ │ ├── VM1Activity.kt
│ │ │ │ ├── VM2Activity.kt
│ │ │ │ ├── VMFragment.kt
│ │ │ │ ├── VMScope.kt
│ │ │ │ ├── ViewModel1.kt
│ │ │ │ ├── ViewModel2.kt
│ │ │ │ └── ViewModelExt.kt
│ │ │ └── viewmodelv2
│ │ │ │ └── ShareViewModel.kt
│ │ │ ├── util
│ │ │ ├── ActivityGestureHelper.kt
│ │ │ ├── CharSequenceExt.kt
│ │ │ ├── CompatibleUtil.kt
│ │ │ ├── FastBlur.java
│ │ │ ├── LogUtils.java
│ │ │ ├── MediaUtil.kt
│ │ │ └── TestUtil.java
│ │ │ └── widget
│ │ │ ├── ArcView.kt
│ │ │ ├── BlurringView.kt
│ │ │ ├── CropSeekBar.kt
│ │ │ ├── CustomTextView.kt
│ │ │ ├── DateView.kt
│ │ │ ├── EventContainerView.kt
│ │ │ ├── FallingView.kt
│ │ │ ├── LooperLinearLayoutManager.kt
│ │ │ ├── LooperLinearLayoutManager2.kt
│ │ │ ├── MarqueTextView.kt
│ │ │ ├── MarqueeViewGroup.kt
│ │ │ ├── MusicCropView.kt
│ │ │ ├── PathView.kt
│ │ │ ├── RecordProgressButton.kt
│ │ │ ├── RecordView.kt
│ │ │ ├── ShadowGroup.kt
│ │ │ ├── SpreadView2.java
│ │ │ ├── StatusView.kt
│ │ │ ├── SuffixAutoCompleteView.kt
│ │ │ ├── TextSeekBar.kt
│ │ │ ├── TopicEditTextView.kt
│ │ │ ├── UploadProgressView.kt
│ │ │ ├── camera
│ │ │ └── CameraInterface.kt
│ │ │ ├── event
│ │ │ ├── EventView1.kt
│ │ │ └── EventView2.kt
│ │ │ ├── reader
│ │ │ ├── DirectDrawer.java
│ │ │ └── RecordRenderer.kt
│ │ │ └── videocrop
│ │ │ └── VideoCropSeekBar.kt
│ └── res
│ │ ├── anim
│ │ ├── anim_alpha_action_inv.xml
│ │ ├── anim_alpha_action_v.xml
│ │ ├── anim_dialog_in_from_b.xml
│ │ ├── anim_dialog_out_from_b.xml
│ │ ├── slide_in_from_bottom.xml
│ │ ├── slide_in_from_left.xml
│ │ ├── slide_in_from_right.xml
│ │ ├── slide_in_from_top.xml
│ │ ├── slide_out_to_bottom.xml
│ │ ├── slide_out_to_left.xml
│ │ ├── slide_out_to_right.xml
│ │ └── slide_out_to_top.xml
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_redpack_boom.gif
│ │ ├── shape_c7_5.xml
│ │ └── shape_email_bg.xml
│ │ ├── layout
│ │ ├── activity_a_s_m.xml
│ │ ├── activity_camera_review.xml
│ │ ├── activity_crop_music.xml
│ │ ├── activity_falling.xml
│ │ ├── activity_g_p_u_image.xml
│ │ ├── activity_home.xml
│ │ ├── activity_looper_recycler_view.xml
│ │ ├── activity_main.xml
│ │ ├── activity_main2.xml
│ │ ├── activity_main3.xml
│ │ ├── activity_main4.xml
│ │ ├── activity_open_g_l.xml
│ │ ├── activity_record.xml
│ │ ├── activity_scrolling.xml
│ │ ├── activity_shadow.xml
│ │ ├── activity_v_m1.xml
│ │ ├── activity_v_m2.xml
│ │ ├── activity_video_crop_seek.xml
│ │ ├── content_scrolling.xml
│ │ ├── dialog_lll.xml
│ │ ├── dialog_record_close.xml
│ │ ├── fragment_vm.xml
│ │ ├── item_redpack.xml
│ │ ├── item_text.xml
│ │ └── view_date.xml
│ │ ├── menu
│ │ └── menu_scrolling.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_bg_default.png
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_moment_thumb.png
│ │ ├── ic_readpack.png
│ │ ├── ic_readpack2.png
│ │ ├── ic_record_pause.png
│ │ ├── ic_shoot_beauty_level_bg.png
│ │ └── img.jpg
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ ├── audio.mp3
│ │ └── video.mp4
│ │ ├── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ids.xml
│ │ ├── strings.xml
│ │ ├── styles.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── frame_file_paths.xml
│ │ └── network_security_config.xml
│ └── test
│ └── java
│ └── com
│ └── cj
│ └── customwidget
│ ├── Bean2.kt
│ ├── CountryBean.java
│ ├── ExampleUnitTest.kt
│ ├── GoodChild.java
│ ├── Goods.java
│ ├── JavaTest.java
│ ├── TestBean.java
│ ├── TestParseInnerProValue.java
│ ├── UnicodeUtil.java
│ ├── code2.json
│ └── country.json
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── myPlugin
├── build.gradle
└── src
│ └── main
│ └── resources
│ └── META-INF
│ └── gradle-plugins
│ └── com.luan.plugin.properties
├── native
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── anative
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ └── native-color.cpp
│ └── java
│ │ └── com
│ │ └── example
│ │ └── anative
│ │ └── Testaaa.kt
│ └── test
│ └── java
│ └── com
│ └── example
│ └── anative
│ └── ExampleUnitTest.kt
└── settings.gradle
/.env:
--------------------------------------------------------------------------------
1 | App_Identifier = "com.xx.xxx"
2 |
3 | # Apple email address
4 | Apple_Id = "xxx@sina.com"
5 |
6 | # TeamId
7 | Team_Id = "xxx"
8 |
9 | # target scheme
10 | Scheme = "xxx"
11 |
12 | # xcodeproj
13 | Xcodeproj ="xxx.xcodeproj"
14 |
15 | # xcworkspace
16 | Workspace="xxx.xcworkspace"
17 |
18 | # ipa输出路径-- Appstore
19 | Appstore_Output_Path = "builds/appstore"
20 |
21 | # ipa输出路径-- 蒲公英
22 | Pgy_Output_Path = "builds/pgy"
23 |
24 | # ipa输出路径-- TestFlight
25 | TF_Output_Path = "./builds/testflight"
26 |
27 | # 蒲公英 的api_key
28 | Pgy_Api_Key = "xxx"
29 |
30 | # 蒲公英 的user_key
31 | Pgy_User_Key = "xxx"
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | # 就是上文说的stages
2 | stages:
3 | - build_debug # 这里就是一个stage,可以定义多个stage,这个stage就是下面的build_debug
4 |
5 | # 构建之前会执行的脚本,这里导入本地的环境变量
6 | before_script:
7 | - export ANDROID_HOME=/Users/lucas/Library/Android/sdk
8 | - export PATH=$PATH:${ANDROID_HOME}/tools
9 | - export PATH=$PATH:${ANDROID_HOME}/platform-tools
10 | - chmod +x ./gradlew
11 |
12 | # 声明一个名叫build_debug的构建任务
13 | build_debug:
14 | stage: build_debug
15 | # 构建中,执行一些脚本
16 | script:
17 | - ./gradlew --stacktrace assembleDevelopDebug
18 | # 指定监听哪一个分支或什么时候触发Pipeline
19 | only:
20 | - tags #这里tags的作用是当修改gitlab项目tag的时候会触发
21 | - test # 监听GitLab的这个分支
22 | # - master
23 | # 指定由哪一个runner运行
24 | tags:
25 | - dev # 这个dev是上文注册Runner时的tag,和注册时候tag一样的话就会用对应的Runner来执行任务
26 | # 指定成功后应附加到任务的文件和目录的列表
27 | artifacts:
28 | paths:
29 | - app/build/outputs/
30 |
31 | # 构建完成之后执行的脚本
32 | #after_script:
33 | # - 这里如果是要配合monkey的话,一般在这个地方执行monkey的脚本
34 |
35 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | CustomWidget
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | xmlns:android
18 |
19 | ^$
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | xmlns:.*
29 |
30 | ^$
31 |
32 |
33 | BY_NAME
34 |
35 |
36 |
37 |
38 |
39 |
40 | .*:id
41 |
42 | http://schemas.android.com/apk/res/android
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | .*:name
52 |
53 | http://schemas.android.com/apk/res/android
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | name
63 |
64 | ^$
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | style
74 |
75 | ^$
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | .*
85 |
86 | ^$
87 |
88 |
89 | BY_NAME
90 |
91 |
92 |
93 |
94 |
95 |
96 | .*
97 |
98 | http://schemas.android.com/apk/res/android
99 |
100 |
101 | ANDROID_ATTRIBUTE_ORDER
102 |
103 |
104 |
105 |
106 |
107 |
108 | .*
109 |
110 | .*
111 |
112 |
113 | BY_NAME
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
25 |
26 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator-enh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/modules/native/src/main/cpp/com_example_anative_NativeColorRenderer.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class com_example_anative_NativeColorRenderer */
4 |
5 | #ifndef _Included_com_example_anative_NativeColorRenderer
6 | #define _Included_com_example_anative_NativeColorRenderer
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | /*
11 | * Class: com_example_anative_NativeColorRenderer
12 | * Method: surfaceCreate
13 | * Signature: (I)V
14 | */
15 | JNIEXPORT void JNICALL Java_com_example_anative_NativeColorRenderer_surfaceCreate
16 | (JNIEnv *, jobject, jint);
17 |
18 | /*
19 | * Class: com_example_anative_NativeColorRenderer
20 | * Method: surfaceChange
21 | * Signature: (II)V
22 | */
23 | JNIEXPORT void JNICALL Java_com_example_anative_NativeColorRenderer_surfaceChange
24 | (JNIEnv *, jobject, jint, jint);
25 |
26 | /*
27 | * Class: com_example_anative_NativeColorRenderer
28 | * Method: drawFrame
29 | * Signature: (Ljavax/microedition/khronos/opengles/GL10;)V
30 | */
31 | JNIEXPORT void JNICALL Java_com_example_anative_NativeColorRenderer_drawFrame
32 | (JNIEnv *, jobject, jobject);
33 |
34 | #ifdef __cplusplus
35 | }
36 | #endif
37 | #endif
38 |
--------------------------------------------------------------------------------
/.idea/modules/native/src/main/cpp/com_example_anative_NativeColorRenderer_Companion.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class com_example_anative_NativeColorRenderer_Companion */
4 |
5 | #ifndef _Included_com_example_anative_NativeColorRenderer_Companion
6 | #define _Included_com_example_anative_NativeColorRenderer_Companion
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | #ifdef __cplusplus
11 | }
12 | #endif
13 | #endif
14 |
--------------------------------------------------------------------------------
/.idea/modules/native/src/main/cpp/com_example_anative_Test.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class com_example_anative_Test */
4 |
5 | #ifndef _Included_com_example_anative_Test
6 | #define _Included_com_example_anative_Test
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | /*
11 | * Class: com_example_anative_Test
12 | * Method: testaaa
13 | * Signature: (I)Ljava/lang/String;
14 | */
15 | JNIEXPORT jstring JNICALL Java_com_example_anative_Test_testaaa
16 | (JNIEnv *, jobject, jint);
17 |
18 | #ifdef __cplusplus
19 | }
20 | #endif
21 | #endif
22 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/OpenGL/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/OpenGL/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id 'kotlin-android-extensions'
5 | }
6 |
7 | android {
8 | compileSdkVersion 30
9 | buildToolsVersion "30.0.2"
10 |
11 | defaultConfig {
12 | minSdkVersion 21
13 | targetSdkVersion 30
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | consumerProguardFiles "consumer-rules.pro"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = '1.8'
33 | }
34 | }
35 |
36 | dependencies {
37 |
38 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
39 | implementation 'androidx.core:core-ktx:1.3.2'
40 | implementation 'androidx.appcompat:appcompat:1.2.0'
41 | implementation 'com.google.android.material:material:1.2.1'
42 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
43 | testImplementation 'junit:junit:4.+'
44 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
45 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
46 |
47 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
48 | }
--------------------------------------------------------------------------------
/OpenGL/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LucasDevelop/CustomView/8870a0ad65a15cd63281931aa1611e773fd84b36/OpenGL/consumer-rules.pro
--------------------------------------------------------------------------------
/OpenGL/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
--------------------------------------------------------------------------------
/OpenGL/src/androidTest/java/com/example/opengl/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.opengl
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.opengl.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/OpenGL/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/OpenGL/src/main/assets/sample/demo1.glsl:
--------------------------------------------------------------------------------
1 | #version 300 es
2 | uniform mat4 u_mvpMatraix;
3 | in vec4 a_position;
4 | in vec4 a_color;
5 | out vec4 v_color;
6 | void main() {
7 | v_color = a_color;
8 | gl_Position = u_mvpMatraix*a_position;
9 | }
10 |
--------------------------------------------------------------------------------
/OpenGL/src/main/java/com/example/opengl/GLMainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.opengl
2 |
3 | import android.content.Intent
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import com.example.opengl.sample.ColorGLActivity
7 | import kotlinx.android.synthetic.main.activity_g_l_main.*
8 |
9 | class GLMainActivity : AppCompatActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContentView(R.layout.activity_g_l_main)
13 | v_color.setOnClickListener{startActivity(Intent(this,ColorGLActivity::class.java))}
14 | }
15 | }
--------------------------------------------------------------------------------
/OpenGL/src/main/java/com/example/opengl/camera/CameraSize.kt:
--------------------------------------------------------------------------------
1 | package com.example.opengl.camera
2 |
3 | class CameraSize {
4 | var width = 0
5 | var height = 0
6 | }
--------------------------------------------------------------------------------
/OpenGL/src/main/java/com/example/opengl/camera/ICamera.kt:
--------------------------------------------------------------------------------
1 | package com.example.opengl.camera
2 |
3 | import android.hardware.Camera
4 | import android.view.SurfaceView
5 |
6 | interface ICamera {
7 | //摄像头-前后
8 | enum class FACING(val value: Int) {
9 | FRONT(Camera.CameraInfo.CAMERA_FACING_FRONT),
10 | BACK(Camera.CameraInfo.CAMERA_FACING_BACK)
11 | }
12 |
13 | //打开相机
14 | fun open(facing: FACING)
15 |
16 | //开始预览
17 | fun preview()
18 |
19 | //关闭相机
20 | fun close()
21 |
22 | //设置预览界面
23 | fun setPreviewTexture(surfaceView: SurfaceView)
24 |
25 | //获取预览大小
26 | fun getPreviewSize(): CameraSize
27 |
28 | //获取预览方向
29 | fun getRotation():Int
30 |
31 | }
--------------------------------------------------------------------------------
/OpenGL/src/main/java/com/example/opengl/sample/ColorGLActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.opengl.sample
2 |
3 | import android.opengl.GLES30
4 | import android.opengl.GLSurfaceView
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import com.example.opengl.R
8 | import javax.microedition.khronos.egl.EGLConfig
9 | import javax.microedition.khronos.opengles.GL10
10 |
11 | class ColorGLActivity : AppCompatActivity() {
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | GLSurfaceView(this).apply {
15 | setContentView(this)
16 | setEGLContextClientVersion(3)
17 | setRenderer(object :GLSurfaceView.Renderer{
18 | override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
19 | GLES30.glClearColor(255f,0f,0f,255f)
20 | }
21 |
22 | override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
23 | //设置窗口
24 | GLES30.glViewport(0,0,width,height)
25 | }
26 |
27 | override fun onDrawFrame(gl: GL10?) {
28 | //把颜色缓冲区设置为预设的颜色
29 | GLES30.glClear(GL10.GL_COLOR_BUFFER_BIT)
30 | }
31 | })
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/OpenGL/src/main/res/layout/activity_color_g_l.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/OpenGL/src/main/res/layout/activity_g_l_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/OpenGL/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/OpenGL/src/test/java/com/example/opengl/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.opengl
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 | }
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/allwins.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LucasDevelop/CustomView/8870a0ad65a15cd63281931aa1611e773fd84b36/app/allwins.jks
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | //apply plugin: 'com.luan.plugin'
5 |
6 | android {
7 | compileSdkVersion 29
8 | buildToolsVersion "29.0.2"
9 |
10 | defaultConfig {
11 | applicationId "com.cj.customwidget"
12 | minSdkVersion 21
13 | targetSdkVersion 29
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | debug {
22 | // minifyEnabled true
23 | debuggable true
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | release {
27 | minifyEnabled true
28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 | compileOptions {
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | }
35 | packagingOptions{
36 | exclude 'classes.dex'
37 | }
38 | kotlinOptions{
39 | jvmTarget = "1.8"
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation fileTree(dir: "libs", include: ["*.jar"])
45 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
46 | implementation 'androidx.core:core-ktx:1.3.2'
47 | implementation 'androidx.appcompat:appcompat:1.2.0'
48 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
49 | // implementation 'androidx.recyclerview:recyclerview:1.1.0'
50 | implementation 'com.google.code.gson:gson:2.8.6'
51 | implementation 'androidx.viewpager2:viewpager2:1.0.0'
52 | implementation project(path: ':OpenGL')
53 | implementation 'com.google.android.material:material:1.3.0'
54 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
55 | testImplementation 'junit:junit:4.12'
56 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
57 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
58 | implementation 'jp.co.cyberagent.android:gpuimage:2.0.4'
59 | implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
60 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
61 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.47'
62 | implementation 'com.blankj:utilcodex:1.30.0'
63 | implementation 'com.github.bumptech.glide:glide:4.12.0'
64 | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
65 | implementation 'com.squareup.picasso:picasso:2.71828'
66 | implementation 'androidx.fragment:fragment-ktx:1.2.4'
67 | }
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LucasDevelop/CustomView/8870a0ad65a15cd63281931aa1611e773fd84b36/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "com.cj.customwidget",
8 | "variantName": "release",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "properties": [],
14 | "versionCode": 1,
15 | "versionName": "1",
16 | "enabled": true,
17 | "outputFile": "app-release.apk"
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/cj/customwidget/Aes.java:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget;
2 |
3 | import android.graphics.Paint;
4 | import android.util.Base64;
5 |
6 | import java.security.AlgorithmParameters;
7 | import java.security.Key;
8 | import java.security.NoSuchAlgorithmException;
9 | import java.security.Security;
10 | import java.security.spec.InvalidParameterSpecException;
11 |
12 | import javax.crypto.Cipher;
13 | import javax.crypto.spec.IvParameterSpec;
14 | import javax.crypto.spec.SecretKeySpec;
15 |
16 | /**
17 | * @author luan
18 | * @package com.cj.customwidget
19 | * @date 2020/7/15
20 | * @des
21 | */
22 | class Aes {
23 | public static int measureHeight(Paint paint) {
24 | Paint.FontMetricsInt fm = paint.getFontMetricsInt();
25 | int textHeight = ~fm.top - (~fm.top - ~fm.ascent) - (fm.bottom - fm.descent);
26 | return textHeight;
27 | }
28 |
29 | private static boolean initialized = false;
30 | public static final String AES_TYPE = "AES/CBC/PKCS7Padding";
31 | public static final String AES_KEY = "AES";
32 | private Aes() {
33 | }
34 |
35 | /**
36 | * 加密
37 | *
38 | * @param key
39 | * @param data
40 | * @param iv
41 | * @return
42 | */
43 | public static String encrypt(String key, String data, String iv) throws Exception{
44 | byte[] dataByte = data.getBytes();
45 | byte[] result = encryptOrDecrypt(key, dataByte, iv, Cipher.ENCRYPT_MODE);
46 | return new String(Base64.encode(result,Base64.DEFAULT));
47 | }
48 |
49 | /**
50 | * AES解密
51 | *
52 | * @param data
53 | * @param key
54 | * @param iv
55 | * @return
56 | */
57 | public static String decrypt(String key, String data, String iv) throws Exception {
58 | byte[] dataByte = Base64.decode(data,Base64.DEFAULT);
59 | return new String(encryptOrDecrypt(key, dataByte, iv, Cipher.DECRYPT_MODE), "UTF-8");
60 | }
61 |
62 | /**
63 | * 加密或者解密
64 | *
65 | * @param key
66 | * @param iv
67 | * @param data
68 | * @param cipherMode
69 | * @return
70 | * @throws Exception
71 | */
72 | public static byte[] encryptOrDecrypt(String key, byte[] data, String iv, int cipherMode) throws Exception{
73 | initialize();
74 | byte[] keyByte = Base64.decode(key,Base64.DEFAULT);
75 | byte[] ivByte = Base64.decode(iv,Base64.DEFAULT);
76 | Cipher cipher = Cipher.getInstance(AES_TYPE);
77 | Key sKeySpec = new SecretKeySpec(keyByte, AES_KEY);
78 | // 初始化
79 | cipher.init(cipherMode, sKeySpec, generateIV(ivByte));
80 | return cipher.doFinal(data);
81 | }
82 |
83 | /**
84 | * 生成iv
85 | *
86 | * @param iv
87 | * @return
88 | * @throws NoSuchAlgorithmException
89 | * @throws InvalidParameterSpecException
90 | */
91 | public static AlgorithmParameters generateIV(byte[] iv) throws NoSuchAlgorithmException, InvalidParameterSpecException {
92 | AlgorithmParameters params = AlgorithmParameters.getInstance(AES_KEY);
93 | params.init(new IvParameterSpec(iv));
94 | return params;
95 | }
96 |
97 | public static void initialize() {
98 | if (initialized) {
99 | return;
100 | }
101 | initialized = true;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/cj/customwidget/Charset.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget
2 |
3 | /**
4 | *
5 | * @author : GuoXuan
6 | * @since : 2019/6/3
7 | */
8 | enum class Charset(val type: String) {
9 | GBK("GBK"),
10 | UTF8("UTF-8"),
11 | ISO_8859_1("ISO-8859-1"),
12 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/cj/customwidget/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget
2 |
3 | import android.util.Base64
4 | import android.util.Log
5 | import androidx.test.platform.app.InstrumentationRegistry
6 | import androidx.test.ext.junit.runners.AndroidJUnit4
7 |
8 | import org.junit.Test
9 | import org.junit.runner.RunWith
10 |
11 | import org.junit.Assert.*
12 | import kotlin.math.log
13 |
14 | /**
15 | * Instrumented test, which will execute on an Android device.
16 | *
17 | * See [testing documentation](http://d.android.com/tools/testing).
18 | */
19 | @RunWith(AndroidJUnit4::class)
20 | class ExampleInstrumentedTest {
21 | @Test
22 | fun useAppContext() {
23 | // Context of the app under test.
24 | // val appContext = InstrumentationRegistry.getInstrumentation().targetContext
25 | // assertEquals("com.cj.customwidget", appContext.packageName)
26 |
27 | val content="{\"traceId\":\"0b29d24449754098aef7474788b3e8fb\",\"signature\":\"DC681BF444FB06D5E7A90DC48C20\",\"signKey\":\"6iW0mC5lC8hX2jC5hQ0jT9cT8cB4mV\",\"page\":1,\"rows\":10,\"type\":0,\"timestamp\":\"1594781299769\",\"token\":\"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMDk2NjUwMTI4MzEwMjM1MTM4IiwiaWF0IjoxNTk0MTk5NTMyfQ.yFXA2tWiISPjOB3bKtOxyrik76-ZcSqmnz3IPDhC2bk\"}"
28 | // EncryptUtils.doAesEncrypt(content, "zoxgBvQQXOHk2wC8t/Irrw==", "XLeeyndP7zP5B4Mipnpk6Q==")
29 | val atoken="khReyvlxzdIV6cBsxQwM6BnYkSsuaFkCp6MlmGWIMNRga9q5j5CW6VC19JxDoDXmxuKiapVVH/jMUMlOOcue5qEXncEMXqE3NclMbdCrkhj5ylowZAMlJQJVlhUhQKeyUTxN3s/2apEByi21oId480313Z0YzjfBMq4aqIlb7Minl2spv1Y30zCpvwxfqHLLg8ssSJayDOPeynd9afiDd/yJt7W6vXwGkK2XpErVqPa+JIQfDtnruI/QuNcsgnwNNVOQ0kCJstvI9yDy48GvluM7fo7WBd/iOoTor0eYh394F8XjFUSE7BU7HbZQI9E0X4p7GjZQMDMFlI7scDL2DSNKMaZG3/r1afvnrl68AyePCufOHaSOfpcEQKorNP/QYMtTW3c0m7sQ6VYYHhoN47RJiPGF46l91J19r8DP47kR8IWqJWnGI+UMWIlqmhWf"
30 | val doAesDecrypt = EncryptUtils.doAesDecrypt(
31 | atoken,
32 | Base64.decode("zoxgBvQQXOHk2wC8t/Irrw==", Base64.DEFAULT),
33 | Base64.decode("XLeeyndP7zP5B4Mipnpk6Q==", Base64.DEFAULT)
34 | )
35 | System.out.println(doAesDecrypt)
36 | Log.e("lucas",doAesDecrypt)
37 | // System.out.println("XLeeyndP7zP5B4Mipnpk6Q==".length)
38 | // System.out.println(String(Base64.decode("MDM5MjAzOTIwMzIwMTgxMg==", Base64.DEFAULT)).length)
39 | // System.out.println("0000000000000000".length)
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/APP.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget
2 |
3 | import android.app.Application
4 | import java.util.ArrayList
5 |
6 | /**
7 | * @package com.cj.customwidget
8 | * @author luan
9 | * @date 2020/8/6
10 | * @des
11 | */
12 | class APP: Application() {
13 | override fun onCreate() {
14 | super.onCreate()
15 | javaClass.superclass?.getDeclaredField("mActivityLifecycleCallbacks").also {
16 | it?.isAccessible = true
17 | val get = it?.get(this) as ArrayList
18 | get.size
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/ASMActivity.java:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.os.Bundle;
6 | import android.util.Log;
7 |
8 | public class ASMActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | Log.d("asm","onCreate");
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_a_s_m);
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.util.Log
6 | import android.webkit.WebSettings
7 | import androidx.appcompat.app.AppCompatActivity
8 | import com.bumptech.glide.Glide
9 | import com.bumptech.glide.load.model.GlideUrl
10 | import com.bumptech.glide.load.model.LazyHeaders
11 | import com.cj.customwidget.page.CropMusicActivity
12 | import com.cj.customwidget.page.LooperRecyclerViewActivity
13 | import com.cj.customwidget.page.ShadowActivity
14 | import com.cj.customwidget.page.VideoCropSeekActivity
15 | import com.cj.customwidget.page.falling.FallingActivity
16 | import com.cj.customwidget.page.viewmodel.VM1Activity
17 | import com.cj.customwidget.util.CompatibleUtil.p
18 | import com.example.opengl.GLMainActivity
19 | import kotlinx.android.synthetic.main.activity_main.*
20 | import java.util.*
21 |
22 | class MainActivity : AppCompatActivity() {
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 | setContentView(R.layout.activity_main)
26 | v_video_crop_seek.setOnClickListener { startActivity(Intent(this, VideoCropSeekActivity::class.java)) }
27 | v_shadow_view.setOnClickListener { startActivity(Intent(this, ShadowActivity::class.java)) }
28 | v_upload_progress.setOnClickListener { startActivity(Intent(this, MainActivity2::class.java)) }
29 | v_viewmodel.setOnClickListener { startActivity(Intent(this, VM1Activity::class.java)) }
30 | v_crop_music.setOnClickListener { startActivity(Intent(this, CropMusicActivity::class.java)) }
31 | v_looper_list.setOnClickListener { startActivity(Intent(this, LooperRecyclerViewActivity::class.java)) }
32 | v_open_gl.setOnClickListener { startActivity(Intent(this, GLMainActivity::class.java)) }
33 | v_falling.setOnClickListener { startActivity(Intent(this, FallingActivity::class.java)) }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/MainActivity2.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget
2 |
3 | import android.animation.ValueAnimator
4 | import android.graphics.BitmapFactory
5 | import android.os.Bundle
6 | import android.view.animation.LinearInterpolator
7 | import androidx.appcompat.app.AppCompatActivity
8 | import com.cj.customwidget.ext.CircleCorp
9 | import com.cj.customwidget.ext.p
10 | import kotlinx.android.synthetic.main.activity_main2.*
11 | import kotlin.concurrent.thread
12 |
13 | class MainActivity2 : AppCompatActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_main2)
17 |
18 | v_upload_progress.setOnClickListener {
19 | thread {
20 | repeat(100) {
21 | v_upload_progress.currentProgress += 1
22 | if (it == 99)
23 | runOnUiThread { }
24 | Thread.sleep(10)
25 | }
26 | }
27 | }
28 | v_img.setImageBitmap(BitmapFactory.decodeResource(resources, R.mipmap.ic_bg_default).CircleCorp())
29 | v_succ.setOnClickListener { v_upload_progress.startSuccAnim() }
30 | v_fail.setOnClickListener { v_upload_progress.startFailAnim() }
31 | }
32 |
33 | fun startAnim() {
34 | val valueAnimator = ValueAnimator()
35 | valueAnimator.interpolator = LinearInterpolator()
36 | valueAnimator.setFloatValues(0f, 1f, 4f)
37 | valueAnimator.duration = 1000
38 | valueAnimator.addUpdateListener {
39 | it.animatedValue.p()
40 | }
41 | valueAnimator.start()
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/ScrollingActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget
2 |
3 | import android.os.Bundle
4 | import com.google.android.material.appbar.CollapsingToolbarLayout
5 | import com.google.android.material.floatingactionbutton.FloatingActionButton
6 | import com.google.android.material.snackbar.Snackbar
7 | import androidx.appcompat.app.AppCompatActivity
8 |
9 | class ScrollingActivity : AppCompatActivity() {
10 |
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | setContentView(R.layout.activity_scrolling)
14 | setSupportActionBar(findViewById(R.id.toolbar))
15 | findViewById(R.id.toolbar_layout).title = title
16 | findViewById(R.id.fab).setOnClickListener { view ->
17 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
18 | .setAction("Action", null).show()
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/ext/CharSequenceExt.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.ext
2 |
3 | import android.graphics.Color
4 | import android.graphics.Typeface
5 | import android.text.Spannable
6 | import android.text.SpannableStringBuilder
7 | import android.text.TextPaint
8 | import android.text.method.LinkMovementMethod
9 | import android.text.style.*
10 | import android.view.View
11 | import android.widget.TextView
12 | import androidx.core.text.toSpannable
13 |
14 | /**
15 | * @package com.cj.impl.ext
16 | * @author luan
17 | * @date 2020/8/24
18 | * @des
19 | */
20 |
21 | //清除样式
22 | fun CharSequence.clearStyle(): SpannableStringBuilder {
23 | val builder = SpannableStringBuilder(this)
24 | builder.clearSpans()
25 | return builder
26 | }
27 |
28 | //将样式绑定到view上
29 | fun SpannableStringBuilder.into(textView: TextView){
30 | textView.movementMethod = LinkMovementMethod.getInstance()
31 | textView.setText(this)
32 | }
33 |
34 | //设置颜色
35 | fun SpannableStringBuilder.setSpanColor(spanText: String, color: Int): SpannableStringBuilder {
36 | if (!contains(spanText)) return this
37 | this.setSpan(
38 | ForegroundColorSpan(color),
39 | indexOf(spanText),
40 | indexOf(spanText) + spanText.length,
41 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
42 | )
43 | return this
44 | }
45 |
46 | //设置事件
47 | fun SpannableStringBuilder.setClick(spanText: String, block:((text:String)->Unit)?=null): SpannableStringBuilder {
48 | if (!contains(spanText)) return this
49 | setSpan(object : ClickableSpan() {
50 | override fun onClick(widget: View) {
51 | block?.invoke(spanText)
52 | }
53 |
54 | override fun updateDrawState(ds: TextPaint) {
55 | // super.updateDrawState(ds)
56 | ds.isUnderlineText = false//去掉下划线
57 | }
58 | }, indexOf(spanText),
59 | indexOf(spanText) + spanText.length,
60 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
61 | return this
62 | }
63 |
64 | //设置大小
65 | fun SpannableStringBuilder.setSpanSize(spanText: String, sizeSp: Int): SpannableStringBuilder {
66 | if (!contains(spanText)) return this
67 | this.setSpan(
68 | AbsoluteSizeSpan(sizeSp, true),
69 | indexOf(spanText),
70 | indexOf(spanText) + spanText.length,
71 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
72 | )
73 | return this
74 | }
75 |
76 | /**
77 | * 设置样式
78 | * @receiver SpannableStringBuilder
79 | * @param spanText String
80 | * @param style Int {@link Typeface}
81 | * @return SpannableStringBuilder
82 | */
83 | fun SpannableStringBuilder.setSpanStyle(spanText: String, style: Int): SpannableStringBuilder {
84 | if (!contains(spanText)) return this
85 | this.setSpan(
86 | StyleSpan(style),
87 | indexOf(spanText),
88 | indexOf(spanText) + spanText.length,
89 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
90 | )
91 | return this
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/ext/Ext.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.ext
2 |
3 | import android.graphics.*
4 | import android.util.Log
5 | import kotlin.math.min
6 |
7 | /**
8 | * @package com.cj.customwidget
9 | * @author luan
10 | * @date 2020/8/11
11 | * @des
12 | */
13 |
14 | fun Any?.p() {
15 | Log.d("lucas", this?.toString() ?: "null")
16 | }
17 |
18 | //bitmap圆形裁剪
19 | fun Bitmap.CircleCorp(): Bitmap {
20 | val createBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
21 | Canvas(createBitmap).apply {
22 | val paint = Paint(Paint.ANTI_ALIAS_FLAG)
23 | val r = min(width, height) / 2f
24 | drawCircle(width / 2f, height / 2f, r, paint)
25 | paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
26 | drawBitmap(this@CircleCorp, 0f, 0f, paint)
27 | }
28 | return createBitmap
29 | }
30 |
31 | //圆角裁剪
32 | fun Bitmap.roundedCorp(
33 | newBitmap: Bitmap? = null,
34 | topLeft: Float = 0f,
35 | topRight: Float = 0f,
36 | bottomLeft: Float = 0f,
37 | bottomRight: Float = 0f
38 | ): Bitmap {
39 | var result: Bitmap? = newBitmap
40 | if (result == null) {
41 | result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
42 | }
43 | //圆角不能大于图片的大小
44 | if (width / 2 < topLeft || width / 2 < topRight || width / 2 < bottomLeft || width / 2 < bottomRight) {
45 | return this
46 | }
47 | if (height / 2 < topLeft || height / 2 < topRight || height / 2 < bottomLeft || height / 2 < bottomRight) {
48 | return this
49 | }
50 | Canvas(result!!).apply {
51 | val paint = Paint()
52 | paint.isAntiAlias = true
53 | val rectF = Rect(0, 0, width, height)
54 | //用贝塞尔曲线绘制圆角路径
55 | val path = Path()
56 | val startPoint = PointF()//起始点
57 | if (topLeft > 0f) {//左上角
58 | startPoint.set(0f, topLeft)
59 | path.moveTo(0f, topLeft)
60 | path.quadTo(0f, 0f, topLeft, 0f)
61 | } else {
62 | startPoint.set(0f, 0f)
63 | path.moveTo(0f, 0f)
64 | }
65 | if (topRight > 0) {//右上
66 | path.lineTo(width.toFloat() - topRight, 0f)
67 | path.quadTo(width.toFloat(), 0f, width.toFloat(), topRight)
68 | } else {
69 | path.lineTo(width.toFloat(), 0f)
70 | }
71 | if (bottomRight > 0) {//右下
72 | path.lineTo(width.toFloat(), height.toFloat() - bottomRight)
73 | path.quadTo(width.toFloat(), height.toFloat(), width.toFloat() - bottomRight, height.toFloat())
74 | } else {
75 | path.lineTo(width.toFloat(), height.toFloat())
76 | }
77 | if (bottomLeft > 0) {//左下
78 | path.lineTo(bottomLeft, height.toFloat())
79 | path.quadTo(0f, height.toFloat(), 0f, height.toFloat() - bottomLeft)
80 | } else {
81 | path.lineTo(0f, height.toFloat())
82 | }
83 | //结束
84 | path.lineTo(startPoint.x,startPoint.y)
85 | drawPath(path, paint)
86 | paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)//裁剪方式
87 | drawBitmap(this@roundedCorp, rectF, rectF, paint)
88 | }
89 | return result
90 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/helper/IAudioPlay.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.helper
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 |
6 | interface IAudioPlay {
7 | fun initAudio()
8 | fun startPlay(path:String)
9 | fun startPlay(context: Context, uri: Uri)
10 | fun stopPlay()
11 | fun resume()
12 | fun pause()
13 | fun release()
14 | fun isPlaying():Boolean
15 | fun seekTo(progress: Int)
16 | fun setOnPrepareComplete(block:(duration:Int)->Unit)
17 | fun setOnProgress(block:(progress:Int)->Unit)
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/helper/MediaPlayerAudioPlay.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.helper
2 |
3 | import android.content.Context
4 | import android.media.MediaPlayer
5 | import android.net.Uri
6 | import android.os.Handler
7 |
8 | class MediaPlayerAudioPlay : IAudioPlay {
9 | var mediaPlayer: MediaPlayer? = null
10 | var onProgressEvent: ((progress: Int) -> Unit)? = null
11 | var onPrepareCompleteEvent: ((duration: Int) -> Unit)? = null
12 | private val timeTask = TimeTask()
13 |
14 | override fun initAudio() {
15 | }
16 |
17 | override fun startPlay(path: String) {
18 | if (mediaPlayer == null) {
19 | mediaPlayer = MediaPlayer()
20 | }
21 | mediaPlayer?.also {
22 | it.reset()
23 | it.setDataSource(path)
24 | it.prepare()
25 | mediaPlayer?.setOnPreparedListener {
26 | onPrepareCompleteEvent?.invoke(it.duration)
27 | }
28 | it.start()
29 | timeTask.start()
30 | }
31 | }
32 |
33 | override fun startPlay(context: Context,uri: Uri) {
34 | if (mediaPlayer == null) {
35 | mediaPlayer = MediaPlayer()
36 | }
37 | mediaPlayer?.also {
38 | it.reset()
39 | it.setDataSource(context,uri)
40 | it.prepare()
41 | mediaPlayer?.setOnPreparedListener {
42 | onPrepareCompleteEvent?.invoke(it.duration)
43 | }
44 | it.start()
45 | timeTask.start()
46 | }
47 | }
48 |
49 | override fun stopPlay() {
50 | timeTask.stop()
51 | mediaPlayer?.stop()
52 | }
53 |
54 | override fun resume() {
55 | mediaPlayer?.start()
56 | timeTask.start()
57 | }
58 |
59 | override fun pause() {
60 | mediaPlayer?.pause()
61 | timeTask.stop()
62 | }
63 |
64 | override fun release() {
65 | timeTask.stop()
66 | mediaPlayer?.stop()
67 | mediaPlayer?.reset()
68 | mediaPlayer?.release()
69 | mediaPlayer = null
70 | }
71 |
72 | override fun isPlaying(): Boolean {
73 | return mediaPlayer?.isPlaying ?: false
74 | }
75 |
76 | override fun seekTo(progress: Int) {
77 | mediaPlayer?.seekTo(progress)
78 | }
79 |
80 | override fun setOnPrepareComplete(block: (duration: Int) -> Unit) {
81 | onPrepareCompleteEvent = block
82 | }
83 |
84 | override fun setOnProgress(block: (progress: Int) -> Unit) {
85 | onProgressEvent = block
86 |
87 | }
88 |
89 | inner class TimeTask : Runnable {
90 | private val handler = Handler()
91 | override fun run() {
92 | mediaPlayer?.also {
93 | onProgressEvent?.invoke(it.currentPosition)
94 | }
95 | handler.postDelayed(this,40)
96 | }
97 |
98 | fun start() {
99 | handler.removeCallbacks(this)
100 | handler.post(this)
101 | }
102 |
103 | fun stop() {
104 | handler.removeCallbacks(this)
105 | }
106 |
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/helper/MusicCropWarp.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.helper
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import androidx.lifecycle.Lifecycle
6 | import androidx.lifecycle.LifecycleEventObserver
7 | import androidx.lifecycle.LifecycleOwner
8 | import com.cj.customwidget.ext.p
9 | import com.cj.customwidget.widget.MusicCropView
10 |
11 | class MusicCropWarp(val owner: LifecycleOwner, val musicCropView: MusicCropView, val iAudioPlay: IAudioPlay) {
12 | var onStartProgressChange:((progress:Int)->Unit)?=null
13 | init {
14 | owner.lifecycle.addObserver(object :LifecycleEventObserver{
15 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
16 | when (event) {
17 | Lifecycle.Event.ON_CREATE -> {
18 | iAudioPlay.initAudio()
19 | }
20 | Lifecycle.Event.ON_RESUME -> {
21 | if (!iAudioPlay.isPlaying()){
22 | iAudioPlay.resume()
23 | }
24 | }
25 | Lifecycle.Event.ON_PAUSE -> {
26 | if (iAudioPlay.isPlaying()){
27 | iAudioPlay.pause()
28 | }
29 | }
30 | Lifecycle.Event.ON_DESTROY -> {
31 | iAudioPlay.release()
32 | }
33 | }
34 | }
35 | })
36 | iAudioPlay.setOnPrepareComplete {
37 | "duration:$it".p()
38 | musicCropView.setDuration(it)
39 | }
40 | iAudioPlay.setOnProgress {
41 | // "progress:$it".log()
42 | musicCropView.setProgress(it)
43 | }
44 | musicCropView.onStartProgressChange={
45 | "seek:$it".p()
46 | iAudioPlay.seekTo(it.toInt())
47 | onStartProgressChange?.invoke(it.toInt())
48 | }
49 | }
50 |
51 | fun startPlay(path:String){
52 | iAudioPlay.startPlay(path)
53 | }
54 |
55 | fun startPlay(context: Context,uri: Uri){
56 | iAudioPlay.startPlay(context, uri)
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/CropMusicActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page
2 |
3 | import android.net.Uri
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import com.cj.customwidget.R
8 | import com.cj.customwidget.helper.MediaPlayerAudioPlay
9 | import com.cj.customwidget.helper.MusicCropWarp
10 | import kotlinx.android.synthetic.main.activity_crop_music.*
11 | import java.io.File
12 |
13 | class CropMusicActivity : AppCompatActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_crop_music)
17 | val musicCropWarp = MusicCropWarp(this, v_crop_music, MediaPlayerAudioPlay())
18 | musicCropWarp.startPlay(this,
19 | Uri.parse(
20 | "android.resource://".plus(packageName).plus(File.separator).plus(R.raw.audio)
21 | ))
22 | musicCropWarp.onStartProgressChange = {
23 | v_time.text = "从 ${formatTime(it/1000)} 开始"
24 | }
25 | }
26 |
27 | private fun formatTime(time: Int): String {
28 | val m = if (time / 60 < 10) "0".plus(time / 60) else (time / 60).toString()
29 | val s = if (time % 60 < 10) "0".plus(time % 60) else (time % 60).toString()
30 | return m.plus(":").plus(s)
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/LooperRecyclerViewActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.os.Handler
6 | import android.widget.TextView
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import androidx.recyclerview.widget.PagerSnapHelper
10 | import androidx.recyclerview.widget.RecyclerView
11 | import com.chad.library.adapter.base.BaseQuickAdapter
12 | import com.chad.library.adapter.base.BaseViewHolder
13 | import com.cj.customwidget.R
14 | import com.cj.customwidget.ext.p
15 | import com.cj.customwidget.widget.LooperLinearLayoutManager
16 | import kotlinx.android.synthetic.main.activity_looper_recycler_view.*
17 |
18 | /**
19 | * File LooperRecyclerViewActivity.kt
20 | * Date 2020/12/10
21 | * Author lucas
22 | * Introduction 无限循环列表
23 | */
24 | class LooperRecyclerViewActivity : AppCompatActivity() {
25 |
26 | val handler = Handler()
27 |
28 | override fun onCreate(savedInstanceState: Bundle?) {
29 | super.onCreate(savedInstanceState)
30 | setContentView(R.layout.activity_looper_recycler_view)
31 | val looperLinearLayoutManager = LooperLinearLayoutManager(this,LinearLayoutManager.VERTICAL,false) as LinearLayoutManager
32 | // val looperLinearLayoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
33 | v_list.layoutManager = looperLinearLayoutManager
34 | val looperAdapter = LooperAdapter()
35 | looperAdapter.addData(List(4) { (it + 1).toString() }.toMutableList())
36 | v_list.adapter = looperAdapter
37 | looperAdapter.bindToRecyclerView(v_list)
38 | val pagerSnapHelper = PagerSnapHelper()
39 | pagerSnapHelper.attachToRecyclerView(v_list)
40 | looperAdapter.registerAdapterDataObserver(object :RecyclerView.AdapterDataObserver(){
41 | override fun onChanged() {
42 | super.onChanged()
43 | "onChanged".p()
44 | }
45 | })
46 | v_list.addOnScrollListener(object :RecyclerView.OnScrollListener(){
47 | override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
48 | super.onScrollStateChanged(recyclerView, newState)
49 | val linearLayoutManager = recyclerView.layoutManager as LinearLayoutManager
50 | val pFirst = linearLayoutManager.findFirstVisibleItemPosition()
51 | val pLast = linearLayoutManager.findLastVisibleItemPosition()
52 | if (RecyclerView.SCROLL_STATE_IDLE == newState){
53 | "pFirst:$pFirst,pLast:$pLast".p()
54 | }
55 | }
56 | })
57 | // v_list.scrollToPosition(2)
58 | // handler.postDelayed({
59 | // "notifyDataSetChanged".p()
60 | // looperAdapter.notifyDataSetChanged()
61 | // },3000)
62 | }
63 |
64 | class LooperAdapter : BaseQuickAdapter(R.layout.item_text) {
65 |
66 | val colors = arrayOf(Color.RED, Color.GREEN, Color.BLUE,Color.YELLOW)
67 |
68 | override fun convert(holder: BaseViewHolder, item: String) {
69 | "onBindViewHolder:${holder.adapterPosition}".p()
70 | (holder.itemView as TextView).apply {
71 | text = data[holder.adapterPosition]
72 | setTextColor(Color.WHITE)
73 | setBackgroundColor(colors[holder.adapterPosition % 4])
74 | }
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/ShadowActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page
2 |
3 | import android.graphics.Outline
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.view.View
7 | import android.view.ViewOutlineProvider
8 | import android.view.ViewTreeObserver
9 | import com.cj.customwidget.R
10 | import com.cj.customwidget.ext.p
11 | import kotlinx.android.synthetic.main.activity_shadow.*
12 |
13 | class ShadowActivity : AppCompatActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_shadow)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/VideoCropSeekActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page
2 |
3 | import android.net.Uri
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import com.cj.customwidget.R
8 | import com.cj.customwidget.ext.p
9 | import kotlinx.android.synthetic.main.activity_video_crop_seek.*
10 | import java.io.File
11 | import java.util.*
12 | import kotlin.concurrent.timerTask
13 |
14 | class VideoCropSeekActivity : AppCompatActivity() {
15 |
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContentView(R.layout.activity_video_crop_seek)
19 | v_seekbar.onSeekChange = {
20 | val selectTime = (v_seekbar.getRightSlideSecond() - v_seekbar.getLeftSlideSecond()).toFloat() / 1000
21 | v_time.text = "已选取$selectTime s"
22 | }
23 | v_seekbar.onInitComplete = { videoDuration ->
24 | //由于没有集成播放器,暂时只跑效果
25 | var progress = 0L
26 | Timer().schedule(timerTask {
27 | progress += 200
28 | if (progress>videoDuration)
29 | progress = 0L
30 | v_seekbar.setProgress(videoDuration, progress)
31 | },0L,200L)
32 | }
33 | v_seekbar.setVideoUri("android.resource://".plus(packageName).plus(File.separator).plus(R.raw.video))
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/falling/FallingActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.falling
2 |
3 | import android.animation.ValueAnimator
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import android.view.animation.Animation
8 | import android.view.animation.Transformation
9 | import com.cj.customwidget.R
10 | import com.cj.customwidget.ext.p
11 | import kotlinx.android.synthetic.main.activity_falling.*
12 |
13 | /**
14 | * File FallingActivity.kt
15 | * Date 12/25/20
16 | * Author lucas
17 | * Introduction 红包雨
18 | */
19 | class FallingActivity : AppCompatActivity() {
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_falling)
23 | v_falling.setAdapter(FallingAdapter().apply { setData(List(100){it}) })
24 | v_falling.startFalling()
25 | Handler().postDelayed({
26 | v_drawer.openDrawer(v_right)
27 | },3000)
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/falling/FallingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.falling
2 |
3 | import android.graphics.Path
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.view.animation.Animation
7 | import android.widget.ImageView
8 | import com.cj.customwidget.R
9 | import com.cj.customwidget.widget.FallingView
10 | import java.util.*
11 | import kotlin.collections.ArrayList
12 |
13 | class FallingAdapter : FallingView.IFallingAdapter(R.layout.item_redpack) {
14 | private val random = Random()
15 | private val animDuration = 6000L//物件动画时长
16 | private val count = 10//一屏显示物件的个数
17 |
18 | private val animInterval = ArrayList()
19 |
20 | fun setData(data: List) {
21 | datas = data
22 | }
23 |
24 | private fun createPath(parent: ViewGroup, position: Int, view: View): Path =
25 | Path().apply {
26 | view.measure(0, 0)
27 | val width = parent.width - view.measuredWidth
28 | val height = parent.height
29 | val swing = width / 3f//x轴摆动范围
30 | //限制动画区间使物件分布均匀
31 | if (animInterval.isEmpty()) {
32 | animInterval.add(Interval(view.measuredWidth / 2f, swing))
33 | animInterval.add(Interval(swing, swing * 2))
34 | animInterval.add(Interval(swing * 2, parent.width - view.measuredWidth / 2f))
35 | }
36 | // "animInterval:${animInterval.size}".p()
37 | val interval: Interval
38 | if (animInterval.size == 1) {
39 | interval = animInterval[0]
40 | } else {
41 | interval = animInterval[random.nextInt(animInterval.size)]
42 | }
43 | animInterval.remove(interval)
44 | val startPointX = random.nextInt(width).toFloat()
45 | moveTo(startPointX, -view.measuredHeight.toFloat())
46 |
47 | //控制点
48 | var point1X = random.nextInt(interval.getLength().toInt()) + interval.start
49 | val point1Y = random.nextInt(height / 2).toFloat()
50 |
51 | var point2X = random.nextInt(interval.getLength().toInt()) +interval.start
52 | val point2Y = random.nextInt(height / 2).toFloat() + height / 2
53 |
54 | var point3X = random.nextInt(interval.getLength().toInt()) + interval.start
55 |
56 | cubicTo(point1X, point1Y, point2X, point2Y, point3X, height.toFloat())
57 | }
58 |
59 |
60 | override fun convert(parent: ViewGroup, holder: FallingView.Holder) {
61 | if (holder.position%20==0){
62 | (holder.view as ImageView).setImageResource(R.mipmap.ic_readpack2)
63 | }else{
64 | (holder.view as ImageView).setImageResource(R.mipmap.ic_readpack)
65 | }
66 | holder.config.startTime = holder.position * (animDuration / count)
67 | holder.view.setOnClickListener {//点中红包回调
68 | // holder.view.clearAnimation()
69 | // holder.view.visibility = View.GONE
70 | }
71 | }
72 |
73 | override fun convertAnim(parent: ViewGroup, holder: FallingView.Holder): Animation {
74 | val path = createPath(parent, holder.position, holder.view)
75 | holder.config.path = path
76 | //旋转方向
77 | val rotation:Float
78 | if (random.nextInt(2)==0){
79 | rotation = 30f*random.nextFloat()
80 | }else{
81 | rotation = -30f*random.nextFloat()
82 | }
83 | val redPackAnim = RedPackAnim(path, rotation, holder.view)
84 | //动画时长-下落速度
85 | redPackAnim.duration = (animDuration*(0.6+random.nextInt(4)*0.1)).toLong()
86 | return redPackAnim
87 | }
88 |
89 | //区间
90 | class Interval(val start: Float, val end: Float) {
91 | fun getLength() = end - start
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/falling/RedPackAnim.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.falling
2 |
3 | import android.graphics.Path
4 | import android.graphics.PathMeasure
5 | import android.view.View
6 | import android.view.animation.Animation
7 | import android.view.animation.Transformation
8 | import java.util.*
9 |
10 | class RedPackAnim(val path: Path, val rotation: Float, val view: View) : Animation() {
11 | val pathMeasure = PathMeasure(path, false)
12 | val point = FloatArray(2)
13 | val tan = FloatArray(2)
14 |
15 | override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
16 | pathMeasure.getPosTan(pathMeasure.length * interpolatedTime, point, tan)
17 | view.x = point[0] - view.measuredWidth / 2
18 | view.y = point[1]
19 | view.rotation = rotation * interpolatedTime
20 | // "point:${point.toList()}".p()
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/opengl/CameraReviewActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.opengl
2 |
3 | import android.hardware.Camera
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.cj.customwidget.R
7 |
8 | /**
9 | * File CameraReviewActivity.kt
10 | * Date 12/16/20
11 | * Author lucas
12 | * Introduction 相机预览
13 | */
14 | class CameraReviewActivity : AppCompatActivity() {
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | setContentView(R.layout.activity_camera_review)
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/opengl/OpenGLActivity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.opengl
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import android.os.Handler
8 | import com.cj.customwidget.R
9 | import kotlinx.android.synthetic.main.activity_open_g_l.*
10 |
11 | /**
12 | * File OpenGLActivity.kt
13 | * Date 12/16/20
14 | * Author lucas
15 | * Introduction opengl演示
16 | */
17 | class OpenGLActivity : AppCompatActivity() {
18 |
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 |
23 | setContentView(R.layout.activity_open_g_l)
24 | v_camera_review.setOnClickListener { startActivity(Intent(this,CameraReviewActivity::class.java)) }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/VM1Activity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import android.content.Intent
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import androidx.activity.viewModels
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.ViewModelProvider
9 | import com.cj.customwidget.R
10 | import com.cj.customwidget.ext.p
11 | import com.cj.customwidget.page.viewmodelv2.shareViewModels
12 | import kotlinx.android.synthetic.main.activity_v_m1.*
13 |
14 | class VM1Activity : AppCompatActivity() {
15 |
16 | val vm: ViewModel1 by shareViewModels("lucas")
17 |
18 | //自定义ViewModelProvider.Factory
19 | val vm2: ViewModel2 by shareViewModels("lucas", object : ViewModelProvider.Factory {
20 | override fun create(modelClass: Class): T {
21 | return ViewModel2("哈哈") as T
22 | }
23 | })
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | // injectViewModel()
28 | "page:${this::class.java.simpleName},vm:${vm}".p()
29 | setContentView(R.layout.activity_v_m1)
30 | v_open.setOnClickListener { startActivity(Intent(this, VM2Activity::class.java)) }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/VM2Activity.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 | import androidx.activity.viewModels
6 | import com.cj.customwidget.R
7 | import com.cj.customwidget.ext.p
8 | import com.cj.customwidget.page.viewmodelv2.shareViewModels
9 |
10 | class VM2Activity : AppCompatActivity() {
11 |
12 | // @VMScope("lucas")
13 | // lateinit var vm:ViewModel1
14 | val vm:ViewModel1 by shareViewModels("lucas")
15 | // val vm by lazy { ViewModelProvider(this).get("lucas",ViewModel1::class.java) }
16 |
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | // injectViewModel()
20 | "page:${this::class.java.simpleName},vm:${vm}".p()
21 | setContentView(R.layout.activity_v_m2)
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/VMFragment.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.FrameLayout
8 | import androidx.fragment.app.Fragment
9 | import com.cj.customwidget.R
10 | import com.cj.customwidget.ext.p
11 | import com.cj.customwidget.page.viewmodelv2.shareViewModels
12 |
13 | class VMFragment: Fragment() {
14 |
15 | // @VMScope("lucas")
16 | // lateinit var vm: ViewModel1
17 | val vm:ViewModel1 by shareViewModels("lucas")
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | // injectViewModel()
22 | // vm.test()
23 | "page:${this::class.java.simpleName},vm:${vm}".p()
24 | }
25 |
26 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
27 | return View.inflate(requireContext(), R.layout.fragment_vm,null)
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/VMScope.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import androidx.lifecycle.ViewModelProvider
4 | import kotlin.reflect.KClass
5 |
6 | /**
7 | * @package com.cj.customwidget.page.viewmodel
8 | * @author luan
9 | * @date 2020/11/2
10 | * @des 用于标记vm的作用域
11 | */
12 | @Retention(AnnotationRetention.RUNTIME)
13 | @Target(AnnotationTarget.FIELD)
14 | annotation class VMScope(val scopeName:String)
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/ViewModel1.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.cj.customwidget.ext.p
5 |
6 | /**
7 | * @package com.cj.customwidget.page.viewmodel
8 | * @author luan
9 | * @date 2020/11/2
10 | * @des
11 | */
12 | class ViewModel1:ViewModel() {
13 |
14 | fun test(){
15 | "aaaaa".p()
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/ViewModel2.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.cj.customwidget.ext.p
5 |
6 | /**
7 | * @package com.cj.customwidget.page.viewmodel
8 | * @author luan
9 | * @date 2020/11/2
10 | * @des
11 | */
12 | class ViewModel2(val param:String):ViewModel() {
13 |
14 | fun test(){
15 | "aaaaa".p()
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodel/ViewModelExt.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodel
2 |
3 | import androidx.lifecycle.*
4 |
5 | /**
6 | * @package com.cj.customwidget.page.viewmodel
7 | * @author luan
8 | * @date 2020/11/2
9 | * @des
10 | */
11 |
12 | private val vMStores = HashMap()
13 |
14 | fun LifecycleOwner.injectViewModel() {
15 | //根据作用域创建商店
16 | this::class.java.declaredFields.forEach { field ->
17 | field.getAnnotation(VMScope::class.java)?.also { scope ->
18 | val element = scope.scopeName
19 | var store: VMStore
20 | if (vMStores.keys.contains(element)) {
21 | store = vMStores[element]!!
22 | } else {
23 | store = VMStore()
24 | vMStores[element] = store
25 | }
26 | store.register(this)
27 | val clazz = field.type as Class
28 | val vm = ViewModelProvider(store, ViewModelProvider.NewInstanceFactory()).get(clazz)
29 | field.set(this, vm)
30 | }
31 | }
32 | }
33 |
34 | class VMStore : ViewModelStoreOwner {
35 |
36 | private val bindTargets = ArrayList()
37 | private var vmStore: ViewModelStore? = null
38 |
39 | fun register(host: LifecycleOwner) {
40 | if (!bindTargets.contains(host)) {
41 | bindTargets.add(host)
42 | host.lifecycle.addObserver(object : LifecycleEventObserver {
43 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
44 | if (event == Lifecycle.Event.ON_DESTROY) {
45 | host.lifecycle.removeObserver(this)
46 | bindTargets.remove(host)
47 | if (bindTargets.isEmpty()) {//如果当前商店没有关联对象,则释放资源
48 | vMStores.entries.find { it.value == this@VMStore }?.also {
49 | vmStore?.clear()
50 | vMStores.remove(it.key)
51 | }
52 | }
53 | }
54 | }
55 | })
56 | }
57 | }
58 |
59 | override fun getViewModelStore(): ViewModelStore {
60 | if (vmStore == null)
61 | vmStore = ViewModelStore()
62 | return vmStore!!
63 | }
64 | }
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/page/viewmodelv2/ShareViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.page.viewmodelv2
2 |
3 | import androidx.lifecycle.*
4 |
5 | val vMStores = HashMap()
6 |
7 | inline fun LifecycleOwner.shareViewModels(
8 | scopeName:String,
9 | factory: ViewModelProvider.Factory?=null
10 | ):Lazy {
11 | val store: VMStore
12 | if (vMStores.keys.contains(scopeName)) {
13 | store = vMStores[scopeName]!!
14 | } else {
15 | store = VMStore()
16 | vMStores[scopeName] = store
17 | }
18 | store.register(this)
19 | return ViewModelLazy(VM::class,{store.viewModelStore},{factory?:ViewModelProvider.NewInstanceFactory()})
20 | }
21 |
22 | class VMStore : ViewModelStoreOwner {
23 |
24 | private val bindTargets = ArrayList()
25 | private var vmStore: ViewModelStore? = null
26 |
27 | fun register(host: LifecycleOwner) {
28 | if (!bindTargets.contains(host)) {
29 | bindTargets.add(host)
30 | host.lifecycle.addObserver(object : LifecycleEventObserver {
31 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
32 | if (event == Lifecycle.Event.ON_DESTROY) {
33 | host.lifecycle.removeObserver(this)
34 | bindTargets.remove(host)
35 | if (bindTargets.isEmpty()) {//如果当前商店没有关联对象,则释放资源
36 | vMStores.entries.find { it.value == this@VMStore }?.also {
37 | vmStore?.clear()
38 | vMStores.remove(it.key)
39 | }
40 | }
41 | }
42 | }
43 | })
44 | }
45 | }
46 |
47 | override fun getViewModelStore(): ViewModelStore {
48 | if (vmStore == null)
49 | vmStore = ViewModelStore()
50 | return vmStore!!
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/util/ActivityGestureHelper.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.util
2 |
3 | import android.content.Context
4 | import android.util.DisplayMetrics
5 | import android.view.MotionEvent
6 | import android.view.View
7 | import android.view.WindowManager
8 | import androidx.activity.ComponentActivity
9 | import androidx.lifecycle.Lifecycle
10 | import androidx.lifecycle.LifecycleEventObserver
11 | import androidx.lifecycle.LifecycleOwner
12 | import java.lang.ref.SoftReference
13 | import kotlin.math.abs
14 |
15 | /**
16 | * @package com.cj.customwidget.util
17 | * @author luan
18 | * @date 2020/10/23
19 | * @des 手势关闭多个界面
20 | */
21 | object ActivityGestureHelper {
22 |
23 | val pages = ArrayList>()
24 | val closeOffset = 0.5//关闭触发范围
25 | var screenWidth:Int = 0
26 |
27 | fun register(activity: ComponentActivity) {
28 | screenWidth = getScreenWidth(activity)
29 | registerEvent(activity)
30 | activity.lifecycle.addObserver(object : LifecycleEventObserver {
31 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
32 | if (event == Lifecycle.Event.ON_DESTROY) {
33 | pages.find { it.get() == activity }?.also {
34 | pages.remove(it)
35 | }
36 | }
37 | }
38 | })
39 | pages.add(SoftReference(activity))
40 | }
41 |
42 | private fun registerEvent(activity: ComponentActivity) {
43 | val decorView = activity.window.decorView
44 | decorView.setOnTouchListener(object : View.OnTouchListener {
45 | var lastX = 0f
46 | var lastY = 0f
47 |
48 | override fun onTouch(v: View, event: MotionEvent): Boolean {
49 | // "activity:$activity,event.x:${event.x},event.y:${event.y}".p()
50 | when (event.action) {
51 | MotionEvent.ACTION_DOWN -> {
52 | lastX = event.x
53 | lastY = event.y
54 | }
55 | MotionEvent.ACTION_MOVE -> {
56 | if (abs(lastX - event.x) > abs(lastY - event.y)) {//横向
57 | if (event.x - lastX > 0) {//右滑
58 | } else if (event.x - lastX < 0) {//左滑
59 | }
60 | pages.forEach {
61 | it.get()?.window?.decorView?.translationX = event.x - screenWidth
62 | }
63 | }
64 | lastX = event.x
65 | lastY = event.y
66 | }
67 | MotionEvent.ACTION_UP->{
68 |
69 | }
70 | }
71 | return true
72 | }
73 | })
74 | }
75 |
76 | fun getScreenWidth(context: Context): Int {
77 | val windowManager =
78 | context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
79 | if (windowManager != null) {
80 | val outMetrics = DisplayMetrics()
81 | windowManager.defaultDisplay.getMetrics(outMetrics)
82 | return outMetrics.widthPixels
83 | }
84 | return 0
85 | }
86 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/util/CharSequenceExt.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.util
2 |
3 | import android.text.Spannable
4 | import android.text.SpannableStringBuilder
5 | import android.text.TextPaint
6 | import android.text.style.*
7 | import android.view.View
8 | import com.cj.customwidget.ext.p
9 |
10 | /**
11 | * @package com.cj.impl.ext
12 | * @author luan
13 | * @date 2020/8/24
14 | * @des
15 | */
16 |
17 | //清除样式
18 | fun CharSequence.clearStyle(): SpannableStringBuilder {
19 | val builder = SpannableStringBuilder(this)
20 | builder.clearSpans()
21 | return builder
22 | }
23 |
24 | //设置颜色
25 | fun SpannableStringBuilder.setSpanColor(spanText: String, color: Int): SpannableStringBuilder {
26 | if (!contains(spanText)) return this
27 | this.setSpan(
28 | ForegroundColorSpan(color),
29 | indexOf(spanText),
30 | indexOf(spanText) + spanText.length,
31 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
32 | )
33 | return this
34 | }
35 |
36 | //设置事件
37 | fun SpannableStringBuilder.setClick(spanText: String, onTextClickListener: OnTextClickListener): SpannableStringBuilder {
38 | if (!contains(spanText)) return this
39 | setSpan(object : ClickableSpan() {
40 | override fun onClick(widget: View) {
41 | "widget".p()
42 | onTextClickListener.onTextClick(spanText)
43 | }
44 |
45 | override fun updateDrawState(ds: TextPaint) {
46 | // super.updateDrawState(ds)
47 | ds.isUnderlineText = false//去掉下划线
48 | }
49 | }, indexOf(spanText),
50 | indexOf(spanText) + spanText.length,
51 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
52 | return this
53 | }
54 |
55 | //设置大小
56 | fun SpannableStringBuilder.setSpanSize(spanText: String, sizeSp: Int): SpannableStringBuilder {
57 | if (!contains(spanText)) return this
58 | this.setSpan(
59 | AbsoluteSizeSpan(sizeSp, true),
60 | indexOf(spanText),
61 | indexOf(spanText) + spanText.length,
62 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
63 | )
64 | return this
65 | }
66 |
67 | /**
68 | * 设置样式
69 | * @receiver SpannableStringBuilder
70 | * @param spanText String
71 | * @param style Int {@link Typeface}
72 | * @return SpannableStringBuilder
73 | */
74 | fun SpannableStringBuilder.setSpanStyle(spanText: String, style: Int): SpannableStringBuilder {
75 | if (!contains(spanText)) return this
76 | this.setSpan(
77 | StyleSpan(style),
78 | indexOf(spanText),
79 | indexOf(spanText) + spanText.length,
80 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
81 | )
82 | return this
83 | }
84 |
85 | interface OnTextClickListener {
86 | fun onTextClick(text: String)
87 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/util/CompatibleUtil.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.util
2 |
3 | import android.os.Build
4 | import android.util.Log
5 | import android.view.ViewGroup
6 | import java.lang.reflect.Field
7 |
8 | /**
9 | * @package com.cj.impl.util
10 | * @author luan
11 | * @date 2020/8/5
12 | * @des android 版本兼容工具
13 | */
14 | object CompatibleUtil {
15 |
16 | fun print(viewGroup: ViewGroup){
17 | val javaClass = viewGroup.javaClass
18 | //找到对象的父类型ViewGroup
19 | var vp: Class? = javaClass.superclass
20 | while (vp != ViewGroup::class.java) {
21 | if (vp == null) break
22 | vp = javaClass.superclass
23 | }
24 | val findClass =
25 | vp?.declaredClasses?.find { it == Class.forName("android.view.ViewGroup\$ChildListForAccessibility") }
26 | findClass.p()
27 | "size:${findClass?.declaredFields?.size}".p()
28 | findClass?.declaredFields?.forEach {
29 | "file:$it".p()
30 | }
31 | }
32 |
33 | //兼容安卓P viewGroup内存泄漏
34 | fun compatLeakViewGroupForP(viewGroup: ViewGroup) {
35 | if (Build.VERSION.SDK_INT != Build.VERSION_CODES.P) return
36 | try {
37 | val javaClass = viewGroup.javaClass
38 | //找到对象的父类型ViewGroup
39 | var vp: Class? = javaClass.superclass
40 | while (vp != ViewGroup::class.java) {
41 | if (vp == null) break
42 | vp = javaClass.superclass
43 | }
44 | val viewClass = vp?.superclass
45 | val mContext: Field? = viewClass?.getDeclaredField("mContext")
46 | mContext?.isAccessible = true
47 | mContext?.set(viewGroup,null)
48 | } catch (e: Exception) {
49 | e.printStackTrace()
50 | }
51 | }
52 |
53 | fun Any?.p(){
54 | Log.d("lucas",this.toString())
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/util/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.util;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * @author luan
7 | * @package com.cj.customwidget.util
8 | * @date 2020/9/28
9 | * @des
10 | */
11 | public class LogUtils {
12 | public static void PrintD(String content, Object... args) {
13 | for (int i = 0; i < Thread.currentThread().getStackTrace().length; i++) {
14 | String realContent = getContent(content, i, args);
15 | Log.d("default", realContent);
16 | }
17 | }
18 |
19 | public static void PrintD(String tag, String content, Object... args) {
20 | Log.d(tag, getContent(content, 4, args));
21 | }
22 |
23 | public static String getNameFromTrace(StackTraceElement[] traceElements, int place) {
24 | StringBuilder taskName = new StringBuilder();
25 | if (traceElements != null && traceElements.length > place) {
26 | StackTraceElement traceElement = traceElements[place];
27 | taskName.append(traceElement.getMethodName());
28 | taskName.append("(").append(traceElement.getFileName()).append(":").append(traceElement.getLineNumber()).append(")");
29 | }
30 | return taskName.toString();
31 | }
32 |
33 | private static String getContent(String msg, int place, Object... args) {
34 | try {
35 | String sourceLinks = getNameFromTrace(Thread.currentThread().getStackTrace(), place);
36 | return sourceLinks + String.format(msg, args);
37 | } catch (Throwable throwable) {
38 | return msg;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cj/customwidget/util/MediaUtil.kt:
--------------------------------------------------------------------------------
1 | package com.cj.customwidget.util
2 |
3 | import android.content.Context
4 | import com.googlecode.mp4parser.authoring.Movie
5 | import com.googlecode.mp4parser.authoring.Track
6 | import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder
7 | import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator
8 | import com.googlecode.mp4parser.authoring.tracks.AppendTrack
9 | import java.io.File
10 | import java.io.RandomAccessFile
11 | import java.lang.Exception
12 |
13 | /**
14 | * @package com.cj.customwidget.util
15 | * @author luan
16 | * @date 2020/7/17
17 | * @des 媒体工具
18 | */
19 | object MediaUtil {
20 |
21 | //多视频合并
22 | fun videoMerge(
23 | videoList: List,
24 | outputFile: File
25 | ) {
26 | val startTime = System.currentTimeMillis()
27 | if (videoList.isEmpty() || videoList.size == 1) {
28 | return
29 | }
30 | try {
31 | val videoTrack = ArrayList