├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── dbnavigator.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── DUGIFMaker ├── font │ ├── Elsie-Black.otf │ ├── HappyMonkey-Regular.ttf │ ├── Lobster-1.3.ttf │ ├── MacondoSwashCaps-Regular.ttf │ ├── Merriweather-Bold.ttf │ ├── Nautilus.otf │ ├── Roboto-Medium.ttf │ ├── Titillium-Bold.otf │ └── skranji-regular.ttf └── recorded │ ├── 20191116_182348.gif │ ├── 20191116_182640.gif │ └── 20191116_182846.gif ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── devyk │ │ └── customview │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── devyk │ │ │ └── customview │ │ │ ├── CanvasSample.kt │ │ │ ├── CustomDrawable.kt │ │ │ ├── CustomViewGroup.kt │ │ │ ├── FlowLayoutActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MapActivity.kt │ │ │ ├── PathActivity.kt │ │ │ ├── SVGDemo1Activity.kt │ │ │ ├── TestActivity1.kt │ │ │ ├── adapter │ │ │ └── RecyclerViewAdapter.kt │ │ │ ├── sample_1 │ │ │ ├── Button.kt │ │ │ ├── CircleView.kt │ │ │ ├── CircleView2.kt │ │ │ ├── CircleView3.kt │ │ │ ├── CustomView_1.kt │ │ │ ├── GustomLIn.kt │ │ │ ├── MyRecyclerView.kt │ │ │ ├── ScrollerLayout.java │ │ │ ├── ScrollerSample_1.kt │ │ │ ├── ScrollerViewPager.kt │ │ │ ├── SlideView1.kt │ │ │ └── SlideView2.kt │ │ │ ├── sample_2 │ │ │ └── WaterfallFlowLayout.java │ │ │ └── utils │ │ │ └── QMUIDisplayHelper.java │ └── res │ │ ├── animator │ │ ├── anim_infinite.xml │ │ ├── anim_search.xml │ │ ├── anim_start.xml │ │ ├── transit.xml │ │ ├── tt_path_one.xml │ │ ├── tt_path_three.xml │ │ └── tt_path_two.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── avft.png │ │ ├── avft_active.png │ │ ├── box_stack.png │ │ ├── box_stack_active.png │ │ ├── bubble_frame.png │ │ ├── bubble_frame_active.png │ │ ├── bubbles.png │ │ ├── bubbles_active.png │ │ ├── bullseye.png │ │ ├── bullseye_active.png │ │ ├── circle_filled.png │ │ ├── circle_filled_active.png │ │ ├── circle_outline.png │ │ ├── circle_outline_active.png │ │ ├── flag_01.xml │ │ ├── heart.png │ │ ├── ic_launcher_background.xml │ │ ├── ic_line.xml │ │ ├── ic_police_car.xml │ │ ├── ic_toutiao.xml │ │ ├── line_animated_car.xml │ │ ├── line_animated_search.xml │ │ ├── line_animated_toutiao.xml │ │ ├── line_animated_vector.xml │ │ └── search_svg.xml │ │ ├── layout │ │ ├── activity_canvas_sample.xml │ │ ├── activity_custom_viewgroup.xml │ │ ├── activity_main.xml │ │ ├── activity_map.xml │ │ ├── activity_path.xml │ │ ├── activity_svg.xml │ │ ├── activity_test.xml │ │ ├── activity_test_1.xml │ │ └── tv.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── attrs_circle.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── devyk │ └── customview │ └── ExampleUnitTest.kt ├── build.gradle ├── custom_view ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── devyk │ │ └── custom_view │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── devyk │ │ │ └── custom_view │ │ │ ├── BitmapUtis.kt │ │ │ ├── base │ │ │ └── BaseView.kt │ │ │ ├── canvas │ │ │ ├── CircleView.kt │ │ │ ├── ClockView.kt │ │ │ ├── CustomDrawable.kt │ │ │ ├── DrawColor.kt │ │ │ ├── GallaryHorizonalScrollView.kt │ │ │ ├── path │ │ │ │ └── PathView.kt │ │ │ └── path_measure │ │ │ │ ├── CarRotate.kt │ │ │ │ ├── FacLoadingView.kt │ │ │ │ ├── PathMeasureView.kt │ │ │ │ └── SpiderWebView.kt │ │ │ ├── flow │ │ │ ├── FlowLayout.kt │ │ │ ├── TagAdapter.kt │ │ │ ├── TagFlowLayout.kt │ │ │ └── TagView.kt │ │ │ ├── paint │ │ │ ├── FilterView.kt │ │ │ ├── LinearGradientTextView.kt │ │ │ ├── MyGradientView.kt │ │ │ ├── RadarGradientView.kt │ │ │ ├── ShadowLayerView.kt │ │ │ ├── XfermodeView.kt │ │ │ ├── ZoomImageView.kt │ │ │ └── xfermode │ │ │ │ ├── GuaGuaCard.kt │ │ │ │ ├── HeartView.kt │ │ │ │ ├── InvertedImageView.kt │ │ │ │ ├── IrregularWaveView.kt │ │ │ │ ├── RoudImageView.kt │ │ │ │ └── TwitterView.kt │ │ │ ├── svg │ │ │ ├── PathParser.java │ │ │ └── map │ │ │ │ ├── Dom2PathData.kt │ │ │ │ ├── Dom2Xml.kt │ │ │ │ ├── MapData.kt │ │ │ │ └── MapView.kt │ │ │ ├── utils.kt │ │ │ └── utils │ │ │ ├── HelpDraw.java │ │ │ ├── HelpPath.java │ │ │ └── Utils.java │ └── res │ │ ├── drawable │ │ ├── arrow.jpg │ │ ├── car.png │ │ ├── circle_shape.png │ │ ├── flag_01.xml │ │ ├── guaguaka.jpg │ │ ├── guaguaka_text1.png │ │ ├── heartmap.png │ │ ├── invert_shade.png │ │ ├── shade.png │ │ ├── twiter_bg.png │ │ ├── twiter_light.png │ │ └── wav.png │ │ ├── layout │ │ └── tv.xml │ │ ├── mipmap-xhdpi │ │ ├── gild_3.jpeg │ │ ├── girl_gaitubao.jpg │ │ └── heart.png │ │ ├── raw │ │ └── chinahigh.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── devyk │ └── custom_view │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 26 | 27 | 28 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 53 | 54 | 55 | 57 | 58 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DUGIFMaker/font/Elsie-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/Elsie-Black.otf -------------------------------------------------------------------------------- /DUGIFMaker/font/HappyMonkey-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/HappyMonkey-Regular.ttf -------------------------------------------------------------------------------- /DUGIFMaker/font/Lobster-1.3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/Lobster-1.3.ttf -------------------------------------------------------------------------------- /DUGIFMaker/font/MacondoSwashCaps-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/MacondoSwashCaps-Regular.ttf -------------------------------------------------------------------------------- /DUGIFMaker/font/Merriweather-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/Merriweather-Bold.ttf -------------------------------------------------------------------------------- /DUGIFMaker/font/Nautilus.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/Nautilus.otf -------------------------------------------------------------------------------- /DUGIFMaker/font/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/Roboto-Medium.ttf -------------------------------------------------------------------------------- /DUGIFMaker/font/Titillium-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/Titillium-Bold.otf -------------------------------------------------------------------------------- /DUGIFMaker/font/skranji-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/font/skranji-regular.ttf -------------------------------------------------------------------------------- /DUGIFMaker/recorded/20191116_182348.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/recorded/20191116_182348.gif -------------------------------------------------------------------------------- /DUGIFMaker/recorded/20191116_182640.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/recorded/20191116_182640.gif -------------------------------------------------------------------------------- /DUGIFMaker/recorded/20191116_182846.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/DUGIFMaker/recorded/20191116_182846.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CustomViewSample 2 | 自定义View 练习 demo 。 3 | 4 | ## 关联文章 5 | - [高级 UI 成长之路 (一) View 的基础知识你必须知道](https://juejin.im/post/5dcff9d3f265da0bd20af0da) 6 | - [高级 UI 成长之路 (二) 深入理解 Android 8.0 View 触摸事件分发机制](https://juejin.im/post/5dd7a4796fb9a07a8f412d17) 7 | - [高级 UI 成长之路 (三) 理解 View 工作原理并带你入自定义 View 门](https://juejin.im/post/5ddff234518825793218d2e4) 8 | - [高级 UI 成长之路 (四) Paint 渲染/滤镜/xfermode 使用](https://juejin.im/post/5de36c43f265da05de5881e8) 9 | - [高级 UI 成长之路 (五) 看完该篇文章 Canvas 你应该会了](https://juejin.im/post/5de514fcf265da060115e02d) 10 | - [高级 UI 成长之路 (六) PathMeasure 制作路径动画](https://juejin.im/post/5de789dce51d4557e76a4a39) 11 | - [高级 UI 成长之路 (七) SVG 基础使用 + 绘制中国地图](https://juejin.im/post/5deb6d41e51d4558052f16ac) 12 | 13 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 29 9 | buildToolsVersion "29.0.2" 10 | defaultConfig { 11 | applicationId "com.devyk.customview" 12 | minSdkVersion 15 13 | targetSdkVersion 29 14 | versionCode 1 15 | versionName "1.0" 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 29 | implementation 'androidx.appcompat:appcompat:1.1.0' 30 | implementation 'androidx.core:core-ktx:1.1.0' 31 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 32 | testImplementation 'junit:junit:4.12' 33 | androidTestImplementation 'androidx.test:runner:1.2.0' 34 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 35 | implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01' 36 | implementation project(path: ':custom_view') 37 | } 38 | -------------------------------------------------------------------------------- /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/devyk/customview/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.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.getTargetContext() 22 | assertEquals("com.devyk.customview", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/CanvasSample.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.app.Activity 4 | import android.graphics.drawable.Drawable 5 | import android.os.Build 6 | import android.os.Bundle 7 | import com.devyk.custom_view.utils.Utils 8 | import kotlinx.android.synthetic.main.activity_canvas_sample.* 9 | 10 | /** 11 | *
12 |  *     author  : devyk on 2019-12-01 20:22
13 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
14 |  *     github  : https://github.com/yangkun19921001
15 |  *     mailbox : yang1001yk@gmail.com
16 |  *     desc    : This is CanvasSample
17 |  * 
18 | */ 19 | class CanvasSample : Activity() { 20 | 21 | private val mImgIds = intArrayOf(//7个 22 | R.drawable.avft, 23 | R.drawable.box_stack, 24 | R.drawable.bubble_frame, 25 | R.drawable.bubbles, 26 | R.drawable.bullseye, 27 | R.drawable.circle_filled, 28 | R.drawable.circle_outline, 29 | 30 | R.drawable.avft, 31 | R.drawable.box_stack, 32 | R.drawable.bubble_frame, 33 | R.drawable.bubbles, 34 | R.drawable.bullseye, 35 | R.drawable.circle_filled, 36 | R.drawable.circle_outline 37 | ) 38 | private val mImgIds_active = intArrayOf( 39 | R.drawable.avft_active, 40 | R.drawable.box_stack_active, 41 | R.drawable.bubble_frame_active, 42 | R.drawable.bubbles_active, 43 | R.drawable.bullseye_active, 44 | R.drawable.circle_filled_active, 45 | R.drawable.circle_outline_active, 46 | R.drawable.avft_active, 47 | R.drawable.box_stack_active, 48 | R.drawable.bubble_frame_active, 49 | R.drawable.bubbles_active, 50 | R.drawable.bullseye_active, 51 | R.drawable.circle_filled_active, 52 | R.drawable.circle_outline_active 53 | ) 54 | 55 | lateinit var revealDrawables: Array 56 | 57 | 58 | override fun onCreate(savedInstanceState: Bundle?) { 59 | Utils.setActivityFullScreen(this.window) 60 | super.onCreate(savedInstanceState) 61 | // setContentView(CircleView(this)) 62 | 63 | setContentView(R.layout.activity_canvas_sample) 64 | 65 | // initData() 66 | 67 | 68 | 69 | } 70 | 71 | 72 | 73 | private fun initData() { 74 | revealDrawables = arrayOfNulls(mImgIds.size) 75 | 76 | for (i in mImgIds.indices ) { 77 | val rd = com.devyk.custom_view.canvas.CustomDrawable( 78 | resources.getDrawable(mImgIds[i]), 79 | resources.getDrawable(mImgIds_active[i]), 80 | com.devyk.custom_view.canvas.CustomDrawable.HORIZONTAL 81 | ) 82 | revealDrawables[i] = rd 83 | } 84 | // gallaryHorizonalScrollView.addImageViews(revealDrawables) 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/CustomDrawable.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.ColorFilter 5 | import android.graphics.Rect 6 | import android.graphics.drawable.Drawable 7 | import android.view.Gravity 8 | 9 | /** 10 | *
 11 |  *     author  : devyk on 2019-12-02 17:00
 12 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
 13 |  *     github  : https://github.com/yangkun19921001
 14 |  *     mailbox : yang1001yk@gmail.com
 15 |  *     desc    : This is CustomDrawable
 16 |  *
 17 |  *
 18 |  * 
19 | */ 20 | public class CustomDrawable : Drawable { 21 | 22 | lateinit var unseleter: Drawable 23 | lateinit var selecter: Drawable 24 | 25 | constructor(unseleter: Drawable, selecter: Drawable) : super() { 26 | this.selecter = selecter 27 | this.unseleter = unseleter 28 | 29 | } 30 | 31 | 32 | override fun draw(canvas: Canvas) { 33 | //得到当前自身 Drawable 的矩形区域 34 | val bounds = bounds 35 | //1. 绘制灰色部分 36 | drawGrayDraw(bounds,canvas) 37 | //2. 绘制彩色部分 38 | drawColorDraw(bounds,canvas) 39 | } 40 | 41 | 42 | /** 43 | * 绘制灰色区域 44 | * @link https://www.cnblogs.com/over140/archive/2011/12/14/2287179.html 45 | */ 46 | private fun drawGrayDraw(bound: Rect, canvas: Canvas) { 47 | val rect = Rect() 48 | Gravity.apply( 49 | Gravity.LEFT,//开始从左边或者右边开始抠图 50 | bound.width(), //目标矩形的宽 51 | bound.height(), //目标矩形的高 52 | bound,//被扣出来的 rect 53 | rect //目标 rect 54 | ) 55 | canvas.save() //保存当前画布 56 | canvas.clipRect(rect) 57 | unseleter.draw(canvas) 58 | canvas.restore() 59 | 60 | } 61 | 62 | /** 63 | * 绘制彩色区域 64 | */ 65 | private fun drawColorDraw(bounds: Rect, canvas: Canvas) { 66 | val rect = Rect() 67 | Gravity.apply( 68 | Gravity.RIGHT,//开始从左边或者右边开始抠图 69 | bounds.width()/3, //目标矩形的宽 70 | bounds.height(), //目标矩形的高 71 | bounds,//被扣出来的 rect 72 | rect //目标 rect 73 | ) 74 | canvas.save() //保存当前画布 75 | canvas.clipRect(rect) 76 | selecter.draw(canvas) 77 | canvas.restore() 78 | 79 | } 80 | 81 | 82 | override fun setAlpha(alpha: Int) { 83 | } 84 | 85 | override fun getOpacity(): Int = 0 86 | 87 | override fun setColorFilter(colorFilter: ColorFilter?) { 88 | } 89 | 90 | override fun onBoundsChange(bounds: Rect) { 91 | super.onBoundsChange(bounds) 92 | //改变之后重新赋值 93 | unseleter.bounds = bounds 94 | selecter.bounds = bounds 95 | } 96 | 97 | override fun getIntrinsicHeight(): Int { 98 | return Math.max(selecter.intrinsicHeight, unseleter.intrinsicHeight) 99 | } 100 | 101 | override fun getIntrinsicWidth(): Int { 102 | return Math.max(selecter.intrinsicWidth, unseleter.intrinsicWidth) 103 | } 104 | 105 | override fun onLevelChange(level: Int): Boolean { 106 | //如果 level 改变,提醒自己需要重绘 107 | invalidateSelf() 108 | return super.onLevelChange(level) 109 | 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/CustomViewGroup.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.app.Activity 4 | import android.graphics.Color 5 | import android.os.Bundle 6 | import android.widget.TextView 7 | import kotlinx.android.synthetic.main.activity_custom_viewgroup.* 8 | import java.util.* 9 | 10 | /** 11 | *
12 |  *     author  : devyk on 2019-11-27 17:27
13 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
14 |  *     github  : https://github.com/yangkun19921001
15 |  *     mailbox : yang1001yk@gmail.com
16 |  *     desc    : This is CustomViewGroup
17 |  * 
18 | */ 19 | class CustomViewGroup : Activity() { 20 | 21 | val content = mutableListOf("中","她其实的","我愿意","除了七仙女以外","当然最好是嫁一个又优秀又","尤其是封建社会","何况别人呢","但后来她实际上还是有很多机会的", 22 | "是一旦发现了就会被沉塘的死罪","是什么让温莎公爵舍弃江山和王位?难道真的是他们幼稚或一时冲动") 23 | 24 | 25 | val contentColor = mutableListOf(Color.BLUE,Color.RED,Color.GREEN,Color.DKGRAY,Color.BLACK, 26 | Color.YELLOW,Color.RED,Color.MAGENTA,Color.LTGRAY,Color.CYAN) 27 | 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | setContentView(R.layout.activity_custom_viewgroup) 32 | 33 | var lists = getMutableList() 34 | lists.forEach { 35 | wf.addView(it) 36 | } 37 | 38 | 39 | } 40 | 41 | private fun getMutableList(): MutableList { 42 | var testViews = mutableListOf() 43 | 44 | 45 | for (index in 0..9){ 46 | testViews.add( TextView(this).also { 47 | it.setText(content.get(index)) 48 | it.setBackgroundResource(R.drawable.flag_01) 49 | it.setTextColor(contentColor.get(index)) 50 | }) 51 | } 52 | 53 | 54 | 55 | return testViews 56 | 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/FlowLayoutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.app.Activity 4 | import android.graphics.Bitmap 5 | import android.graphics.BitmapFactory 6 | import android.os.Bundle 7 | import android.util.Log 8 | import android.view.LayoutInflater 9 | import android.view.View 10 | import android.widget.TextView 11 | import com.devyk.custom_view.flow.FlowLayout 12 | import com.devyk.custom_view.flow.TagAdapter 13 | import kotlinx.android.synthetic.main.activity_test.* 14 | 15 | 16 | /** 17 | *
18 |  *     author  : devyk on 2019-11-27 15:18
19 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
20 |  *     github  : https://github.com/yangkun19921001
21 |  *     mailbox : yang1001yk@gmail.com
22 |  *     desc    : This is FlowLayoutActivity
23 |  * 
24 | */ 25 | class FlowLayoutActivity : Activity() { 26 | 27 | var TAG = this.javaClass.simpleName 28 | 29 | private val mVals = arrayOf( 30 | "Hello", 31 | "Android", 32 | "Weclome Hi ", 33 | "Button", 34 | "TextView", 35 | "Hello", 36 | "Android", 37 | "Weclome", 38 | "Button ImageView", 39 | "TextView", 40 | "Helloworld", 41 | "Android", 42 | "Weclome Hello", 43 | "Button Text", 44 | "TextView" 45 | ) 46 | 47 | override fun onCreate(savedInstanceState: Bundle?) { 48 | super.onCreate(savedInstanceState) 49 | setContentView(R.layout.activity_test) 50 | 51 | mFlowLayout.setAdapter(object : TagAdapter(mVals) { 52 | 53 | override fun getView(parent: FlowLayout, position: Int, s: String): View { 54 | val tv = LayoutInflater.from(applicationContext).inflate( 55 | R.layout.tv, 56 | mFlowLayout, false 57 | ) as TextView 58 | tv.text = s 59 | Log.d(TAG,s); 60 | return tv 61 | } 62 | 63 | override fun setSelected(position: Int, s: String): Boolean { 64 | return s == "Android" 65 | } 66 | }) 67 | 68 | 69 | 70 | } 71 | 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.os.Bundle 4 | import android.view.MotionEvent 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.recyclerview.widget.LinearLayoutManager 7 | import com.devyk.customview.adapter.RecyclerViewAdapter 8 | import kotlinx.android.synthetic.main.activity_main.* 9 | 10 | class MainActivity : AppCompatActivity() { 11 | 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | 16 | setContentView(R.layout.activity_main) 17 | 18 | 19 | recyclerView.layoutManager = LinearLayoutManager(this) 20 | recyclerView2.layoutManager = LinearLayoutManager(this) 21 | recyclerView3.layoutManager = LinearLayoutManager(this) 22 | 23 | recyclerView.adapter = RecyclerViewAdapter(getListData("页面一")) 24 | recyclerView2.adapter = RecyclerViewAdapter(getListData("页面二")) 25 | recyclerView3.adapter = RecyclerViewAdapter(getListData("页面三")) 26 | 27 | val width = recyclerView.width; 28 | val height = recyclerView.height; 29 | 30 | println("recyclerView:$width + $height") 31 | 32 | 33 | recyclerView.post { 34 | val width = recyclerView.width; 35 | val height = recyclerView.height; 36 | 37 | println("recyclerView post :$width + $height") 38 | } 39 | } 40 | 41 | private fun getListData(i: String): List< String> { 42 | var list = mutableListOf(i) 43 | 44 | for (index in 1 .. 100){ 45 | list.add("hello:$index") 46 | } 47 | return list 48 | 49 | } 50 | 51 | 52 | override fun dispatchTouchEvent(ev: MotionEvent): Boolean { 53 | if (ev.action == MotionEvent.ACTION_DOWN) 54 | println("事件分发机制开始分发 ----> Activity dispatchTouchEvent") 55 | return super.dispatchTouchEvent(ev) 56 | } 57 | 58 | 59 | override fun onTouchEvent(event: MotionEvent): Boolean { 60 | if (event.action == MotionEvent.ACTION_DOWN) 61 | println("事件分发机制处理 ----> Activity onTouchEvent 执行") 62 | return super.onTouchEvent(event) 63 | } 64 | 65 | override fun onResume() { 66 | super.onResume() 67 | val width = recyclerView.width; 68 | val height = recyclerView.height; 69 | 70 | println("recyclerView:$width + $height") 71 | } 72 | 73 | 74 | override fun onWindowFocusChanged(hasFocus: Boolean) { 75 | super.onWindowFocusChanged(hasFocus) 76 | val width = recyclerView.width; 77 | val height = recyclerView.height; 78 | 79 | println("recyclerView:$width + $height") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/MapActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.view.WindowManager 6 | 7 | /** 8 | *
 9 |  *     author  : devyk on 2019-12-06 15:23
10 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
11 |  *     github  : https://github.com/yangkun19921001
12 |  *     mailbox : yang1001yk@gmail.com
13 |  *     desc    : This is MapActivity 地图显示控件
14 |  * 
15 | */ 16 | 17 | public class MapActivity : Activity() { 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN) 21 | setContentView(R.layout.activity_map) 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/PathActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.view.Window 6 | import android.view.WindowManager 7 | import com.devyk.custom_view.canvas.path.PathView 8 | import com.devyk.custom_view.canvas.path_measure.CarRotate 9 | import com.devyk.custom_view.canvas.path_measure.FaceLoadingView 10 | import com.devyk.custom_view.canvas.path_measure.PathMeasureView 11 | import com.devyk.custom_view.canvas.path_measure.SpiderWebView 12 | 13 | /** 14 | *
15 |  *     author  : devyk on 2019-12-03 16:18
16 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
17 |  *     github  : https://github.com/yangkun19921001
18 |  *     mailbox : yang1001yk@gmail.com
19 |  *     desc    : This is PathActivity
20 |  * 
21 | */ 22 | 23 | public class PathActivity : Activity(){ 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN) 28 | // setContentView(PathView(this)) 29 | // setContentView(PathMeasureView(this)) 30 | // setContentView(CarRotate(this)) 31 | // setContentView(FaceLoadingView(this)) 32 | setContentView(SpiderWebView(this)) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/SVGDemo1Activity.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.annotation.SuppressLint 4 | import android.graphics.drawable.Animatable 5 | import android.graphics.drawable.Drawable 6 | import android.os.Build 7 | import android.os.Bundle 8 | import android.os.Handler 9 | import android.os.Message 10 | import androidx.annotation.RequiresApi 11 | import androidx.appcompat.app.AppCompatActivity 12 | import androidx.vectordrawable.graphics.drawable.Animatable2Compat 13 | import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat 14 | import kotlinx.android.synthetic.main.activity_svg.* 15 | 16 | 17 | /** 18 | *
19 |  *     author  : devyk on 2019-12-06 23:30
20 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
21 |  *     github  : https://github.com/yangkun19921001
22 |  *     mailbox : yang1001yk@gmail.com
23 |  *     desc    : This is SVGDemo1Activity
24 |  * 
25 | */ 26 | class SVGDemo1Activity : AppCompatActivity() { 27 | 28 | 29 | var reStartTT = @SuppressLint("HandlerLeak") 30 | object : Handler() { 31 | override fun handleMessage(msg: Message) { 32 | super.handleMessage(msg) 33 | startAnimatabe(R.drawable.line_animated_toutiao, true) 34 | } 35 | 36 | } 37 | 38 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 39 | override fun onCreate(savedInstanceState: Bundle?) { 40 | super.onCreate(savedInstanceState) 41 | setContentView(R.layout.activity_svg) 42 | 43 | //水滴动画 44 | startWaterDropAnimator.setOnClickListener { 45 | startAnimatabe(R.drawable.line_animated_vector, false) 46 | } 47 | //搜索动画 48 | startSearchAnimator.setOnClickListener { 49 | startAnimatabe(R.drawable.line_animated_search, false) 50 | } 51 | //执行警车动画 52 | startPoliceCarAnimator.setOnClickListener { 53 | startAnimatabe(R.drawable.line_animated_car, false) 54 | } 55 | //执行头条动画 56 | startTTAnimator.setOnClickListener { 57 | startAnimatabe(R.drawable.line_animated_toutiao, true) 58 | } 59 | } 60 | 61 | private fun startAnimatabe(lineAnimatedVector: Int, isRegister: Boolean): Animatable { 62 | val animatedVectorDrawable = AnimatedVectorDrawableCompat.create(this, lineAnimatedVector) 63 | iv.setImageDrawable(animatedVectorDrawable) 64 | val animatable = iv.drawable as Animatable 65 | animatable.start() 66 | animatedVectorDrawable!!.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() { 67 | override fun onAnimationEnd(drawable: Drawable?) { 68 | super.onAnimationEnd(drawable) 69 | if (!isRegister) return 70 | animatedVectorDrawable.unregisterAnimationCallback(this) 71 | //重新开始在 xml 设置 restart 无效暂时用 Handler 实现了。 72 | reStartTT.sendEmptyMessage(0) 73 | 74 | } 75 | }) 76 | return animatable 77 | 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/TestActivity1.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview 2 | 3 | import android.app.Activity 4 | import android.app.Dialog 5 | import android.os.Bundle 6 | 7 | /** 8 | *
 9 |  *     author  : devyk on 2019-11-29 22:18
10 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
11 |  *     github  : https://github.com/yangkun19921001
12 |  *     mailbox : yang1001yk@gmail.com
13 |  *     desc    : This is TestActivity1
14 |  * 
15 | */ 16 | public class TestActivity1 : Activity(){ 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | 20 | // setContentView(R.layout.activity_test_1) 21 | // setContentView(R.layout.activity_test_1) 22 | 23 | 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/adapter/RecyclerViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.adapter 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.devyk.customview.R 10 | 11 | /** 12 | *
13 |  *     author  : devyk on 2019-11-20 23:54
14 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
15 |  *     github  : https://github.com/yangkun19921001
16 |  *     mailbox : yang1001yk@gmail.com
17 |  *     desc    : This is RecyclerViewAdapter
18 |  * 
19 | */ 20 | class RecyclerViewAdapter(list: List) : RecyclerView.Adapter() { 21 | 22 | 23 | var listData = list 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 25 | val view = LayoutInflater.from(parent.context).inflate(android.R.layout.simple_expandable_list_item_1, null) 26 | return MyViewHolder(view) 27 | 28 | } 29 | 30 | override fun getItemCount(): Int { 31 | 32 | return listData.size 33 | } 34 | 35 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 36 | var viewHolder = holder as MyViewHolder 37 | var data = listData.get(position) 38 | 39 | viewHolder.position = position 40 | viewHolder.txt?.run { 41 | setText(data) 42 | } 43 | 44 | 45 | } 46 | } 47 | 48 | 49 | class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 50 | 51 | public var position: Int? = null 52 | 53 | public var txt: TextView? = null 54 | 55 | 56 | init { 57 | txt = itemView.findViewById(android.R.id.text1) 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/Button.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import androidx.appcompat.widget.AppCompatButton 7 | 8 | /** 9 | *
10 |  *     author  : devyk on 2019-11-17 16:15
11 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
12 |  *     github  : https://github.com/yangkun19921001
13 |  *     mailbox : yang1001yk@gmail.com
14 |  *     desc    : This is Button
15 |  * 
16 | */ 17 | 18 | public class Button(context: Context?, attrs: AttributeSet?) : AppCompatButton(context, attrs) { 19 | 20 | 21 | override fun dispatchTouchEvent(event: MotionEvent): Boolean { 22 | when (event.action) { 23 | MotionEvent.ACTION_DOWN -> { 24 | println("事件分发机制开始分发 ----> 子View dispatchTouchEvent ACTION_DOWN") 25 | parent.requestDisallowInterceptTouchEvent(false) 26 | } 27 | MotionEvent.ACTION_MOVE -> { 28 | println("事件分发机制开始分发 ----> 子View dispatchTouchEvent ACTION_MOVE") 29 | if (true){ 30 | parent.requestDisallowInterceptTouchEvent(false) 31 | } 32 | } 33 | MotionEvent.ACTION_UP -> { 34 | println("事件分发机制开始分发 ----> 子View dispatchTouchEvent ACTION_UP") 35 | } 36 | } 37 | return super.dispatchTouchEvent(event) 38 | } 39 | 40 | override fun onTouchEvent(event: MotionEvent): Boolean { 41 | var isHandler = false 42 | when (event.action) { 43 | MotionEvent.ACTION_DOWN -> { 44 | println("事件分发机制处理 ----> 子View onTouchEvent ACTION_DOWN") 45 | isHandler = true 46 | } 47 | MotionEvent.ACTION_MOVE -> { 48 | println("事件分发机制处理 ----> 子View onTouchEvent ACTION_MOVE") 49 | isHandler = false 50 | } 51 | MotionEvent.ACTION_UP -> { 52 | println("事件分发机制处理 ----> 子View onTouchEvent ACTION_UP") 53 | isHandler = true 54 | } 55 | } 56 | return isHandler 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/CircleView.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 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.view.View 9 | 10 | /** 11 | *
12 |  *     author  : devyk on 2019-11-16 14:22
13 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
14 |  *     github  : https://github.com/yangkun19921001
15 |  *     mailbox : yang1001yk@gmail.com
16 |  *     desc    : This is CircleView
17 |  * 
18 | */ 19 | class CircleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { 20 | 21 | var mWidth = 0; 22 | var mHeight = 0; 23 | 24 | 25 | override fun draw(canvas: Canvas) { 26 | super.draw(canvas) 27 | canvas.drawCircle(400f,400f,200f, Paint().also { 28 | it.isAntiAlias = true 29 | it.strokeWidth = 5f 30 | it.strokeCap = Paint.Cap.ROUND 31 | it.color = Color.RED 32 | }) 33 | } 34 | 35 | 36 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 37 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) 38 | val widthMode = MeasureSpec.getMode(widthMeasureSpec) 39 | val heightMode = MeasureSpec.getMode(heightMeasureSpec) 40 | 41 | val widthSize = MeasureSpec.getSize(widthMeasureSpec) 42 | val heightSize = MeasureSpec.getSize(heightMeasureSpec) 43 | 44 | /** 45 | * 说明在布局中使用了 wrap_content 模式 46 | */ 47 | if (widthMeasureSpec == MeasureSpec.AT_MOST && heightMeasureSpec == MeasureSpec.AT_MOST){ 48 | setMeasuredDimension(mWidth,mHeight) 49 | }else if (widthMeasureSpec == MeasureSpec.AT_MOST){ 50 | setMeasuredDimension(mWidth,heightSize) 51 | }else if (heightMeasureSpec == MeasureSpec.AT_MOST){ 52 | setMeasuredDimension(widthSize,mHeight) 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/CircleView2.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 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.view.View 9 | 10 | /** 11 | *
12 |  *     author  : devyk on 2019-11-27 15:02
13 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
14 |  *     github  : https://github.com/yangkun19921001
15 |  *     mailbox : yang1001yk@gmail.com
16 |  *     desc    : This is CircleView2
17 |  * 
18 | */ 19 | class CircleView2: View { 20 | 21 | val color = Color.RED 22 | 23 | val paint = Paint(Paint.ANTI_ALIAS_FLAG) 24 | 25 | constructor(context: Context) : super(context) { 26 | init() 27 | } 28 | 29 | 30 | 31 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 32 | init() 33 | } 34 | 35 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { 36 | init() 37 | } 38 | 39 | 40 | override fun draw(canvas: Canvas) { 41 | super.draw(canvas) 42 | val height = height 43 | val width = width 44 | val radius = Math.min(width, height) / 2f 45 | 46 | canvas.drawCircle(width/2f,height/2f,radius,paint) 47 | 48 | 49 | } 50 | 51 | 52 | private fun init() { 53 | paint.setColor(color) 54 | paint.isAntiAlias = true 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/CircleView3.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 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.view.View 9 | import android.icu.lang.UCharacter.GraphemeClusterBreak.T 10 | import com.devyk.customview.R 11 | 12 | 13 | /** 14 | *
15 |  *     author  : devyk on 2019-11-27 16:32
16 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
17 |  *     github  : https://github.com/yangkun19921001
18 |  *     mailbox : yang1001yk@gmail.com
19 |  *     desc    : This is CircleView3
20 |  * 
21 | */ 22 | class CircleView3 : View { 23 | 24 | 25 | var color = Color.RED 26 | 27 | val paint = Paint(Paint.ANTI_ALIAS_FLAG) 28 | 29 | constructor(context: Context) : super(context) { 30 | init() 31 | } 32 | 33 | 34 | 35 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 36 | initTypedrray(context,attrs) 37 | init() 38 | } 39 | 40 | private fun initTypedrray(context: Context, attrs: AttributeSet) { 41 | //拿到自定义属性组 42 | val obtainStyledAttributes = context.obtainStyledAttributes(attrs, R.styleable.CircleView) 43 | color = obtainStyledAttributes.getColor(R.styleable.CircleView_circle_view_color, Color.RED) 44 | obtainStyledAttributes.recycle() 45 | 46 | } 47 | 48 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { 49 | init() 50 | } 51 | 52 | 53 | /** 54 | * 解决 wrap_content 55 | */ 56 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 57 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) 58 | val widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec) 59 | val widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec) 60 | val heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec) 61 | val heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec) 62 | if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) { 63 | setMeasuredDimension(200, 200) 64 | } else if (widthSpecMode == View.MeasureSpec.AT_MOST) { 65 | setMeasuredDimension(200, heightSpecSize) 66 | } else if (heightSpecMode == View.MeasureSpec.AT_MOST) { 67 | setMeasuredDimension(widthSpecSize, 200) 68 | } 69 | } 70 | 71 | 72 | /** 73 | * 解决 padding 74 | */ 75 | override fun draw(canvas: Canvas) { 76 | super.draw(canvas) 77 | 78 | val paddingLeft = paddingLeft 79 | val paddingRight = paddingRight 80 | val paddingBottom = paddingBottom 81 | val paddingTop = paddingTop 82 | val height = height - paddingBottom - paddingTop 83 | val width = width - paddingLeft - paddingRight 84 | val radius = Math.min(width, height) / 2f 85 | 86 | canvas.drawCircle(paddingLeft + width/2f,paddingTop + height/2f,radius,paint) 87 | 88 | 89 | } 90 | 91 | 92 | private fun init() { 93 | paint.setColor(color) 94 | paint.isAntiAlias = true 95 | } 96 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/CustomView_1.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 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.view.* 9 | import android.widget.Scroller 10 | 11 | /** 12 | *
 13 |  *     author  : devyk on 2019-11-14 00:22
 14 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
 15 |  *     github  : https://github.com/yangkun19921001
 16 |  *     mailbox : yang1001yk@gmail.com
 17 |  *     desc    : This is CustomView_1
 18 |  * 
19 | */ 20 | class CustomView_1 : View { 21 | 22 | 23 | 24 | /** 25 | * 定义一个画笔 26 | */ 27 | private var pint = Paint(); 28 | 29 | private val mSollor = Scroller(context); 30 | 31 | /** 32 | * 获取最小滑动 33 | */ 34 | private var scaledDoubleTapSlop = 0 35 | 36 | /** 37 | * 速度追踪 38 | */ 39 | private lateinit var obtain :VelocityTracker 40 | 41 | /** 42 | * 手势 43 | */ 44 | private lateinit var mGetDetector : GestureDetector 45 | 46 | /** 47 | * 平滑滑动 48 | */ 49 | private lateinit var mScroller: Scroller 50 | 51 | 52 | constructor(context: Context?) : super(context) { 53 | init() 54 | } 55 | 56 | private fun init() { 57 | pint.strokeWidth = 10f 58 | pint.color = Color.RED 59 | pint.isAntiAlias = true 60 | pint.strokeCap = Paint.Cap.ROUND 61 | 62 | mScroller = Scroller(context) 63 | 64 | 65 | /** 66 | * 系统所能识别出来的被认为滑动的最小距离 67 | */ 68 | scaledDoubleTapSlop = ViewConfiguration.get(context).scaledDoubleTapSlop; 69 | mGetDetector.setIsLongpressEnabled(false) 70 | 71 | /** 72 | * 手势识别 73 | */ 74 | mGetDetector = GestureDetector(context,object :GestureDetector.OnGestureListener{ 75 | override fun onShowPress(e: MotionEvent?) { 76 | println("onShowPress") 77 | } 78 | 79 | override fun onSingleTapUp(e: MotionEvent?): Boolean { 80 | println("onSingleTapUp") 81 | return true 82 | } 83 | 84 | override fun onDown(e: MotionEvent?): Boolean { 85 | println("onDown") 86 | return true 87 | } 88 | 89 | override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean { 90 | println("onFling") 91 | return true 92 | } 93 | 94 | override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean { 95 | println("onScroll") 96 | return true 97 | } 98 | 99 | override fun onLongPress(e: MotionEvent?) { 100 | println("onLongPress") 101 | } 102 | }) 103 | } 104 | 105 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { 106 | init() 107 | } 108 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { 109 | init() 110 | } 111 | 112 | 113 | override fun onDraw(canvas: Canvas) { 114 | super.onDraw(canvas) 115 | canvas.drawCircle(400f, 400f, 200f, pint) 116 | 117 | 118 | val x = x 119 | val y = y 120 | val translationX = translationX 121 | val translationY = translationY 122 | 123 | println("x:$x y:$y translationX:$translationX translationY:$translationY") 124 | 125 | 126 | } 127 | 128 | 129 | var lastX = 0 130 | var lasty = 0 131 | 132 | override fun onTouchEvent(event: MotionEvent): Boolean { 133 | var rawX = event.getRawX() 134 | var rawY = event.getRawY() 135 | 136 | 137 | 138 | when (event?.action) { 139 | MotionEvent.ACTION_DOWN -> { 140 | /** 141 | * 速度追踪 142 | */ 143 | obtain = VelocityTracker.obtain() 144 | obtain.addMovement(event) 145 | println("ACTION_DOWN getX:$scrollX getY:$y") 146 | } 147 | MotionEvent.ACTION_UP -> { 148 | 149 | var targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); 150 | var dx = targetIndex * getWidth() - getScrollX(); 151 | // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面 152 | mScroller.startScroll(-100, 0, dx - 100, 0); 153 | invalidate(); 154 | 155 | } 156 | MotionEvent.ACTION_MOVE -> { 157 | obtain.computeCurrentVelocity(1000) 158 | val xVelocity = obtain.getXVelocity() 159 | val yVelocity = obtain.getYVelocity() 160 | val curY = -(rawY - lasty) 161 | val curX = -(rawX- lastX) 162 | 163 | println("curX:$curX curY:$curY") 164 | obtain.clear() 165 | scrollBy(-curX.toInt(),-curY.toInt()) 166 | } 167 | } 168 | lastX = rawX.toInt() 169 | lasty = rawY.toInt() 170 | return true 171 | 172 | } 173 | 174 | 175 | 176 | 177 | override fun computeScroll() { 178 | super.computeScroll() 179 | // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 180 | if (mScroller.computeScrollOffset()) { 181 | scrollTo(-100,-100); 182 | invalidate(); 183 | } 184 | } 185 | 186 | 187 | 188 | 189 | 190 | 191 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/GustomLIn.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import android.widget.LinearLayout 7 | 8 | /** 9 | *
10 |  *     author  : devyk on 2019-11-17 16:15
11 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
12 |  *     github  : https://github.com/yangkun19921001
13 |  *     mailbox : yang1001yk@gmail.com
14 |  *     desc    : This is Button
15 |  * 
16 | */ 17 | 18 | public class GustomLIn(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) { 19 | 20 | /** 21 | * 是否需要拦截父容器的点击事件方法 22 | */ 23 | private var isIntercepted = false 24 | 25 | /** 26 | * 记录上一下的坐标 27 | */ 28 | private var mLastXIntercept = 0f 29 | private var mLastYIntercept = 0f 30 | 31 | override fun onTouchEvent(event: MotionEvent): Boolean { 32 | if (event.action == MotionEvent.ACTION_DOWN) 33 | println("事件分发机制处理 ----> 父容器 LinearLayout onTouchEvent") 34 | return false 35 | } 36 | 37 | override fun dispatchTouchEvent(ev: MotionEvent): Boolean { 38 | if (ev.action == MotionEvent.ACTION_DOWN) 39 | println("事件分发机制开始分发 ----> 父容器 dispatchTouchEvent") 40 | return super.dispatchTouchEvent(ev) 41 | } 42 | 43 | override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { 44 | when (ev.action) { 45 | MotionEvent.ACTION_DOWN -> { 46 | isIntercepted = false 47 | println("事件分发机制开始分发 ----> onInterceptTouchEvent ACTION_DOWN") 48 | } 49 | MotionEvent.ACTION_MOVE -> { 50 | //拦截子类的移动事件 51 | if (true) { 52 | println("事件分发机制开始分发 ----> 拦截子类的移动事件 onInterceptTouchEvent") 53 | isIntercepted = true 54 | } else { 55 | isIntercepted = false 56 | } 57 | 58 | } 59 | MotionEvent.ACTION_UP -> { 60 | isIntercepted = false 61 | println("事件分发机制开始分发 ----> onInterceptTouchEvent ACTION_UP") 62 | } 63 | } 64 | return isIntercepted 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/MyRecyclerView.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import android.view.MotionEvent 7 | import android.view.View 8 | import androidx.recyclerview.widget.RecyclerView 9 | 10 | /** 11 | *
12 |  *     author  : devyk on 2019-11-20 00:08
13 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
14 |  *     github  : https://github.com/yangkun19921001
15 |  *     mailbox : yang1001yk@gmail.com
16 |  *     desc    : This is HorizontalScrollView
17 |  * 
18 | */ 19 | class MyRecyclerView(context: Context, attrs: AttributeSet?) : RecyclerView(context, attrs) { 20 | 21 | /** 22 | * 分别记录我们上次滑动的坐标 23 | */ 24 | private var mLastX = 0; 25 | private var mLastY = 0; 26 | 27 | constructor(context: Context) : this(context, null) 28 | 29 | 30 | /** 31 | * 重写分发事件 32 | */ 33 | override fun dispatchTouchEvent(ev: MotionEvent): Boolean { 34 | val x = ev.getX().toInt() 35 | val y = ev.getY().toInt() 36 | 37 | when (ev.action) { 38 | MotionEvent.ACTION_DOWN -> { 39 | var par = parent as ScrollerViewPager 40 | //请求父类不要拦截事件 41 | par.requestDisallowInterceptTouchEvent(true) 42 | Log.d("dispatchTouchEvent", "---》子ACTION_DOWN"); 43 | } 44 | MotionEvent.ACTION_MOVE -> { 45 | val deltaX = x - mLastX 46 | val deltaY = y - mLastY 47 | 48 | if (Math.abs(deltaX) > Math.abs(deltaY)){ 49 | var par = parent as ScrollerViewPager 50 | Log.d("dispatchTouchEvent", "dx:" + deltaX + " dy:" + deltaY); 51 | //交于父类来处理 52 | par.requestDisallowInterceptTouchEvent(false) 53 | } 54 | } 55 | MotionEvent.ACTION_UP -> { 56 | } 57 | } 58 | 59 | 60 | mLastX = x 61 | mLastY = y 62 | return super.dispatchTouchEvent(ev) 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/ScrollerLayout.java: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | import android.view.ViewConfiguration; 8 | import android.view.ViewGroup; 9 | import android.widget.Scroller; 10 | import androidx.core.view.ViewConfigurationCompat; 11 | 12 | /** 13 | * Created by guolin on 16/1/12. 14 | */ 15 | public class ScrollerLayout extends ViewGroup { 16 | 17 | /** 18 | * 用于完成滚动操作的实例 19 | */ 20 | private Scroller mScroller; 21 | 22 | /** 23 | * 判定为拖动的最小移动像素数 24 | */ 25 | private int mTouchSlop; 26 | 27 | /** 28 | * 手机按下时的屏幕坐标 29 | */ 30 | private float mXDown; 31 | 32 | /** 33 | * 手机当时所处的屏幕坐标 34 | */ 35 | private float mXMove; 36 | 37 | /** 38 | * 上次触发ACTION_MOVE事件时的屏幕坐标 39 | */ 40 | private float mXLastMove; 41 | 42 | /** 43 | * 界面可滚动的左边界 44 | */ 45 | private int leftBorder; 46 | 47 | /** 48 | * 界面可滚动的右边界 49 | */ 50 | private int rightBorder; 51 | 52 | public ScrollerLayout(Context context, AttributeSet attrs) { 53 | super(context, attrs); 54 | // 第一步,创建Scroller的实例 55 | mScroller = new Scroller(context); 56 | ViewConfiguration configuration = ViewConfiguration.get(context); 57 | // 获取TouchSlop值 58 | mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); 59 | } 60 | 61 | @Override 62 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 63 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 64 | int childCount = getChildCount(); 65 | for (int i = 0; i < childCount; i++) { 66 | View childView = getChildAt(i); 67 | // 为ScrollerLayout中的每一个子控件测量大小 68 | measureChild(childView, widthMeasureSpec, heightMeasureSpec); 69 | } 70 | } 71 | 72 | @Override 73 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 74 | if (changed) { 75 | int childCount = getChildCount(); 76 | for (int i = 0; i < childCount; i++) { 77 | View childView = getChildAt(i); 78 | // 为ScrollerLayout中的每一个子控件在水平方向上进行布局 79 | childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight()); 80 | } 81 | // 初始化左右边界值 82 | leftBorder = getChildAt(0).getLeft(); 83 | rightBorder = getChildAt(getChildCount() - 1).getRight(); 84 | } 85 | } 86 | 87 | @Override 88 | public boolean onInterceptTouchEvent(MotionEvent ev) { 89 | switch (ev.getAction()) { 90 | case MotionEvent.ACTION_DOWN: 91 | mXDown = ev.getRawX(); 92 | mXLastMove = mXDown; 93 | break; 94 | case MotionEvent.ACTION_MOVE: 95 | mXMove = ev.getRawX(); 96 | float diff = Math.abs(mXMove - mXDown); 97 | mXLastMove = mXMove; 98 | // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件 99 | if (diff > mTouchSlop) { 100 | return true; 101 | } 102 | break; 103 | } 104 | return super.onInterceptTouchEvent(ev); 105 | } 106 | 107 | @Override 108 | public boolean onTouchEvent(MotionEvent event) { 109 | switch (event.getAction()) { 110 | case MotionEvent.ACTION_MOVE: 111 | mXMove = event.getRawX(); 112 | int scrolledX = (int) (mXLastMove - mXMove); 113 | if (getScrollX() + scrolledX < leftBorder) { 114 | scrollTo(leftBorder, 0); 115 | return true; 116 | } else if (getScrollX() + getWidth() + scrolledX > rightBorder) { 117 | scrollTo(rightBorder - getWidth(), 0); 118 | return true; 119 | } 120 | scrollBy(scrolledX, 0); 121 | mXLastMove = mXMove; 122 | break; 123 | case MotionEvent.ACTION_UP: 124 | // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面 125 | int targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); 126 | int dx = targetIndex * getWidth() - getScrollX(); 127 | // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面 128 | mScroller.startScroll(getScrollX(), 0, dx, 0); 129 | invalidate(); 130 | break; 131 | } 132 | return super.onTouchEvent(event); 133 | } 134 | 135 | @Override 136 | public void computeScroll() { 137 | // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 138 | if (mScroller.computeScrollOffset()) { 139 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 140 | invalidate(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/ScrollerSample_1.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import android.widget.Button 7 | import android.widget.LinearLayout 8 | import android.widget.Scroller 9 | 10 | /** 11 | *
12 |  *     author  : devyk on 2019-11-16 13:47
13 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
14 |  *     github  : https://github.com/yangkun19921001
15 |  *     mailbox : yang1001yk@gmail.com
16 |  *     desc    : This is ScrollerSample_1
17 |  * 
18 | */ 19 | 20 | class ScrollerSample_1 : LinearLayout { 21 | 22 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) 23 | 24 | constructor(context: Context) : super(context) 25 | 26 | /** 27 | * 定义滑动 Scroller 28 | */ 29 | private val mScroller = Scroller(context) 30 | 31 | 32 | public fun smoothScrollTo(destX: Int = -100, destY: Int = -100) { 33 | //滑动了的位置 34 | val scrollX = scrollY; 35 | val delta = destY - scrollY; 36 | //2000 ms 内滑动到 destX 位置,效果就是缓慢滑动 37 | mScroller.startScroll(scrollX, 0, 0, delta, 2000) 38 | invalidate() 39 | } 40 | 41 | override fun computeScroll() { 42 | if (mScroller.computeScrollOffset()) { 43 | scrollTo(mScroller.currX, mScroller.currY) 44 | postInvalidate() 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/SlideView1.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 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.view.MotionEvent 9 | import android.view.View 10 | 11 | /** 12 | *
13 |  *     author  : devyk on 2019-11-16 18:01
14 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
15 |  *     github  : https://github.com/yangkun19921001
16 |  *     mailbox : yang1001yk@gmail.com
17 |  *     desc    : This is SlideView1
18 |  * 
19 | */ 20 | public class SlideView1(context: Context?, attrs: AttributeSet?) : View(context, attrs) { 21 | 22 | /** 23 | * 记录上次滑动的坐标 24 | */ 25 | private var mLastX = 0; 26 | private var mLastY = 0; 27 | 28 | /** 29 | * 初始化画笔 30 | */ 31 | val paint = Paint().apply { 32 | color = Color.BLACK 33 | isAntiAlias = true 34 | strokeWidth = 3f 35 | } 36 | 37 | 38 | override fun onTouchEvent(event: MotionEvent): Boolean { 39 | when (event.action) { 40 | MotionEvent.ACTION_DOWN -> { 41 | //拿到相对于屏幕按下的坐标点 42 | mLastX = event.getX().toInt(); 43 | mLastY = event.getY().toInt(); 44 | println("拿到相对于屏幕按下的坐标点: x:$mLastX y:$mLastY") 45 | 46 | } 47 | MotionEvent.ACTION_MOVE -> { 48 | var offsetX = event.getX().toInt() - mLastX;//计算 View 新的摆放位置 49 | var offsetY = event.getY().toInt() - mLastY; 50 | //重新放置新的位置 51 | layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); 52 | } 53 | 54 | MotionEvent.ACTION_UP -> { 55 | 56 | } 57 | } 58 | return true//消耗触摸事件 59 | } 60 | 61 | override fun onDraw(canvas: Canvas) { 62 | super.onDraw(canvas) 63 | canvas.drawCircle(300f, 300f, 150f, paint) 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devyk/customview/sample_1/SlideView2.kt: -------------------------------------------------------------------------------- 1 | package com.devyk.customview.sample_1 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.view.MotionEvent 9 | import android.view.View 10 | import android.widget.Scroller 11 | 12 | /** 13 | *
14 |  *     author  : devyk on 2019-11-16 18:38
15 |  *     blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
16 |  *     github  : https://github.com/yangkun19921001
17 |  *     mailbox : yang1001yk@gmail.com
18 |  *     desc    : This is SlideView2
19 |  * 
20 | */ 21 | 22 | public class SlideView2(context: Context?, attrs: AttributeSet?) : View(context, attrs) { 23 | 24 | /** 25 | * 记录上次滑动的坐标 26 | */ 27 | private var mLastX = 0; 28 | private var mLastY = 0; 29 | 30 | private val mScroller = Scroller(context) 31 | 32 | /** 33 | * 初始化画笔 34 | */ 35 | val paint = Paint().apply { 36 | color = Color.BLACK 37 | isAntiAlias = true 38 | strokeWidth = 3f 39 | } 40 | 41 | 42 | override fun onTouchEvent(event: MotionEvent): Boolean { 43 | when (event.action) { 44 | MotionEvent.ACTION_DOWN -> { 45 | //拿到相对于屏幕按下的坐标点 46 | mLastX = event.getRawX().toInt(); 47 | mLastY = event.getRawY().toInt(); 48 | println("拿到相对于屏幕按下的坐标点1: x:$mLastX y:$mLastY") 49 | 50 | } 51 | MotionEvent.ACTION_MOVE -> { 52 | println("拿到相对于屏幕按下的坐标点2: x:${event.rawX} y:${event.getRawY()}") 53 | x = event.getRawX() - mLastX 54 | y = event.getRawY() - mLastY 55 | 56 | 57 | translationX = event.getRawX() - mLastX 58 | translationY = event.getRawY() - mLastY 59 | println("拿到相对于屏幕按下的坐标点3: x:${x} y:${y}") 60 | } 61 | 62 | MotionEvent.ACTION_UP -> { 63 | 64 | } 65 | } 66 | return true//消耗触摸事件 67 | } 68 | 69 | override fun onDraw(canvas: Canvas) { 70 | super.onDraw(canvas) 71 | canvas.drawCircle(300f, 300f, 150f, paint) 72 | } 73 | } -------------------------------------------------------------------------------- /app/src/main/res/animator/anim_infinite.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/animator/anim_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/animator/anim_start.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/animator/transit.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/animator/tt_path_one.xml: -------------------------------------------------------------------------------- 1 | 2 | //按顺序执行 5 | 6 | //依次执行 pathData 位置变换 7 | 30 | 54 | 78 | 102 | -------------------------------------------------------------------------------- /app/src/main/res/animator/tt_path_three.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 25 | 44 | 63 | 82 | -------------------------------------------------------------------------------- /app/src/main/res/animator/tt_path_two.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 25 | 45 | 65 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/avft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/avft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/avft_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/avft_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/box_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/box_stack.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/box_stack_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/box_stack_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bubble_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/bubble_frame.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bubble_frame_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/bubble_frame_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bubbles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/bubbles.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bubbles_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/bubbles_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bullseye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/bullseye.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bullseye_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/bullseye_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/circle_filled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_filled_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/circle_filled_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/circle_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_outline_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/circle_outline_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/flag_01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangkun19921001/CustomViewSample/791b3e93dd641c95d2d7381a33278ef320663c41/app/src/main/res/drawable/heart.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_line.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_police_car.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_toutiao.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 34 | 46 | 47 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/line_animated_car.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | 23 | 25 | 27 | 28 | 29 | 31 | 32 | 34 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/line_animated_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/line_animated_toutiao.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/line_animated_vector.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/search_svg.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 19 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_canvas_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_custom_viewgroup.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_map.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_path.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_svg.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | 17 | 18 | 19 |