├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── starts
│ │ └── hencoderview
│ │ └── ExampleInstrumentedTest.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── kotlin
│ └── com
│ │ └── starts
│ │ └── hencoderview
│ │ ├── HenApplication.kt
│ │ ├── MainActivity.kt
│ │ ├── SpanUtils.java
│ │ ├── container
│ │ ├── BottomSheetLayout.kt
│ │ ├── CustomLayout.kt
│ │ └── MovieDetailContainer.kt
│ │ ├── remote
│ │ └── ApiService.kt
│ │ ├── scene
│ │ ├── ConstrainCompareScene.kt
│ │ ├── ConstrainTestScene.kt
│ │ ├── CustomLayoutScene.kt
│ │ ├── CustomUIScene.kt
│ │ ├── EllipsizedTextScene.kt
│ │ ├── FlipBoardScene.kt
│ │ ├── FloatRecyclerScene.kt
│ │ ├── FragmentTestScene.kt
│ │ ├── LinearLayoutScene.kt
│ │ ├── LinearTestScene.kt
│ │ ├── MainScene.kt
│ │ ├── ParticleScatteringScene.kt
│ │ ├── PathScene.kt
│ │ ├── RelateLayoutScene.kt
│ │ ├── ScaleAlphaAnimScene.kt
│ │ ├── ScaleImageScene.kt
│ │ ├── SportScene.kt
│ │ ├── TagLayoutScene.kt
│ │ ├── TransitionScene.kt
│ │ ├── ViewBindingScene.kt
│ │ ├── ViewGroupScene.kt
│ │ └── WaveViewScene.kt
│ │ ├── ui
│ │ ├── EmptyFragment.kt
│ │ ├── ListFragment.kt
│ │ ├── MemoryLeakActivity.kt
│ │ ├── ScalePageTransformer.java
│ │ └── ViewPagerBottomSheetBehavior.kt
│ │ ├── util
│ │ ├── Ext.kt
│ │ └── QMUIAlignMiddleImageSpan.java
│ │ └── view
│ │ ├── ColoredTextView.java
│ │ ├── CustomLayout.kt
│ │ ├── CustomUITest.kt
│ │ ├── EllipsizeIconTextView.kt
│ │ ├── FlipBoardView.kt
│ │ ├── ParticleScatteringView.kt
│ │ ├── PathLayoutManager.kt
│ │ ├── PathView.kt
│ │ ├── PreViewOutLineProvider.kt
│ │ ├── ScaleImageView.kt
│ │ ├── SportView.kt
│ │ ├── TagLayout.kt
│ │ ├── TestButton.kt
│ │ ├── TestConstraintLayout.kt
│ │ ├── TestEditText.kt
│ │ ├── TestFloatActionButton.kt
│ │ ├── TestImageView.kt
│ │ ├── TestLinearLayout.kt
│ │ ├── TestTextView.kt
│ │ ├── TestViewLayout.kt
│ │ ├── TransitionLayout.kt
│ │ └── WaveView.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── ic_launcher_background.xml
│ ├── ic_star.png
│ ├── info_background.xml
│ ├── layer_list_progress_drawable.xml
│ ├── playing_com_into.png
│ ├── shape_progressbar_bg.xml
│ ├── shape_progressbar_progress.xml
│ ├── shape_white.xml
│ └── singapore.jpg
│ ├── layout
│ ├── activity_main.xml
│ ├── activity_memory_lead.xml
│ ├── chin_demo.xml
│ ├── constrain_compare_layout.xml
│ ├── constrain_layout_demo.xml
│ ├── demo_main_holder.xml
│ ├── fragment_empty.xml
│ ├── fragment_list.xml
│ ├── google_contrain_demo.xml
│ ├── holder_bootom_sheet.xml
│ ├── layout_barrier_demo.xml
│ ├── lineralayout_ui_test.xml
│ ├── radio_demo.xml
│ ├── relatelayout_gone_test.xml
│ ├── relatelayout_ui_test.xml
│ ├── scene_constraint_layout_test.xml
│ ├── scene_ellipsized_text.xml
│ ├── scene_flip_bord_view.xml
│ ├── scene_linear_layout_test.xml
│ ├── scene_music_album_detail.xml
│ ├── scene_particle_scattering.xml
│ ├── scene_path.xml
│ ├── scene_scale_alpha.xml
│ ├── scene_scale_image_view.xml
│ ├── scene_sport_view.xml
│ ├── scene_tag_layout.xml
│ ├── scene_viewpager_test.xml
│ ├── scene_wave_view.xml
│ ├── view_douban_bottom_content.xml
│ ├── view_douban_toolbar.xml
│ └── view_pager.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
│ ├── avatar.webp
│ ├── ic_launcher.png
│ ├── ic_launcher_round.png
│ ├── img_egg.jpg
│ ├── personal_bg.webp
│ ├── red.png
│ ├── share.webp
│ ├── ui_scale_img.jpg
│ └── ui_scale_img_2.jpg
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimen.xml
│ ├── material_colors.xml
│ ├── strings.xml
│ └── styles.xml
├── 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/*
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | /projectFilesBackup
12 | /components/com/bana
13 |
14 | index.android.bundle
15 | *.log
16 | *.ipr
17 | *.iws
18 | apk/
19 | *.apk
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HenCoderView
2 | 工作过程中和平时,针对自定义View进行的实践和总结
3 |
4 | ### 双RecyclerView浮层联动
5 | [](https://imgtu.com/i/LtkGhF)
6 |
7 | 仿照豆瓣详情页写的滚动视图,不过豆瓣本身是做的两个View,一个用来拖拽,一个用来滑动,这里做了个升级,直接用一个ViewGroup来实现拖动和无缝滚动
8 | 动图中显示效果为两个RecyclerView实现,内部可以无限制使用类似多类型Adapter方面的库,任意组装数据,构造多类型复杂列表
9 |
10 | ### 思路:
11 | 1. 通过自定义ViewGroup来实现布局部分,onMeasure和onLayout中自定义RecyclerView的测绘和布局
12 | 2. 通过NestScrollParent接口处理滚动联动和fling的分发
13 | 3. 通过ViewDragHelper来处理拖拽
14 | 4. 通过事件分发机制解决事件冲突,处理重叠部分的拖拽冲突
15 |
16 | ### 轮播图的水波纹背景
17 | 
18 |
19 | 项目中实际应用的轮播图水波纹背景,将图片的保存使用LurCache单独存放,在实际开发过程中可以使用图片加载框架,
20 | 加载网络图片到内存,充当背景,也可以自己修改逻辑使用纯色暂时充当背景
21 | ### 思路:
22 | 1. 通过BitmapCache单独使用图片的存取功能
23 | 2. 通过在registerOnPageChangeCallback中使用下面的代码来判断是向左滑动中还是向右滑动中
24 |
25 |
26 | if(positionOffset != 0f && positionOffsetPixels != 0){
27 | if (position + positionOffset > sumPositionAndPositionOffset) {
28 | //right to left
29 |
30 | fromRightToLeft = true
31 | currentPosition = fixPosition(position)ø
32 | nextPosition = fixPosition(currentPosition + 1)
33 |
34 | } else {
35 | calculateDirection()
36 | //left to right
37 |
38 | fromRightToLeft = false
39 | currentPosition = fixPosition(position + 1)
40 | nextPosition = fixPosition(position)
41 |
42 | }
43 | sumPositionAndPositionOffset = position + positionOffset
44 | }else{
45 | //最后一刻,当前位置和下一个位置 ,则根据position来确定
46 | currentPosition = position
47 | nextPo
48 | sition = fixPosition(position + 1)
49 | }
50 |
51 |
52 | 3. 为避免越界使用fixPosition()方法控制越界情况
53 | 4. 在onDraw()方法中,使用创建的canvas和bitmap,对要操作的bitmap进行绘制,并使用PorterDuffXfermode(PorterDuff.Mode.SRC_IN) , (PorterDuff.Mode.DST_OVER)
54 | 注意:
55 | 1. 在使用过程中,先画的是目标图像,后画的源图像,使用过程中注意使用离屏缓冲,避免port的使用失效
56 | 2. port的原理是在融合出,通过算法处理颜色保留
57 |
58 | ### 网易云特效
59 | 
60 |
61 | ### 思路:
62 | 1. 封装Particle粒子类(小的圆点),主要是用来保存每个点的坐标,速度,角度,透明度,偏移量
63 | 2. 使用for循环,通过使用drawCircle,500个圆点根据x,y坐标进行绘制(测量绘制时间,毫秒数小于16,绘制很安全)
64 | 3. 通过ValueAnimator,控制选点从中心向周围飘散
65 | 4. 使用三角函数,计算x,y坐标随时间推移的下一个点
66 |
67 |
68 | ### 红板报特效
69 | 
70 | ### 思路:
71 | 1. 将 bitmap分为两部分看, 一部分为被camera翻转的部分,一部分为正常部分
72 | 2. 先调试,使用裁切api分成两部分
73 | 3. 然后使用camera绘制被翻转的一部分
74 | 4. 在固定绘制另外一部分
75 | 5. 最后使用动画控制旋转的角度,来达到效果
76 |
77 |
78 | ### Taglayout
79 | 
80 | ### 思路:
81 | 1. 整体上,是测绘完所有子View,得到子View的坐标点,然后保存起来,在onlayout中使用
82 | 2. 通过for循环,遍历所有子View,通过measureChildWithMargins方法,讲View的宽高,配合剩余的在纵横两个方向上的空间,得到子View的合适位置
83 | 3. 判断是否超过ViewGroup的最大空间
84 | 4. 在onLayout中使用
85 |
86 |
87 | ### ScaleImageView
88 | 
89 | ### 思路:
90 | 1. 在绘制方法中,使用drawBitmap 配合 translate scale 方法,对Bitmap进行操作
91 | 2. 在onTouchEvent中,使用GestureDetectorCompat对View收到的事件进行捕捉,包括,双击,按压,fling
92 | 3. 通过控制变量offsetX offsetY ,实现偏移操作,在onFling方法,使用postOnAnimation 推进动画
93 |
94 |
95 | ### 环形进度条
96 |
97 | 
98 |
99 | ### 思路:
100 | 1. 先画原,再画弧线(进度条完成)
101 | 2. 文字要根据实际需求,通过getTextBounds判断画文字的位置,因为文字不一样,可能导致中心点变化(基线的固定,和文字是否越过基线)
102 | 3. 通过drawText 绘制文字
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | //apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 32
7 |
8 | defaultConfig {
9 | applicationId "com.starts.hencoderview"
10 | minSdkVersion 22
11 | targetSdkVersion 32
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | sourceSets {
19 | main {
20 | java {
21 | java.srcDirs += 'src/main/kotlin'
22 | }
23 | }
24 | }
25 |
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 |
31 | kotlinOptions {
32 | jvmTarget = JavaVersion.VERSION_1_8.toString()
33 | }
34 |
35 | viewBinding{
36 | enabled = true
37 | }
38 |
39 | // dataBinding {
40 | // enabled true
41 | // }
42 |
43 | buildTypes {
44 | release {
45 | minifyEnabled false
46 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
47 | }
48 | }
49 | namespace 'com.starts.hencoderview'
50 |
51 | }
52 | ext{
53 | scene_version = '1.3.1'
54 | }
55 | dependencies {
56 | implementation fileTree(dir: 'libs', include: ['*.jar'])
57 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
58 | implementation 'androidx.appcompat:appcompat:1.4.2'
59 | implementation 'androidx.core:core-ktx:1.7.0'
60 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
61 | implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
62 | implementation 'com.google.android.material:material:1.5.0'
63 | testImplementation 'junit:junit:4.13.2'
64 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
65 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
66 | implementation 'com.jakewharton.timber:timber:5.0.1'
67 |
68 | implementation "com.github.bytedance.scene:scene:$scene_version"
69 | implementation "com.github.bytedance.scene:scene_navigation:$scene_version"
70 | implementation "com.github.bytedance.scene:scene_ui:$scene_version"
71 | implementation "com.github.bytedance.scene:scene_dialog:$scene_version"
72 | implementation "com.github.bytedance.scene:scene_shared_element_animation:$scene_version"
73 | implementation "com.github.bytedance.scene:scene_ktx:$scene_version"
74 |
75 | implementation 'com.github.zhpanvip:BannerViewPager:3.1.4'
76 |
77 | implementation 'com.squareup.retrofit2:retrofit:2.9.0'
78 | implementation 'com.squareup.okhttp3:okhttp:4.9.0'
79 |
80 |
81 | implementation 'com.github.bumptech.glide:glide:4.11.0'
82 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
83 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
84 | }
85 |
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/starts/hencoderview/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview
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.starts.hencoderview", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/HenApplication.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview
2 |
3 | import android.app.Application
4 | import timber.log.Timber
5 | import kotlin.properties.Delegates
6 |
7 | /**
8 |
9 | *文件描述:.
10 | *作者:Created by Administrator on 2020/11/10.
11 | *版本号:1.0
12 |
13 | */
14 | class HenApplication : Application() {
15 |
16 | companion object{
17 | var instance: HenApplication by Delegates.notNull()
18 | }
19 |
20 | override fun onCreate() {
21 | super.onCreate()
22 | instance = this
23 | Timber.plant(Timber.DebugTree())
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview
2 |
3 | import com.bytedance.scene.Scene
4 | import com.bytedance.scene.ui.SceneActivity
5 | import com.starts.hencoderview.scene.MainScene
6 |
7 | class MainActivity : SceneActivity() {
8 |
9 | override fun supportRestore(): Boolean {
10 | return false
11 | }
12 |
13 | override fun getHomeSceneClass(): Class {
14 | return MainScene::class.java
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/container/BottomSheetLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.container
2 |
3 | import android.content.Context
4 | import android.graphics.drawable.GradientDrawable
5 | import android.graphics.drawable.GradientDrawable.RECTANGLE
6 | import android.util.AttributeSet
7 | import android.view.View
8 | import androidx.core.content.ContextCompat
9 | import androidx.viewpager2.widget.ViewPager2
10 | import com.google.android.material.tabs.TabLayout
11 | import com.starts.hencoderview.R
12 |
13 | /**
14 | * 底部弹出式的 view 容器
15 | * @author lorizhao
16 | * @since 2022/3/22
17 | */
18 |
19 | class BottomSheetLayout : CustomLayout {
20 | companion object{
21 | const val TAG = "BottomSheetLayout"
22 | }
23 |
24 | /**
25 | * 拖拽的时候能展示的小白条
26 | * 不要用Gone ,引起重测,导致性能浪费,也会导致TabLayout位置升高
27 | */
28 | val dragTip = View(context).apply {
29 | layoutParams = LayoutParams(40.dp , 4.dp)
30 | this@BottomSheetLayout.addView(this)
31 | this.background = GradientDrawable().apply {
32 | setColor(ContextCompat.getColor(context, R.color.cardview_dark_background))
33 | shape = RECTANGLE
34 | val floatArray = floatArrayOf(20.dp * 1f,20.dp * 1f,20.dp * 1f,20.dp * 1f,20.dp * 1f,20.dp * 1f,20.dp * 1f,20.dp * 1f)
35 | cornerRadii = floatArray
36 | }
37 | }
38 |
39 | val bottomTabLayout = TabLayout(context).apply {
40 | layoutParams = LayoutParams(matchParent , 45.dp)
41 | this@BottomSheetLayout.addView(this)
42 | }
43 | val bottomViewPager = ViewPager2(context).apply {
44 | layoutParams = LayoutParams(matchParent , matchParent)
45 | overScrollMode = OVER_SCROLL_NEVER
46 | this@BottomSheetLayout.addView(this)
47 | }
48 |
49 | constructor(context: Context) : super(context)
50 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
51 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
52 | context,
53 | attrs,
54 | defStyleAttr
55 | )
56 |
57 | override fun onMeasureChildren(widthMeasureSpec: Int, heightMeasureSpec: Int):Dimension {
58 | dragTip.autoMeasure()
59 | bottomTabLayout.autoMeasure()
60 | bottomViewPager.measure(widthMeasureSpec,heightMeasureSpec - dragTip.measuredHeight - bottomTabLayout.measuredHeight)
61 | return Dimension(widthMeasureSpec,heightMeasureSpec)
62 | }
63 |
64 | /**
65 | * 布局TabLayout 和 ViewPager,还有拖拽的时候能展示的小白条
66 | */
67 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
68 | dragTip.layout(horizontalCenterX(dragTip) , 8.dp)
69 | bottomTabLayout.layout(0,dragTip.bottom + 20.dp)
70 | bottomViewPager.layout(0,dragTip.bottom + 20.dp)
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/container/CustomLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.container
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.util.AttributeSet
6 | import android.util.Log
7 | import android.view.HapticFeedbackConstants
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import androidx.core.view.*
11 |
12 | @Suppress("MemberVisibilityCanBePrivate")
13 | abstract class CustomLayout : ViewGroup {
14 |
15 |
16 | protected val Int.dp: Int get() = (this * resources.displayMetrics.density + 0.5f).toInt()
17 |
18 | constructor(context: Context?) : super(context)
19 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
20 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
21 | context,
22 | attrs,
23 | defStyleAttr
24 | )
25 |
26 | class LayoutParams(width: Int, height: Int) : MarginLayoutParams(width, height)
27 |
28 | override fun generateDefaultLayoutParams(): LayoutParams {
29 | return LayoutParams(matchParent, wrapContent)
30 | }
31 |
32 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
33 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
34 | val dimension = this.onMeasureChildren(widthMeasureSpec, heightMeasureSpec)
35 | setMeasuredDimension(dimension.width, dimension.height)
36 | }
37 |
38 | protected data class Dimension(val width: Int, val height: Int)
39 |
40 | protected abstract fun onMeasureChildren(
41 | widthMeasureSpec: Int,
42 | heightMeasureSpec: Int
43 | ): Dimension
44 |
45 | protected fun View.autoMeasure() {
46 | if (isGone) return
47 | measure(
48 | this.defaultWidthMeasureSpec(parentView = this@CustomLayout),
49 | this.defaultHeightMeasureSpec(parentView = this@CustomLayout)
50 | )
51 | }
52 |
53 | protected fun View.forEachAutoMeasure() {
54 | forEach { it.autoMeasure() }
55 | }
56 |
57 | protected fun View.layout(x: Int, y: Int, fromRight: Boolean = false) {
58 | if (isGone) return
59 | if (!fromRight) {
60 | layout(x, y, x + measuredWidth, y + measuredHeight)
61 | } else {
62 | layout(this@CustomLayout.measuredWidth - x - measuredWidth, y)
63 | }
64 | }
65 |
66 | protected val View.measuredWidthWithMargins get() = (measuredWidth + marginLeft + marginRight)
67 | protected val View.measuredHeightWithMargins get() = (measuredHeight + marginTop + marginBottom)
68 |
69 | protected fun View.defaultWidthMeasureSpec(parentView: ViewGroup): Int {
70 | return when (layoutParams.width) {
71 | ViewGroup.LayoutParams.MATCH_PARENT -> parentView.measuredWidth.toExactlyMeasureSpec()
72 | ViewGroup.LayoutParams.WRAP_CONTENT -> Int.MAX_VALUE.toAtMostMeasureSpec()
73 | 0 -> throw IllegalAccessException("Need special treatment for $this")
74 | else -> layoutParams.width.toExactlyMeasureSpec()
75 | }
76 | }
77 |
78 | protected fun View.defaultHeightMeasureSpec(parentView: ViewGroup): Int {
79 | return when (layoutParams.height) {
80 | ViewGroup.LayoutParams.MATCH_PARENT -> parentView.measuredHeight.toExactlyMeasureSpec()
81 | ViewGroup.LayoutParams.WRAP_CONTENT -> Int.MAX_VALUE.toAtMostMeasureSpec()
82 | 0 -> throw IllegalAccessException("Need special treatment for $this")
83 | else -> layoutParams.height.toExactlyMeasureSpec()
84 | }
85 | }
86 |
87 | protected fun Int.toExactlyMeasureSpec(): Int {
88 | return MeasureSpec.makeMeasureSpec(this, MeasureSpec.EXACTLY)
89 | }
90 |
91 | protected fun Int.toAtMostMeasureSpec(): Int {
92 | return MeasureSpec.makeMeasureSpec(this, MeasureSpec.AT_MOST)
93 | }
94 |
95 | fun addView(child: View, width: Int, height: Int, apply: (LayoutParams.() -> Unit)) {
96 | val params = generateDefaultLayoutParams()
97 | params.width = width
98 | params.height = height
99 | apply(params)
100 | super.addView(child, params)
101 | }
102 |
103 | fun View.overScrollNever() {
104 | overScrollMode = View.OVER_SCROLL_NEVER
105 | }
106 |
107 | fun ViewGroup.horizontalCenterX(child: View): Int {
108 | return (measuredWidth - child.measuredWidth) / 2
109 | }
110 |
111 | fun ViewGroup.verticalCenterTop(child: View): Int {
112 | return (measuredHeight - child.measuredHeight) / 2
113 | }
114 | }
115 |
116 | const val matchParent = ViewGroup.LayoutParams.MATCH_PARENT
117 | const val wrapContent = ViewGroup.LayoutParams.WRAP_CONTENT
118 |
119 | fun View.transparentBackground() {
120 | setBackgroundColor(Color.TRANSPARENT)
121 | }
122 |
123 | val View.parentView get() = parent as ViewGroup
124 |
125 | fun View?.performHapticFeedbackSafely() {
126 | try {
127 | this?.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
128 | } catch (t: Throwable) {
129 | Log.e("CustomLayout", t.toString())
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/remote/ApiService.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.remote
2 |
3 | import retrofit2.Call
4 | import retrofit2.http.GET
5 | import retrofit2.http.Path
6 |
7 | /**
8 |
9 | *文件描述:.
10 | *作者:Created by LostStars on 2020/11/17.
11 | *版本号:1.0
12 |
13 | */
14 | interface ApiService {
15 | @GET ("users/{user}/repos")
16 | fun requestUserInfo(@Path("user") user:String):Call
17 |
18 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ConstrainCompareScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.view.ViewTreeObserver
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.databinding.ConstrainCompareLayoutBinding
10 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
11 | import timber.log.Timber
12 |
13 | class ConstrainCompareScene : AppCompatScene() {
14 | var firstDate = 0L
15 | var secondDate = 0L
16 | val preDraw = ViewTreeObserver.OnPreDrawListener {
17 | secondDate = System.currentTimeMillis()
18 | val result = secondDate - firstDate
19 | firstDate = secondDate
20 | Timber.tag("preDraw").d("result = $result")
21 | true
22 | }
23 | lateinit var binding: ConstrainCompareLayoutBinding
24 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
25 | firstDate = System.currentTimeMillis()
26 | binding = ConstrainCompareLayoutBinding.inflate(p0)
27 | binding.root.viewTreeObserver.addOnPreDrawListener(preDraw)
28 | return binding.root
29 | }
30 |
31 | override fun onActivityCreated(savedInstanceState: Bundle?) {
32 | super.onActivityCreated(savedInstanceState)
33 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
34 | }
35 | override fun onDestroyView() {
36 | binding.root.viewTreeObserver.removeOnPreDrawListener(preDraw)
37 | super.onDestroyView()
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ConstrainTestScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Build
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import android.os.Looper
8 | import android.view.FrameMetrics
9 | import android.view.LayoutInflater
10 | import android.view.View
11 | import android.view.ViewGroup
12 | import android.widget.Toast
13 | import androidx.annotation.RequiresApi
14 | import com.bytedance.scene.ui.template.AppCompatScene
15 | import com.starts.hencoderview.databinding.SceneConstraintLayoutTestBinding
16 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
17 | import timber.log.Timber
18 |
19 | class ConstrainTestScene : AppCompatScene() {
20 | lateinit var binding: SceneConstraintLayoutTestBinding
21 | @SuppressLint("SetTextI18n")
22 | @RequiresApi(Build.VERSION_CODES.N)
23 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
24 | binding = SceneConstraintLayoutTestBinding.inflate(p0)
25 | return binding.root
26 | }
27 |
28 | @SuppressLint("SetTextI18n")
29 | override fun onActivityCreated(savedInstanceState: Bundle?) {
30 | super.onActivityCreated(savedInstanceState)
31 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
32 | binding.tvShort.postDelayed({binding.tvShort.text = "我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,"},4000)
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/CustomLayoutScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.bytedance.scene.ui.template.AppCompatScene
8 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
9 | import com.starts.hencoderview.view.TestViewLayout
10 | import com.starts.hencoderview.view.matchParent
11 | import com.starts.hencoderview.view.wrapContent
12 |
13 | class CustomLayoutScene : AppCompatScene() {
14 | private lateinit var rootView: TestViewLayout
15 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
16 | rootView = TestViewLayout(p1.context)
17 | return rootView
18 | }
19 |
20 | override fun onActivityCreated(savedInstanceState: Bundle?) {
21 | super.onActivityCreated(savedInstanceState)
22 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
23 | setToolbarVisible(true)
24 | rootView.leftTv.postDelayed({
25 | rootView.leftTv.text =
26 | "我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字"
27 | }, 4000)
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/CustomUIScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.view.ViewTreeObserver
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
10 | import com.starts.hencoderview.view.CustomUITest
11 | import timber.log.Timber
12 |
13 | class CustomUIScene : AppCompatScene() {
14 | lateinit var root: CustomUITest
15 | var firstDate = 0L
16 | var secondDate = 0L
17 | val preDraw = ViewTreeObserver.OnPreDrawListener {
18 | secondDate = System.currentTimeMillis()
19 | val result = secondDate - firstDate
20 | firstDate = secondDate
21 | Timber.tag("preDraw").d("result = $result")
22 | true
23 | }
24 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
25 | firstDate = System.currentTimeMillis()
26 | root = CustomUITest(context = p1.context)
27 | root.viewTreeObserver.addOnPreDrawListener(preDraw)
28 | return root
29 | }
30 |
31 | override fun onActivityCreated(savedInstanceState: Bundle?) {
32 | super.onActivityCreated(savedInstanceState)
33 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
34 | }
35 |
36 |
37 | override fun onDestroyView() {
38 | root.viewTreeObserver.removeOnPreDrawListener(preDraw)
39 | super.onDestroyView()
40 |
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/EllipsizedTextScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.R
10 | import com.starts.hencoderview.databinding.SceneEllipsizedTextBinding
11 | import com.starts.hencoderview.util.sp
12 |
13 |
14 | /**
15 |
16 | *文件描述:.实现 文字...文字,文字...icon,文字...文字+icon的样式
17 | * 当设置了isDisplay = true ,在Ellipsize = end 并且 多行的情况下,图片才会显示,并不会直接追加文本末尾
18 | * keepText不管在当前TextView是在缩略状态,keepText都会被保留
19 | * 非缩略状态下,会自动通过SpannableStringBuilder方案实现图文混排
20 | *
21 | * 作者:Created by Lorizhao on 2021/3/4.
22 | * 版本号:1.0
23 | *
24 | * 使用时,请使用 setTextWithKeepText 方法设置 保留图片和 保留文字
25 | * 单独修改文字的颜色和字体大小,并不会触发重绘,请提前修改,最后通过 setTextWithKeepText 方法触发
26 | * 可以单独设置保留文字的 大小,颜色,如果需要修改对齐方式,那就重写在OnDraw()方法中的绘制方法
27 | *
28 | * 在onMeasure()方法中判断文字是否会超出,并设置needOverDraw,触发在onDraw()方法中追加绘制图片和文字
29 | * 在onMeasure(),onDraw()方法中调用setText()方法后会再次出发onMeasure(),onDraw(),所以要注意结束条件,自测onMeasure(),onDraw()方法的调用次数
30 | * 作者:Created by lorizhao on 2021/2/8.
31 | * 版本号:1.0
32 | */
33 | class EllipsizedTextScene : AppCompatScene() {
34 | lateinit var binding: SceneEllipsizedTextBinding
35 | override fun onCreateContentView(
36 | inflater: LayoutInflater,
37 | container: ViewGroup,
38 | savedInstanceState: Bundle?
39 | ): View {
40 | binding = SceneEllipsizedTextBinding.inflate(inflater)
41 | return binding.root
42 | }
43 |
44 | override fun onActivityCreated(savedInstanceState: Bundle?) {
45 | super.onActivityCreated(savedInstanceState)
46 | val text =
47 | "电影主题曲《my heart will go on 》,电影主题曲《my heart will go ,"
48 | binding.tv1.text = text
49 | binding.tv1.setKeepTextSize(sp(12))
50 | binding.tv1.setKeepTextColor( Color.GREEN)
51 | binding.tv1.setTextWithKeepText(text , "已经播放30%",false, R.drawable.playing_com_into, -1 , -1 )
52 | binding.tv1.text = text
53 | }
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/FlipBoardScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.animation.Animator
4 | import android.animation.AnimatorSet
5 | import android.animation.ObjectAnimator
6 | import android.os.Bundle
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.view.animation.DecelerateInterpolator
11 | import android.view.animation.LinearInterpolator
12 | import android.widget.SeekBar
13 | import com.bytedance.scene.ui.template.AppCompatScene
14 | import com.starts.hencoderview.R
15 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
16 | import com.starts.hencoderview.view.FlipBoardView
17 |
18 |
19 | /**
20 |
21 | *文件描述:.
22 | *作者:Created by Administrator on 2020/4/30.
23 | *版本号:1.0
24 |
25 | */
26 | class FlipBoardScene : AppCompatScene() {
27 | private lateinit var flipView:FlipBoardView
28 | lateinit var seekRotate: SeekBar
29 | lateinit var seekCamera:SeekBar
30 | override fun onCreateContentView(
31 | inflater: LayoutInflater,
32 | container: ViewGroup,
33 | savedInstanceState: Bundle?
34 | ): View {
35 | val view = inflater.inflate(R.layout.scene_flip_bord_view, null, false)
36 | flipView = view.findViewById(R.id.flipView)
37 | seekRotate = view.findViewById(R.id.seekRotate)
38 | seekCamera = view.findViewById(R.id.seekCamera)
39 | return view
40 | }
41 |
42 | override fun onActivityCreated(savedInstanceState: Bundle?) {
43 | super.onActivityCreated(savedInstanceState)
44 | setToolbarVisible(true)
45 | setStatusBarVisible(true)
46 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
47 |
48 | val cameraAnimator = ObjectAnimator.ofFloat(flipView, "cameraAngle", 0f, 45f)
49 | cameraAnimator.interpolator = LinearInterpolator()
50 | cameraAnimator.duration = 300
51 |
52 | val animator = ObjectAnimator.ofFloat(flipView, "angle", 0f, 360f)
53 | animator.interpolator = DecelerateInterpolator()
54 | animator.duration = 1000
55 |
56 | val animatorSet = AnimatorSet()
57 | animatorSet.playSequentially(cameraAnimator, animator)
58 | animatorSet.startDelay = 500
59 | animatorSet.start()
60 | animatorSet.addListener(object : Animator.AnimatorListener {
61 | override fun onAnimationStart(animation: Animator) {
62 | }
63 |
64 | override fun onAnimationEnd(animation: Animator) {
65 | }
66 |
67 | override fun onAnimationCancel(animation: Animator) {
68 | }
69 |
70 | override fun onAnimationRepeat(animation: Animator) {
71 | }
72 |
73 | })
74 |
75 | seekRotate.setOnSeekBarChangeListener(object :SeekBar.OnSeekBarChangeListener{
76 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
77 | flipView.angle = (progress/100f) * 360
78 | }
79 |
80 | override fun onStartTrackingTouch(seekBar: SeekBar?) {
81 | }
82 |
83 | override fun onStopTrackingTouch(seekBar: SeekBar?) {
84 | }
85 |
86 | })
87 |
88 | seekCamera.setOnSeekBarChangeListener(object :SeekBar.OnSeekBarChangeListener{
89 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
90 | flipView.cameraAngle = (progress/100f) * 90
91 | }
92 |
93 | override fun onStartTrackingTouch(seekBar: SeekBar?) {
94 | }
95 |
96 | override fun onStopTrackingTouch(seekBar: SeekBar?) {
97 | }
98 |
99 | })
100 | }
101 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/FloatRecyclerScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.core.view.isGone
8 | import androidx.fragment.app.Fragment
9 | import androidx.recyclerview.widget.LinearLayoutManager
10 | import androidx.recyclerview.widget.RecyclerView
11 | import androidx.viewpager2.adapter.FragmentStateAdapter
12 | import com.bytedance.scene.ktx.requireFragmentActivity
13 | import com.bytedance.scene.ui.template.AppCompatScene
14 | import com.starts.hencoderview.R
15 | import com.starts.hencoderview.container.MovieDetailContainer
16 | import com.starts.hencoderview.ui.ListFragment
17 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
18 | import com.starts.hencoderview.view.ColoredTextView
19 |
20 | /**
21 |
22 | *文件描述:.
23 | *作者:Created by lorizhao on 2022/3/1.
24 | *版本号:1.0
25 |
26 | */
27 | class FloatRecyclerScene : AppCompatScene() {
28 | lateinit var binding: MovieDetailContainer
29 | override fun onCreateContentView(
30 | inflater: LayoutInflater,
31 | container: ViewGroup,
32 | savedInstanceState: Bundle?
33 | ): View {
34 | binding = MovieDetailContainer(context = container.context)
35 | return binding
36 | }
37 |
38 | override fun onActivityCreated(savedInstanceState: Bundle?) {
39 | super.onActivityCreated(savedInstanceState)
40 | setToolbarVisible(false)
41 | setStatusBarVisible(false)
42 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
43 | val data = arrayListOf("第一个","2000","346","50000","2000","346","50000","2000","346","50000","2000","346","50000","2000","346","50000","2000","346","50000","最后3个","最后2个","最后一个")
44 | val adapter = InnerAdapter(data)
45 | binding.topRecyclerView .adapter = adapter
46 | binding.topRecyclerView.layoutManager = LinearLayoutManager(requireSceneContext())
47 | // val titles = arrayListOf("影评","讨论")
48 | val fragments = arrayListOf(ListFragment())
49 | val viewPagerAdapter = object :FragmentStateAdapter(requireFragmentActivity()){
50 | override fun getItemCount(): Int {
51 | return fragments.size
52 | }
53 |
54 | override fun createFragment(position: Int): Fragment {
55 | return fragments[position]
56 | }
57 | }
58 | binding.bottomSheetLayout.bottomTabLayout.isGone = true
59 | binding.bottomSheetLayout.bottomViewPager.adapter = viewPagerAdapter
60 | // TabLayoutMediator(binding.bottomSheetLayout.bottomTabLayout,binding.bottomSheetLayout.bottomViewPager){tab,positon->
61 | // tab.text = titles[positon]
62 | // }.attach()
63 | }
64 | }
65 |
66 | class InnerAdapter(private val data:List) : RecyclerView.Adapter(){
67 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InnerHolder {
68 | return InnerHolder(LayoutInflater.from(parent.context).inflate(R.layout.holder_bootom_sheet,parent,false))
69 | }
70 |
71 | override fun onBindViewHolder(holder: InnerHolder, position: Int) {
72 | holder.tvText.text = data[position]
73 | }
74 |
75 | override fun getItemCount(): Int {
76 | return data.size
77 | }
78 |
79 |
80 | }
81 |
82 | class InnerHolder(item:View) : RecyclerView.ViewHolder(item){
83 | val tvText: ColoredTextView = item.findViewById(R.id.tvText)
84 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/FragmentTestScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.viewpager2.adapter.FragmentStateAdapter
9 | import com.bytedance.scene.ktx.requireFragmentActivity
10 | import com.bytedance.scene.ui.template.AppCompatScene
11 | import com.starts.hencoderview.databinding.SceneViewpagerTestBinding
12 | import com.starts.hencoderview.ui.EmptyFragment
13 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
14 |
15 | /**
16 |
17 | *文件描述:.
18 | *作者:Created by Administrator on 2020/7/16.
19 | *版本号:1.0
20 |
21 | */
22 | class FragmentTestScene : AppCompatScene() {
23 |
24 |
25 | var f1: Fragment? = null
26 | var f2: Fragment? = null
27 |
28 | lateinit var binding: SceneViewpagerTestBinding
29 |
30 | override fun onCreateContentView(
31 | inflater: LayoutInflater,
32 | container: ViewGroup,
33 | savedInstanceState: Bundle?
34 | ): View {
35 | binding = SceneViewpagerTestBinding.inflate(inflater)
36 | return binding.root
37 | // return inflater.inflate(R.layout.scene_viewpager_test,null)
38 | }
39 |
40 | override fun onActivityCreated(savedInstanceState: Bundle?) {
41 | super.onActivityCreated(savedInstanceState)
42 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
43 | val fragments = arrayListOf(EmptyFragment(),EmptyFragment(),EmptyFragment(),EmptyFragment(),EmptyFragment())
44 | binding.bt1.setOnClickListener {
45 | val b = requireFragmentActivity().supportFragmentManager.beginTransaction()
46 | if (f1 == null) {
47 | f1 = EmptyFragment()
48 | b.add(binding.viewPager1.id, f1!!)
49 | } else {
50 | b.show(f1!!)
51 | f2?.let {
52 | b.hide(it)
53 | }
54 | }
55 | b.commitAllowingStateLoss()
56 | }
57 | binding.bt2.setOnClickListener {
58 | val b = requireFragmentActivity().supportFragmentManager.beginTransaction()
59 | if (f2 == null) {
60 | f2 = EmptyFragment()
61 | b.add(binding.viewPager1.id, f2!!)
62 | } else {
63 | b.show(f2!!)
64 | f1?.let {
65 | b.hide(it)
66 | }
67 |
68 | }
69 | b.commitAllowingStateLoss()
70 | }
71 | binding.viewPager2.offscreenPageLimit = 1
72 | binding.viewPager2.isUserInputEnabled = false
73 |
74 | binding.viewPager2.adapter = object : FragmentStateAdapter(requireFragmentActivity()) {
75 | override fun getItemCount(): Int {
76 | return fragments.size
77 | }
78 |
79 | override fun createFragment(position: Int): Fragment {
80 | return EmptyFragment()
81 | }
82 |
83 | }
84 |
85 | binding.bt3.setOnClickListener {
86 | binding.viewPager2.currentItem = 0
87 | }
88 |
89 | binding.bt4.setOnClickListener {
90 | binding.viewPager2.setCurrentItem(2,false)
91 | }
92 |
93 | }
94 |
95 |
96 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/LinearLayoutScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.view.ViewTreeObserver
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.databinding.LineralayoutUiTestBinding
10 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
11 | import timber.log.Timber
12 |
13 | class LinearLayoutScene : AppCompatScene() {
14 | lateinit var binding:LineralayoutUiTestBinding
15 |
16 | var firstDate = 0L
17 | var secondDate = 0L
18 | val preDraw = ViewTreeObserver.OnPreDrawListener {
19 | secondDate = System.currentTimeMillis()
20 | val result = secondDate - firstDate
21 | firstDate = secondDate
22 | Timber.tag("preDraw").d("result = $result")
23 | true
24 | }
25 |
26 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
27 | firstDate = System.currentTimeMillis()
28 | binding = LineralayoutUiTestBinding.inflate(p0)
29 | binding.root.viewTreeObserver.addOnPreDrawListener(preDraw)
30 | return binding.root
31 | }
32 |
33 | override fun onActivityCreated(savedInstanceState: Bundle?) {
34 | super.onActivityCreated(savedInstanceState)
35 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
36 | }
37 |
38 |
39 | override fun onDestroyView() {
40 | binding.root.viewTreeObserver.removeOnPreDrawListener(preDraw)
41 | super.onDestroyView()
42 |
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/LinearTestScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Build
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import android.os.Looper
8 | import android.view.*
9 | import android.widget.Toast
10 | import androidx.annotation.RequiresApi
11 | import com.bytedance.scene.ui.template.AppCompatScene
12 | import com.starts.hencoderview.databinding.SceneLinearLayoutTestBinding
13 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
14 | import timber.log.Timber
15 |
16 | class LinearTestScene : AppCompatScene() {
17 | lateinit var binding: SceneLinearLayoutTestBinding
18 | @SuppressLint("SetTextI18n")
19 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
20 | binding = SceneLinearLayoutTestBinding.inflate(p0)
21 | return binding.root
22 | }
23 |
24 | @SuppressLint("SetTextI18n")
25 | override fun onActivityCreated(savedInstanceState: Bundle?) {
26 | super.onActivityCreated(savedInstanceState)
27 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
28 | binding.tvShort.text = "我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,我是长文字,"
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ParticleScatteringScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.graphics.BitmapFactory
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.databinding.SceneParticleScatteringBinding
10 | import com.starts.hencoderview.databinding.SceneViewpagerTestBinding
11 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
12 |
13 | /**
14 |
15 | *文件描述:.
16 | *作者:Created by Administrator on 2020/10/23.
17 | *版本号:1.0
18 |
19 | */
20 | class ParticleScatteringScene: AppCompatScene() {
21 |
22 | lateinit var binding: SceneParticleScatteringBinding
23 |
24 | override fun onCreateContentView(
25 | inflater: LayoutInflater,
26 | container: ViewGroup,
27 | savedInstanceState: Bundle?
28 | ): View {
29 | binding = SceneParticleScatteringBinding.inflate(inflater)
30 | return binding.root
31 | }
32 |
33 | override fun onActivityCreated(savedInstanceState: Bundle?) {
34 | super.onActivityCreated(savedInstanceState)
35 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
36 |
37 |
38 |
39 | }
40 |
41 |
42 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/PathScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
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.SeekBar
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.databinding.ScenePathBinding
10 |
11 | /**
12 |
13 | *文件描述:.
14 | *作者:Created by LostStars on 2020/11/26.
15 | *版本号:1.0
16 |
17 | */
18 | class PathScene : AppCompatScene() {
19 | lateinit var binding: ScenePathBinding
20 |
21 | override fun onCreateContentView(
22 | inflater: LayoutInflater,
23 | container: ViewGroup,
24 | savedInstanceState: Bundle?
25 | ): View? {
26 | binding = ScenePathBinding.inflate(inflater)
27 | return binding.root
28 | }
29 |
30 |
31 | override fun onActivityCreated(savedInstanceState: Bundle?) {
32 | super.onActivityCreated(savedInstanceState)
33 | setTitle("PathView")
34 | binding.seekBar1.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
35 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
36 |
37 | }
38 |
39 | override fun onStartTrackingTouch(seekBar: SeekBar?) {
40 | }
41 |
42 | override fun onStopTrackingTouch(seekBar: SeekBar) {
43 | binding.pathView.startPercentage = seekBar.progress
44 | }
45 |
46 | })
47 |
48 | binding.seekBar2.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
49 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
50 |
51 | }
52 |
53 | override fun onStartTrackingTouch(seekBar: SeekBar?) {
54 | }
55 |
56 | override fun onStopTrackingTouch(seekBar: SeekBar) {
57 | binding.pathView.endPercentage = seekBar.progress
58 | }
59 |
60 | })
61 |
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/RelateLayoutScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.view.ViewTreeObserver
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.databinding.RelatelayoutUiTestBinding
10 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
11 | import timber.log.Timber
12 |
13 | class RelateLayoutScene : AppCompatScene() {
14 | lateinit var binding : RelatelayoutUiTestBinding
15 | var firstDate = 0L
16 | var secondDate = 0L
17 | val preDraw = ViewTreeObserver.OnPreDrawListener {
18 | secondDate = System.currentTimeMillis()
19 | val result = secondDate - firstDate
20 | firstDate = secondDate
21 | Timber.tag("preDraw").d("result = $result")
22 | true
23 | }
24 |
25 |
26 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
27 | firstDate = System.currentTimeMillis()
28 | binding = RelatelayoutUiTestBinding.inflate(p0)
29 | binding.root.viewTreeObserver.addOnPreDrawListener(preDraw)
30 | return binding.root
31 | }
32 |
33 | override fun onActivityCreated(savedInstanceState: Bundle?) {
34 | super.onActivityCreated(savedInstanceState)
35 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
36 | }
37 |
38 |
39 | override fun onDestroyView() {
40 | binding.root.viewTreeObserver.removeOnPreDrawListener(preDraw)
41 | super.onDestroyView()
42 |
43 | }
44 |
45 |
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ScaleAlphaAnimScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.animation.Animator
4 | import android.animation.AnimatorSet
5 | import android.animation.ObjectAnimator
6 | import android.animation.ValueAnimator
7 | import android.os.Bundle
8 | import android.view.LayoutInflater
9 | import android.view.View
10 | import android.view.ViewGroup
11 | import android.view.animation.AnimationSet
12 | import android.view.animation.DecelerateInterpolator
13 | import android.view.animation.LinearInterpolator
14 | import com.bytedance.scene.ui.template.AppCompatScene
15 | import com.starts.hencoderview.databinding.SceneScaleAlphaBinding
16 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
17 |
18 | /**
19 |
20 | *文件描述:.
21 | *作者:Created by lorizhao on 2021/10/31.
22 | *版本号:1.0
23 |
24 | */
25 | class ScaleAlphaAnimScene : AppCompatScene() {
26 | lateinit var binding: SceneScaleAlphaBinding
27 | override fun onCreateContentView(
28 | inflater: LayoutInflater,
29 | container: ViewGroup,
30 | savedInstanceState: Bundle?
31 | ): View {
32 | binding = SceneScaleAlphaBinding.inflate(inflater)
33 | return binding.root
34 | }
35 |
36 | override fun onActivityCreated(savedInstanceState: Bundle?) {
37 | super.onActivityCreated(savedInstanceState)
38 | setStatusBarVisible(true)
39 | setToolbarVisible(true)
40 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
41 | startScaleAnimation(binding.view)
42 | }
43 |
44 | private fun startScaleAnimation(view: View) {
45 | val scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.4f)
46 | val scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.4f)
47 |
48 | val alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
49 |
50 | scaleX.repeatCount = ValueAnimator.INFINITE
51 | scaleY.repeatCount = ValueAnimator.INFINITE
52 | alpha.repeatCount = ValueAnimator.INFINITE
53 |
54 | scaleX.repeatMode = ValueAnimator.RESTART
55 | scaleY.repeatMode = ValueAnimator.RESTART
56 | alpha.repeatMode = ValueAnimator.RESTART
57 | val animatorSet = AnimatorSet()
58 | animatorSet.playTogether(scaleX,scaleY,alpha)
59 | animatorSet.interpolator = DecelerateInterpolator()
60 | animatorSet.duration = 800
61 | animatorSet.start()
62 |
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ScaleImageScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.bytedance.scene.ui.template.AppCompatScene
8 | import com.starts.hencoderview.R
9 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
10 |
11 | /**
12 |
13 | *文件描述:.
14 | *作者:Created by Administrator on 2020/5/20.
15 | *版本号:1.0
16 |
17 | */
18 | class ScaleImageScene :AppCompatScene() {
19 | override fun onCreateContentView(
20 | inflater: LayoutInflater,
21 | container: ViewGroup,
22 | savedInstanceState: Bundle?
23 | ): View {
24 | return inflater.inflate(R.layout.scene_scale_image_view,null)
25 | }
26 |
27 | override fun onActivityCreated(savedInstanceState: Bundle?) {
28 | super.onActivityCreated(savedInstanceState)
29 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
30 |
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/SportScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.animation.Animator
4 | import android.animation.ObjectAnimator
5 | import android.os.Bundle
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import android.view.animation.DecelerateInterpolator
10 | import android.widget.SeekBar
11 | import com.bytedance.scene.ui.template.AppCompatScene
12 | import com.starts.hencoderview.R
13 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
14 | import com.starts.hencoderview.view.SportView
15 |
16 | /**
17 |
18 | *文件描述:.
19 | *作者:Created by Administrator on 2020/4/29.
20 | *版本号:1.0
21 |
22 | */
23 | class SportScene : AppCompatScene() {
24 |
25 | lateinit var sportView: SportView
26 | lateinit var seekBar: SeekBar
27 |
28 | override fun onCreateContentView(
29 | inflater: LayoutInflater,
30 | container: ViewGroup,
31 | savedInstanceState: Bundle?
32 | ): View {
33 | val view = inflater.inflate(R.layout.scene_sport_view, null, false)
34 | sportView = view.findViewById(R.id.sportView)
35 | seekBar = view.findViewById(R.id.seekBar)
36 | return view
37 | }
38 |
39 | override fun onActivityCreated(savedInstanceState: Bundle?) {
40 | super.onActivityCreated(savedInstanceState)
41 | setToolbarVisible(true)
42 | setStatusBarVisible(true)
43 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
44 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
45 | // requireActivity().window.decorView.systemUiVisibility =
46 | // View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
47 | // }
48 | val animator = ObjectAnimator.ofInt(sportView, "progress", 0, 65)
49 | animator.interpolator = DecelerateInterpolator()
50 | animator.duration = 1000
51 | animator.startDelay = 500
52 | animator.addListener(object : Animator.AnimatorListener {
53 | override fun onAnimationRepeat(animation: Animator) {
54 |
55 | }
56 |
57 | override fun onAnimationEnd(animation: Animator) {
58 | seekBar.visibility = View.VISIBLE
59 | }
60 |
61 | override fun onAnimationCancel(animation: Animator) {
62 | }
63 |
64 | override fun onAnimationStart(animation: Animator) {
65 | }
66 |
67 | })
68 |
69 | animator.start()
70 |
71 | seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
72 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
73 | sportView.progress = progress
74 | }
75 |
76 | override fun onStartTrackingTouch(seekBar: SeekBar?) {
77 | }
78 |
79 | override fun onStopTrackingTouch(seekBar: SeekBar?) {
80 | }
81 |
82 | })
83 |
84 |
85 | }
86 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/TagLayoutScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.bytedance.scene.ui.template.AppCompatScene
8 | import com.starts.hencoderview.R
9 |
10 | /**
11 |
12 | *文件描述:.
13 | *作者:Created by Administrator on 2020/5/9.
14 | *版本号:1.0
15 |
16 | */
17 | class TagLayoutScene : AppCompatScene() {
18 | override fun onCreateContentView(
19 | inflater: LayoutInflater,
20 | container: ViewGroup,
21 | savedInstanceState: Bundle?
22 | ): View? {
23 | return inflater.inflate(R.layout.scene_tag_layout,null)
24 | }
25 |
26 | override fun onActivityCreated(savedInstanceState: Bundle?) {
27 | super.onActivityCreated(savedInstanceState)
28 | setTitle("TagLayout")
29 | }
30 |
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/TransitionScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.transition.Scene
8 | import androidx.transition.TransitionManager
9 | import com.bytedance.scene.ui.template.AppCompatScene
10 | import com.starts.hencoderview.container.CustomLayout
11 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
12 | import com.starts.hencoderview.util.getScreenWidth
13 | import com.starts.hencoderview.view.TransitionLayout
14 |
15 | class TransitionScene : AppCompatScene() {
16 | private lateinit var rootView : TransitionLayout
17 | override fun onCreateContentView(
18 | inflater: LayoutInflater,
19 | container: ViewGroup,
20 | savedInstanceState: Bundle?
21 | ): View {
22 | rootView = TransitionLayout(container.context)
23 | return rootView
24 | }
25 |
26 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
27 | super.onViewCreated(view, savedInstanceState)
28 | setToolbarVisible(true)
29 | setStatusBarVisible(true)
30 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
31 | rootView.button.setOnClickListener {
32 | val scene2 = Scene(rootView)
33 | val manager = TransitionManager()
34 | manager.transitionTo(scene2)
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ViewBindingScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.viewbinding.ViewBinding
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 |
10 | class ViewBindingScene :AppCompatScene() {
11 | lateinit var binding:T
12 | override fun onCreateContentView(p0: LayoutInflater, p1: ViewGroup, p2: Bundle?): View {
13 | return binding.root
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/ViewGroupScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.graphics.drawable.ColorDrawable
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.view.ViewGroup.LayoutParams.MATCH_PARENT
9 | import com.bytedance.scene.ui.template.AppCompatScene
10 | import com.starts.hencoderview.util.getMaterialColor
11 |
12 | /**
13 |
14 | *文件描述:.
15 | *作者:Created by zhaosibo on 2021/3/24.
16 | *版本号:1.0
17 |
18 | */
19 | class ViewGroupScene : AppCompatScene() {
20 |
21 |
22 | override fun onCreateContentView(
23 | inflater: LayoutInflater,
24 | container: ViewGroup,
25 | savedInstanceState: Bundle?
26 | ): View {
27 | val root = View(container.context).apply {
28 | layoutParams = ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT)
29 | background = ColorDrawable(getMaterialColor(resources , 1))
30 | }
31 | return root
32 | }
33 |
34 |
35 |
36 | override fun onActivityCreated(savedInstanceState: Bundle?) {
37 | super.onActivityCreated(savedInstanceState)
38 | setStatusBarVisible(true)
39 | setToolbarVisible(true)
40 | setTitle("ViewGroupScene")
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/scene/WaveViewScene.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.scene
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import com.bytedance.scene.ui.template.AppCompatScene
9 | import com.starts.hencoderview.databinding.SceneWaveViewBinding
10 | import com.starts.hencoderview.ui.ScalePageTransformer
11 | import com.starts.hencoderview.util.ARGUMENTS_STRING_TITLE
12 | import com.starts.hencoderview.util.dp2px
13 | import com.starts.hencoderview.view.ViewPager2LoopAdapter
14 | import com.starts.hencoderview.view.ViewPager2LoopViewHolder
15 | import com.zhpan.bannerview.BannerViewPager
16 | import com.zhpan.indicator.enums.IndicatorSlideMode
17 | import com.zhpan.indicator.enums.IndicatorStyle
18 |
19 | /**
20 |
21 | *文件描述:.
22 | *作者:Created by Administrator on 2020/11/10.
23 | *版本号:1.0
24 |
25 | */
26 | class WaveViewScene : AppCompatScene() {
27 | lateinit var binding: SceneWaveViewBinding
28 |
29 | private val bannerAdapter = ViewPager2LoopAdapter()
30 |
31 | private lateinit var banner: BannerViewPager
32 |
33 |
34 | private val imageList = arrayListOf(
35 | "https://pic2.zhimg.com/80/v2-f6466210c615e67f70cb8c1f4bf4621f_720w.jpg?source=1940ef5c",
36 | "https://pic2.zhimg.com/80/v2-40a15ca300e5c32eb58bab6b54614cd8_720w.jpg?source=1940ef5c",
37 | "https://pic1.zhimg.com/80/v2-c3e869051a97652c5cd0289e8a9005a4_720w.jpg?source=1940ef5c",
38 | "https://pic1.zhimg.com/80/v2-361f4e84b5e728232485356ee50f2fe8_720w.jpg?source=1940ef5c",
39 | "https://pic2.zhimg.com/80/v2-c9cd38c46a5828d9875be3461229924f_720w.jpg?source=1940ef5c",
40 | "https://pic1.zhimg.com/80/v2-0efb007b8ea5f888bd5fe8de34611b9e_720w.jpg?source=1940ef5c",
41 | "https://pic2.zhimg.com/80/v2-561182aaf0345f806cd2e2df734e4ea8_720w.jpg?source=1940ef5c",
42 | "https://pic1.zhimg.com/80/v2-524e5a7f3589575ad0162d0c0fea4f22_720w.jpg?source=1940ef5c"
43 | )
44 |
45 | override fun onCreateContentView(
46 | inflater: LayoutInflater,
47 | container: ViewGroup,
48 | savedInstanceState: Bundle?
49 | ): View {
50 | binding = SceneWaveViewBinding.inflate(inflater)
51 | banner = binding.banner as BannerViewPager
52 | return binding.root
53 | }
54 |
55 | override fun onActivityCreated(savedInstanceState: Bundle?) {
56 | super.onActivityCreated(savedInstanceState)
57 | setTitle(requireArguments().getString(ARGUMENTS_STRING_TITLE))
58 |
59 | banner.apply {
60 | adapter = bannerAdapter
61 | setAutoPlay(true)
62 | setLifecycleRegistry(lifecycle)
63 | setIndicatorStyle(IndicatorStyle.ROUND_RECT)
64 | setIndicatorSliderGap(dp2px(5))
65 | setIndicatorMargin(0, 0, 0, dp2px(16))
66 | setIndicatorSlideMode(IndicatorSlideMode.NORMAL)
67 | setIndicatorSliderRadius(dp2px(10), dp2px(5))
68 | setPageTransformer(ScalePageTransformer())
69 | setScrollDuration(800)
70 | setIndicatorSliderColor(Color.parseColor("#80ffffff"), Color.WHITE)
71 | }.create(imageList)
72 | binding.waveView.bindViewPager(banner)
73 | }
74 |
75 |
76 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/ui/EmptyFragment.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.ui
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.util.Log
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import androidx.constraintlayout.widget.ConstraintLayout
10 | import androidx.fragment.app.Fragment
11 | import com.starts.hencoderview.R
12 | import com.starts.hencoderview.databinding.FragmentEmptyBinding
13 | import java.util.*
14 | import java.util.logging.Logger
15 |
16 | /**
17 |
18 | *文件描述:.
19 | *作者:Created by Administrator on 2020/7/16.
20 | *版本号:1.0
21 |
22 | */
23 | class EmptyFragment : Fragment() {
24 |
25 | lateinit var binding:FragmentEmptyBinding
26 |
27 | override fun onCreateView(
28 | inflater: LayoutInflater,
29 | container: ViewGroup?,
30 | savedInstanceState: Bundle?
31 | ): View? {
32 | // return super.onCreateView(inflater, container, savedInstanceState)
33 | binding = FragmentEmptyBinding.inflate(inflater)
34 | return binding.root
35 | }
36 |
37 |
38 | @Deprecated("Deprecated in Java")
39 | override fun onActivityCreated(savedInstanceState: Bundle?) {
40 | super.onActivityCreated(savedInstanceState)
41 | val random = Random()
42 | val r = random.nextInt(256)
43 | val g = random.nextInt(256)
44 | val b = random.nextInt(256)
45 | binding.root.setBackgroundColor(Color.rgb(r, g, b))
46 | binding.tvTag.text = hashCode().toString()
47 | }
48 |
49 | override fun onHiddenChanged(hidden: Boolean) {
50 | super.onHiddenChanged(hidden)
51 | Log.d("fragmentLife", "onHiddenChanged${hidden}")
52 | }
53 |
54 | @Deprecated("Deprecated in Java")
55 | override fun setUserVisibleHint(isVisibleToUser: Boolean) {
56 | super.setUserVisibleHint(isVisibleToUser)
57 | Log.d("fragmentLife", "setUserVisibleHint${isVisibleToUser}")
58 | }
59 |
60 | override fun onStart() {
61 | super.onStart()
62 | Log.d("fragmentLife", "hashCode:${this.hashCode()}:onStart")
63 | }
64 |
65 | override fun onResume() {
66 | super.onResume()
67 | Log.d("fragmentLife", "hashCode:${this.hashCode()}onResume")
68 | }
69 |
70 |
71 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/ui/ListFragment.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.ui
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import androidx.recyclerview.widget.RecyclerView
10 | import com.starts.hencoderview.scene.InnerAdapter
11 |
12 | /**
13 |
14 | *文件描述:.
15 | *作者:Created by lorizhao on 2022/3/2.
16 | *版本号:1.0
17 |
18 | */
19 | class ListFragment:Fragment() {
20 | override fun onCreateView(
21 | inflater: LayoutInflater,
22 | container: ViewGroup?,
23 | savedInstanceState: Bundle?
24 | ): View {
25 | val binding = RecyclerView(requireContext()).apply {
26 | this.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)
27 | }
28 | val data = arrayListOf("我是第一个","12323124","48999584","239863436",
29 | "12323124","48999584","239863436","12323124","48999584","239863436","12323124",
30 | "48999584","239863436","12323124","48999584","239863436","12323124",
31 | "48999584","我是最后一个")
32 | binding.adapter = InnerAdapter(data)
33 | binding.layoutManager = LinearLayoutManager(requireContext())
34 | return binding
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/ui/MemoryLeakActivity.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.ui
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.widget.TextView
7 | import android.widget.Toast
8 | import androidx.appcompat.app.AppCompatActivity
9 | import androidx.recyclerview.widget.RecyclerView
10 | import com.starts.hencoderview.R
11 | import com.starts.hencoderview.remote.ApiService
12 | import leakcanary.LeakCanary
13 | import okhttp3.OkHttpClient
14 | import okhttp3.Request
15 | import retrofit2.Call
16 | import retrofit2.Callback
17 | import retrofit2.Response
18 | import retrofit2.Retrofit
19 | import java.io.IOException
20 | import kotlin.concurrent.thread
21 |
22 | /**
23 |
24 | *文件描述:.
25 | *作者:Created by Administrator on 2020/11/13.
26 | *版本号:1.0
27 |
28 | */
29 | class MemoryLeakActivity : AppCompatActivity() {
30 |
31 | lateinit var tvPost: TextView
32 |
33 | private val handler = Handler{
34 | tvPost.text = "收到"
35 | true
36 | }
37 |
38 | override fun onCreate(savedInstanceState: Bundle?) {
39 | super.onCreate(savedInstanceState)
40 | setContentView(R.layout.activity_memory_lead)
41 |
42 | tvPost = findViewById(R.id.tvPost)
43 | tvPost.setOnClickListener {
44 | handler.postDelayed({
45 | Toast.makeText(this,"开始执行" ,Toast.LENGTH_LONG).show()
46 | },15000)
47 | }
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/ui/ScalePageTransformer.java:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.ui;
2 |
3 | import android.view.View;
4 |
5 | import androidx.viewpager2.widget.ViewPager2;
6 |
7 | /**
8 | * @author Administrator
9 | */
10 |
11 | public class ScalePageTransformer implements ViewPager2.PageTransformer {
12 |
13 | private static final float MIN_SCALE = 0.8f;
14 | private static final float MIN_ALPHA = 1f;
15 |
16 | /**
17 | * 当ViewPager滑动时,每一个界面都会回掉该方法,并且该方法跟随滑动不断回调
18 | * 参数:
19 | * view:当前停留显示的页面(注意:下面的循环不要直接用该view,因为该view会被自动加上一层FrameLayout布局)
20 | * position:当前页面滑动的位置,以当前页面的左侧边为0,两边终点分别为-1和1
21 | */
22 | @Override
23 | public void transformPage(View view, float position) {
24 | int pageWidth = view.getWidth();
25 | int pageHeight = view.getHeight();
26 | // [-Infinity,-1)
27 | if (position < -1) {
28 | // This page is way off-screen to the left.
29 | view.setAlpha(0);
30 |
31 | } else if (position <= 1) {
32 | // a页滑动至b页 ; a页从 0.0 -1 ;b页从1 ~ 0.0
33 | // Modify the default slide transition to shrink the page as well
34 | float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
35 | float vertMargin = pageHeight * (1 - scaleFactor) / 2;
36 | float horzMargin = pageWidth * (1 - scaleFactor) / 2;
37 | if (position < 0) {
38 | //api版本>=11,view相对原始位置的偏移量,不影响view属性
39 | view.setTranslationX(horzMargin - vertMargin / 2);
40 | } else {
41 | view.setTranslationX(-horzMargin + vertMargin / 2);
42 | }
43 |
44 | // Scale the page down (between MIN_SCALE and 1)
45 | view.setScaleX(scaleFactor);
46 | view.setScaleY(scaleFactor);
47 |
48 | // Fade the page relative to its size.
49 | view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE)
50 | / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
51 |
52 | } else {
53 | // This page is way off-screen to the right.
54 | view.setAlpha(0);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/util/Ext.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.util
2 |
3 | import android.content.res.Resources
4 | import android.graphics.Color
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.starts.hencoderview.R
8 |
9 | /**
10 |
11 | *文件描述:.
12 | *作者:Created by Administrator on 2020/4/29.
13 | *版本号:1.0
14 |
15 | */
16 | fun dp2px(dp: Int): Int {
17 | val density = Resources.getSystem().displayMetrics.density
18 | return (dp * density).toInt()
19 | }
20 |
21 | fun sp(sp: Int): Float {
22 | val scaledDensity = Resources.getSystem().displayMetrics.scaledDensity
23 | return sp * scaledDensity
24 | }
25 |
26 | fun getMaterialColor(resources: Resources, index: Int): Int {
27 | val colors = resources.obtainTypedArray(R.array.mdcolor_300);
28 | val returnColor = colors.getColor(index % colors.length(), Color.BLACK);
29 | colors.recycle();
30 | return returnColor
31 | }
32 |
33 | fun getScreenWidth(): Int {
34 | return Resources.getSystem().displayMetrics.widthPixels
35 | }
36 |
37 | fun getScreenHeight(): Int {
38 | return Resources.getSystem().displayMetrics.heightPixels
39 | }
40 |
41 | fun View.isUnder(rawX: Float, rawY: Float): Boolean {
42 | val xy = IntArray(2)
43 | getLocationOnScreen(xy)
44 | return rawX.toInt() in xy[0]..(xy[0] + width) && rawY.toInt() in xy[1]..(xy[1] + height)
45 | }
46 |
47 | /**
48 | * 寻找 ViewGroup 中某个符合条件的子 View,支持递归遍历其子 View
49 | */
50 | fun ViewGroup.findFirst(recursively: Boolean, predict: (View) -> Boolean): View? {
51 | for (i in 0 until childCount) {
52 | val v = getChildAt(i)
53 | if (predict(v)) {
54 | return v
55 | }
56 | if (recursively) {
57 | return (v as? ViewGroup)?.findFirst(recursively, predict) ?: continue
58 | }
59 | }
60 | return null
61 | }
62 |
63 | const val ARGUMENTS_STRING_TITLE = "ARGUMENTS_STRING_TITLE"
64 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/util/QMUIAlignMiddleImageSpan.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Tencent is pleased to support the open source community by making QMUI_Android available.
3 | *
4 | * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
5 | *
6 | * Licensed under the MIT License (the "License"); you may not use this file except in
7 | * compliance with the License. You may obtain a copy of the License at
8 | *
9 | * http://opensource.org/licenses/MIT
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
12 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 | * either express or implied. See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.starts.hencoderview.util;
18 |
19 | import android.graphics.Canvas;
20 | import android.graphics.Paint;
21 | import android.graphics.Rect;
22 | import android.graphics.drawable.Drawable;
23 | import android.text.style.ImageSpan;
24 |
25 | /**
26 | * 支持垂直居中的ImageSpan
27 | *
28 | * @author cginechen
29 | * @date 2016-03-17
30 | */
31 | public class QMUIAlignMiddleImageSpan extends ImageSpan {
32 |
33 | public static final int ALIGN_MIDDLE = -100; // 不要和父类重复
34 |
35 | /**
36 | * 规定这个Span占几个字的宽度
37 | */
38 | private float mFontWidthMultiple = -1f;
39 |
40 | /**
41 | * 是否避免父类修改FontMetrics,如果为 false 则会走父类的逻辑, 会导致FontMetrics被更改
42 | */
43 | private boolean mAvoidSuperChangeFontMetrics = false;
44 |
45 | @SuppressWarnings("FieldCanBeLocal") private int mWidth;
46 |
47 | /**
48 | * @param d 作为 span 的 Drawable
49 | * @param verticalAlignment 垂直对齐方式, 如果要垂直居中, 则使用 {@link #ALIGN_MIDDLE}
50 | */
51 | public QMUIAlignMiddleImageSpan(Drawable d, int verticalAlignment) {
52 | super(d, verticalAlignment);
53 | }
54 |
55 | /**
56 | * @param d 作为 span 的 Drawable
57 | * @param verticalAlignment 垂直对齐方式, 如果要垂直居中, 则使用 {@link #ALIGN_MIDDLE}
58 | * @param fontWidthMultiple 设置这个Span占几个中文字的宽度, 当该值 > 0 时, span 的宽度为该值*一个中文字的宽度; 当该值 <= 0 时, span 的宽度由 {@link #mAvoidSuperChangeFontMetrics} 决定
59 | */
60 | public QMUIAlignMiddleImageSpan(Drawable d, int verticalAlignment, float fontWidthMultiple) {
61 | this(d, verticalAlignment);
62 | if (fontWidthMultiple >= 0) {
63 | mFontWidthMultiple = fontWidthMultiple;
64 | }
65 | }
66 |
67 | @Override
68 | public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
69 | if (mAvoidSuperChangeFontMetrics) {
70 | Drawable d = getDrawable();
71 | Rect rect = d.getBounds();
72 | mWidth = rect.right;
73 | } else {
74 | mWidth = super.getSize(paint, text, start, end, fm);
75 | }
76 | if (mFontWidthMultiple > 0) {
77 | mWidth = (int) (paint.measureText("子") * mFontWidthMultiple);
78 | }
79 | return mWidth;
80 | }
81 |
82 | @Override
83 | public void draw(Canvas canvas, CharSequence text, int start, int end,
84 | float x, int top, int y, int bottom, Paint paint) {
85 | if (mVerticalAlignment == ALIGN_MIDDLE) {
86 | Drawable d = getDrawable();
87 | canvas.save();
88 |
89 | // // 注意如果这样实现会有问题:TextView 有 lineSpacing 时,这里 bottom 偏大,导致偏下
90 | // int transY = bottom - d.getBounds().bottom; // 底对齐
91 | // transY -= (paint.getFontMetricsInt().bottom - paint.getFontMetricsInt().top) / 2 - d.getBounds().bottom / 2; // 居中对齐
92 | // canvas.translate(x, transY);
93 | // d.draw(canvas);
94 | // canvas.restore();
95 |
96 | Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
97 | int fontTop = y + fontMetricsInt.top;
98 | int fontMetricsHeight = fontMetricsInt.bottom - fontMetricsInt.top;
99 | int iconHeight = d.getBounds().bottom - d.getBounds().top;
100 | int iconTop = fontTop + (fontMetricsHeight - iconHeight) / 2;
101 | canvas.translate(x, iconTop);
102 | d.draw(canvas);
103 | canvas.restore();
104 | } else {
105 | super.draw(canvas, text, start, end, x, top, y, bottom, paint);
106 | }
107 | }
108 |
109 | /**
110 | * 是否避免父类修改FontMetrics,如果为 false 则会走父类的逻辑, 会导致FontMetrics被更改
111 | */
112 | public void setAvoidSuperChangeFontMetrics(boolean avoidSuperChangeFontMetrics) {
113 | mAvoidSuperChangeFontMetrics = avoidSuperChangeFontMetrics;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/ColoredTextView.java:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | import com.starts.hencoderview.util.ExtKt;
12 |
13 | import java.util.Random;
14 |
15 | public class ColoredTextView extends androidx.appcompat.widget.AppCompatTextView {
16 | private static final int[] COLORS = {
17 | Color.parseColor("#E91E63"),
18 | Color.parseColor("#673AB7"),
19 | Color.parseColor("#3F51B5"),
20 | Color.parseColor("#2196F3"),
21 | Color.parseColor("#009688"),
22 | Color.parseColor("#FF9800"),
23 | Color.parseColor("#FF5722"),
24 | Color.parseColor("#795548")
25 | };
26 | private static final int[] TEXT_SIZES = {
27 | 16, 22, 28
28 | };
29 | private static final Random random = new Random();
30 | private static final int CORNER_RADIUS = ExtKt.dp2px(4);
31 | private static final int X_PADDING = ExtKt.dp2px(16);
32 | private static final int Y_PADDING = ExtKt.dp2px(8);
33 |
34 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
35 |
36 | public ColoredTextView(Context context, @Nullable AttributeSet attrs) {
37 | super(context, attrs);
38 | }
39 |
40 | {
41 | setTextColor(Color.WHITE);
42 | setTextSize(TEXT_SIZES[random.nextInt(3)]);
43 | paint.setColor(COLORS[random.nextInt(COLORS.length)]);
44 | setPadding(X_PADDING, Y_PADDING, X_PADDING, Y_PADDING);
45 | }
46 |
47 | @Override
48 | protected void onDraw(Canvas canvas) {
49 | canvas.drawRoundRect(0, 0, getWidth(), getHeight(), CORNER_RADIUS, CORNER_RADIUS, paint);
50 | super.onDraw(canvas);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/CustomLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.util.AttributeSet
6 | import android.view.HapticFeedbackConstants
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import androidx.core.view.*
10 | import timber.log.Timber
11 |
12 | @Suppress("MemberVisibilityCanBePrivate")
13 | abstract class CustomLayout : ViewGroup {
14 |
15 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
16 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
17 | val dimension = this.onMeasureChildren(widthMeasureSpec, heightMeasureSpec)
18 | setMeasuredDimension(dimension.width, dimension.height)
19 | }
20 |
21 | protected data class Dimension(val width: Int, val height: Int)
22 |
23 | protected abstract fun onMeasureChildren(
24 | widthMeasureSpec: Int,
25 | heightMeasureSpec: Int
26 | ): Dimension
27 |
28 | protected fun View.autoMeasure() {
29 | if (isGone) return
30 | measure(
31 | this.defaultWidthMeasureSpec(parentView = this@CustomLayout),
32 | this.defaultHeightMeasureSpec(parentView = this@CustomLayout)
33 | )
34 | }
35 |
36 |
37 | protected fun View.defaultWidthMeasureSpec(parentView: ViewGroup): Int {
38 | return when (layoutParams.width) {
39 | ViewGroup.LayoutParams.MATCH_PARENT -> parentView.measuredWidth.toExactlyMeasureSpec()
40 | ViewGroup.LayoutParams.WRAP_CONTENT -> ViewGroup.LayoutParams.WRAP_CONTENT.toAtMostMeasureSpec()
41 | 0 -> throw IllegalAccessException("Need special treatment for $this")
42 | else -> layoutParams.width.toExactlyMeasureSpec()
43 | }
44 | }
45 |
46 | protected val Int.dp: Int get() = (this * resources.displayMetrics.density + 0.5f).toInt()
47 |
48 | constructor(context: Context?) : super(context)
49 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
50 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
51 | context,
52 | attrs,
53 | defStyleAttr
54 | )
55 |
56 | class LayoutParams(width: Int, height: Int) : MarginLayoutParams(width, height)
57 |
58 | override fun generateDefaultLayoutParams(): LayoutParams {
59 | return LayoutParams(matchParent, wrapContent)
60 | }
61 |
62 |
63 |
64 | protected fun View.defaultHeightMeasureSpec(parentView: ViewGroup): Int {
65 | return when (layoutParams.height) {
66 | ViewGroup.LayoutParams.MATCH_PARENT -> parentView.measuredHeight.toExactlyMeasureSpec()
67 | ViewGroup.LayoutParams.WRAP_CONTENT -> Int.MAX_VALUE.toAtMostMeasureSpec()
68 | 0 -> throw IllegalAccessException("Need special treatment for $this")
69 | else -> layoutParams.height.toExactlyMeasureSpec()
70 | }
71 | }
72 |
73 |
74 | protected fun View.forEachAutoMeasure() {
75 | forEach { it.autoMeasure() }
76 | }
77 |
78 | protected fun View.layout(x: Int, y: Int, fromRight: Boolean = false) {
79 | if (isGone) return
80 | if (!fromRight) {
81 | layout(x, y, x + measuredWidth, y + measuredHeight)
82 | } else {
83 | layout(this@CustomLayout.measuredWidth - x - measuredWidth, y)
84 | }
85 | }
86 |
87 | protected val View.measuredWidthWithMargins get() = (measuredWidth + marginLeft + marginRight)
88 | protected val View.measuredHeightWithMargins get() = (measuredHeight + marginTop + marginBottom)
89 |
90 |
91 |
92 | protected fun Int.toExactlyMeasureSpec(): Int {
93 | return MeasureSpec.makeMeasureSpec(this, MeasureSpec.EXACTLY)
94 | }
95 |
96 | protected fun Int.toAtMostMeasureSpec(): Int {
97 | return MeasureSpec.makeMeasureSpec(this, MeasureSpec.AT_MOST)
98 | }
99 |
100 | fun addView(child: View, width: Int, height: Int, apply: (LayoutParams.() -> Unit)) {
101 | val params = generateDefaultLayoutParams()
102 | params.width = width
103 | params.height = height
104 | apply(params)
105 | super.addView(child, params)
106 | }
107 |
108 | fun View.overScrollNever() {
109 | overScrollMode = View.OVER_SCROLL_NEVER
110 | }
111 |
112 | fun ViewGroup.horizontalCenterX(child: View): Int {
113 | return (measuredWidth - child.measuredWidth) / 2
114 | }
115 |
116 | fun View.horizontalCenterX(child: View): Int {
117 | return (measuredWidth - child.measuredWidth) / 2
118 | }
119 |
120 | fun ViewGroup.verticalCenterTop(child: View): Int {
121 | return (measuredHeight - child.measuredHeight) / 2
122 | }
123 |
124 | fun View.verticalCenterTop(child: View): Int {
125 | return (measuredHeight - child.measuredHeight) / 2
126 | }
127 | }
128 |
129 | const val matchParent = ViewGroup.LayoutParams.MATCH_PARENT
130 | const val wrapContent = ViewGroup.LayoutParams.WRAP_CONTENT
131 |
132 | fun View.transparentBackground() {
133 | setBackgroundColor(Color.TRANSPARENT)
134 | }
135 |
136 | val View.parentView get() = parent as ViewGroup
137 |
138 | fun View?.performHapticFeedbackSafely() {
139 | try {
140 | this?.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
141 | } catch (t: Throwable) {
142 | Timber.tag("CustomLayout").e(t.toString())
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/CustomUITest.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.Canvas
6 | import android.graphics.Color
7 | import android.graphics.Outline
8 | import android.graphics.Paint
9 | import android.util.AttributeSet
10 | import android.view.View
11 | import android.view.ViewOutlineProvider
12 | import android.widget.ImageView
13 | import android.widget.TextView
14 | import androidx.core.content.ContextCompat
15 |
16 | import com.google.android.material.floatingactionbutton.FloatingActionButton
17 | import com.starts.hencoderview.R
18 | import timber.log.Timber
19 |
20 | class CustomUITest(context: Context) : CustomLayout(context) {
21 |
22 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
23 | color = ContextCompat.getColor(context, R.color.pink_300)
24 | textSize = 32f
25 | }
26 | private var measureCount = 0
27 | init {
28 | setWillNotDraw(false)
29 | }
30 | private val commonMargin = 12.dp
31 | private val topImg = TestImageView(context).apply {
32 | scaleType = ImageView.ScaleType.CENTER_CROP
33 | layoutParams = LayoutParams(matchParent, 280.dp)
34 | setImageResource(R.mipmap.personal_bg)
35 | this@CustomUITest.addView(this)
36 | }
37 | private val avatar = TestImageView(context).apply {
38 | layoutParams = LayoutParams(60.dp, 60.dp)
39 | setImageResource(R.mipmap.avatar)
40 | outlineProvider = object : ViewOutlineProvider() {
41 | override fun getOutline(view: View, outline: Outline) {
42 | outline.setRoundRect(0, 0, view.width, view.height, 60f); }
43 | }
44 | this@CustomUITest.addView(this)
45 | }
46 | private val floatingActionButton = TestFloatActionButton(context).apply {
47 | layoutParams = LayoutParams(wrapContent, wrapContent)
48 | this@CustomUITest.addView(this)
49 | }
50 | private val title = TestTextView(context).apply {
51 | layoutParams = LayoutParams(wrapContent, wrapContent)
52 | text = "UIShare"
53 | textSize = 18f
54 | this@CustomUITest.addView(this)
55 | }
56 | private val introduce = TestTextView(context).apply {
57 | layoutParams = LayoutParams(wrapContent, wrapContent)
58 | text = "在Android界面的展示中,我们常见工作流程是在xml写好布局文件,之后在Activity或者fragment中展示一个UI界面, 画面需要经" +
59 | "过xml解析,之后window要通过setView方法实际构造各个View对象,并且按照层级排列好,之后Android系统再找到合适" +
60 | "的时机,触发measure,layout,draw这一流程,但是随着布局嵌套的加深,xml的解析越来越复杂,测绘的次数会逐步提高,逐渐到" +
61 | "达失控的地步。那么,需要一种方式可以一次性解决这个问题,让我们的布局不再需要多次测量来确定自己的宽高,答案就是完全自己" +
62 | "接管ViewGroup的onMeasure,onLayout过"
63 | this@CustomUITest.addView(this)
64 | }
65 | private val share = TestImageView(context).apply {
66 | layoutParams = LayoutParams(24.dp, 24.dp)
67 | setImageResource(R.mipmap.share)
68 | this@CustomUITest.addView(this)
69 | }
70 |
71 | override fun onMeasureChildren(widthMeasureSpec: Int, heightMeasureSpec: Int): Dimension {
72 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
73 | topImg.autoMeasure()
74 | avatar.autoMeasure()
75 | floatingActionButton.autoMeasure()
76 | title.autoMeasure()
77 | share.autoMeasure()
78 | val spaceHeight = (MeasureSpec.getSize(heightMeasureSpec) -
79 | title.measuredHeight -
80 | topImg.measuredHeight -
81 | commonMargin * 2).toExactlyMeasureSpec()
82 | val spaceWidth = (MeasureSpec.getSize(widthMeasureSpec) - avatar.measuredWidth - share.measuredWidth - 4 * commonMargin).toExactlyMeasureSpec()
83 | introduce.measure(spaceWidth, spaceHeight)
84 | return Dimension(widthMeasureSpec, heightMeasureSpec)
85 | }
86 |
87 | override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
88 | topImg.layout(l, t)
89 | avatar.layout(commonMargin, topImg.bottom + commonMargin)
90 | floatingActionButton.layout(
91 | commonMargin,
92 | topImg.bottom - avatar.measuredHeight / 2,
93 | true
94 | )
95 | title.layout(avatar.right + commonMargin, topImg.bottom + commonMargin)
96 | introduce.layout(avatar.right + commonMargin, title.bottom + commonMargin)
97 | share.layout(commonMargin, introduce.top, true)
98 | }
99 |
100 | override fun onDraw(canvas: Canvas) {
101 | super.onDraw(canvas)
102 | canvas.drawText("c=$measureCount" , 40f,40f,paint)
103 | }
104 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/FlipBoardView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.*
5 | import android.util.AttributeSet
6 | import android.view.View
7 | import com.starts.hencoderview.R
8 | import kotlin.math.sin
9 |
10 | /**
11 |
12 | *文件描述:.
13 | *作者:Created by Administrator on 2020/4/30.
14 | *版本号:1.0
15 |
16 | */
17 | class FlipBoardView(context: Context, attrs: AttributeSet) : View(context, attrs) {
18 |
19 | private lateinit var bitmap: Bitmap
20 | lateinit var srcRect: Rect
21 | private lateinit var center: Point
22 | private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
23 | private val camera = Camera()
24 |
25 | private var radius: Double = 0.00
26 |
27 | var angle: Float = 0f
28 | set(value) {
29 | field = value
30 | invalidate()
31 | }
32 |
33 | var cameraAngle: Float = 0f
34 | set(value) {
35 | field = value
36 | invalidate()
37 | }
38 | init {
39 |
40 | camera.setLocation(0f, 0f, -6f * context.resources.displayMetrics.density)
41 | }
42 |
43 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
44 | super.onSizeChanged(w, h, oldw, oldh)
45 | center = Point(w / 2, h / 2)
46 |
47 | bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.red)
48 | srcRect = Rect(0, 0, bitmap.width, bitmap.height)
49 | radius = (bitmap.width) * sin(Math.toRadians(45.0))
50 | }
51 |
52 | override fun onDraw(canvas: Canvas) {
53 | super.onDraw(canvas)
54 | // canvas.drawColor(ContextCompat.getColor(context, R.color.colorAccent))
55 | canvas.drawBitmap(bitmap, 0f, 0f, paint)
56 |
57 | canvas.save()
58 | canvas.translate(center.x.toFloat(), center.y.toFloat())
59 | canvas.rotate(angle)
60 | canvas.clipRect(-radius.toFloat(), -radius.toFloat(), radius.toFloat(), 0f)
61 | canvas.rotate(-angle)
62 | canvas.translate(-center.x.toFloat(), -center.y.toFloat())
63 | canvas.drawBitmap(
64 | bitmap,
65 | center.x - bitmap.width / 2f,
66 | center.y - bitmap.height / 2f,
67 | paint
68 | )
69 | canvas.restore()
70 |
71 | canvas.save()
72 | canvas.translate(center.x.toFloat(), center.y.toFloat())
73 | canvas.rotate(angle)
74 | camera.save()
75 | camera.rotateX(cameraAngle)
76 | camera.applyToCanvas(canvas)
77 | camera.restore()
78 | canvas.clipRect(-radius.toFloat(), 0f, radius.toFloat(), radius.toFloat())
79 | canvas.rotate(-angle)
80 | canvas.translate(-center.x.toFloat(), -center.y.toFloat())
81 | canvas.drawBitmap(
82 | bitmap,
83 | center.x - bitmap.width / 2f,
84 | center.y - bitmap.height / 2f,
85 | paint
86 | )
87 | canvas.restore()
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/ParticleScatteringView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.animation.ValueAnimator
4 | import android.content.Context
5 | import android.graphics.*
6 | import android.util.AttributeSet
7 | import android.util.Log
8 | import android.view.View
9 | import android.view.animation.LinearInterpolator
10 | import com.starts.hencoderview.R
11 | import kotlin.math.*
12 | import kotlin.random.Random
13 | import kotlin.system.measureTimeMillis
14 |
15 | /**
16 |
17 | *文件描述:.
18 | *作者:Created by Administrator on 2020/10/23.
19 | *版本号:1.0
20 |
21 | */
22 | class ParticleScatteringView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
23 |
24 | var viewWidth = 0
25 | var viewHeight = 0
26 |
27 |
28 | var imageAngle = 0f
29 |
30 | var centerX = 0f
31 | var centerY = 0f
32 |
33 | val imagePaint = Paint(Paint.ANTI_ALIAS_FLAG)
34 | lateinit var imageShader: BitmapShader
35 |
36 | //粒子的数量
37 | val particleCount = 500
38 | val particlePaint = Paint(Paint.ANTI_ALIAS_FLAG)
39 |
40 |
41 | val imageBitmap: Bitmap by lazy {
42 | val option = BitmapFactory.Options()
43 | option.outWidth = (viewWidth * 1f / 2).toInt()
44 | option.outHeight = (viewHeight * 1f / 2).toInt()
45 | BitmapFactory.decodeResource(context.resources, R.mipmap.ui_scale_img, option)
46 | }
47 |
48 |
49 | val bitmapCanvas = Canvas()
50 | //粒子集合
51 | val particleList = ArrayList()
52 |
53 | private var particleAnimator = ValueAnimator.ofFloat(0f, 1f)
54 |
55 | private var innerCirclePath = Path()
56 | private var pathMeasure = PathMeasure()
57 |
58 | init {
59 | particleAnimator.duration = 4000
60 | particleAnimator.repeatCount = -1
61 | particleAnimator.interpolator = LinearInterpolator()
62 | particleAnimator.addUpdateListener {
63 | updateParticle()
64 | imageAngle = 360 * it.animatedValue as Float
65 | invalidate()//重绘界面
66 | }
67 | }
68 |
69 | private fun updateParticle() {
70 | particleList.forEach {
71 | if (it.offset > it.maxOffset) {
72 | it.offset = 0f
73 | it.speed = (Random.nextInt(2, 4)).toFloat()
74 | }
75 | it.alpha = ((1f - it.offset / it.maxOffset) * 225f).toInt()
76 |
77 | it.offset += it.speed
78 | // if(it.offset >= it.maxOffset){
79 | // return
80 | // }else{
81 | it.x = centerX + cos(it.angle) * (310f + it.offset)
82 |
83 | if (it.y > centerY) {
84 | it.y = (sin(it.angle) * (310f + it.offset) + centerY)
85 | } else {
86 | it.y = (centerY - sin(it.angle) * (310f + it.offset))
87 | }
88 |
89 | it.offset += it.speed
90 |
91 | }
92 | }
93 |
94 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
95 | super.onSizeChanged(w, h, oldw, oldh)
96 | viewWidth = w
97 | viewHeight = h
98 |
99 | centerX = viewWidth * 1f / 2
100 | centerY = viewHeight * 1f / 2
101 |
102 | innerCirclePath.addCircle(
103 | centerX,
104 | centerY,
105 | 310f, Path.Direction.CCW
106 | )
107 |
108 | pathMeasure.setPath(innerCirclePath, false)
109 | val singleLength = pathMeasure.length / particleCount
110 |
111 |
112 |
113 |
114 | imageShader = BitmapShader(imageBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
115 | imagePaint.shader = imageShader
116 |
117 |
118 | particlePaint.color = Color.WHITE
119 | particlePaint.style = Paint.Style.FILL
120 | for (i in 0..particleCount) {
121 | val particle = Particle()
122 | val localArray = FloatArray(2)
123 | val tanArray = FloatArray(2)
124 |
125 | if (pathMeasure.getPosTan(i * singleLength, localArray, tanArray)) {
126 | particle.x = localArray[0] + (Random.nextInt(10) - 20)
127 | particle.y = localArray[1] + (Random.nextInt(10) - 20)
128 | }
129 |
130 | particle.speed = (Random.nextInt(2, 4)).toFloat()
131 | particle.angle = acos((localArray[0] - centerX) / 280f)
132 | particleList.add(particle)
133 | }
134 | particleAnimator.start()
135 |
136 | }
137 |
138 | val bitmap:Bitmap by lazy {
139 | Bitmap.createBitmap(viewWidth, viewHeight,
140 | Bitmap.Config.ARGB_8888)
141 | }
142 |
143 | override fun onDraw(canvas: Canvas) {
144 | super.onDraw(canvas)
145 | canvas.save()
146 |
147 | canvas.rotate(imageAngle , centerX , centerY)
148 | canvas.drawCircle(
149 | centerX,
150 | centerY,
151 | 300f,
152 | imagePaint
153 | )
154 | canvas.restore()
155 | val time = measureTimeMillis {
156 | particleList.forEach {
157 | particlePaint.alpha = it.alpha
158 | canvas.drawCircle(it.x, it.y, it.radius, particlePaint)
159 | }
160 | }
161 |
162 | Log.d("ParticleScatteringView", "time = $time")
163 |
164 | }
165 |
166 | class Particle {
167 | var x: Float = 0f//X坐标
168 | var y: Float = 0f//Y坐标
169 | var radius: Float = 3f//半径
170 | var speed: Float = 0f//速度
171 | var alpha: Int = 1//透明度
172 | var angle = 0f
173 | val maxOffset = 150f
174 | var offset = 0f
175 | }
176 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/PathLayoutManager.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.os.Looper
4 | import androidx.recyclerview.widget.LinearLayoutManager
5 | import androidx.recyclerview.widget.RecyclerView
6 | import java.util.logging.Handler
7 |
8 | /**
9 |
10 | *文件描述:.
11 | *作者:Created by Administrator on 2020/10/28.
12 | *版本号:1.0
13 |
14 | */
15 | class PathLayoutManager : RecyclerView.LayoutManager() {
16 | override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
17 | return RecyclerView.LayoutParams( RecyclerView.LayoutParams.WRAP_CONTENT , RecyclerView.LayoutParams.WRAP_CONTENT)
18 | }
19 |
20 | override fun onMeasure(
21 | recycler: RecyclerView.Recycler,
22 | state: RecyclerView.State,
23 | widthSpec: Int,
24 | heightSpec: Int
25 | ) {
26 | super.onMeasure(recycler, state, widthSpec, heightSpec)
27 | }
28 |
29 |
30 | override fun isAutoMeasureEnabled(): Boolean {
31 | return super.isAutoMeasureEnabled()
32 | }
33 |
34 | override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
35 | super.onLayoutChildren(recycler, state)
36 |
37 | detachAndScrapAttachedViews(recycler)
38 | removeAndRecycleAllViews(recycler)
39 |
40 | }
41 |
42 | override fun canScrollHorizontally(): Boolean {
43 | return super.canScrollHorizontally()
44 | }
45 |
46 | override fun canScrollVertically(): Boolean {
47 | return super.canScrollVertically()
48 | }
49 |
50 | override fun scrollHorizontallyBy(
51 | dx: Int,
52 | recycler: RecyclerView.Recycler?,
53 | state: RecyclerView.State?
54 | ): Int {
55 | return super.scrollHorizontallyBy(dx, recycler, state)
56 | }
57 |
58 | override fun scrollVerticallyBy(
59 | dy: Int,
60 | recycler: RecyclerView.Recycler?,
61 | state: RecyclerView.State?
62 | ): Int {
63 | return super.scrollVerticallyBy(dy, recycler, state)
64 | }
65 |
66 |
67 | override fun scrollToPosition(position: Int) {
68 | super.scrollToPosition(position)
69 | }
70 |
71 | override fun smoothScrollToPosition(
72 | recyclerView: RecyclerView?,
73 | state: RecyclerView.State?,
74 | position: Int
75 | ) {
76 | super.smoothScrollToPosition(recyclerView, state, position)
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/PathView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.*
5 | import android.util.AttributeSet
6 | import android.view.MotionEvent
7 | import android.view.View
8 | import com.starts.hencoderview.util.dp2px
9 | import com.starts.hencoderview.util.getMaterialColor
10 | import kotlin.math.min
11 |
12 | /**
13 |
14 | *文件描述:.
15 | *作者:Created by LostStars on 2020/11/26.
16 | *版本号:1.0
17 |
18 | */
19 | class PathView : View {
20 |
21 | private var path = Path()
22 | private val darkPaint = Paint(Paint.ANTI_ALIAS_FLAG)
23 | private val lightPaint = Paint(Paint.ANTI_ALIAS_FLAG)
24 | var startPercentage = 20
25 | set(value) {
26 | field = value
27 | calculationPath()
28 | }
29 |
30 | var endPercentage = 80
31 | set(value) {
32 | if(value>startPercentage){
33 | field = value
34 | calculationPath()
35 |
36 | }
37 | }
38 |
39 | val lightPoints = ArrayList()
40 | val darkPoints = ArrayList()
41 |
42 | private val singleDistance = 1
43 |
44 | init {
45 | darkPaint.style = Paint.Style.STROKE
46 | darkPaint.strokeWidth = dp2px(4) * 1f
47 | darkPaint.color = getMaterialColor(resources,3)
48 |
49 | lightPaint.style = Paint.Style.STROKE
50 | lightPaint.strokeWidth = dp2px(4) * 1f
51 | lightPaint.color = getMaterialColor(resources,4)
52 |
53 | }
54 |
55 | constructor(context: Context?) : super(context)
56 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
57 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
58 | context,
59 | attrs,
60 | defStyleAttr
61 | )
62 |
63 | constructor(
64 | context: Context?,
65 | attrs: AttributeSet?,
66 | defStyleAttr: Int,
67 | defStyleRes: Int
68 | ) : super(context, attrs, defStyleAttr, defStyleRes)
69 |
70 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
71 | super.onSizeChanged(w, h, oldw, oldh)
72 | }
73 |
74 | override fun onTouchEvent(event: MotionEvent): Boolean {
75 | when(event.action){
76 | MotionEvent.ACTION_DOWN->{
77 | path.moveTo(event.x , event.y)
78 | return true
79 | }
80 | MotionEvent.ACTION_MOVE->{
81 | path.lineTo(event.x,event.y)
82 | }
83 | MotionEvent.ACTION_UP->{
84 | calculationPath()
85 | }
86 | MotionEvent.ACTION_CANCEL->{
87 |
88 | invalidate()
89 | }
90 | }
91 |
92 | return super.onTouchEvent(event)
93 | }
94 |
95 | override fun onDraw(canvas: Canvas) {
96 | super.onDraw(canvas)
97 | canvas.drawPath(path,darkPaint)
98 | canvas.drawPoints(lightPoints.toFloatArray(),lightPaint)
99 | }
100 |
101 |
102 | private fun calculationPath(){
103 | val pathMeasure = PathMeasure(path ,false)
104 | val pathLength = pathMeasure.length
105 | val count = (pathLength/singleDistance).toInt()
106 | var currentDistance = 0f
107 | for (i in 0 until count){
108 | currentDistance+=singleDistance
109 | val point = FloatArray(2)
110 | pathMeasure.getPosTan(currentDistance,point,null)
111 | darkPoints.add(point[0])
112 | darkPoints.add(point[1])
113 | }
114 | val start = (darkPoints.size * startPercentage*1f /100).toInt()
115 | val end = min((darkPoints.size * endPercentage *1f/100).toInt(), darkPoints.size-1)
116 | lightPoints.clear()
117 | lightPoints.addAll(darkPoints.subList(start,end))
118 | invalidate()
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/PreViewOutLineProvider.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.graphics.Outline
4 | import android.graphics.Rect
5 | import android.view.View
6 | import android.view.ViewOutlineProvider
7 |
8 | class PreViewOutLineProvider : ViewOutlineProvider() {
9 | override fun getOutline(view: View?, outline: Outline?) {
10 | val rect = Rect()
11 | view?.getGlobalVisibleRect(rect)
12 | val selfRect = Rect(0,0,rect.width(),rect.height())
13 | outline?.setRoundRect(selfRect,20f)
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/ScaleImageView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.animation.ObjectAnimator
4 | import android.content.Context
5 | import android.graphics.Bitmap
6 | import android.graphics.BitmapFactory
7 | import android.graphics.Canvas
8 | import android.graphics.Paint
9 | import android.util.AttributeSet
10 | import android.util.Log
11 | import android.view.GestureDetector
12 | import android.view.MotionEvent
13 | import android.view.View
14 | import android.widget.OverScroller
15 | import androidx.core.view.GestureDetectorCompat
16 | import com.starts.hencoderview.R
17 | import kotlin.math.max
18 | import kotlin.math.min
19 |
20 | /**
21 |
22 | *文件描述:.
23 | *作者:Created by Administrator on 2020/5/20.
24 | *版本号:1.0
25 |
26 | */
27 | class ScaleImageView(context: Context, attrs: AttributeSet) : View(context, attrs),
28 | GestureDetector.OnDoubleTapListener, GestureDetector.OnGestureListener , Runnable {
29 |
30 | private var originalOffsetX = 0f
31 | private var originalOffsetY = 0f
32 |
33 | private var currentScale = 0f
34 | set(value) {
35 | field = value
36 | invalidate()
37 | }
38 | private var bigScale = 0f
39 | private var smallScale = 0f
40 |
41 | private val OVER_SCALE_FACTOR = 1.2f
42 |
43 | private var isBig = false
44 |
45 | private var offsetX = 0f
46 | private var offsetY = 0f
47 |
48 | private val gestureDetector: GestureDetectorCompat by lazy {
49 | GestureDetectorCompat(context, this)
50 | }
51 |
52 | private val scroller = OverScroller(context)
53 |
54 | private val scaleAnimation: ObjectAnimator by lazy {
55 | ObjectAnimator.ofFloat(this, "currentScale", smallScale, bigScale)
56 | }
57 |
58 | private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
59 | private val bitmap: Bitmap by lazy {
60 | BitmapFactory.decodeResource(context.resources, R.mipmap.ui_scale_img_2)
61 | }
62 |
63 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
64 | super.onSizeChanged(w, h, oldw, oldh)
65 |
66 | originalOffsetX = (width * 1f - bitmap.width) / 2f
67 | originalOffsetY = (height * 1f - bitmap.height) / 2f
68 |
69 | if (bitmap.width * 1f / bitmap.height > width * 1f / height) {
70 | smallScale = width * 1f / bitmap.width
71 | bigScale = height * 1f / bitmap.height * OVER_SCALE_FACTOR
72 | } else {
73 | smallScale = height * 1f / bitmap.height
74 | bigScale = width * 1f / bitmap.width * OVER_SCALE_FACTOR
75 | }
76 | currentScale = smallScale
77 | }
78 |
79 | override fun onDraw(canvas: Canvas) {
80 | super.onDraw(canvas)
81 | val scaleFraction = (currentScale - smallScale) / (bigScale - smallScale)
82 | canvas.translate(offsetX * scaleFraction, offsetY * scaleFraction)
83 | //参数里的 sx sy 是横向和纵向的放缩倍数; px py 是放缩的轴心。
84 | canvas.scale(currentScale, currentScale, width / 2f, height / 2f)
85 | canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint)
86 |
87 | }
88 |
89 | override fun onTouchEvent(event: MotionEvent): Boolean {
90 | return gestureDetector.onTouchEvent(event)
91 | }
92 |
93 | override fun onDoubleTap(e: MotionEvent): Boolean {
94 | isBig = !isBig
95 | if (isBig) {
96 | scaleAnimation.start()
97 | } else {
98 | scaleAnimation.reverse()
99 | }
100 | return false
101 | }
102 |
103 | override fun onDoubleTapEvent(e: MotionEvent) = false
104 | override fun onSingleTapConfirmed(e: MotionEvent) = false
105 | override fun onShowPress(e: MotionEvent) {
106 |
107 | }
108 |
109 | override fun onSingleTapUp(e: MotionEvent): Boolean = false
110 |
111 | override fun onDown(e: MotionEvent) = true
112 |
113 | override fun onFling(
114 | e1: MotionEvent,
115 | e2: MotionEvent,
116 | velocityX: Float,
117 | velocityY: Float
118 | ): Boolean {
119 | if (isBig) {
120 | scroller.fling(
121 | offsetX.toInt(),
122 | offsetY.toInt(),
123 | velocityX.toInt(),
124 | velocityY.toInt(),
125 | (-(bitmap.width * bigScale - width * 1f) / 2).toInt(),
126 | ((bitmap.width * bigScale - width * 1f) / 2).toInt(),
127 | (-(bitmap.height * bigScale - height * 1f) / 2).toInt(),
128 | ((bitmap.height * bigScale - height * 1f) / 2).toInt(),
129 | 100,
130 | 100
131 | )
132 | postOnAnimation(this)
133 |
134 | }
135 |
136 |
137 | return false
138 | }
139 |
140 | override fun onScroll(
141 | e1: MotionEvent,
142 | e2: MotionEvent,
143 | distanceX: Float,
144 | distanceY: Float
145 | ): Boolean {
146 | Log.d("TEST" ,"distanceY = $distanceY,distanceX = $distanceX")
147 | if (isBig) {
148 | offsetX -= distanceX
149 | offsetY -= distanceY
150 | fixOffsets()
151 | Log.d("onScroll", "distanceX:${distanceX} , distanceY:${distanceY}")
152 | invalidate()
153 | }
154 | return false
155 | }
156 |
157 | override fun onLongPress(e: MotionEvent) {
158 |
159 | }
160 |
161 | private fun fixOffsets() {
162 | offsetX = min(offsetX, (bitmap.width * bigScale - width * 1f) / 2)
163 | offsetX = max(offsetX, -(bitmap.width * bigScale - width * 1f) / 2)
164 |
165 | offsetY = min(offsetY, (bitmap.height * bigScale - height * 1f) / 2)
166 | offsetY = max(offsetY, -(bitmap.height * bigScale - height * 1f) / 2)
167 |
168 | }
169 |
170 | override fun run() {
171 | if(scroller.computeScrollOffset()){
172 | offsetX = scroller.currX.toFloat()
173 | offsetY = scroller.currY.toFloat()
174 | invalidate()
175 | postOnAnimation(this)
176 | }
177 | }
178 |
179 |
180 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/SportView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.*
5 | import android.util.AttributeSet
6 | import android.view.View
7 | import com.starts.hencoderview.util.dp2px
8 | import com.starts.hencoderview.util.sp
9 |
10 | /**
11 |
12 | *文件描述:.
13 | *作者:Created by Administrator on 2020/4/29.
14 | *版本号:1.0
15 |
16 | */
17 | class SportView(context: Context, attrs: AttributeSet) : View(context, attrs) {
18 |
19 | private val outCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG)
20 | private val intCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG)
21 | private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
22 |
23 | lateinit var pointCenter: Point
24 |
25 | val radius = dp2px(100)
26 |
27 | val lineWidth = dp2px(15).toFloat()
28 |
29 | lateinit var rectF: RectF
30 |
31 | var progress = 0
32 | get() {
33 | return field
34 | }
35 | set(value) {
36 | field = value
37 | invalidate()
38 | }
39 |
40 | private val maxProgress = 100f
41 |
42 | private var bounds: Rect
43 |
44 |
45 | init {
46 | outCirclePaint.color = Color.parseColor("#cccccc")
47 | intCirclePaint.color = Color.parseColor("#45A4FF")
48 | textPaint.color = Color.parseColor("#45A4FF")
49 |
50 | outCirclePaint.strokeWidth = lineWidth
51 | intCirclePaint.strokeWidth = lineWidth
52 |
53 | outCirclePaint.strokeCap = Paint.Cap.ROUND
54 | intCirclePaint.strokeCap = Paint.Cap.ROUND
55 |
56 | outCirclePaint.style = Paint.Style.STROKE
57 | intCirclePaint.style = Paint.Style.STROKE
58 |
59 | textPaint.textSize = sp(32)
60 |
61 | bounds = Rect()
62 | }
63 |
64 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
65 | super.onSizeChanged(w, h, oldw, oldh)
66 | pointCenter = Point(w / 2, h / 2)
67 | rectF = RectF(
68 | pointCenter.x - radius.toFloat(),
69 | pointCenter.y - radius.toFloat(),
70 | pointCenter.x + radius.toFloat(),
71 | pointCenter.y + radius.toFloat()
72 | )
73 | }
74 |
75 | override fun onDraw(canvas: Canvas) {
76 | super.onDraw(canvas)
77 | canvas.drawCircle(
78 | pointCenter.x.toFloat(),
79 | pointCenter.y.toFloat(),
80 | radius.toFloat(),
81 | outCirclePaint
82 | )
83 |
84 | val angle = progress / maxProgress * 360
85 | canvas.drawArc(rectF, 270f, angle, false, intCirclePaint)
86 |
87 | textPaint.getTextBounds("${progress}%", 0, "${progress}%".length, bounds)
88 | canvas.drawText(
89 | "${progress}%",
90 | 0,
91 | "${progress}%".length,
92 | pointCenter.x - bounds.width() / 2f,
93 | pointCenter.y + bounds.height() / 2f,
94 | textPaint
95 | )
96 |
97 | }
98 |
99 |
100 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TagLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.Rect
6 | import android.util.AttributeSet
7 | import android.view.ViewGroup
8 | import kotlin.math.max
9 |
10 |
11 | /**
12 |
13 | *文件描述:.
14 | *作者:Created by Administrator on 2020/5/9.
15 | *版本号:1.0
16 | */
17 | class TagLayout(context: Context?, attrs: AttributeSet?) : ViewGroup(context, attrs) {
18 |
19 | private val childBounds = ArrayList()
20 | //1.调用每个子 View 的 measure() 来计算子 View 的尺寸
21 | //2.计算子 View 的位置并保存子 View 的位置和尺寸
22 | //3.计算自己的尺寸并用 setMeasuredDimension() 保存
23 | @SuppressLint("DrawAllocation")
24 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
25 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
26 |
27 | val widthMeasureSize = MeasureSpec.getSize(widthMeasureSpec)
28 | // val widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec)
29 |
30 | var widthUsed = 0
31 | var heightUsed = 0
32 |
33 | var maxHeight = 0
34 | var lineWidthUsed = 0
35 |
36 | for(i in 0 until childCount ){
37 | val child = getChildAt(i)
38 | measureChildWithMargins(child,widthMeasureSpec, 0 , heightMeasureSpec ,heightUsed)
39 | if(lineWidthUsed + child.measuredWidth > widthMeasureSize){
40 | heightUsed += maxHeight
41 | maxHeight = 0
42 | lineWidthUsed = 0
43 | measureChildWithMargins(child,widthMeasureSpec, 0 , heightMeasureSpec,heightUsed)
44 | }
45 |
46 |
47 | if(i < childBounds.size){
48 | val bound = childBounds[i]
49 | bound.set(lineWidthUsed , heightUsed, lineWidthUsed + child.measuredWidth ,heightUsed + child.measuredHeight)
50 | }else{
51 | val bound = Rect()
52 | bound.set(lineWidthUsed , heightUsed, lineWidthUsed + child.measuredWidth ,heightUsed + child.measuredHeight)
53 | childBounds.add(bound)
54 | }
55 |
56 | maxHeight = max(maxHeight , child.measuredHeight)
57 |
58 | lineWidthUsed += child.measuredWidth
59 |
60 | widthUsed = max(lineWidthUsed , widthUsed)
61 | }
62 | setMeasuredDimension(widthUsed , heightUsed+ maxHeight)
63 | }
64 |
65 | override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
66 | for (i in 0 until childCount) {
67 | val bound = childBounds[i]
68 | val child = getChildAt(i)
69 | child.layout(bound.left, bound.top, bound.right, bound.bottom)
70 | }
71 |
72 | }
73 |
74 | override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
75 | return MarginLayoutParams(context, attrs)
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestButton.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Paint
6 | import android.util.AttributeSet
7 | import androidx.appcompat.widget.AppCompatButton
8 | import androidx.core.content.ContextCompat
9 | import com.starts.hencoderview.R
10 | import timber.log.Timber
11 |
12 | class TestButton:AppCompatButton {
13 |
14 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
15 | color = ContextCompat.getColor(context, R.color.pink_300)
16 | textSize = 32f
17 | }
18 | var measureCount = 0
19 |
20 | constructor(context: Context) : super(context)
21 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
22 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
23 | context,
24 | attrs,
25 | defStyleAttr
26 | )
27 |
28 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
29 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
30 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
31 | }
32 |
33 | override fun onDraw(canvas: Canvas) {
34 | super.onDraw(canvas)
35 | canvas.drawText("c=$measureCount" , 40f,40f,paint)
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestConstraintLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Paint
7 | import android.util.AttributeSet
8 | import androidx.constraintlayout.widget.ConstraintLayout
9 | import androidx.core.content.ContextCompat
10 | import com.starts.hencoderview.R
11 | import timber.log.Timber
12 |
13 | class TestConstraintLayout : ConstraintLayout {
14 | init {
15 | setWillNotDraw(false)
16 | }
17 |
18 | private var measureCount = 0
19 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
20 | color = ContextCompat.getColor(context, R.color.pink_300)
21 | textSize = 32f
22 | }
23 |
24 | constructor(context: Context) : super(context)
25 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
26 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
27 | context,
28 | attrs,
29 | defStyleAttr
30 | )
31 |
32 | constructor(
33 | context: Context,
34 | attrs: AttributeSet?,
35 | defStyleAttr: Int,
36 | defStyleRes: Int
37 | ) : super(context, attrs, defStyleAttr, defStyleRes)
38 |
39 |
40 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
41 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
42 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
43 | }
44 |
45 | override fun onDraw(canvas: Canvas) {
46 | super.onDraw(canvas)
47 | canvas.drawText("measureCount = $measureCount" , 40f,40f,paint)
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestEditText.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Paint
6 | import android.util.AttributeSet
7 | import android.widget.EditText
8 | import androidx.core.content.ContextCompat
9 | import com.starts.hencoderview.R
10 | import timber.log.Timber
11 |
12 | class TestEditText : androidx.appcompat.widget.AppCompatEditText {
13 |
14 |
15 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
16 | color = ContextCompat.getColor(context, R.color.pink_300)
17 | textSize = 32f
18 | }
19 | var measureCount = 0
20 |
21 | constructor(context: Context) : super(context)
22 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
23 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
24 | context,
25 | attrs,
26 | defStyleAttr
27 | )
28 |
29 |
30 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
31 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
32 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
33 | }
34 |
35 | override fun onDraw(canvas: Canvas) {
36 | super.onDraw(canvas)
37 | canvas.drawText("c=$measureCount" , 40f,40f,paint)
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestFloatActionButton.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Paint
6 | import android.util.AttributeSet
7 | import androidx.core.content.ContextCompat
8 | import com.starts.hencoderview.R
9 | import timber.log.Timber
10 |
11 | class TestFloatActionButton : com.google.android.material.floatingactionbutton.FloatingActionButton {
12 |
13 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
14 | color = ContextCompat.getColor(context, R.color.pink_300)
15 | textSize = 32f
16 | }
17 | var measureCount = 0
18 | constructor(context: Context) : super(context)
19 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
20 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
21 | context,
22 | attrs,
23 | defStyleAttr
24 | )
25 |
26 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
27 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
28 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
29 | }
30 |
31 | override fun onDraw(canvas: Canvas) {
32 | super.onDraw(canvas)
33 | canvas.drawText("c=$measureCount" , 40f,40f,paint)
34 | }
35 |
36 |
37 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestImageView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Paint
7 | import android.util.AttributeSet
8 | import androidx.appcompat.widget.AppCompatImageView
9 | import androidx.core.content.ContextCompat
10 | import com.starts.hencoderview.R
11 | import timber.log.Timber
12 |
13 | class TestImageView: AppCompatImageView{
14 |
15 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
16 | color = ContextCompat.getColor(context, R.color.pink_300)
17 | textSize = 32f
18 | }
19 | var measureCount = 0
20 |
21 | constructor(context: Context) : super(context)
22 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
23 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
24 | context,
25 | attrs,
26 | defStyleAttr
27 | )
28 |
29 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
30 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
31 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
32 | }
33 |
34 | override fun onDraw(canvas: Canvas) {
35 | super.onDraw(canvas)
36 | canvas.drawText("c=$measureCount" , 40f,40f,paint)
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestLinearLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Paint
7 | import android.util.AttributeSet
8 | import android.widget.LinearLayout
9 | import timber.log.Timber
10 |
11 | class TestLinearLayout :LinearLayout {
12 | init {
13 | setWillNotDraw(false)
14 | }
15 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
16 | color = Color.BLACK
17 | textSize = 32f
18 | }
19 | private var measureCount = 0
20 | constructor(context: Context?) : super(context)
21 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
22 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
23 | context,
24 | attrs,
25 | defStyleAttr
26 | )
27 |
28 | constructor(
29 | context: Context?,
30 | attrs: AttributeSet?,
31 | defStyleAttr: Int,
32 | defStyleRes: Int
33 | ) : super(context, attrs, defStyleAttr, defStyleRes)
34 |
35 |
36 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
37 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
38 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
39 | }
40 |
41 | override fun onDraw(canvas: Canvas) {
42 | super.onDraw(canvas)
43 | canvas.drawText("measureCount = $measureCount" , 40f,40f,paint)
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestTextView.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Paint
7 | import android.util.AttributeSet
8 | import androidx.core.content.ContextCompat
9 | import com.starts.hencoderview.R
10 | import timber.log.Timber
11 |
12 | class TestTextView :androidx.appcompat.widget.AppCompatTextView {
13 | val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
14 | color = ContextCompat.getColor(context, R.color.pink_300)
15 | textSize = 32f
16 | }
17 | var measureCount = 0
18 | constructor(context: Context) : super(context)
19 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
20 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
21 | context,
22 | attrs,
23 | defStyleAttr
24 | )
25 |
26 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
27 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
28 | Timber.tag("onMeasure").d("measureCount = ${measureCount++} hashcode = ${this.hashCode()}")
29 | }
30 |
31 | override fun onDraw(canvas: Canvas) {
32 | super.onDraw(canvas)
33 | canvas.drawText("c=$measureCount" , 40f,40f,paint)
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TestViewLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.util.AttributeSet
6 | import androidx.core.content.ContextCompat
7 | import androidx.core.view.setPadding
8 | import com.starts.hencoderview.R
9 |
10 | class TestViewLayout(context: Context) : CustomLayout(context) {
11 |
12 | private val topBg = TestTextView(context).apply {
13 | this.layoutParams = LayoutParams(matchParent, matchParent)
14 | setBackgroundColor(ContextCompat.getColor(context, R.color.blue_300))
15 | }
16 |
17 | private val bottomBg = TestTextView(context).apply {
18 | this.layoutParams = LayoutParams(matchParent, matchParent)
19 | setBackgroundColor(ContextCompat.getColor(context, R.color.blue_grey_300))
20 | }
21 |
22 | val leftTv = TestTextView(context).apply {
23 | this.layoutParams = LayoutParams(matchParent, wrapContent)
24 | setBackgroundColor(ContextCompat.getColor(context, R.color.red_300))
25 | setPadding(20.dp)
26 | }
27 |
28 | private val rightTv = TestTextView(context).apply {
29 | this.layoutParams = LayoutParams(matchParent, wrapContent)
30 | setBackgroundColor(ContextCompat.getColor(context, R.color.orange_300))
31 | setPadding(20.dp)
32 | }
33 |
34 | init {
35 | this@TestViewLayout.addView(topBg)
36 | this@TestViewLayout.addView(bottomBg)
37 | this@TestViewLayout.addView(leftTv)
38 | this@TestViewLayout.addView(rightTv)
39 | setBackgroundColor(Color.WHITE)
40 | }
41 |
42 | override fun onMeasureChildren(widthMeasureSpec: Int, heightMeasureSpec: Int): Dimension {
43 | topBg.measure(
44 | widthMeasureSpec,
45 | ((MeasureSpec.getSize(heightMeasureSpec) - 80.dp) / 2).toExactlyMeasureSpec()
46 | )
47 | bottomBg.measure(
48 | widthMeasureSpec,
49 | ((MeasureSpec.getSize(heightMeasureSpec) - 80.dp) / 2).toExactlyMeasureSpec()
50 | )
51 | leftTv.measure(
52 | (MeasureSpec.getSize(widthMeasureSpec) / 2).toExactlyMeasureSpec(),
53 | MeasureSpec.getSize(heightMeasureSpec).toAtMostMeasureSpec()
54 | )
55 | rightTv.measure(
56 | (MeasureSpec.getSize(widthMeasureSpec) / 2).toExactlyMeasureSpec(),
57 | MeasureSpec.getSize(heightMeasureSpec).toAtMostMeasureSpec()
58 | )
59 |
60 | return Dimension(widthMeasureSpec, heightMeasureSpec)
61 | }
62 |
63 | override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
64 | topBg.layout(0, 40.dp)
65 | bottomBg.layout(0, topBg.bottom)
66 | leftTv.layout(0, topBg.top + topBg.verticalCenterTop(leftTv))
67 | rightTv.layout(leftTv.right, topBg.top + topBg.verticalCenterTop(rightTv))
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/starts/hencoderview/view/TransitionLayout.kt:
--------------------------------------------------------------------------------
1 | package com.starts.hencoderview.view
2 |
3 | import android.animation.LayoutTransition
4 | import android.content.Context
5 | import android.view.Gravity
6 | import android.view.View
7 | import android.widget.Button
8 | import android.widget.TextView
9 | import androidx.core.content.ContextCompat
10 | import androidx.core.view.forEach
11 | import com.starts.hencoderview.R
12 | import com.starts.hencoderview.container.CustomLayout
13 | import com.starts.hencoderview.container.matchParent
14 | import com.starts.hencoderview.container.wrapContent
15 | import com.starts.hencoderview.util.dp2px
16 | import com.starts.hencoderview.util.getScreenWidth
17 |
18 | class TransitionLayout(context: Context) : CustomLayout(context) {
19 | val button = Button(context).apply {
20 | layoutParams = LayoutParams(dp2px(120), dp2px(40))
21 | text ="增加"
22 | gravity = Gravity.CENTER
23 | }
24 |
25 | val tv1 = TextView(context).apply {
26 | layoutParams = LayoutParams(getScreenWidth()/2 , getScreenWidth()/2)
27 | setBackgroundColor(ContextCompat.getColor(context, R.color.blue_300))
28 | text = "tv1"
29 | }
30 |
31 | val tv2 = TextView(context).apply {
32 | layoutParams = CustomLayout.LayoutParams(getScreenWidth() / 2, getScreenWidth() / 2)
33 | setBackgroundColor(ContextCompat.getColor(context, R.color.red_300))
34 | text = "tv2"
35 | }
36 |
37 | init {
38 | this@TransitionLayout.addView(button)
39 | this@TransitionLayout.addView(tv1)
40 |
41 | }
42 |
43 | override fun onMeasureChildren(widthMeasureSpec: Int, heightMeasureSpec: Int): Dimension {
44 | forEachAutoMeasure()
45 | return Dimension(matchParent, matchParent)
46 | }
47 |
48 | override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
49 | tv1.layout(0,0)
50 | tv2.layout(0, tv1.right)
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/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/drawable/ic_star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/drawable/ic_star.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/info_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/layer_list_progress_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 | -
9 |
10 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/playing_com_into.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/drawable/playing_com_into.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_progressbar_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_progressbar_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/singapore.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/drawable/singapore.jpg
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_memory_lead.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/chin_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
23 |
24 |
36 |
37 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/constrain_compare_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
26 |
27 |
34 |
35 |
45 |
46 |
54 |
55 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/constrain_layout_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
22 |
32 |
33 |
38 |
39 |
44 |
45 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/demo_main_holder.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/google_contrain_demo.xml:
--------------------------------------------------------------------------------
1 |
13 |
19 |
20 |
33 |
34 |
47 |
48 |
58 |
59 |
71 |
72 |
81 |
82 |
91 |
92 |
104 |
105 |
114 |
115 |
124 |
125 |
141 |
142 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/holder_bootom_sheet.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
18 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_barrier_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
28 |
29 |
35 |
36 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/lineralayout_ui_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
18 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
37 |
42 |
43 |
52 |
53 |
57 |
58 |
69 |
70 |
84 |
85 |
86 |
87 |
88 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/radio_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
26 |
27 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/relatelayout_gone_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
24 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/relatelayout_ui_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
17 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
42 |
43 |
54 |
55 |
64 |
65 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_constraint_layout_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
27 |
28 |
29 |
40 |
41 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_ellipsized_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_flip_bord_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
26 |
27 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_linear_layout_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
33 |
34 |
35 |
36 |
37 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_music_album_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
26 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_particle_scattering.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_path.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
22 |
23 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_scale_alpha.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_scale_image_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_sport_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_viewpager_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
22 |
30 |
31 |
32 |
33 |
34 |
41 |
42 |
43 |
51 |
52 |
53 |
61 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/scene_wave_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_douban_bottom_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_douban_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
30 |
31 |
44 |
45 |
56 |
57 |
67 |
68 |
77 |
78 |
90 |
91 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/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/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/avatar.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/avatar.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/img_egg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/img_egg.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/personal_bg.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/personal_bg.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/red.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/share.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/share.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ui_scale_img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/ui_scale_img.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ui_scale_img_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xhdpi/ui_scale_img_2.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #1565c0
4 | #0d47a1
5 | #42a5f5
6 | #ffffffff
7 |
8 | #f36c60
9 | #aed581
10 | #4db6ac
11 | #f06292
12 | #a1887f
13 | #ba68c8
14 | #dce775
15 | #90a4ae
16 | #9575cd
17 | #42bd41
18 | #7986cb
19 | #fff176
20 | #91a7ff
21 | #ffb74d
22 | #4fc3f7
23 | #ff8a65
24 | #4dd0e1
25 | #e0e0e0
26 | #78909C
27 |
28 | #99000000
29 | #fafafa
30 |
31 | #4D97FF
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 56dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/material_colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @color/red_300
5 | - @color/light_green_300
6 | - @color/teal_300
7 | - @color/pink_300
8 | - @color/brown_300
9 | - @color/purple_300
10 | - @color/lime_300
11 | - @color/blue_grey_300
12 | - @color/deep_purple_300
13 | - @color/green_300
14 | - @color/indigo_300
15 | - @color/yellow_300
16 | - @color/blue_300
17 | - @color/orange_300
18 | - @color/light_blue_300
19 | - @color/deep_orange_300
20 | - @color/cyan_300
21 | - @color/grey_300
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HenCoderView
3 | 在Android界面的展示中,我们常见工作流程是在xml写好布局文件,之后在Activity或者fragment中展示一个UI界面, 画面需要经
4 | 过xml解析,之后window要通过setView方法实际构造各个View对象,并且按照层级排列好,之后Android系统再找到合适
5 | 的时机,触发measure,layout,draw这一流程,但是随着布局嵌套的加深,xml的解析越来越复杂,测绘的次数会逐步提高,逐渐到
6 | 达失控的地步。那么,需要一种方式可以一次性解决这个问题,让我们的布局不再需要多次测量来确定自己的宽高,答案就是完全自己
7 | 接管ViewGroup的onMeasure,onLayout过程
8 | As the sun began to set over the horizon, Mary sat on the edge of the pier and dangled her feet over the water. She had come to this spot every evening for the past week in an attempt to clear her mind and find some peace. As the sound of waves lapping against the pier filled her ears, Mary closed her eyes and took a deep breath in, allowing the salty air to fill her lungs. For a moment, she felt truly content and at ease, the worries of her everyday life fading away into the background. As the last rays of sunlight disappeared over the water, Mary stood up from the pier and walked back towards her car, feeling refreshed and ready to take on whatever challenges the next day may bring.
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.7.20'
5 | repositories {
6 | google()
7 | mavenCentral()
8 | maven { url 'https://jitpack.io' }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:7.1.2'
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 | google()
22 | mavenCentral()
23 | maven { url 'https://jitpack.io' }
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
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
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhaoSiBo/HenCoderView/9fb1557a4c1fcfcea9e1334545bde840fa3652a4/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Apr 28 10:22:33 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-7.4-bin.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 | rootProject.name='HenCoderView'
2 | include ':app'
3 |
--------------------------------------------------------------------------------