├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yazhi1992 │ │ └── practice │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── yazhi1992 │ │ │ └── practice │ │ │ ├── MainActivity.java │ │ │ ├── annnotation │ │ │ ├── ContentView.kt │ │ │ ├── InjectOnClick.kt │ │ │ ├── InjectView.kt │ │ │ ├── KotlinTest.java │ │ │ ├── TestAnnotationActivity.kt │ │ │ └── ViewIndectUtils.kt │ │ │ ├── arthmetic_practive │ │ │ └── ArthmeticActivity.java │ │ │ ├── bezier_anim │ │ │ ├── BezierAnimActivity.java │ │ │ └── BezierAnimView.java │ │ │ ├── eleme_button │ │ │ ├── ElemeBtn.java │ │ │ └── ElemeBtnActivity.java │ │ │ ├── flowlayout │ │ │ ├── FlowLayoutActivity.java │ │ │ └── Flowlayout.java │ │ │ ├── hencoder │ │ │ ├── HenCoder.java │ │ │ ├── ViwOneActivity.java │ │ │ └── one │ │ │ │ └── ViewOne.java │ │ │ ├── hook_icon │ │ │ ├── HookIcon.java │ │ │ └── HookIconActivity.java │ │ │ ├── immersion_status_bar │ │ │ ├── ImmersionActionBarActivity.java │ │ │ ├── ImmersionBarActivity.java │ │ │ ├── ImmersionImageActivity.java │ │ │ ├── ImmersionNavActivity.java │ │ │ ├── MainImmersionActivity.java │ │ │ └── StatusBarUtils.java │ │ │ ├── line_chart_view │ │ │ ├── LineChart.java │ │ │ ├── LineChartActivity.java │ │ │ └── ValueBean.java │ │ │ ├── question_button │ │ │ ├── OnTouchActivity.kt │ │ │ ├── OnTouchReturnFalseButton.java │ │ │ ├── OnTouchReturnTrueButton.java │ │ │ ├── QuestionActivity.kt │ │ │ └── SizeActivity.kt │ │ │ ├── ripple_view │ │ │ ├── RippleActivity.java │ │ │ └── RippleView.java │ │ │ ├── rotate_circle │ │ │ ├── RotateCircleActivity.java │ │ │ └── RotateCircleView.java │ │ │ ├── streak_progress_bar │ │ │ └── StreakProgressbar.java │ │ │ ├── swipelayout │ │ │ ├── SideMenuLayout.java │ │ │ ├── SwipeLayout.java │ │ │ └── SwipeLayoutActivity.kt │ │ │ ├── utils │ │ │ └── Utils.java │ │ │ ├── vertical_text │ │ │ ├── VerticalText.java │ │ │ └── VerticalTextActivity.java │ │ │ ├── wave_view │ │ │ ├── BezierWave.java │ │ │ ├── SinWave.java │ │ │ └── WaveViewActivity.java │ │ │ └── wheel_view │ │ │ ├── WheelView.java │ │ │ └── WheelViewActivity.java │ └── res │ │ ├── drawable-v21 │ │ ├── ic_menu_camera.xml │ │ ├── ic_menu_gallery.xml │ │ ├── ic_menu_manage.xml │ │ ├── ic_menu_send.xml │ │ ├── ic_menu_share.xml │ │ └── ic_menu_slideshow.xml │ │ ├── drawable │ │ ├── shape.xml │ │ └── side_nav_bar.xml │ │ ├── layout │ │ ├── activity_arthmetic.xml │ │ ├── activity_bezier_anim.xml │ │ ├── activity_eleme_btn.xml │ │ ├── activity_flow_layout.xml │ │ ├── activity_hen_coder.xml │ │ ├── activity_hook_icon.xml │ │ ├── activity_immersion_action_bar.xml │ │ ├── activity_immersion_bar.xml │ │ ├── activity_immersion_image.xml │ │ ├── activity_immersion_nav.xml │ │ ├── activity_immersion_status_bar.xml │ │ ├── activity_line_chart.xml │ │ ├── activity_lint.xml │ │ ├── activity_main.xml │ │ ├── activity_on_touch.xml │ │ ├── activity_question.xml │ │ ├── activity_ripple.xml │ │ ├── activity_rotate_circle.xml │ │ ├── activity_size.xml │ │ ├── activity_swipe_layout.xml │ │ ├── activity_test_annotation.xml │ │ ├── activity_vertical_text.xml │ │ ├── activity_viw_one.xml │ │ ├── activity_wave_view.xml │ │ ├── activity_wheel_view.xml │ │ ├── app_bar_immersion_nav.xml │ │ ├── content_immersion_nav.xml │ │ └── nav_header_immersion_nav.xml │ │ ├── menu │ │ ├── activity_immersion_nav_drawer.xml │ │ └── immersion_nav.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── bg.png │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── nbg.jpg │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-v19 │ │ ├── dimens.xml │ │ └── styles.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── drawables.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── yazhi1992 │ └── practice │ └── ExampleUnitTest.java ├── 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/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | 10 | # OSX 11 | 12 | *.DS_Store 13 | 14 | 15 | # Gradle files 16 | build/ 17 | .gradle/ 18 | */build/ 19 | 20 | # Intellij project files 21 | *.iml 22 | *.ipr 23 | *.iws 24 | .idea/ 25 | 26 | # Built application files 27 | *.apk 28 | *.ap_ 29 | 30 | 31 | # Files for the Dalvik VM 32 | *.dex 33 | 34 | 35 | # Java class files 36 | *.class 37 | 38 | # Eclipse project files 39 | .classpath 40 | .project 41 | 42 | # Generated files 43 | antLauncher/bin 44 | antLauncher/gen 45 | 46 | # generated files 47 | bin/ 48 | gen/ 49 | 50 | 51 | # Local configuration file (sdk path, etc) 52 | local.properties 53 | 54 | 55 | # Log Files 56 | *.log 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 各种写着玩的自定义控件demo 2 | 3 | 有时网上看到一些比较有意思的开源项目,有时间的话就会自己也撸一个出来,但是一般只关注实现样式、动画等,不会太去细致完整地完成,俗称占个坑~ 4 | 5 | 持续更新中... 6 | 7 | [github地址](https://github.com/yazhi1992/Practice) 8 | 9 | ## 下载模块 10 | 11 | 前阵子项目里需要重写下载模块,于是简单写了个下载模块。[DDownload](https://github.com/yazhi1992/DDownload)了解一下。 12 | 13 | ## 流式标签 14 | 15 | 只做了简单又菜逼的实现,后面再优化~ 16 | 17 | ![流式标签](http://upload-images.jianshu.io/upload_images/1929170-c80f532e02c2d893.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 18 | 19 | ## 侧滑菜单 20 | 21 | ![侧滑菜单](http://upload-images.jianshu.io/upload_images/1929170-0657bca5cb3faf7f.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 22 | 23 | ## 饿了么加入购物车按钮 24 | 25 | ![eleme.gif](http://upload-images.jianshu.io/upload_images/1929170-992de19b7ba750e4.gif?imageMogr2/auto-orient/strip) 26 | 27 | ## 仿支付宝支付成功动画 28 | 29 | ![支付宝支付成功动画](http://upload-images.jianshu.io/upload_images/1929170-ee6377a352720bba.gif?imageMogr2/auto-orient/strip) 30 | 31 | ## 波浪效果进度条(正弦函数实现+贝塞尔曲线实现) 32 | 33 | ![波浪效果.gif](http://upload-images.jianshu.io/upload_images/1929170-d0ce2b7f6f107f98.gif?imageMogr2/auto-orient/strip) 34 | 35 | ## 竖排文字 36 | 37 | ![竖排文字.png](http://upload-images.jianshu.io/upload_images/1929170-c3b0efc81a30dbfc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 38 | 39 | ## 多功能按钮 40 | 41 | [RoundLoadingView](https://github.com/yazhi1992/LibraryProject/blob/master/yazhilib/src/main/java/com/yazhi1992/yazhilib/widget/RoundView/RoundLoadingView.java) 可自定义圆角、正常/点击/不可用时的背景/文字/边缘(stroke)颜色,并添加了 loading 状态和倒计时状态的按钮 42 | 43 | ![RoundLoadingView](http://upload-images.jianshu.io/upload_images/1929170-276c9468a8c79928.gif?imageMogr2/auto-orient/strip) 44 | 45 | ## 滚动轮播控件 46 | 47 | [AutoLoopView](https://github.com/yazhi1992/LibraryProject/blob/master/yazhilib/src/main/java/com/yazhi1992/yazhilib/widget/LoopView/AutoLoopView.java),目前还只是很简单的一个滚动轮播效果,后续可能会扩展类似于淘宝和大众点评的双行/带图片的轮播视图,先挖个坑~ 48 | 49 | ![轮播视图](http://upload-images.jianshu.io/upload_images/1929170-665c66011885aafc.gif?imageMogr2/auto-orient/strip) 50 | 51 | ## 指示器和 ViewPager 滑动效果 52 | 53 | [ViewPagerIndicator](https://github.com/yazhi1992/LibraryProject/blob/master/yazhilib/src/main/java/com/yazhi1992/yazhilib/widget/ViewPagerIndicator.java) 54 | 55 | ![banner、指示器](http://upload-images.jianshu.io/upload_images/1929170-2c3b2eb649c3d1eb.gif?imageMogr2/auto-orient/strip) 56 | 57 | ## 折线图 58 | 59 | ![折线](http://upload-images.jianshu.io/upload_images/1929170-a87c66d0610bbe81.gif?imageMogr2/auto-orient/strip) 60 | 61 | ## 渐变圆环 62 | 真机效果没这么丑的~ 63 | ![圆环.gif](http://upload-images.jianshu.io/upload_images/1929170-adf6c82d173fdcb5.gif?imageMogr2/auto-orient/strip) 64 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.0" 7 | defaultConfig { 8 | applicationId "com.yazhi1992.practice" 9 | minSdkVersion 15 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | // Enabling multidex support. 16 | multiDexEnabled true 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | lintOptions { 25 | textReport true // 输出lint报告 26 | abortOnError true 27 | textOutput 'stdout' 28 | check 'LogUse' 29 | checkReleaseBuilds true 30 | } 31 | } 32 | 33 | dependencies { 34 | compile fileTree(dir: 'libs', include: ['*.jar']) 35 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 36 | exclude group: 'com.android.support', module: 'support-annotations' 37 | }) 38 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 39 | compile 'com.android.support:appcompat-v7:25.3.1' 40 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 41 | compile 'com.android.support:design:25.3.1' 42 | testCompile 'junit:junit:4.12' 43 | } 44 | 45 | repositories { 46 | mavenCentral() 47 | } 48 | 49 | apply plugin: 'kotlin-android-extensions' 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/zengyazhi/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/yazhi1992/practice/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.yazhi1992.practice", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 47 | 48 | 52 | 53 | 56 | 57 | 58 | 59 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.Toast; 9 | 10 | import com.yazhi1992.practice.annnotation.TestAnnotationActivity; 11 | import com.yazhi1992.practice.arthmetic_practive.ArthmeticActivity; 12 | import com.yazhi1992.practice.bezier_anim.BezierAnimActivity; 13 | import com.yazhi1992.practice.eleme_button.ElemeBtnActivity; 14 | import com.yazhi1992.practice.flowlayout.FlowLayoutActivity; 15 | import com.yazhi1992.practice.hencoder.HenCoder; 16 | import com.yazhi1992.practice.hook_icon.HookIconActivity; 17 | import com.yazhi1992.practice.immersion_status_bar.MainImmersionActivity; 18 | import com.yazhi1992.practice.line_chart_view.LineChartActivity; 19 | import com.yazhi1992.practice.question_button.QuestionActivity; 20 | import com.yazhi1992.practice.ripple_view.RippleActivity; 21 | import com.yazhi1992.practice.rotate_circle.RotateCircleActivity; 22 | import com.yazhi1992.practice.vertical_text.VerticalTextActivity; 23 | import com.yazhi1992.practice.wave_view.WaveViewActivity; 24 | 25 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | 32 | findViewById(R.id.elemeBtn).setOnClickListener(this); 33 | findViewById(R.id.immersionStatusBarBtn).setOnClickListener(this); 34 | findViewById(R.id.rotateBtn).setOnClickListener(this); 35 | findViewById(R.id.waveBtn).setOnClickListener(this); 36 | findViewById(R.id.verticalTextBtn).setOnClickListener(this); 37 | findViewById(R.id.bezierBtn).setOnClickListener(this); 38 | findViewById(R.id.rippleBtn).setOnClickListener(this); 39 | findViewById(R.id.lineChart).setOnClickListener(this); 40 | findViewById(R.id.wheelView).setOnClickListener(this); 41 | findViewById(R.id.alipayHookIconBtn).setOnClickListener(this); 42 | findViewById(R.id.hencoder_practive).setOnClickListener(this); 43 | findViewById(R.id.annotation_test).setOnClickListener(this); 44 | findViewById(R.id.question_practive).setOnClickListener(this); 45 | findViewById(R.id.arthmetic_practive).setOnClickListener(this); 46 | findViewById(R.id.flowlayout).setOnClickListener(this); 47 | 48 | Toast.makeText(this, "test", Toast.LENGTH_SHORT).show(); 49 | Log.d("test", "test"); 50 | } 51 | 52 | @Override 53 | public void onClick(View v) { 54 | switch (v.getId()) { 55 | case R.id.elemeBtn: 56 | //饿了么购物车按钮 57 | startActivity(new Intent(MainActivity.this, ElemeBtnActivity.class)); 58 | break; 59 | case R.id.rotateBtn: 60 | //饿了么购物车按钮 61 | startActivity(new Intent(this, RotateCircleActivity.class)); 62 | break; 63 | case R.id.waveBtn: 64 | //波浪效果 65 | startActivity(new Intent(this, WaveViewActivity.class)); 66 | break; 67 | case R.id.verticalTextBtn: 68 | //竖排文字 69 | startActivity(new Intent(this, VerticalTextActivity.class)); 70 | break; 71 | case R.id.bezierBtn: 72 | //贝塞尔动画 73 | startActivity(new Intent(this, BezierAnimActivity.class)); 74 | break; 75 | case R.id.rippleBtn: 76 | //涟漪 77 | startActivity(new Intent(this, RippleActivity.class)); 78 | break; 79 | case R.id.lineChart: 80 | //折线图 81 | startActivity(new Intent(this, LineChartActivity.class)); 82 | break; 83 | case R.id.wheelView: 84 | //折线图 85 | startActivity(new Intent(this, LineChartActivity.class)); 86 | break; 87 | case R.id.alipayHookIconBtn: 88 | //支付宝支付成功打钩动画 89 | startActivity(new Intent(this, HookIconActivity.class)); 90 | break; 91 | case R.id.immersionStatusBarBtn: 92 | //沉浸式状态栏 93 | startActivity(new Intent(this, MainImmersionActivity.class)); 94 | break; 95 | case R.id.hencoder_practive: 96 | //HenCoder 练习代码 97 | startActivity(new Intent(this, HenCoder.class)); 98 | break; 99 | case R.id.annotation_test: 100 | //注解小 demo 101 | startActivity(new Intent(this, TestAnnotationActivity.class)); 102 | break; 103 | case R.id.question_practive: 104 | //解惑 105 | startActivity(new Intent(this, QuestionActivity.class)); 106 | break; 107 | case R.id.arthmetic_practive: 108 | //算法 109 | startActivity(new Intent(this, ArthmeticActivity.class)); 110 | break; 111 | case R.id.flowlayout: 112 | //流式标签 113 | startActivity(new Intent(this, FlowLayoutActivity.class)); 114 | break; 115 | default: 116 | break; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/annnotation/ContentView.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.annnotation 2 | 3 | /** 4 | * Created by zengyazhi on 2017/8/23. 5 | */ 6 | 7 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) //可以修饰类、接口、枚举等 8 | annotation class ContentView(val value: Int) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/annnotation/InjectOnClick.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.annnotation 2 | 3 | /** 4 | * Created by zengyazhi on 2017/8/23. 5 | */ 6 | 7 | @Target(AnnotationTarget.FUNCTION) //修饰成员变量 8 | annotation class InjectOnClick(val value: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/annnotation/InjectView.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.annnotation 2 | 3 | /** 4 | * Created by zengyazhi on 2017/8/23. 5 | */ 6 | 7 | @Target(AnnotationTarget.FIELD) //修饰成员变量 8 | annotation class InjectView(val value: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/annnotation/KotlinTest.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.annnotation; 2 | 3 | /** 4 | * Created by zengyazhi on 2017/8/24. 5 | */ 6 | 7 | public class KotlinTest { 8 | private void tesxt() { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/annnotation/TestAnnotationActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.annnotation 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.widget.TextView 7 | import android.widget.Toast 8 | 9 | import com.yazhi1992.practice.R 10 | 11 | @ContentView(R.layout.activity_test_annotation) 12 | class TestAnnotationActivity : AppCompatActivity() { 13 | 14 | @InjectView(R.id.tv_test) 15 | lateinit var tv:TextView 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | ViewIndectUtils.inject(this) 20 | 21 | tv.text = "修改文字" 22 | } 23 | 24 | @InjectOnClick(R.id.tv_test) 25 | private fun clickTv(v: View) { 26 | Toast.makeText(this, "test", Toast.LENGTH_SHORT).show(); 27 | tv.text = "点击文字" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/annnotation/ViewIndectUtils.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.annnotation 2 | 3 | import android.app.Activity 4 | import android.view.View 5 | import java.lang.reflect.Proxy 6 | 7 | /** 8 | * Created by zengyazhi on 2017/8/23. 9 | */ 10 | 11 | object ViewIndectUtils { 12 | fun inject(activity: Activity) { 13 | injectContentView(activity) 14 | injectView(activity) 15 | injectOnclick(activity) 16 | } 17 | 18 | private fun injectContentView(activity: Activity) { 19 | val clazz = activity.javaClass 20 | //拿到 activity 的 ContentView 注解 21 | val contentView = clazz.getAnnotation(ContentView::class.java) 22 | if (contentView != null) { 23 | //如果这个activity上面存在这个注解的话,就取出这个注解对应的value值,其实就是前面说的布局文件。 24 | val layoutId = contentView.value 25 | //反射 26 | try { 27 | val method = clazz.getMethod("setContentView", Int::class.javaPrimitiveType) 28 | method.invoke(activity, layoutId) 29 | } catch (e: Exception) { 30 | e.printStackTrace() 31 | } 32 | // activity.setContentView(layoutId) 33 | } 34 | } 35 | 36 | private fun injectView(activity: Activity) { 37 | val clazz = activity.javaClass 38 | //activity 的所有成员变量 39 | for (field in clazz.declaredFields) { 40 | val injectView = field.getAnnotation(InjectView::class.java) 41 | if (injectView != null) { 42 | try { 43 | val findViewByIdMethod = clazz.getMethod("findViewById", Int::class.javaPrimitiveType) 44 | val view = findViewByIdMethod.invoke(activity, injectView.value) 45 | field.set(activity, view) 46 | } catch (e: Exception) { 47 | e.printStackTrace() 48 | } 49 | // val view = activity.findViewById(injectView.value) 50 | // field.set(activity, view) 51 | } 52 | } 53 | } 54 | 55 | private fun injectOnclick(activity: Activity) { 56 | val clazz = activity.javaClass 57 | for (method2 in clazz.declaredMethods) { 58 | val injectClick = method2.getAnnotation(InjectOnClick::class.java) 59 | if (injectClick != null) { 60 | method2.isAccessible = true 61 | val clickListener = Proxy.newProxyInstance(View.OnClickListener::class.java.classLoader 62 | , arrayOf>(View.OnClickListener::class.java)) { proxy, method, args -> method2.invoke(activity, *args)} 63 | try { 64 | val findViewByIdMethod = clazz.getMethod("findViewById", Int::class.javaPrimitiveType) 65 | val view = findViewByIdMethod.invoke(activity, injectClick.value) 66 | val setOnClickListenerMethod = view.javaClass.getMethod("setOnClickListener", View.OnClickListener::class.java) 67 | setOnClickListenerMethod.invoke(view, clickListener) 68 | } catch (e: Exception) { 69 | e.printStackTrace() 70 | } 71 | } 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/arthmetic_practive/ArthmeticActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.arthmetic_practive; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.View; 7 | 8 | import com.yazhi1992.practice.R; 9 | 10 | public class ArthmeticActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_arthmetic); 16 | 17 | findViewById(R.id.arthmetic_choose).setOnClickListener(new View.OnClickListener() { 18 | @Override 19 | public void onClick(View v) { 20 | //选择排序 21 | int[] array = new int[]{2, 8, 12, 4, 10, 25, 7, 9}; 22 | //选择排序,每一轮都选出最小的数并排在该轮的头部 23 | for (int i = 0; i < (array.length - 1); i++) { 24 | int temp = array[i]; 25 | int tempIndex = i; 26 | for (int j = (i + 1); j < array.length; j++) { 27 | if (array[j] < temp) { 28 | temp = array[j]; 29 | tempIndex = j; 30 | } 31 | } 32 | //一轮比较完,将最小值排在该轮的头部 33 | array[tempIndex] = array[i]; 34 | array[i] = temp; 35 | } 36 | 37 | for (int i = 0; i < array.length; i++) { 38 | Log.e("zyz", "" + array[i]); 39 | } 40 | } 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/bezier_anim/BezierAnimActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.bezier_anim; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | import com.yazhi1992.practice.wave_view.BezierWave; 9 | 10 | public class BezierAnimActivity extends AppCompatActivity { 11 | 12 | private BezierAnimView mView; 13 | private BezierWave mMWaveView; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_bezier_anim); 19 | 20 | mView = (BezierAnimView) findViewById(R.id.bezierView); 21 | mMWaveView = (BezierWave) findViewById(R.id.bezierWave); 22 | 23 | findViewById(R.id.bezieBtn).setOnClickListener(new View.OnClickListener() { 24 | @Override 25 | public void onClick(View v) { 26 | // mView.start(); 27 | mMWaveView.start(); 28 | } 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/bezier_anim/BezierAnimView.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.bezier_anim; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.support.annotation.Nullable; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | import com.yazhi1992.practice.R; 13 | 14 | /** 15 | * Created by zengyazhi on 17/4/1. 16 | *

17 | * 贝塞尔动画 18 | * 参考:http://www.jcodecraeer.com/a/anzhuokaifa/2017/0105/6936.html 19 | * http://blog.csdn.net/z82367825/article/details/51599245 20 | */ 21 | 22 | public class BezierAnimView extends View { 23 | 24 | private Paint mPaint; 25 | private int mHeight; 26 | private int mWidth; 27 | private Path mPath; 28 | private int mColor1; 29 | private int mColor2; 30 | private int mRadiu; 31 | //Y轴偏移量 32 | private int mYOffset; 33 | private ValueAnimator mValueAnimator; 34 | private int Y_OFFSET = 10; 35 | 36 | public BezierAnimView(Context context) { 37 | this(context, null); 38 | } 39 | 40 | public BezierAnimView(Context context, @Nullable AttributeSet attrs) { 41 | this(context, attrs, 0); 42 | } 43 | 44 | public BezierAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 45 | super(context, attrs, defStyleAttr); 46 | init(context); 47 | } 48 | 49 | private void init(Context context) { 50 | mPaint = new Paint(); 51 | mColor1 = context.getResources().getColor(R.color.my_color); 52 | mColor2 = context.getResources().getColor(R.color.my_color2); 53 | mPaint.setAntiAlias(true); 54 | mPaint.setDither(true); 55 | mPaint.setStyle(Paint.Style.FILL); 56 | 57 | mPath = new Path(); 58 | 59 | mRadiu = 60; 60 | } 61 | 62 | // TODO: 17/4/1 QQ未读粘脸效果 63 | 64 | 65 | @Override 66 | protected void onDraw(Canvas canvas) { 67 | super.onDraw(canvas); 68 | mPath.reset(); 69 | mPath.moveTo(0, mHeight / 2); 70 | mPath.lineTo(mWidth, mHeight / 2); 71 | mPath.lineTo(mWidth, mHeight); 72 | mPath.lineTo(0, mHeight); 73 | mPath.close(); 74 | //画正方形 75 | mPaint.setColor(mColor1); 76 | canvas.drawPath(mPath, mPaint); 77 | 78 | //画水滴 79 | mPaint.setColor(mColor2); 80 | canvas.drawCircle(mWidth / 2, mHeight * 3 / 5 + mRadiu * 2 - mYOffset, mRadiu, mPaint); 81 | } 82 | 83 | @Override 84 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 85 | super.onSizeChanged(w, h, oldw, oldh); 86 | mWidth = w; 87 | mHeight = h; 88 | 89 | mValueAnimator = ValueAnimator.ofInt(0, mHeight / 2); 90 | mValueAnimator.setDuration(2000); 91 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 92 | @Override 93 | public void onAnimationUpdate(ValueAnimator animation) { 94 | mYOffset = (int) animation.getAnimatedValue(); 95 | postInvalidate(); 96 | } 97 | }); 98 | 99 | } 100 | 101 | public void start() { 102 | mValueAnimator.start(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/eleme_button/ElemeBtnActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.eleme_button; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | public class ElemeBtnActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_eleme_btn); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/flowlayout/FlowLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.flowlayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class FlowLayoutActivity extends AppCompatActivity { 12 | 13 | List data = new ArrayList<>(); 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_flow_layout); 19 | 20 | data.add("我要做远方的忠诚的儿子"); 21 | data.add("和物质的"); 22 | data.add("短暂情人"); 23 | data.add("和所有以梦为马的"); 24 | data.add("诗人一样"); 25 | data.add("我"); 26 | data.add("不得不和"); 27 | data.add("烈士和小丑"); 28 | data.add("走在同一道路上"); 29 | 30 | Flowlayout flowLayout = (Flowlayout) findViewById(R.id.flowlayout); 31 | flowLayout.setData(data); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/flowlayout/Flowlayout.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.flowlayout; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.LinearLayout; 9 | import android.widget.TextView; 10 | 11 | import com.yazhi1992.practice.R; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by zengyazhi on 2017/12/5. 18 | */ 19 | 20 | public class Flowlayout extends ViewGroup { 21 | private List mData; 22 | private Context mContext; 23 | //每一行view 24 | protected List> mAllViews = new ArrayList>(); 25 | protected List mLineHeight = new ArrayList(); 26 | protected List mLineWidth = new ArrayList(); 27 | private List mLineViews = new ArrayList<>(); 28 | private int mGravity = -1; 29 | private static final int LEFT = -1; 30 | private static final int CENTER = 0; 31 | private static final int RIGHT = 1; 32 | 33 | public Flowlayout(Context context) { 34 | this(context, null); 35 | } 36 | 37 | public Flowlayout(Context context, AttributeSet attrs) { 38 | this(context, attrs, 0); 39 | } 40 | 41 | public Flowlayout(Context context, AttributeSet attrs, int defStyleAttr) { 42 | super(context, attrs, defStyleAttr); 43 | mContext = context; 44 | } 45 | 46 | @Override 47 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 48 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); 49 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); 50 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec); 51 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec); 52 | //每行的宽高 53 | int lineWidth = 0; 54 | int lineHeight = 0; 55 | //测量子view后计算出的自身的宽高 56 | int width = 0; 57 | int height = 0; 58 | 59 | for (int i = 0; i < getChildCount(); i++) { 60 | View child = getChildAt(i); 61 | if (child.getVisibility() == View.GONE) { 62 | continue; 63 | } 64 | //测量该子view 65 | measureChild(child, widthMeasureSpec, heightMeasureSpec); 66 | MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); 67 | //获取子view宽高 68 | int childWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; 69 | int childHeight = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; 70 | if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { 71 | //已经超出viewgroup宽度了,换行 72 | width = Math.max(width, lineWidth); 73 | lineWidth = childWidth; 74 | lineHeight = childHeight; 75 | //换行了,要加上高度 76 | height += childHeight; 77 | } else { 78 | //还未超出viewgroup宽度 79 | lineWidth += childWidth; 80 | //实时计算高度 81 | if (childHeight > lineHeight) { 82 | //如果同一行出现了更高的view,则重新计算高度 83 | height = height - lineHeight; 84 | lineHeight = childHeight; 85 | height += lineHeight; 86 | } 87 | } 88 | } 89 | //测量完后报告最终计算的宽高 90 | setMeasuredDimension( 91 | modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), 92 | modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// 93 | ); 94 | } 95 | 96 | @Override 97 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 98 | mAllViews.clear(); 99 | mLineHeight.clear(); 100 | mLineWidth.clear(); 101 | mLineViews.clear(); 102 | 103 | int lineWidth = 0; 104 | int lineHeight = 0; 105 | 106 | for (int i = 0; i < getChildCount(); i++) { 107 | View child = getChildAt(i); 108 | if (child.getVisibility() == View.GONE) continue; 109 | MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); 110 | int childWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; 111 | int childHeight = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;; 112 | if (childWidth + lineWidth > getWidth() - getPaddingLeft() - getPaddingRight()) { 113 | //换行 114 | mLineHeight.add(lineHeight); 115 | mLineWidth.add(lineWidth); 116 | mAllViews.add(mLineViews); 117 | //重置line相关参数 118 | lineWidth = 0; 119 | lineHeight = childHeight; 120 | mLineViews = new ArrayList<>(); 121 | } 122 | lineWidth += childWidth; 123 | lineHeight = Math.max(lineHeight, childHeight); 124 | mLineViews.add(child); 125 | } 126 | mLineHeight.add(lineHeight); 127 | mLineWidth.add(lineWidth); 128 | mAllViews.add(mLineViews); 129 | 130 | int left = getPaddingLeft(); 131 | int top = getPaddingTop(); 132 | 133 | int lineNum = mAllViews.size(); 134 | 135 | //为每一行的view排位 136 | for (int i = 0; i < lineNum; i++) { 137 | mLineViews = mAllViews.get(i); 138 | lineHeight = mLineHeight.get(i); 139 | 140 | //设置位置 141 | // set gravity 142 | int currentLineWidth = this.mLineWidth.get(i); 143 | switch (this.mGravity) { 144 | case LEFT: 145 | left = getPaddingLeft(); 146 | break; 147 | case CENTER: 148 | left = (getWidth() - currentLineWidth) / 2 + getPaddingLeft(); 149 | break; 150 | case RIGHT: 151 | left = getWidth() - currentLineWidth + getPaddingLeft(); 152 | break; 153 | } 154 | 155 | for (int j = 0; j < mLineViews.size(); j++) { 156 | View child = mLineViews.get(j); 157 | if (child.getVisibility() == View.GONE) continue; 158 | MarginLayoutParams lp = (MarginLayoutParams) child 159 | .getLayoutParams(); 160 | 161 | int lc = left + lp.leftMargin; 162 | int tc = top + lp.topMargin; 163 | int rc = lc + child.getMeasuredWidth(); 164 | int bc = tc + child.getMeasuredHeight(); 165 | 166 | child.layout(lc, tc, rc, bc); 167 | 168 | left += child.getMeasuredWidth() + lp.leftMargin 169 | + lp.rightMargin; 170 | } 171 | top += lineHeight; 172 | } 173 | } 174 | 175 | public void setData(List data) { 176 | this.mData = data; 177 | if (data == null || data.isEmpty()) return; 178 | for (int i = 0; i < data.size(); i++) { 179 | TextView textView = new TextView(mContext); 180 | textView.setText(data.get(i)); 181 | textView.setTextSize(20); 182 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 183 | layoutParams.setMargins(10, 10, 10, 10); 184 | textView.setLayoutParams(layoutParams); 185 | textView.setTextColor(Color.WHITE); 186 | textView.setPadding(10, 5,10, 5); 187 | textView.setBackgroundColor(getResources().getColor(R.color.blue)); 188 | addView(textView); 189 | } 190 | } 191 | 192 | @Override 193 | public LayoutParams generateLayoutParams(AttributeSet attrs) { 194 | return new MarginLayoutParams(getContext(), attrs); 195 | } 196 | 197 | @Override 198 | protected LayoutParams generateDefaultLayoutParams() { 199 | return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 200 | } 201 | 202 | @Override 203 | protected LayoutParams generateLayoutParams(LayoutParams p) { 204 | return new MarginLayoutParams(p); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/hencoder/HenCoder.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.hencoder; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | import com.yazhi1992.practice.R; 9 | 10 | public class HenCoder extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_hen_coder); 16 | 17 | findViewById(R.id.button_one).setOnClickListener(new View.OnClickListener() { 18 | @Override 19 | public void onClick(View v) { 20 | startActivity(new Intent(HenCoder.this, ViwOneActivity.class)); 21 | } 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/hencoder/ViwOneActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.hencoder; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | public class ViwOneActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_viw_one); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/hencoder/one/ViewOne.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.hencoder.one; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.os.Build; 8 | import android.support.annotation.Nullable; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | 12 | /** 13 | * Created by zengyazhi on 2017/11/27. 14 | */ 15 | 16 | public class ViewOne extends View { 17 | private Paint mPaint; 18 | 19 | public ViewOne(Context context) { 20 | this(context, null); 21 | } 22 | 23 | public ViewOne(Context context, @Nullable AttributeSet attrs) { 24 | this(context, attrs, 0); 25 | } 26 | 27 | public ViewOne(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 30 | } 31 | 32 | @Override 33 | protected void onDraw(Canvas canvas) { 34 | super.onDraw(canvas); 35 | mPaint.setColor(Color.BLUE); 36 | //空心 37 | mPaint.setStyle(Paint.Style.STROKE); 38 | canvas.drawCircle(100, 100, 30, mPaint); 39 | canvas.drawRect(50, 150, 150, 200, mPaint); 40 | //实心 41 | mPaint.setStyle(Paint.Style.FILL); 42 | canvas.drawCircle(200, 100, 30, mPaint); 43 | canvas.drawRect(200, 150, 300, 200, mPaint); 44 | //绘制点(空心实心没有差别) 45 | mPaint.setStrokeWidth(50); 46 | mPaint.setStrokeCap(Paint.Cap.ROUND); 47 | canvas.drawPoint(50, 300, mPaint); 48 | mPaint.setStrokeCap(Paint.Cap.SQUARE); 49 | canvas.drawPoint(150, 300, mPaint); 50 | mPaint.setStyle(Paint.Style.STROKE); 51 | mPaint.setStrokeCap(Paint.Cap.ROUND); 52 | canvas.drawPoint(250, 300, mPaint); 53 | mPaint.setStrokeCap(Paint.Cap.SQUARE); 54 | canvas.drawPoint(350, 300, mPaint); 55 | //绘制连续点 56 | mPaint.setStyle(Paint.Style.FILL); 57 | mPaint.setStrokeCap(Paint.Cap.ROUND); 58 | float[] points = {100, 400, 200, 400, 100, 500, 200, 500}; 59 | canvas.drawPoints(points, mPaint); 60 | //绘制线 61 | mPaint.setStrokeWidth(10); 62 | canvas.drawLine(100, 550, 450, 600, mPaint); 63 | //绘制椭圆 64 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 65 | canvas.drawOval(100, 600, 500, 800, mPaint); 66 | mPaint.setStyle(Paint.Style.STROKE); 67 | //可见绘制椭圆指定的点就是左上右下四个边界点的坐标 68 | canvas.drawRect(100, 600, 500, 800, mPaint); 69 | //绘制圆角矩形 70 | canvas.drawRoundRect(100, 850, 500, 950, 50, 50, mPaint); 71 | //绘制扇形 72 | // sweepAngle 扫过的角度 73 | // useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。 74 | // STROKE 且连接到圆心,则是空心扇形 75 | canvas.drawArc(100, 1000, 300, 1200, 90, 90, true, mPaint); 76 | // STROKE 且不连接到圆心,就是弧形 77 | canvas.drawArc(100, 1000, 300, 1200, 270, 90, false, mPaint); 78 | mPaint.setStyle(Paint.Style.FILL); 79 | // FILL 且连接到圆心,则是实心扇形 80 | canvas.drawArc(100, 1300, 300, 1500, 90, 90, true, mPaint); 81 | canvas.drawArc(100, 1300, 300, 1500, 270, 90, false, mPaint); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/hook_icon/HookIcon.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.hook_icon; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.Path; 10 | import android.graphics.RectF; 11 | import android.support.annotation.Nullable; 12 | import android.util.AttributeSet; 13 | import android.view.View; 14 | import android.view.animation.LinearInterpolator; 15 | 16 | import com.yazhi1992.practice.utils.Utils; 17 | 18 | /** 19 | * Created by zengyazhi on 2017/8/14. 20 | *

21 | * 仿支付宝支付成功打钩动画 22 | */ 23 | 24 | public class HookIcon extends View { 25 | 26 | private Paint mPaint; 27 | private int mCircleRadiu; 28 | private Context mContext; 29 | private int mWidth; 30 | private int mHeight; 31 | private ValueAnimator mValueAnimator; 32 | //已绘制的角度 33 | private int mRealAngle; 34 | //起始旋转角度 35 | private int mBeginAngle; 36 | //圆是否已绘制完成 37 | private boolean mCompletePaint; 38 | private RectF mRect; 39 | //钩的绘制进度 0-1 40 | private float mDrawProgress; 41 | private float mDrawHookProgress; 42 | //钩长短比例为1:2,故小段的绘制时间占总时长的1/3 43 | private float mFirstHookPercent = 0.33f; 44 | private float mSecondHookPercent = 0.66f; 45 | private float mPaintWidth; 46 | 47 | public HookIcon(Context context) { 48 | this(context, null); 49 | } 50 | 51 | public HookIcon(Context context, @Nullable AttributeSet attrs) { 52 | this(context, attrs, 0); 53 | } 54 | 55 | public HookIcon(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 56 | super(context, attrs, defStyleAttr); 57 | mContext = context; 58 | init(); 59 | initAnim(); 60 | } 61 | 62 | private void init() { 63 | //圆的半径默认20 64 | mCircleRadiu = (int) Utils.dp2px(mContext, 30); 65 | 66 | mPaint = new Paint(); 67 | mPaint.setStyle(Paint.Style.STROKE); 68 | mPaint.setAntiAlias(true); 69 | 70 | mPaint.setColor(Color.BLUE); 71 | mPaint.setStrokeCap(Paint.Cap.SQUARE); 72 | mPaintWidth = 7; 73 | mPaint.setStrokeWidth(mPaintWidth); 74 | 75 | //从270度处开始绘制 76 | mBeginAngle = 270; 77 | } 78 | 79 | /** 80 | * 初始化动画 81 | */ 82 | private void initAnim() { 83 | mValueAnimator = ValueAnimator.ofFloat(0, 2); 84 | mValueAnimator.setDuration(1000); 85 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 86 | @Override 87 | public void onAnimationUpdate(ValueAnimator animation) { 88 | mDrawProgress = (Float) animation.getAnimatedValue(); 89 | postInvalidate(); 90 | } 91 | }); 92 | mValueAnimator.setInterpolator(new LinearInterpolator()); 93 | } 94 | 95 | public void startAnim() { 96 | mValueAnimator.start(); 97 | } 98 | 99 | @Override 100 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 101 | super.onSizeChanged(w, h, oldw, oldh); 102 | mWidth = w; 103 | mHeight = h; 104 | 105 | mRect = new RectF(); 106 | mRect.left = mWidth / 2 - mCircleRadiu; 107 | mRect.top = mHeight / 2 - mCircleRadiu; 108 | mRect.right = mWidth / 2 + mCircleRadiu; 109 | mRect.bottom = mHeight / 2 + mCircleRadiu; 110 | } 111 | 112 | @Override 113 | protected void onDraw(Canvas canvas) { 114 | super.onDraw(canvas); 115 | if (mPaint != null && mRect != null) { 116 | //绘制圆 117 | canvas.save(); 118 | if (mDrawProgress > 1) { 119 | //圆已绘制完成 120 | canvas.rotate(360 + mBeginAngle, mWidth / 2, mHeight / 2); 121 | canvas.drawArc(mRect, 0, 360, false, mPaint); 122 | } else { 123 | //根据百分比应绘制计算扇形的弧度 124 | int realAngle = (int) (360 * mDrawProgress); 125 | canvas.rotate(realAngle + mBeginAngle, mWidth / 2, mHeight / 2); 126 | canvas.drawArc(mRect, 360 - realAngle, realAngle, false, mPaint); 127 | } 128 | canvas.restore(); 129 | if (mDrawProgress > 1) { 130 | mDrawHookProgress = mDrawProgress - 1; 131 | //绘制钩 132 | Path path = new Path(); 133 | //圆心 mWidth/2 ,mHeight/2 134 | //钩的左边起点 135 | float startPointX = (float) (mWidth / 2 - 0.53d * mCircleRadiu); 136 | float startPointY = (float) (mHeight / 2 + 0.05d * mCircleRadiu); 137 | path.moveTo(startPointX, startPointY); 138 | //钩的转折点 139 | //从左起点到转折点 x/y 移动的距离(因为是45度,所以x/y 移动距离相等) 140 | float firstHookMoveDistance = (float) (0.35d * mCircleRadiu); 141 | float secondHookMoveDistance = 2 * firstHookMoveDistance; 142 | if (mDrawHookProgress >= mFirstHookPercent) { 143 | //开始绘制第二段了 144 | path.lineTo(startPointX + firstHookMoveDistance, startPointY + firstHookMoveDistance); 145 | //转折点到右终点的移动距离 146 | float secondPercent = (mDrawHookProgress - mFirstHookPercent) / (1 - mFirstHookPercent); 147 | path.lineTo(startPointX + firstHookMoveDistance + secondHookMoveDistance * secondPercent 148 | , startPointY + firstHookMoveDistance - secondHookMoveDistance * secondPercent); 149 | //绘制钩第二段 150 | canvas.drawPath(path, mPaint); 151 | } else { 152 | //还在绘制钩的第一段 153 | path.lineTo(startPointX + firstHookMoveDistance * (mDrawHookProgress / mFirstHookPercent), startPointY + firstHookMoveDistance * (mDrawHookProgress / mFirstHookPercent)); 154 | canvas.drawPath(path, mPaint); 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/hook_icon/HookIconActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.hook_icon; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | 9 | /** 10 | * 仿支付宝支付成功打钩动画 11 | */ 12 | public class HookIconActivity extends AppCompatActivity { 13 | 14 | private HookIcon mHookIcon; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_hook_icon); 20 | 21 | mHookIcon = (HookIcon) findViewById(R.id.hookIcon); 22 | 23 | findViewById(R.id.btnStart).setOnClickListener(new View.OnClickListener() { 24 | @Override 25 | public void onClick(View v) { 26 | mHookIcon.startAnim(); 27 | } 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/immersion_status_bar/ImmersionActionBarActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.immersion_status_bar; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | 9 | public class ImmersionActionBarActivity extends AppCompatActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_immersion_action_bar); 15 | 16 | StatusBarUtils.with(this) 17 | .setIsActionBar(true) 18 | .clearActionBarShadow() 19 | .setDrawable(getResources().getDrawable(R.drawable.shape)) 20 | .init(); 21 | 22 | getSupportActionBar().setBackgroundDrawable(getResources().getDrawable(R.drawable.shape)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/immersion_status_bar/ImmersionBarActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.immersion_status_bar; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | public class ImmersionBarActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | 14 | setContentView(R.layout.activity_immersion_bar); 15 | 16 | StatusBarUtils.with(this) 17 | .setColor(getResources().getColor(R.color.blue)) 18 | // .setDrawable(getResources().getDrawable(R.drawable.shape)) 19 | .init(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/immersion_status_bar/ImmersionImageActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.immersion_status_bar; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | /** 9 | * 沉浸式图片 10 | */ 11 | public class ImmersionImageActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_immersion_image); 17 | 18 | StatusBarUtils.with(this) 19 | .init(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/immersion_status_bar/ImmersionNavActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.immersion_status_bar; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.NavigationView; 5 | import android.support.v4.view.GravityCompat; 6 | import android.support.v4.widget.DrawerLayout; 7 | import android.support.v7.app.ActionBarDrawerToggle; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | 13 | import com.yazhi1992.practice.R; 14 | 15 | public class ImmersionNavActivity extends AppCompatActivity 16 | implements NavigationView.OnNavigationItemSelectedListener { 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_immersion_nav); 22 | 23 | StatusBarUtils.with(this) 24 | .setDrawerLayoutContentId(true, R.id.rl_content) 25 | .setColor(getResources().getColor(R.color.blue)) 26 | .init(); 27 | 28 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 29 | 30 | setSupportActionBar(toolbar); 31 | 32 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 33 | ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( 34 | this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); 35 | drawer.setDrawerListener(toggle); 36 | toggle.syncState(); 37 | 38 | NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 39 | navigationView.setNavigationItemSelectedListener(this); 40 | } 41 | 42 | @Override 43 | public void onBackPressed() { 44 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 45 | if (drawer.isDrawerOpen(GravityCompat.START)) { 46 | drawer.closeDrawer(GravityCompat.START); 47 | } else { 48 | super.onBackPressed(); 49 | } 50 | } 51 | 52 | @Override 53 | public boolean onCreateOptionsMenu(Menu menu) { 54 | // Inflate the menu; this adds items to the action bar if it is present. 55 | getMenuInflater().inflate(R.menu.immersion_nav, menu); 56 | return true; 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem item) { 61 | // Handle action bar item clicks here. The action bar will 62 | // automatically handle clicks on the Home/Up button, so long 63 | // as you specify a parent activity in AndroidManifest.xml. 64 | int id = item.getItemId(); 65 | 66 | //noinspection SimplifiableIfStatement 67 | if (id == R.id.action_settings) { 68 | return true; 69 | } 70 | 71 | return super.onOptionsItemSelected(item); 72 | } 73 | 74 | @SuppressWarnings("StatementWithEmptyBody") 75 | @Override 76 | public boolean onNavigationItemSelected(MenuItem item) { 77 | // Handle navigation view item clicks here. 78 | int id = item.getItemId(); 79 | 80 | if (id == R.id.nav_camera) { 81 | // Handle the camera action 82 | } else if (id == R.id.nav_gallery) { 83 | 84 | } else if (id == R.id.nav_slideshow) { 85 | 86 | } else if (id == R.id.nav_manage) { 87 | 88 | } else if (id == R.id.nav_share) { 89 | 90 | } else if (id == R.id.nav_send) { 91 | 92 | } 93 | 94 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 95 | drawer.closeDrawer(GravityCompat.START); 96 | return true; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/immersion_status_bar/MainImmersionActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.immersion_status_bar; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import com.yazhi1992.practice.R; 9 | 10 | 11 | public class MainImmersionActivity extends AppCompatActivity implements View.OnClickListener { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | 17 | setContentView(R.layout.activity_immersion_status_bar); 18 | 19 | findViewById(R.id.immersion_img_btn).setOnClickListener(this); 20 | findViewById(R.id.immersion_normal_btn).setOnClickListener(this); 21 | findViewById(R.id.immersion_nav_btn).setOnClickListener(this); 22 | findViewById(R.id.immersion_actionbar_btn).setOnClickListener(this); 23 | } 24 | 25 | @Override 26 | public void onClick(View v) { 27 | switch (v.getId()) { 28 | case R.id.immersion_img_btn: 29 | startActivity(new Intent(this, ImmersionImageActivity.class)); 30 | break; 31 | case R.id.immersion_normal_btn: 32 | startActivity(new Intent(this, ImmersionBarActivity.class)); 33 | break; 34 | case R.id.immersion_nav_btn: 35 | startActivity(new Intent(this, ImmersionNavActivity.class)); 36 | break; 37 | case R.id.immersion_actionbar_btn: 38 | startActivity(new Intent(this, ImmersionActionBarActivity.class)); 39 | break; 40 | default: 41 | break; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/immersion_status_bar/StatusBarUtils.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.immersion_status_bar; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.Drawable; 7 | import android.os.Build; 8 | import android.support.v4.widget.DrawerLayout; 9 | import android.support.v7.app.ActionBar; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.util.Log; 12 | import android.util.TypedValue; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.Window; 16 | import android.view.WindowManager; 17 | import android.widget.LinearLayout; 18 | 19 | /** 20 | * Created by zengyazhi on 2017/8/17. 21 | */ 22 | 23 | public class StatusBarUtils { 24 | private Activity mActivity; 25 | //状态栏颜色 26 | private int mColor = -1; 27 | //状态栏drawble 28 | private Drawable mDrawable; 29 | //是否是最外层布局是 DrawerLayout 的侧滑菜单 30 | private boolean mIsDrawerLayout; 31 | //是否包含 ActionBar 32 | private boolean mIsActionBar; 33 | //侧滑菜单页面的内容视图 34 | private int mContentResourseIdInDrawer; 35 | 36 | public StatusBarUtils(Activity activity) { 37 | mActivity = activity; 38 | } 39 | 40 | public static StatusBarUtils with(Activity activity) { 41 | return new StatusBarUtils(activity); 42 | } 43 | 44 | public int getColor() { 45 | return mColor; 46 | } 47 | 48 | public StatusBarUtils setColor(int color) { 49 | mColor = color; 50 | return this; 51 | } 52 | 53 | public Drawable getDrawable() { 54 | return mDrawable; 55 | } 56 | 57 | public StatusBarUtils setDrawable(Drawable drawable) { 58 | mDrawable = drawable; 59 | return this; 60 | } 61 | 62 | public boolean isDrawerLayout() { 63 | return mIsDrawerLayout; 64 | } 65 | 66 | public boolean isActionBar() { 67 | return mIsActionBar; 68 | } 69 | 70 | public StatusBarUtils setIsActionBar(boolean actionBar) { 71 | mIsActionBar = actionBar; 72 | return this; 73 | } 74 | 75 | /** 76 | * 是否是最外层布局为 DrawerLayout 的侧滑菜单 77 | * 78 | * @param drawerLayout 是否最外层布局为 DrawerLayout 79 | * @param contentId 内容视图的 id 80 | * @return 81 | */ 82 | public StatusBarUtils setDrawerLayoutContentId(boolean drawerLayout, int contentId) { 83 | mIsDrawerLayout = drawerLayout; 84 | mContentResourseIdInDrawer = contentId; 85 | return this; 86 | } 87 | 88 | public void init() { 89 | fullScreen(mActivity); 90 | if (mColor != -1) { 91 | //设置了状态栏颜色 92 | addStatusViewWithColor(mActivity, mColor); 93 | } 94 | if (mDrawable != null) { 95 | //设置了状态栏 drawble,例如渐变色 96 | addStatusViewWithDrawble(mActivity, mDrawable); 97 | } 98 | if (isDrawerLayout()) { 99 | //未设置 fitsSystemWindows 且是侧滑菜单,需要设置 fitsSystemWindows 以解决 4.4 上侧滑菜单上方白条问题 100 | fitsSystemWindows(mActivity); 101 | } 102 | if (isActionBar()) { 103 | //要增加内容视图的 paddingTop,否则内容被 ActionBar 遮盖 104 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 105 | ViewGroup rootView = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); 106 | rootView.setPadding(0, getStatusBarHeight(mActivity) + getActionBarHeight(mActivity), 0, 0); 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * 去除 ActionBar 阴影 113 | */ 114 | public StatusBarUtils clearActionBarShadow() { 115 | if (Build.VERSION.SDK_INT >= 21) { 116 | ActionBar supportActionBar = ((AppCompatActivity) mActivity).getSupportActionBar(); 117 | if (supportActionBar != null) { 118 | supportActionBar.setElevation(0); 119 | } 120 | } 121 | return this; 122 | } 123 | 124 | /** 125 | * 设置页面最外层布局 FitsSystemWindows 属性 126 | * 127 | * @param activity 128 | */ 129 | private void fitsSystemWindows(Activity activity) { 130 | ViewGroup contentFrameLayout = (ViewGroup) activity.findViewById(android.R.id.content); 131 | View parentView = contentFrameLayout.getChildAt(0); 132 | if (parentView != null && Build.VERSION.SDK_INT >= 14) { 133 | parentView.setFitsSystemWindows(true); 134 | //布局预留状态栏高度的 padding 135 | if (parentView instanceof DrawerLayout) { 136 | DrawerLayout drawer = (DrawerLayout) parentView; 137 | //将主页面顶部延伸至status bar;虽默认为false,但经测试,DrawerLayout需显示设置 138 | drawer.setClipToPadding(false); 139 | } 140 | } 141 | } 142 | 143 | /** 144 | * 利用反射获取状态栏高度 145 | * 146 | * @return 147 | */ 148 | public static int getStatusBarHeight(Activity activity) { 149 | int result = 0; 150 | //获取状态栏高度的资源id 151 | int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); 152 | if (resourceId > 0) { 153 | result = activity.getResources().getDimensionPixelSize(resourceId); 154 | } 155 | Log.e("getStatusBarHeight", result + ""); 156 | return result; 157 | } 158 | 159 | /** 160 | * 获得 ActionBar 的高度 161 | * 162 | * @param context 163 | * @return 164 | */ 165 | public static int getActionBarHeight(Context context) { 166 | int result = 0; 167 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 168 | TypedValue tv = new TypedValue(); 169 | context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); 170 | result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics()); 171 | } 172 | return result; 173 | } 174 | 175 | /** 176 | * 添加状态栏占位视图 177 | * 178 | * @param activity 179 | */ 180 | private void addStatusViewWithColor(Activity activity, int color) { 181 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 182 | if (isDrawerLayout()) { 183 | //要在内容布局增加状态栏,否则会盖在侧滑菜单上 184 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 185 | //DrawerLayout 则需要在第一个子视图即内容试图中添加padding 186 | View parentView = rootView.getChildAt(0); 187 | LinearLayout linearLayout = new LinearLayout(activity); 188 | linearLayout.setOrientation(LinearLayout.VERTICAL); 189 | View statusBarView = new View(activity); 190 | ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 191 | getStatusBarHeight(activity)); 192 | statusBarView.setBackgroundColor(color); 193 | //添加占位状态栏到线性布局中 194 | linearLayout.addView(statusBarView, lp); 195 | //侧滑菜单 196 | DrawerLayout drawer = (DrawerLayout) parentView; 197 | //内容视图 198 | View content = activity.findViewById(mContentResourseIdInDrawer); 199 | //将内容视图从 DrawerLayout 中移除 200 | drawer.removeView(content); 201 | //添加内容视图 202 | linearLayout.addView(content, content.getLayoutParams()); 203 | //将带有占位状态栏的新的内容视图设置给 DrawerLayout 204 | drawer.addView(linearLayout, 0); 205 | } else { 206 | //设置 paddingTop 207 | ViewGroup rootView = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); 208 | rootView.setPadding(0, getStatusBarHeight(mActivity), 0, 0); 209 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 210 | //直接设置状态栏颜色 211 | activity.getWindow().setStatusBarColor(color); 212 | } else { 213 | //增加占位状态栏 214 | ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView(); 215 | View statusBarView = new View(activity); 216 | ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 217 | getStatusBarHeight(activity)); 218 | statusBarView.setBackgroundColor(color); 219 | decorView.addView(statusBarView, lp); 220 | } 221 | } 222 | } 223 | } 224 | 225 | /** 226 | * 添加状态栏占位视图 227 | * 228 | * @param activity 229 | */ 230 | private void addStatusViewWithDrawble(Activity activity, Drawable drawable) { 231 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 232 | //占位状态栏 233 | View statusBarView = new View(activity); 234 | ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 235 | getStatusBarHeight(activity)); 236 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 237 | statusBarView.setBackground(drawable); 238 | } else { 239 | statusBarView.setBackgroundDrawable(drawable); 240 | } 241 | if (isDrawerLayout()) { 242 | //要在内容布局增加状态栏,否则会盖在侧滑菜单上 243 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 244 | //DrawerLayout 则需要在第一个子视图即内容试图中添加padding 245 | View parentView = rootView.getChildAt(0); 246 | LinearLayout linearLayout = new LinearLayout(activity); 247 | linearLayout.setOrientation(LinearLayout.VERTICAL); 248 | //添加占位状态栏到线性布局中 249 | linearLayout.addView(statusBarView, lp); 250 | //侧滑菜单 251 | DrawerLayout drawer = (DrawerLayout) parentView; 252 | //内容视图 253 | View content = activity.findViewById(mContentResourseIdInDrawer); 254 | //将内容视图从 DrawerLayout 中移除 255 | drawer.removeView(content); 256 | //添加内容视图 257 | linearLayout.addView(content, content.getLayoutParams()); 258 | //将带有占位状态栏的新的内容视图设置给 DrawerLayout 259 | drawer.addView(linearLayout, 0); 260 | } else { 261 | //增加占位状态栏,并增加状态栏高度的 paddingTop 262 | ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView(); 263 | decorView.addView(statusBarView, lp); 264 | //设置 paddingTop 265 | ViewGroup rootView = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); 266 | rootView.setPadding(0, getStatusBarHeight(mActivity), 0, 0); 267 | } 268 | } 269 | } 270 | 271 | /** 272 | * 通过设置全屏,设置状态栏透明 273 | * 274 | * @param activity 275 | */ 276 | private void fullScreen(Activity activity) { 277 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 278 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 279 | //5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色 280 | Window window = activity.getWindow(); 281 | View decorView = window.getDecorView(); 282 | //两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间 283 | int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 284 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 285 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 286 | decorView.setSystemUiVisibility(option); 287 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 288 | window.setStatusBarColor(Color.TRANSPARENT); 289 | //导航栏颜色也可以正常设置 290 | // window.setNavigationBarColor(Color.TRANSPARENT); 291 | } else { 292 | Window window = activity.getWindow(); 293 | WindowManager.LayoutParams attributes = window.getAttributes(); 294 | int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 295 | int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 296 | attributes.flags |= flagTranslucentStatus; 297 | // attributes.flags |= flagTranslucentNavigation; 298 | window.setAttributes(attributes); 299 | } 300 | } 301 | } 302 | 303 | /** 304 | * 通过设置全屏,设置状态栏透明 导航栏黑色 305 | * 306 | * @param activity 307 | */ 308 | public static void setStatusTransparent(Activity activity) { 309 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 310 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 311 | Window window = activity.getWindow(); 312 | 313 | WindowManager.LayoutParams attributes = window.getAttributes(); 314 | int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 315 | int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 316 | // attributes.flags |= flagTranslucentStatus; 317 | attributes.flags |= flagTranslucentNavigation; 318 | window.setAttributes(attributes); 319 | 320 | window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 321 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 322 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 323 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 324 | window.setStatusBarColor(Color.TRANSPARENT); 325 | window.setNavigationBarColor(Color.TRANSPARENT); 326 | } else { 327 | Window window = activity.getWindow(); 328 | WindowManager.LayoutParams attributes = window.getAttributes(); 329 | int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 330 | int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 331 | attributes.flags |= flagTranslucentStatus; 332 | attributes.flags |= flagTranslucentNavigation; 333 | window.setAttributes(attributes); 334 | } 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/line_chart_view/LineChart.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.line_chart_view; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.Path; 9 | import android.graphics.PorterDuff; 10 | import android.graphics.PorterDuffXfermode; 11 | import android.graphics.RectF; 12 | import android.graphics.Region; 13 | import android.support.annotation.Nullable; 14 | import android.util.AttributeSet; 15 | import android.util.TypedValue; 16 | import android.view.MotionEvent; 17 | import android.view.View; 18 | 19 | import com.yazhi1992.practice.R; 20 | import com.yazhi1992.practice.utils.Utils; 21 | 22 | import java.math.BigDecimal; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * Created by zengyazhi on 17/4/11. 30 | *

31 | * 折线图 32 | * 参考:https://github.com/xiaoyunfei/LineChart 33 | */ 34 | 35 | public class LineChart extends View { 36 | 37 | //屏幕宽高 38 | private int mWidth; 39 | private int mHeight; 40 | //坐标轴字体大小 41 | private float mAxisTextSize; 42 | //坐标轴颜色 43 | private int mAxisColor; 44 | //坐标轴文字颜色 45 | private int mAxisTextColor; 46 | //折线颜色 47 | private int mChartColor; 48 | //绘制坐标轴画笔 49 | private Paint mAxisPaint; 50 | //绘制坐标轴文字画笔 51 | private Paint mAxisTextPaint; 52 | //绘制折线画笔 53 | private Paint mChartPaint; 54 | //坐标轴画笔宽度 55 | private int mAxisWidth; 56 | //数值 57 | private List mValueList = new ArrayList<>(); 58 | private int mMaxYValue; 59 | private int mMinYValue; 60 | //y轴坐标值最大宽度 61 | private float mMaxYWidth; 62 | //x轴坐标值高度 63 | private float mXHeight; 64 | private Context mContext; 65 | private Path mChartPath; 66 | //action_down时的横坐标 67 | private float mStartX; 68 | //绘制起始点的坐标 69 | private float mStartPaintX; 70 | //action_down时,开始绘制的x坐标 71 | private float mActionDownPaintX; 72 | //手指拖动时x轴的偏移量 73 | private float mOffset; 74 | //x轴起始绘制点 75 | private float mOriginalX; 76 | //折线点的点击范围 77 | private Map mRegionMap = new HashMap<>(); 78 | //点击的点在数组中的位置 79 | private int mClickNum = -1; 80 | //y轴的最大/最小刻度值 81 | private int mYAxisMaxValue; 82 | private int mYAxisMinValue; 83 | //x刻度间隔 84 | private float mXDistance; 85 | //Y轴的高度与值得范围 86 | private float mYUsedHeight; 87 | private float mYUserValue; 88 | //刻度长度 89 | private float mLength; 90 | //每次ondraw绘制遍历时判断path是否已重置 91 | private boolean mReMoveTo; 92 | private ValueAnimator mValueAnimator; 93 | 94 | public LineChart(Context context) { 95 | this(context, null); 96 | } 97 | 98 | public LineChart(Context context, @Nullable AttributeSet attrs) { 99 | this(context, attrs, 0); 100 | } 101 | 102 | public LineChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 103 | super(context, attrs, defStyleAttr); 104 | init(context); 105 | } 106 | 107 | /** 108 | * 设置数值 109 | * 110 | * @param valueList 111 | */ 112 | public void setValueList(List valueList) { 113 | mValueList = valueList; 114 | mMaxYValue = 0; 115 | mMinYValue = 0; 116 | calculateDrawValue(); 117 | postInvalidate(); 118 | startAnim(); 119 | } 120 | 121 | /** 122 | * 计算绘制需要的一些值 123 | * y轴文字最大宽度、y轴最大、最小刻度值 124 | * 如果y轴是数值,即计算最大数值的宽度 125 | */ 126 | private void calculateDrawValue() { 127 | if (mMaxYValue == 0) { 128 | for (int i = 0; i < mValueList.size(); i++) { 129 | ValueBean valueBean = mValueList.get(i); 130 | mMaxYValue = Math.max(mMaxYValue, valueBean.getYValue()); 131 | if (i == 0) { 132 | mMinYValue = valueBean.getYValue(); 133 | } else { 134 | mMinYValue = Math.min(mMinYValue, valueBean.getYValue()); 135 | } 136 | } 137 | } 138 | 139 | //对y轴最大最小值的换算,以免都使用0为起始刻度,使得在数值较大时几乎就是一条直线 140 | String s = Integer.toString(mMaxYValue); 141 | //几位数 142 | int length = s.length(); 143 | int pow = (int) Math.pow(10, length - 2); 144 | double d1 = mMaxYValue * 1d / pow; 145 | int newi1 = mMaxYValue / pow; 146 | BigDecimal bigDecimal1 = new BigDecimal(d1).setScale(0, BigDecimal.ROUND_HALF_UP); 147 | if (newi1 < bigDecimal1.doubleValue()) { 148 | //五入 149 | mYAxisMaxValue = (newi1 + 1) * pow; 150 | } else { 151 | //四舍 152 | int newPow = pow / 10; 153 | int newI = mMaxYValue / newPow; 154 | mYAxisMaxValue = (newI + 1) * newPow; 155 | } 156 | 157 | String s2 = Integer.toString(mMinYValue); 158 | //几位数 159 | int length2 = s2.length(); 160 | if (length2 <= 2) { 161 | mYAxisMinValue = 0; 162 | } else { 163 | int pow2 = (int) Math.pow(10, length2 - 2); 164 | double d2 = mMinYValue * 1d / pow2; 165 | int i2 = mMinYValue / pow2; 166 | BigDecimal bigDecimal = new BigDecimal(d2).setScale(0, BigDecimal.ROUND_HALF_UP); 167 | if (i2 < bigDecimal.doubleValue()) { 168 | //五入 169 | int pow3 = pow2 / 10; 170 | int i3 = mMaxYValue / pow3; 171 | mYAxisMinValue = (i3 - 1) * pow3; 172 | } else { 173 | //四舍 174 | mYAxisMinValue = i2 * pow2; 175 | } 176 | } 177 | 178 | //计算坐标轴值得宽高,确定顶点位置 179 | if (mMaxYWidth == 0 && mMaxYValue != 0) { 180 | //y坐标值宽度,与y坐标距离10dp 181 | mMaxYWidth = mAxisTextPaint.measureText(Integer.toString(mMaxYValue)) + Utils.dp2px(mContext, 10); 182 | //x坐标值高度,与x坐标距离10dp 183 | mXHeight = -mAxisTextPaint.ascent() + Utils.dp2px(mContext, 10); 184 | mOriginalX = mMaxYWidth; 185 | mActionDownPaintX = mOriginalX; 186 | } 187 | } 188 | 189 | @Override 190 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 191 | super.onSizeChanged(w, h, oldw, oldh); 192 | mWidth = w; 193 | mHeight = h; 194 | } 195 | 196 | private void init(Context context) { 197 | mContext = context; 198 | mChartPath = new Path(); 199 | initDefaultValue(); 200 | initPaint(); 201 | } 202 | 203 | /** 204 | * 初始化默认值 205 | */ 206 | private void initDefaultValue() { 207 | mAxisTextSize = Utils.dp2px(mContext, 12); 208 | mXDistance = Utils.dp2px(mContext, 40); 209 | mChartColor = mContext.getResources().getColor(R.color.my_color); 210 | mAxisColor = Color.BLACK; 211 | mAxisTextColor = Color.BLACK; 212 | mAxisWidth = 5; 213 | } 214 | 215 | private void initPaint() { 216 | mAxisPaint = new Paint(); 217 | mAxisPaint.setAntiAlias(true); 218 | mAxisPaint.setStrokeWidth(mAxisWidth); 219 | mAxisPaint.setColor(mAxisColor); 220 | mAxisPaint.setStrokeCap(Paint.Cap.ROUND); 221 | 222 | mAxisTextPaint = new Paint(); 223 | mAxisTextPaint.setAntiAlias(true); 224 | mAxisTextPaint.setTextSize(mAxisTextSize); 225 | mAxisTextPaint.setColor(mAxisTextColor); 226 | mAxisTextPaint.setStrokeCap(Paint.Cap.ROUND); 227 | 228 | mChartPaint = new Paint(); 229 | mChartPaint.setAntiAlias(true); 230 | mChartPaint.setStrokeWidth(mAxisWidth); 231 | mChartPaint.setStrokeCap(Paint.Cap.ROUND); 232 | mChartPaint.setColor(mChartColor); 233 | mChartPaint.setStyle(Paint.Style.STROKE); 234 | } 235 | 236 | @Override 237 | protected void onDraw(Canvas canvas) { 238 | super.onDraw(canvas); 239 | drawAxis(canvas); 240 | drawYAxis(canvas); 241 | drawXAxisAndLine(canvas); 242 | } 243 | 244 | /** 245 | * 绘制x轴与折线 246 | * 247 | * @param canvas 248 | */ 249 | private void drawXAxisAndLine(Canvas canvas) { 250 | //开始绘制的第一个点的x坐标 251 | mStartPaintX = mActionDownPaintX + mOffset; 252 | if (mStartPaintX > mOriginalX) { 253 | //拖到最左边,不允许继续往右拖动 254 | mStartPaintX = mOriginalX; 255 | } else if (mStartPaintX < mOriginalX - (mValueList.size() - mWidth / mXDistance + 1) * mXDistance) { 256 | //拖到最右边,不允许继续往左拖动 257 | mStartPaintX = mOriginalX - (mValueList.size() - mWidth / mXDistance + 1) * mXDistance; 258 | if (mValueAnimator != null && mValueAnimator.isRunning()) { 259 | mValueAnimator.cancel(); 260 | } 261 | } 262 | float startPaintX = mStartPaintX; 263 | mRegionMap.clear(); 264 | mReMoveTo = false; 265 | mChartPath.reset(); 266 | for (int i = 0; i < mValueList.size(); i++) { 267 | if (startPaintX < -mXDistance) { 268 | startPaintX += mXDistance; 269 | continue; 270 | } 271 | ValueBean valueBean = mValueList.get(i); 272 | if (startPaintX > 0 && startPaintX < mWidth && startPaintX >= mOriginalX) { 273 | //绘制x轴刻度 274 | canvas.drawLine(startPaintX, mHeight - mXHeight, startPaintX, mHeight - mXHeight - mLength, mAxisPaint); 275 | //绘制x轴刻度值 276 | String xValue = valueBean.getXValue(); 277 | canvas.drawText(xValue, startPaintX - mAxisTextPaint.measureText(xValue) / 2, mHeight, mAxisTextPaint); 278 | } 279 | //构造折线path 280 | Path path = new Path(); 281 | //点20dp范围内可点击 282 | path.addCircle(startPaintX, mHeight - mXHeight - (valueBean.getYValue() - mYAxisMinValue) / mYUserValue * mYUsedHeight, Utils.dp2px(mContext, 20), Path.Direction.CW); 283 | if (!mValueAnimator.isRunning()) { 284 | //动画停止时才计算点的点击范围 285 | Region region = new Region(); 286 | region.setPath(path, new Region(0, 0, mWidth, mHeight)); 287 | mRegionMap.put(i, region); 288 | } 289 | if (mReMoveTo) { 290 | mChartPath.moveTo(startPaintX, mHeight - mXHeight - (valueBean.getYValue() - mYAxisMinValue) / mYUserValue * mYUsedHeight); 291 | mReMoveTo = true; 292 | } else { 293 | mChartPath.lineTo(startPaintX, mHeight - mXHeight - (valueBean.getYValue() - mYAxisMinValue) / mYUserValue * mYUsedHeight); 294 | } 295 | startPaintX += mXDistance; 296 | if (startPaintX > mWidth + mXDistance) { 297 | //只绘制屏幕内的内容 298 | break; 299 | } 300 | } 301 | //新开图层 302 | int layerId = canvas.saveLayer(0, 0, mWidth, mHeight, null, Canvas.ALL_SAVE_FLAG); 303 | if (mClickNum != -1) { 304 | //点击了该点,绘制数值 305 | canvas.drawText(Integer.toString(mValueList.get(mClickNum).getYValue()), mStartPaintX + mXDistance * mClickNum - mAxisTextPaint.measureText(Integer.toString(mValueList.get(mClickNum).getYValue())) / 2, mHeight - mXHeight - (mValueList.get(mClickNum).getYValue() - mYAxisMinValue) / mYUserValue * mYUsedHeight - Utils.dp2px(mContext, 15), mAxisTextPaint); 306 | } 307 | //绘制折线 308 | mChartPaint.setXfermode(null); 309 | mChartPaint.setColor(mChartColor); 310 | mChartPaint.setStyle(Paint.Style.STROKE); 311 | canvas.drawPath(mChartPath, mChartPaint); 312 | //将折线超出x轴坐标的部分截取掉 313 | mChartPaint.setStyle(Paint.Style.FILL); 314 | mChartPaint.setColor(Color.TRANSPARENT); 315 | mChartPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 316 | RectF rectF = new RectF(0, 0, mMaxYWidth, mHeight); 317 | canvas.drawRect(rectF, mChartPaint); 318 | //保存图层 319 | canvas.restoreToCount(layerId); 320 | } 321 | 322 | /** 323 | * 绘制y轴刻度与刻度值 324 | * 325 | * @param canvas 326 | */ 327 | private void drawYAxis(Canvas canvas) { 328 | mLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()); 329 | //y刻度间隔 330 | float yDistance = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics()); 331 | //总共多少刻度,空余10%高度 332 | int number = (int) ((mHeight - mXHeight) * 0.9 / yDistance); 333 | //y刻度值间隔,间距取整 334 | int yValue = (int) Math.ceil((mYAxisMaxValue - mYAxisMinValue) / number); 335 | //y轴使用到的高度(用于计算每个值所在高度时使用) 336 | mYUsedHeight = number * yDistance; 337 | mYUserValue = number * yValue; 338 | //绘制y轴最小刻度值 339 | canvas.drawText(Integer.toString(mYAxisMinValue), 0 + mAxisTextPaint.measureText(Integer.toString(mMaxYValue)) - mAxisTextPaint.measureText(Integer.toString(mYAxisMinValue)), mHeight - mXHeight + mAxisWidth / 2 - mAxisTextPaint.ascent() / 2, mAxisTextPaint); 340 | for (int i = 1; i <= number; i++) { 341 | //绘制y轴刻度 342 | canvas.drawLine(mMaxYWidth, mHeight - mXHeight - yDistance * i + mAxisWidth / 2, mMaxYWidth + mLength, mHeight - mXHeight - yDistance * i + mAxisWidth / 2, mAxisPaint); 343 | //绘制y轴刻度值 344 | int yNumber = mYAxisMinValue + yValue * i; 345 | canvas.drawText(Integer.toString(yNumber), 0 + mAxisTextPaint.measureText(Integer.toString(mMaxYValue)) - mAxisTextPaint.measureText(Integer.toString(yNumber)), mHeight - mXHeight - yDistance * i + mAxisWidth / 2 - mAxisTextPaint.ascent() / 2, mAxisTextPaint); 346 | } 347 | } 348 | 349 | /** 350 | * 绘制坐标轴 351 | * 352 | * @param canvas 353 | */ 354 | private void drawAxis(Canvas canvas) { 355 | //绘制x轴 356 | canvas.drawLine(mMaxYWidth, mHeight - mXHeight, mWidth, mHeight - mXHeight, mAxisPaint); 357 | //绘制y轴 358 | canvas.drawLine(mMaxYWidth, mHeight - mXHeight, mMaxYWidth, 0, mAxisPaint); 359 | } 360 | 361 | @Override 362 | public boolean onTouchEvent(MotionEvent event) { 363 | switch (event.getAction()) { 364 | case MotionEvent.ACTION_DOWN: 365 | mActionDownPaintX = mStartPaintX; 366 | mOffset = 0; 367 | mStartX = event.getX(); 368 | //判断是否点击了折线点 369 | for (Map.Entry entry : mRegionMap.entrySet()) { 370 | if (entry.getValue().contains((int) (event.getX()), (int) (event.getY()))) { 371 | mClickNum = entry.getKey(); 372 | postInvalidate(); 373 | break; 374 | } 375 | } 376 | break; 377 | case MotionEvent.ACTION_MOVE: 378 | float x = event.getX(); 379 | mOffset = x - mStartX; 380 | postInvalidate(); 381 | break; 382 | case MotionEvent.ACTION_UP: 383 | break; 384 | default: 385 | break; 386 | } 387 | return true; 388 | } 389 | 390 | public void startAnim() { 391 | //最大移动距离 392 | float maxOffset = -(mValueList.size() - mWidth / mXDistance + 1) * mXDistance; 393 | mValueAnimator = ValueAnimator.ofFloat(0, maxOffset); 394 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 395 | @Override 396 | public void onAnimationUpdate(ValueAnimator animation) { 397 | mOffset = (float) animation.getAnimatedValue(); 398 | postInvalidate(); 399 | } 400 | }); 401 | mValueAnimator.setDuration((long) (-maxOffset * 1.5)); 402 | mValueAnimator.start(); 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/line_chart_view/LineChartActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.line_chart_view; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import static android.R.attr.value; 15 | 16 | public class LineChartActivity extends AppCompatActivity { 17 | 18 | private LineChart mLineChart; 19 | //值 20 | private List mData = new ArrayList<>(); 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_line_chart); 26 | 27 | int time = 0; 28 | for (int i = 0; i < 20; i++) { 29 | mData.add(new ValueBean("" + time, (int) (1000 + Math.random() * 200))); 30 | time++; 31 | } 32 | 33 | mLineChart = (LineChart) findViewById(R.id.lineChartView); 34 | mLineChart.setValueList(mData); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/line_chart_view/ValueBean.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.line_chart_view; 2 | 3 | /** 4 | * Created by zengyazhi on 17/4/11. 5 | * 6 | * 数值model 7 | */ 8 | 9 | public class ValueBean { 10 | private String mXValue; 11 | private int mYValue; 12 | 13 | public ValueBean(String XValue, int YValue) { 14 | mXValue = XValue; 15 | mYValue = YValue; 16 | } 17 | 18 | public String getXValue() { 19 | return mXValue; 20 | } 21 | 22 | public void setXValue(String XValue) { 23 | mXValue = XValue; 24 | } 25 | 26 | public int getYValue() { 27 | return mYValue; 28 | } 29 | 30 | public void setYValue(int YValue) { 31 | mYValue = YValue; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/question_button/OnTouchActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.question_button 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.View 7 | 8 | import com.yazhi1992.practice.R 9 | 10 | class OnTouchActivity : AppCompatActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_on_touch) 15 | 16 | // findViewById(R.id.btn1).setOnTouchListener { v, event -> true} 17 | findViewById(R.id.btn1).setOnClickListener(object :View.OnClickListener { 18 | override fun onClick(v: View?) { 19 | Log.e("zyz", "btn1") 20 | } 21 | }) 22 | 23 | // findViewById(R.id.btn2).setOnTouchListener { v, event -> true} 24 | findViewById(R.id.btn2).setOnClickListener(object :View.OnClickListener { 25 | override fun onClick(v: View?) { 26 | Log.e("zyz", "btn2") 27 | } 28 | }) 29 | 30 | // findViewById(R.id.btn3).setOnTouchListener { v, event -> true} 31 | findViewById(R.id.btn3).setOnClickListener(object :View.OnClickListener { 32 | override fun onClick(v: View?) { 33 | Log.e("zyz", "btn3") 34 | } 35 | }) 36 | // findViewById(R.id.btn4).setOnTouchListener { v, event -> true} 37 | findViewById(R.id.btn4).setOnClickListener(object :View.OnClickListener { 38 | override fun onClick(v: View?) { 39 | Log.e("zyz", "btn4") 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/question_button/OnTouchReturnFalseButton.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.question_button; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | import android.widget.Button; 7 | 8 | /** 9 | * Created by zengyazhi on 2017/9/4. 10 | */ 11 | 12 | public class OnTouchReturnFalseButton extends Button { 13 | 14 | public OnTouchReturnFalseButton(Context context) { 15 | super(context); 16 | } 17 | 18 | public OnTouchReturnFalseButton(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public OnTouchReturnFalseButton(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | @Override 27 | public boolean onTouchEvent(MotionEvent event) { 28 | // return super.onTouchEvent(event); 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/question_button/OnTouchReturnTrueButton.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.question_button; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.MotionEvent; 6 | import android.view.ViewGroup; 7 | 8 | /** 9 | * Created by zengyazhi on 2017/9/4. 10 | */ 11 | 12 | public class OnTouchReturnTrueButton extends ViewGroup { 13 | 14 | public OnTouchReturnTrueButton(Context context) { 15 | super(context); 16 | } 17 | 18 | public OnTouchReturnTrueButton(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public OnTouchReturnTrueButton(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | @Override 27 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 28 | 29 | } 30 | 31 | @Override 32 | public boolean onTouchEvent(MotionEvent event) { 33 | return super.onTouchEvent(event); 34 | // return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/question_button/QuestionActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.question_button 2 | 3 | import android.content.Intent 4 | import android.support.v7.app.AppCompatActivity 5 | import android.os.Bundle 6 | import android.util.Log 7 | import android.view.View 8 | 9 | import com.yazhi1992.practice.R 10 | 11 | class QuestionActivity : AppCompatActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setContentView(R.layout.activity_question) 16 | 17 | findViewById(R.id.dp_practive).setOnClickListener(object : View.OnClickListener { 18 | override fun onClick(v: View?) { 19 | startActivity(Intent(this@QuestionActivity, SizeActivity::class.java)) 20 | } 21 | }) 22 | 23 | findViewById(R.id.onTouch_practive).setOnClickListener(object : View.OnClickListener { 24 | override fun onClick(v: View?) { 25 | startActivity(Intent(this@QuestionActivity, OnTouchActivity::class.java)) 26 | } 27 | }) 28 | 29 | findViewById(R.id.arthmetic_practive).setOnClickListener(object : View.OnClickListener { 30 | override fun onClick(v: View?) { 31 | val array1 = intArrayOf(1, 3, 8) 32 | val array2 = intArrayOf(2, 3, 4, 7, 9) 33 | 34 | val max = Math.max(array1.size, array2.size) 35 | 36 | var newArray = IntArray(array1.size + array2.size) 37 | var i = 0 38 | var j = 0 39 | var newIndex = 0 40 | 41 | while (i < array1.size && j < array2.size) { 42 | if (array1[i] > array2[j]) { 43 | newArray[newIndex++] = array2[j++] 44 | } else { 45 | newArray[newIndex++] = array1[i++] 46 | } 47 | } 48 | 49 | while (i < array1.size) { 50 | newArray[newIndex++] = array1[i++] 51 | } 52 | 53 | while (j < array2.size) { 54 | newArray[newIndex++] = array2[j++] 55 | } 56 | 57 | // while (newIndex < newArray.size) { 58 | // var value1 = -1 59 | // var value2 = -1 60 | // if (i < array1.size) { 61 | // //未超出 62 | // value1 = array1[i] 63 | // } else { 64 | // //有一个数组已添加完,另一个数组直接全部添加进新数组 65 | // newArray[newIndex] = array2[j] 66 | // j++ 67 | // newIndex++ 68 | // continue 69 | // } 70 | // if (j < array2.size) { 71 | // //未超出 72 | // value2 = array2[j] 73 | // } else { 74 | // //有一个数组已添加完,另一个数组直接全部添加进新数组 75 | // newArray[newIndex] = array1[i] 76 | // i++ 77 | // newIndex++ 78 | // continue 79 | // } 80 | // if (value1 != -1 && value2 != -1) { 81 | // 82 | // } 83 | // } 84 | 85 | for (i in 0..(newArray.size - 1)) { 86 | Log.e("zyz", "" + newArray[i]) 87 | } 88 | } 89 | }) 90 | 91 | findViewById(R.id.arthmetic_practive_2).setOnClickListener(object : View.OnClickListener { 92 | override fun onClick(v: View?) { 93 | val array = intArrayOf(2, 8, 12, 4, 10, 25, 7, 9) 94 | 95 | //选择排序,每一轮都选出最小的数并排在该轮的头部 96 | 97 | for (i in 0..(array.size - 2)) { 98 | var temp = array[i] 99 | var tempIndex = i 100 | for (j in (i + 1)..(array.size-1)) { 101 | if(array[j] < temp) { 102 | //保存较小值的数值和位置 103 | temp = array[j] 104 | tempIndex = j 105 | } 106 | } 107 | //一轮比较完,将最小值排在该轮的头部 108 | array[tempIndex] = array[i] 109 | array[i] = temp 110 | } 111 | 112 | for (i in 0..(array.size - 1)) { 113 | Log.e("zyz", "" + array[i]) 114 | } 115 | } 116 | }) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/question_button/SizeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.question_button 2 | 3 | import android.graphics.Point 4 | import android.os.Build 5 | import android.support.v7.app.AppCompatActivity 6 | import android.os.Bundle 7 | import android.util.DisplayMetrics 8 | import android.widget.TextView 9 | 10 | import com.yazhi1992.practice.R 11 | 12 | class SizeActivity : AppCompatActivity() { 13 | 14 | lateinit var mTvSize: TextView 15 | lateinit var mTvInfo: TextView 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_size) 20 | 21 | mTvSize = findViewById(R.id.tv_size) as TextView 22 | mTvInfo = findViewById(R.id.tv_info) as TextView 23 | 24 | mTvSize.post(object : Runnable { 25 | override fun run() { 26 | mTvSize.text = "width:${mTvSize.width}" 27 | } 28 | }) 29 | 30 | var point = Point() 31 | val stringBuffer = StringBuffer() 32 | 33 | val defaultDisplay = windowManager.defaultDisplay 34 | defaultDisplay.getSize(point) 35 | stringBuffer.append("defaultDisplay.getSize() :") 36 | stringBuffer.append("\n w: ${point.x} h: ${point.y}") 37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 38 | defaultDisplay.getRealSize(point) 39 | stringBuffer.append("\n\ndefaultDisplay.getRealSize() (API >= 17) :") 40 | stringBuffer.append("\n point.x: ${point.x} point.y: ${point.y}") 41 | } 42 | 43 | val dm = DisplayMetrics() 44 | defaultDisplay.getMetrics(dm) 45 | stringBuffer.append("\n\ndisplayMetrics.density : ${dm.density}") 46 | stringBuffer.append("\n\ndefaultDisplay.getMetrics() :") 47 | stringBuffer.append("\n widthPixels: ${dm.widthPixels} heightPixels: ${dm.heightPixels}") 48 | 49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 50 | defaultDisplay.getRealMetrics(dm) 51 | stringBuffer.append("\n\ndefaultDisplay.getRealMetrics() (API >= 17) :") 52 | stringBuffer.append("\n widthPixels: ${dm.widthPixels} heightPixels: ${dm.heightPixels}") 53 | } 54 | 55 | val displayMetrics = application.resources.displayMetrics 56 | stringBuffer.append("\n\ngetResources().getDisplayMetrics :") 57 | stringBuffer.append("\n widthPixels: ${displayMetrics.widthPixels} heightPixels: ${displayMetrics.heightPixels}") 58 | 59 | 60 | mTvInfo.text = stringBuffer.toString() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/ripple_view/RippleActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.ripple_view; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | 9 | public class RippleActivity extends AppCompatActivity { 10 | 11 | private RippleView mRippleView; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_ripple); 17 | 18 | mRippleView = (RippleView) findViewById(R.id.rippleView); 19 | 20 | findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() { 21 | @Override 22 | public void onClick(View v) { 23 | mRippleView.star(); 24 | } 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/ripple_view/RippleView.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.ripple_view; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.PorterDuff; 9 | import android.graphics.PorterDuffXfermode; 10 | import android.support.annotation.Nullable; 11 | import android.util.AttributeSet; 12 | import android.view.View; 13 | 14 | import com.yazhi1992.practice.R; 15 | 16 | /** 17 | * Created by zengyazhi on 17/3/21. 18 | *

19 | * 涟漪 20 | */ 21 | 22 | public class RippleView extends View { 23 | 24 | //内部圆的半径 25 | private int ORIGINAL_RADIU; 26 | //最大半径 27 | private int MAX_RADIU; 28 | //三个不断散开的圆的半径 29 | private int mRadiu = ORIGINAL_RADIU; 30 | private int mRadiu2 = -1; 31 | private int mRadiu3 = -1; 32 | private Paint mPaint; 33 | //屏幕宽高 34 | private int mHeight; 35 | private int mWidth; 36 | //是否已绘制完成,进入旋转阶段 37 | private Thread mThread; 38 | private int mColor; 39 | private boolean mStartAnim; 40 | 41 | public RippleView(Context context) { 42 | super(context); 43 | init(context); 44 | } 45 | 46 | public RippleView(Context context, @Nullable AttributeSet attrs) { 47 | super(context, attrs); 48 | init(context); 49 | } 50 | 51 | public RippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 52 | super(context, attrs, defStyleAttr); 53 | init(context); 54 | } 55 | 56 | private void init(Context context) { 57 | //关闭GPU硬件加速 58 | setLayerType(View.LAYER_TYPE_SOFTWARE, null); 59 | mColor = context.getResources().getColor(R.color.my_color); 60 | } 61 | 62 | @Override 63 | protected void onDraw(Canvas canvas) { 64 | super.onDraw(canvas); 65 | if (mPaint != null) { 66 | mPaint.setColor(mColor); 67 | //绘制外部圆 68 | drawCircle(canvas, mRadiu); 69 | drawCircle(canvas, mRadiu2); 70 | drawCircle(canvas, mRadiu3); 71 | //绘制内部白色圆 72 | mPaint.setColor(Color.WHITE); 73 | mPaint.setShader(null); 74 | canvas.drawCircle(mWidth / 2, mHeight / 2, ORIGINAL_RADIU, mPaint); 75 | } 76 | } 77 | 78 | /** 79 | * 绘制圆 80 | * @param canvas 81 | * @param radiu 82 | */ 83 | private void drawCircle(Canvas canvas, int radiu) { 84 | if (radiu > ORIGINAL_RADIU && radiu < MAX_RADIU) { 85 | setAlpha(radiu); 86 | canvas.drawCircle(mWidth / 2, mHeight / 2, radiu, mPaint); 87 | } 88 | } 89 | 90 | /** 91 | * 设置内部空白圆圈的半径 92 | * @param width 93 | */ 94 | public void setOriginalRadiu(int width) { 95 | ORIGINAL_RADIU = width - 10; 96 | mRadiu = ORIGINAL_RADIU; 97 | postInvalidate(); 98 | } 99 | 100 | /** 101 | * 根据半径设置透明度 102 | * @param radiu 103 | */ 104 | private void setAlpha(int radiu) { 105 | double v = (radiu - ORIGINAL_RADIU) * 1d / (MAX_RADIU - ORIGINAL_RADIU); 106 | double v1 = (1 - v) * 255; 107 | if (v1 > 255) { 108 | v1 = 255; 109 | } 110 | if (v1 < 0) { 111 | v1 = 0; 112 | } 113 | mPaint.setAlpha((int) v1); 114 | } 115 | 116 | /** 117 | * 开始执行动画 118 | */ 119 | public void star() { 120 | mStartAnim = true; 121 | if (mThread == null) { 122 | mThread = new Thread(new Runnable() { 123 | @Override 124 | public void run() { 125 | while (mStartAnim) { 126 | //当最外圈圆到达1/3出时开始绘制第二圈圆 127 | if (mRadiu2 == -1 && mRadiu > ((MAX_RADIU - ORIGINAL_RADIU) / 3)) { 128 | mRadiu2 = mRadiu - ((MAX_RADIU - ORIGINAL_RADIU) / 3); 129 | } 130 | //当最外圈圆到达2/3出时开始绘制第三圈圆 131 | if (mRadiu3 == -1 && mRadiu > ((MAX_RADIU - ORIGINAL_RADIU) / 3)) { 132 | mRadiu3 = mRadiu - 2 * ((MAX_RADIU - ORIGINAL_RADIU) / 3); 133 | } 134 | 135 | //三圈圆到达最大半径时则自动从最小半径开始重新绘制,不断重复 136 | if (mRadiu >= MAX_RADIU) { 137 | mRadiu = ORIGINAL_RADIU; 138 | } else if (mRadiu2 >= MAX_RADIU) { 139 | mRadiu2 = ORIGINAL_RADIU; 140 | } else if (mRadiu3 >= MAX_RADIU) { 141 | mRadiu3 = ORIGINAL_RADIU; 142 | } 143 | 144 | //半径加大,不断外散 145 | if (mRadiu < MAX_RADIU) { 146 | mRadiu++; 147 | } 148 | if (mRadiu2 != -1 && mRadiu2 < MAX_RADIU) { 149 | mRadiu2++; 150 | } 151 | if (mRadiu3 != -1 && mRadiu3 < MAX_RADIU) { 152 | mRadiu3++; 153 | } 154 | postInvalidate(); 155 | try { 156 | Thread.sleep(12); 157 | } catch (InterruptedException e) { 158 | e.printStackTrace(); 159 | } 160 | } 161 | } 162 | }); 163 | mThread.start(); 164 | } 165 | } 166 | 167 | /** 168 | * 停止动画 169 | */ 170 | public void stop() { 171 | mStartAnim = false; 172 | mThread = null; 173 | mRadiu = ORIGINAL_RADIU; 174 | mRadiu2 = -1; 175 | mRadiu3 = -1; 176 | postInvalidate(); 177 | } 178 | 179 | @Override 180 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 181 | super.onSizeChanged(w, h, oldw, oldh); 182 | mWidth = w; 183 | mHeight = h; 184 | mPaint = new Paint(); 185 | mPaint.setDither(true); 186 | mPaint.setAntiAlias(true); 187 | mPaint.setStyle(Paint.Style.FILL); 188 | mPaint.setStrokeJoin(Paint.Join.ROUND); 189 | mPaint.setStrokeCap(Paint.Cap.ROUND); 190 | mPaint.setColor(mColor); 191 | MAX_RADIU = mWidth / 2; 192 | postInvalidate(); 193 | 194 | ValueAnimator valueAnimator = ValueAnimator.ofInt(0, MAX_RADIU); 195 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 196 | @Override 197 | public void onAnimationUpdate(ValueAnimator animation) { 198 | mRadiu = (int) animation.getAnimatedValue(); 199 | if (mRadiu2 == -1 && mRadiu > ((MAX_RADIU - ORIGINAL_RADIU) / 3)) { 200 | mRadiu2 = mRadiu - ((MAX_RADIU - ORIGINAL_RADIU) / 3); 201 | } 202 | } 203 | }); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/rotate_circle/RotateCircleActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.rotate_circle; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | 9 | public class RotateCircleActivity extends AppCompatActivity { 10 | 11 | private RotateCircleView mView; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_rotate_circle); 17 | 18 | mView = (RotateCircleView) findViewById(R.id.rotateCircelView); 19 | findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { 20 | @Override 21 | public void onClick(View v) { 22 | mView.star(); 23 | } 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/rotate_circle/RotateCircleView.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.rotate_circle; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.RectF; 10 | import android.graphics.SweepGradient; 11 | import android.support.annotation.Nullable; 12 | import android.util.AttributeSet; 13 | import android.util.TypedValue; 14 | import android.view.View; 15 | import android.view.animation.LinearInterpolator; 16 | 17 | import com.yazhi1992.practice.R; 18 | 19 | 20 | /** 21 | * Created by zengyazhi on 17/3/21. 22 | *

23 | * 旋转圆弧动画 24 | */ 25 | 26 | public class RotateCircleView extends View { 27 | 28 | //直径 29 | private int mRadiu = 40; 30 | private RectF mRect; 31 | private Paint mPaint; 32 | private int mHeight; 33 | private int mWidth; 34 | private int mRealAngle; 35 | private SweepGradient mSweepGradient; 36 | //是否已绘制完成,进入旋转阶段 37 | private boolean mCompletePaint; 38 | private int mColor; 39 | private ValueAnimator mValueAnimator; 40 | 41 | public RotateCircleView(Context context) { 42 | this(context, null); 43 | } 44 | 45 | public RotateCircleView(Context context, @Nullable AttributeSet attrs) { 46 | this(context, attrs, 0); 47 | } 48 | 49 | public RotateCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 50 | super(context, attrs, defStyleAttr); 51 | init(context); 52 | } 53 | 54 | private void init(Context context) { 55 | mColor = context.getResources().getColor(R.color.my_color); 56 | 57 | mPaint = new Paint(); 58 | mPaint.setDither(true); 59 | mPaint.setAntiAlias(true); 60 | mPaint.setColor(mColor); 61 | mPaint.setStyle(Paint.Style.STROKE); 62 | mPaint.setStrokeJoin(Paint.Join.ROUND); 63 | mPaint.setStrokeCap(Paint.Cap.ROUND); 64 | 65 | mRadiu = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, context.getResources().getDisplayMetrics()); 66 | 67 | initAnim(); 68 | } 69 | 70 | /** 71 | * 初始化动画 72 | */ 73 | private void initAnim() { 74 | mValueAnimator = ValueAnimator.ofInt(0, 360); 75 | mValueAnimator.setDuration(1800); 76 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 77 | @Override 78 | public void onAnimationUpdate(ValueAnimator animation) { 79 | mRealAngle = (int) animation.getAnimatedValue(); 80 | postInvalidate(); 81 | } 82 | }); 83 | mValueAnimator.setInterpolator(new LinearInterpolator()); 84 | mValueAnimator.addListener(new Animator.AnimatorListener() { 85 | @Override 86 | public void onAnimationStart(Animator animation) { 87 | 88 | } 89 | 90 | @Override 91 | public void onAnimationEnd(Animator animation) { 92 | } 93 | 94 | @Override 95 | public void onAnimationCancel(Animator animation) { 96 | 97 | } 98 | 99 | @Override 100 | public void onAnimationRepeat(Animator animation) { 101 | mCompletePaint = true; 102 | } 103 | }); 104 | mValueAnimator.setRepeatCount(ValueAnimator.INFINITE); 105 | mValueAnimator.setRepeatMode(ValueAnimator.RESTART); 106 | } 107 | 108 | @Override 109 | protected void onDraw(Canvas canvas) { 110 | super.onDraw(canvas); 111 | if (mPaint != null) { 112 | int paintAngle; 113 | if (mCompletePaint) { 114 | paintAngle = 360; 115 | } else { 116 | paintAngle = mRealAngle; 117 | } 118 | canvas.rotate(mRealAngle, mWidth / 2, mHeight / 2); 119 | mPaint.setShader(mSweepGradient); 120 | mPaint.setStrokeWidth(mRadiu); 121 | mPaint.setStyle(Paint.Style.STROKE); 122 | mPaint.setStrokeCap(Paint.Cap.ROUND); 123 | canvas.drawArc(mRect, 360 - paintAngle, paintAngle, false, mPaint); 124 | 125 | //画被白色盖住的半个头部 126 | if (paintAngle > 2) { 127 | mPaint.setShader(null); 128 | mPaint.setStyle(Paint.Style.FILL); 129 | mPaint.setStrokeCap(Paint.Cap.SQUARE); 130 | mPaint.setStrokeWidth(1); 131 | RectF oval2 = new RectF(mWidth - mRadiu * 3 / 2, mHeight / 2 - mRadiu / 2 - 2 132 | , mWidth - mRadiu / 2, mHeight / 2 + mRadiu / 2 - 2); 133 | canvas.drawArc(oval2, 0, 180, true, mPaint); 134 | } 135 | 136 | } 137 | } 138 | 139 | //开始执行动画 140 | public void star() { 141 | if (mValueAnimator != null) { 142 | if (mValueAnimator.isRunning()) { 143 | mValueAnimator.cancel(); 144 | } else { 145 | mCompletePaint = false; 146 | mValueAnimator.start(); 147 | } 148 | } 149 | } 150 | 151 | @Override 152 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 153 | super.onSizeChanged(w, h, oldw, oldh); 154 | mWidth = w; 155 | mHeight = h; 156 | 157 | mRect = new RectF(); 158 | mRect.left = mRadiu; 159 | mRect.top = mRadiu; 160 | mRect.right = mWidth - mRadiu; 161 | mRect.bottom = mHeight - mRadiu; 162 | 163 | int[] ints = new int[]{Color.WHITE, mColor}; 164 | mSweepGradient = new SweepGradient(mWidth / 2, mHeight / 2, ints, null); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/streak_progress_bar/StreakProgressbar.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.streak_progress_bar; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.graphics.PixelFormat; 9 | import android.graphics.RectF; 10 | import android.util.AttributeSet; 11 | import android.util.Log; 12 | import android.view.SurfaceHolder; 13 | import android.view.SurfaceView; 14 | 15 | /** 16 | * Created by zengyazhi on 17/2/13. 17 | * 条纹进度条 18 | */ 19 | 20 | public class StreakProgressbar extends SurfaceView implements SurfaceHolder.Callback{ 21 | 22 | private Canvas canvas; 23 | // 条纹宽度 24 | private int hig = 35; 25 | private int a = 0; 26 | private SurfaceHolder surfaceHolder; 27 | private boolean isThreadRunning = true; 28 | private Thread Loadingthread = null; 29 | 30 | private void initview(Context context) { 31 | context = context; 32 | setZOrderOnTop(true); 33 | surfaceHolder = this.getHolder(); 34 | surfaceHolder.setFormat(PixelFormat.TRANSPARENT); 35 | surfaceHolder.addCallback(this); 36 | } 37 | 38 | public StreakProgressbar(Context context) { 39 | super(context); 40 | initview(context); 41 | } 42 | 43 | public StreakProgressbar(Context context, AttributeSet attrs) { 44 | super(context, attrs); 45 | initview(context); 46 | } 47 | 48 | public StreakProgressbar(Context context, AttributeSet attrs, int defStyleAttr) { 49 | super(context, attrs, defStyleAttr); 50 | initview(context); 51 | } 52 | 53 | @Override 54 | public void surfaceCreated(SurfaceHolder holder) { 55 | 56 | } 57 | 58 | @Override 59 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 60 | 61 | } 62 | 63 | @Override 64 | public void surfaceDestroyed(SurfaceHolder holder) { 65 | 66 | } 67 | 68 | // 开启动画线程 69 | public void start2() { 70 | isThreadRunning = true; 71 | Loadingthread = new Thread() { 72 | @Override 73 | public void run() { 74 | while (isThreadRunning) { 75 | try { 76 | if (surfaceHolder != null) { 77 | canvas = surfaceHolder.lockCanvas(); 78 | if (canvas != null) { 79 | drawView(); 80 | surfaceHolder.unlockCanvasAndPost(canvas); 81 | } 82 | // 动画执行间隔,即滚动速度 83 | Thread.sleep(100); 84 | a++; 85 | } 86 | } catch (InterruptedException e) { 87 | Thread.currentThread().interrupt(); 88 | Log.d("InstallView", "InterruptedException"); 89 | } 90 | } 91 | } 92 | }; 93 | Loadingthread.start(); 94 | } 95 | 96 | // 绘制动画 97 | private void drawView() { 98 | final Paint paint_green = generatePaint(Color.parseColor("#F1C2C1"), 99 | Paint.Style.FILL, 1); 100 | final Paint paint_red = generatePaint(Color.parseColor("#ffffff"), 101 | Paint.Style.FILL, 1); 102 | canvas.clipRect(new RectF(0, 0, getWidth(), 100)); 103 | int i; 104 | for (i = 0; i < getWidth() + 100; i = i + hig * 2) { 105 | // 画出第一个粉色梯形 106 | Path path = new Path(); 107 | path.moveTo(i, 0); 108 | path.lineTo(i + hig, 0); 109 | path.lineTo(-60 + i, getHeight()); 110 | path.lineTo(-(60 + hig) + i, getHeight()); 111 | // 第二个白色梯形 112 | Path path2 = new Path(); 113 | path2.moveTo(i + hig, 0); 114 | path2.lineTo(i + hig * 2, 0); 115 | path2.lineTo(-60 + i + hig, getHeight()); 116 | path2.lineTo(-(60 + hig) + i + hig, getHeight()); 117 | // 死循环来控制梯形交错显示 118 | if (a % 2 == 0) { 119 | canvas.drawPath(path, paint_green); 120 | canvas.drawPath(path2, paint_red); 121 | } else { 122 | canvas.drawPath(path, paint_red); 123 | canvas.drawPath(path2, paint_green); 124 | } 125 | } 126 | } 127 | 128 | private Paint generatePaint(int color, Paint.Style style, int width) { 129 | Paint paint = new Paint(); 130 | paint.setColor(color); 131 | paint.setStyle(style); 132 | paint.setAntiAlias(true); 133 | paint.setStrokeWidth(width); 134 | return paint; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/swipelayout/SideMenuLayout.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.swipelayout; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | import android.view.VelocityTracker; 8 | import android.view.View; 9 | import android.view.ViewConfiguration; 10 | import android.widget.LinearLayout; 11 | import android.widget.Scroller; 12 | 13 | public class SideMenuLayout extends LinearLayout { 14 | 15 | private View viewContent; 16 | private View viewMenu; 17 | private int menuWidth; 18 | 19 | private Scroller mScroller;//用于滑动 20 | private VelocityTracker mVelocityTracker; //用于侦测速度的 21 | //开始位置 22 | private float xStart; 23 | private float yStart; 24 | //坐标位置 25 | private float xMove; 26 | private float yMove; 27 | //记录上次坐标位置 28 | private float xLast; 29 | 30 | private int touchSlop; 31 | 32 | public SideMenuLayout(Context context) { 33 | this(context, null); 34 | } 35 | 36 | public SideMenuLayout(Context context, @Nullable AttributeSet attrs) { 37 | super(context, attrs); 38 | //设置水平显示,初始化Scroller 39 | setOrientation(HORIZONTAL); 40 | mScroller = new Scroller(getContext()); 41 | mVelocityTracker = VelocityTracker.obtain(); 42 | touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 43 | } 44 | 45 | @Override 46 | protected void onFinishInflate() { 47 | super.onFinishInflate(); 48 | //初始化两个View 49 | if (getChildCount() != 2) { 50 | throw new IllegalArgumentException("子view的数量必须为2个"); 51 | } 52 | viewContent = getChildAt(0); 53 | viewMenu = getChildAt(1); 54 | } 55 | 56 | //设置两个子View的位置 57 | @Override 58 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 59 | super.onLayout(changed, l, t, r, b); 60 | //设置内容的位置 61 | //设置菜单的位置 62 | menuWidth = viewMenu.getMeasuredWidth(); 63 | viewContent.layout(0, 0, r, b - t); 64 | viewMenu.layout(r - l, 0, r - l + menuWidth, b - t); 65 | } 66 | 67 | // @Override 68 | // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 69 | // super.onMeasure(widthMeasureSpec, heightMeasureSpec); 70 | // int width = MeasureSpec.getSize(widthMeasureSpec); 71 | // int widthMode = MeasureSpec.getMode(widthMeasureSpec); 72 | // int height = MeasureSpec.getSize(heightMeasureSpec); 73 | // int heightMode = MeasureSpec.getMode(heightMeasureSpec); 74 | // 75 | // WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 76 | // int setWidth = windowManager.getDefaultDisplay().getWidth(); 77 | // int setHeight = windowManager.getDefaultDisplay().getHeight(); 78 | // 79 | // if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { 80 | // setMeasuredDimension(setWidth, setHeight); 81 | // } else if (widthMode == MeasureSpec.AT_MOST) { 82 | // setMeasuredDimension(setWidth, height); 83 | // } else if (heightMode == MeasureSpec.AT_MOST) { 84 | // setMeasuredDimension(width, setHeight); 85 | // } 86 | // } 87 | 88 | 89 | // @Override 90 | // protected void onSizeChanged(int w, int h, int oldw, int oldh) { 91 | // super.onSizeChanged(w, h, oldw, oldh); 92 | // menuWidth = viewMenu.getWidth(); 93 | // } 94 | 95 | @Override 96 | public boolean onInterceptTouchEvent(MotionEvent ev) { 97 | boolean intercept = false; 98 | xMove = ev.getRawX(); 99 | yMove = ev.getRawY(); 100 | switch (ev.getAction()) { 101 | case MotionEvent.ACTION_DOWN: 102 | //记录点下的位置 103 | if (!mScroller.isFinished()) { 104 | mScroller.abortAnimation(); 105 | } 106 | xStart = xMove; 107 | yStart = yMove; 108 | xLast = xMove; 109 | break; 110 | case MotionEvent.ACTION_MOVE: 111 | //当前位置与下,x,y位置比较 112 | float xDistance = Math.abs(xMove - xStart); 113 | float yDistance = Math.abs(yMove - yStart); 114 | //横向滑动大于纵向,触发条目的滑动,否则,交给子View去处理 115 | if (xDistance > yDistance && xDistance > touchSlop) { 116 | requestDisallowInterceptTouchEvent(true); 117 | intercept = true; 118 | } else { 119 | requestDisallowInterceptTouchEvent(false); 120 | intercept = false; 121 | } 122 | 123 | } 124 | return intercept; 125 | } 126 | 127 | @Override 128 | public boolean onTouchEvent(MotionEvent event) { 129 | mVelocityTracker.addMovement(event); 130 | xMove = event.getRawX(); 131 | //处理横向滑动 132 | switch (event.getAction()) { 133 | case MotionEvent.ACTION_DOWN: 134 | return true; 135 | case MotionEvent.ACTION_MOVE: 136 | float viewX = -getScrollX(); 137 | float viewToX = viewX + xMove - xLast; 138 | int viewToXDec = (int) (Math.min(Math.max(viewToX, -menuWidth), 0)); 139 | scrollTo(-viewToXDec, 0); 140 | xLast = xMove; 141 | //滑动时新坐标 142 | break; 143 | case MotionEvent.ACTION_UP: 144 | //手指抬起时,根据目前位置判断如何滚动 145 | mVelocityTracker.computeCurrentVelocity(1000); 146 | float xVelocity = mVelocityTracker.getXVelocity(); 147 | //先根据速度,当速度很快时根据速度方向判断收起还是展开,当速度小时,根据位置判断收起还是展开 148 | if (Math.abs(xVelocity) >= 50) { 149 | if (xVelocity > 0) { 150 | close(); 151 | } else { 152 | open(); 153 | } 154 | } else { 155 | float viewUpX = getScrollX(); 156 | if (viewUpX > 0 && viewUpX < menuWidth / 2) { 157 | //隐藏菜单 158 | close(); 159 | } else if (viewUpX >= menuWidth / 2 && viewUpX < menuWidth) { 160 | //全部展示菜单 161 | open(); 162 | } 163 | } 164 | break; 165 | } 166 | return super.onTouchEvent(event); 167 | } 168 | 169 | //获取当前是否是打开的 170 | public boolean isOpen() { 171 | boolean isOpen = getScrollX() > 0; 172 | return isOpen; 173 | } 174 | 175 | //全部打开 176 | public void open() { 177 | smoothScrollTo(menuWidth); 178 | } 179 | 180 | //全部关闭 181 | public void close() { 182 | smoothScrollTo(0); 183 | } 184 | 185 | private void smoothScrollTo(int destX) { 186 | int scrollX = getScrollX(); 187 | int deltaX = destX - scrollX;; 188 | mScroller.startScroll(scrollX, getScrollY(), deltaX, 0, 500); 189 | invalidate(); 190 | } 191 | 192 | @Override 193 | public void computeScroll() { 194 | if (mScroller.computeScrollOffset()) { 195 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 196 | postInvalidate(); 197 | } 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/swipelayout/SwipeLayout.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.swipelayout; 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.VelocityTracker; 8 | import android.view.View; 9 | import android.view.ViewConfiguration; 10 | import android.view.ViewGroup; 11 | import android.widget.Scroller; 12 | 13 | /** 14 | * Created by zengyazhi on 2017/12/15. 15 | */ 16 | 17 | public class SwipeLayout extends ViewGroup { 18 | 19 | private View mContentView; 20 | private View mMenuLayout; 21 | //默认横向 22 | private int mGravity; 23 | //用于抬手时平滑滚动,也可以用animator实现 24 | private Scroller mScroller; 25 | //侦测滑动速度 26 | private VelocityTracker mVelocityTracker; 27 | private float mTouchX; 28 | private int mMenuWidth; 29 | private int mMoveDistance; 30 | private float mStartX; 31 | private int mScaledTouchSlop; 32 | 33 | public SwipeLayout(Context context) { 34 | super(context); 35 | } 36 | 37 | public SwipeLayout(Context context, AttributeSet attrs) { 38 | super(context, attrs); 39 | mScroller = new Scroller(context); 40 | mVelocityTracker = VelocityTracker.obtain(); 41 | mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 42 | } 43 | 44 | @Override 45 | protected void onFinishInflate() { 46 | super.onFinishInflate(); 47 | mContentView = getChildAt(0); 48 | mMenuLayout = getChildAt(1); 49 | } 50 | 51 | @Override 52 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 53 | mMenuWidth = mMenuLayout.getMeasuredWidth(); 54 | mContentView.layout(0, 0, r, b); 55 | mMenuLayout.layout(r - l, 0, r - l + mMenuWidth, b); 56 | } 57 | 58 | @Override 59 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 60 | int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); 61 | int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); 62 | int modeWidth = MeasureSpec.getMode(widthMeasureSpec); 63 | int modeHeight = MeasureSpec.getMode(heightMeasureSpec); 64 | 65 | int width = 0; 66 | int height = 0; 67 | 68 | for (int i = 0; i < getChildCount(); i++) { 69 | View child = getChildAt(i); 70 | measureChild(child, widthMeasureSpec, heightMeasureSpec); 71 | int measuredWidth = child.getMeasuredWidth(); 72 | int measuredHeight = child.getMeasuredHeight(); 73 | width += measuredWidth; 74 | height = Math.max(height, measuredHeight); 75 | } 76 | 77 | //测量完后报告最终计算的宽高 78 | setMeasuredDimension( 79 | modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), 80 | modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// 81 | ); 82 | } 83 | 84 | @Override 85 | public boolean onInterceptTouchEvent(MotionEvent ev) { 86 | switch (ev.getAction()) { 87 | case MotionEvent.ACTION_DOWN: 88 | mStartX = ev.getX(); 89 | mTouchX = ev.getX() + mMoveDistance; 90 | break; 91 | case MotionEvent.ACTION_MOVE: 92 | float x = ev.getX(); 93 | if (Math.abs(mStartX - x) > mScaledTouchSlop) { 94 | //判断为左右滑动,拦截交由 onTouchEvent 处理侧滑逻辑 95 | return true; 96 | } else { 97 | return false; 98 | } 99 | default: 100 | break; 101 | } 102 | return super.onInterceptTouchEvent(ev); 103 | } 104 | 105 | @Override 106 | public boolean onTouchEvent(MotionEvent event) { 107 | mVelocityTracker.addMovement(event); 108 | switch (event.getAction()) { 109 | case MotionEvent.ACTION_MOVE: 110 | float moveX = event.getX() - mTouchX; 111 | Log.e("zyz", moveX + " " + mMenuWidth); 112 | if (moveX < 0) { 113 | //内容栏被左滑 114 | mMoveDistance = (int) -moveX; 115 | //左滑最大距离为菜单宽度 116 | if (mMoveDistance > mMenuWidth) { 117 | mMoveDistance = mMenuWidth; 118 | } 119 | } else { 120 | mMoveDistance = 0; 121 | } 122 | //手指拖动时实时滑动 123 | scrollTo(mMoveDistance, 0); 124 | break; 125 | case MotionEvent.ACTION_UP: 126 | //如果滑动速度快,根据滑动方向判断是打开还是关闭,如果速度慢,根据滑动位置判断 127 | mVelocityTracker.computeCurrentVelocity(1000); 128 | float xVelocity = mVelocityTracker.getXVelocity(); 129 | if (Math.abs(xVelocity) > 50) { 130 | if (xVelocity > 0) { 131 | //右滑关闭 132 | close(); 133 | } else { 134 | //左滑打开 135 | open(); 136 | } 137 | } else { 138 | //根据位置判断是打开还是关闭 139 | if (mMoveDistance > mMenuWidth / 2) { 140 | //打开 141 | open(); 142 | } else { 143 | //关闭 144 | close(); 145 | } 146 | } 147 | invalidate(); 148 | break; 149 | default: 150 | break; 151 | } 152 | return true; 153 | } 154 | 155 | private void close() { 156 | int move = -mMoveDistance; 157 | mMoveDistance = 0; 158 | smoothScroll(move); 159 | } 160 | 161 | private void open() { 162 | int move = mMenuWidth - mMoveDistance; 163 | mMoveDistance = mMenuWidth; 164 | smoothScroll(move); 165 | } 166 | 167 | private void smoothScroll(int distance) { 168 | //平滑滚动 169 | mScroller.startScroll(getScrollX(), 0, distance, 0); 170 | } 171 | 172 | @Override 173 | public void computeScroll() { 174 | if (mScroller.computeScrollOffset()) { 175 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 176 | postInvalidate(); 177 | } 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/swipelayout/SwipeLayoutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.swipelayout 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.widget.Toast 7 | import com.yazhi1992.practice.R 8 | import kotlinx.android.synthetic.main.activity_swipe_layout.* 9 | 10 | class SwipeLayoutActivity : AppCompatActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_swipe_layout) 15 | 16 | tv_content.setOnClickListener(View.OnClickListener { Toast.makeText(this, "内容", Toast.LENGTH_SHORT).show() }) 17 | tvMenu1.setOnClickListener(View.OnClickListener { Toast.makeText(this, "菜单1", Toast.LENGTH_SHORT).show() }) 18 | tvMenu2.setOnClickListener(View.OnClickListener { Toast.makeText(this, "菜单2", Toast.LENGTH_SHORT).show() }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.utils; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.graphics.Matrix; 8 | import android.util.TypedValue; 9 | 10 | /** 11 | * Created by zengyazhi on 17/3/31. 12 | */ 13 | 14 | public class Utils { 15 | /** 16 | * 根据drawble文件名得到bitmap 17 | * 18 | * @param context 用来获得Resources 19 | * @param width 想要获得的bitmap的宽 20 | * @param height 想要获得的bitmap的高 21 | * @param drawble drawble图片名称 22 | * @return 23 | */ 24 | public static Bitmap getBitmap(Context context, int width, int height, int drawble) { 25 | // 得到图像 26 | Resources resources = context.getResources(); 27 | Bitmap bitmap = BitmapFactory.decodeResource(resources, drawble); 28 | 29 | int bWidth = bitmap.getWidth(); 30 | int bHeight = bitmap.getHeight(); 31 | 32 | float scaleX = (float) width / bWidth; 33 | float scaleY = (float) height / bHeight; 34 | 35 | Matrix matrix = new Matrix(); 36 | matrix.setScale(scaleX, scaleY); 37 | 38 | return Bitmap.createBitmap(bitmap, 0, 0, bWidth, bHeight, matrix, true); 39 | } 40 | 41 | public static float dp2px(Context Context, int dp) { 42 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Context.getResources().getDisplayMetrics()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/vertical_text/VerticalText.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.vertical_text; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.support.annotation.Nullable; 8 | import android.util.AttributeSet; 9 | import android.util.TypedValue; 10 | import android.view.View; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | 16 | /** 17 | * Created by zengyazhi on 17/3/31. 18 | *

19 | * 竖直排列的文字控件 20 | *

21 | * 参考:https://github.com/lybeat/PlumbTextView 22 | */ 23 | 24 | public class VerticalText extends View { 25 | 26 | private String mText = "世间所有的相遇,都是久别重逢"; 27 | //分割符号 28 | private String mSubStringStr = ","; 29 | private Paint mTextPaint; 30 | private float mTextSize; 31 | //屏幕宽高 32 | private int mWidth; 33 | private int mHeight; 34 | //单个字符高度 35 | private float mCharHeight; 36 | //单个字符宽度 37 | private int mCharWidth; 38 | //上下字间距 39 | private float mTopBottomSpacing; 40 | //左右行间距 41 | private float mLeftRightSpacing; 42 | //每列需要绘制的文字列表 43 | private List mFormatTexts = new ArrayList<>(); 44 | //计算每行可容纳文字数目后得出的上下边距 45 | private float mCalculatePaddingTopBottom; 46 | //计算总列数后得出的左右边距 47 | private float mCalculatePaddingLeftRight; 48 | //一列最多可容纳的字数 49 | private int mOneLineTextNum; 50 | //列数 51 | private int mLineNum; 52 | 53 | public VerticalText(Context context) { 54 | this(context, null); 55 | } 56 | 57 | public VerticalText(Context context, @Nullable AttributeSet attrs) { 58 | this(context, attrs, 0); 59 | } 60 | 61 | public VerticalText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 62 | super(context, attrs, defStyleAttr); 63 | init(); 64 | } 65 | 66 | private void init() { 67 | mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15f, getResources().getDisplayMetrics()); 68 | mLeftRightSpacing = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, getResources().getDisplayMetrics()); 69 | mTopBottomSpacing = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, getResources().getDisplayMetrics()); 70 | 71 | mTextPaint = new Paint(); 72 | mTextPaint.setAntiAlias(true); 73 | mTextPaint.setColor(Color.BLACK); 74 | mTextPaint.setTextSize(mTextSize); 75 | 76 | //测量文字宽度 77 | mCharWidth = (int) mTextPaint.measureText("一"); 78 | } 79 | 80 | @Override 81 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 82 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 83 | int wMode = MeasureSpec.getMode(widthMeasureSpec); 84 | int wSize = MeasureSpec.getSize(widthMeasureSpec); 85 | int hMode = MeasureSpec.getMode(heightMeasureSpec); 86 | int hSize = MeasureSpec.getSize(heightMeasureSpec); 87 | 88 | if (hMode == MeasureSpec.EXACTLY) { 89 | mHeight = hSize; 90 | setCalculatePaddingTopBottom(); 91 | } else { 92 | if (!mSubStringStr.isEmpty()) { 93 | //有分割符号 94 | String[] split = mText.split(mSubStringStr); 95 | for (String s : split) { 96 | mHeight = Math.max(mHeight, getPaddingTop() + getTotalHeight(s) + getPaddingBottom()); 97 | } 98 | } else { 99 | mHeight = getPaddingTop() + getTotalHeight(mText) + getPaddingBottom(); 100 | } 101 | //防止超出父容器 102 | mHeight = mHeight > hSize ? hSize : mHeight; 103 | } 104 | 105 | mFormatTexts.clear(); 106 | if (!mSubStringStr.isEmpty()) { 107 | //有分割符号 108 | String[] split = mText.split(mSubStringStr); 109 | for (String s : split) { 110 | getFormatTexts(s); 111 | } 112 | } else { 113 | getFormatTexts(mText); 114 | } 115 | 116 | if (wMode == MeasureSpec.EXACTLY) { 117 | mWidth = wSize; 118 | setCalculatePaddingLeftRight(); 119 | } else { 120 | if (!mSubStringStr.isEmpty()) { 121 | //有分割符号 122 | mWidth = (int) (getPaddingLeft() + mFormatTexts.size() * (mCharWidth + mLeftRightSpacing) - mLeftRightSpacing + getPaddingRight()); 123 | } else { 124 | //控件可容纳内容的高度 125 | int num = getOneLineTextNum(); 126 | //总共的列数 127 | int lineNum = getLineNum(num); 128 | mWidth = (int) (getPaddingLeft() + lineNum * (mCharWidth + mLeftRightSpacing) - mLeftRightSpacing + getPaddingRight()); 129 | } 130 | mWidth = mWidth > wSize ? wSize : mWidth; 131 | } 132 | 133 | setMeasuredDimension(mWidth, mHeight); 134 | } 135 | 136 | /** 137 | * 所有字符竖排的高度(包括与下一个文字的边距) 138 | * 139 | * @param str 140 | * @return 141 | */ 142 | private int getTotalHeight(String str) { 143 | return (int) ((getCharHeight() + mTopBottomSpacing) * str.length() - mTopBottomSpacing); 144 | } 145 | 146 | /** 147 | * 单个字符的高度 148 | * 149 | * @return 150 | */ 151 | private float getCharHeight() { 152 | if (mCharHeight == 0) { 153 | mCharHeight = Math.abs(mTextPaint.ascent()); 154 | } 155 | return mCharHeight; 156 | } 157 | 158 | @Override 159 | protected void onDraw(Canvas canvas) { 160 | super.onDraw(canvas); 161 | float x = mWidth - getPaddingRight() - mCalculatePaddingLeftRight; 162 | float y; 163 | for (int i = 0; i < mFormatTexts.size(); i++) { 164 | //遍历写列表中的文字 165 | String thisLineText = mFormatTexts.get(i); 166 | x = i == 0 ? x - mCharWidth : x - mCharWidth - mLeftRightSpacing; 167 | //换行后,y轴坐标重置 168 | y = getCharHeight() + mCalculatePaddingTopBottom; 169 | for (int j = 0; j < thisLineText.length(); j++) { 170 | y = j == 0 ? y + getPaddingTop() : y + getCharHeight() + mTopBottomSpacing; 171 | //绘制text中 j 到 j+1 的字,y坐标是baseline的高度 172 | canvas.drawText(thisLineText, j, j + 1, x, y, mTextPaint); 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * 根据列数分割每列要绘制的字符串 179 | * 180 | * @param str 181 | * @return 182 | */ 183 | public List getFormatTexts(String str) { 184 | //控件可容纳内容的高度 185 | int num = getOneLineTextNum(); 186 | //总共的列数 187 | int lineNum = getLineNum(num); 188 | if (lineNum > 1) { 189 | //大于一列 190 | for (int i = 0; i <= str.length(); i += num) { 191 | if (i + num >= str.length()) { 192 | //超出边界,直接切割至字符串末位 193 | String substring = str.substring(i); 194 | if (!substring.isEmpty()) { 195 | mFormatTexts.add(substring); 196 | } 197 | } else { 198 | mFormatTexts.add(str.substring(i, i + num)); 199 | } 200 | } 201 | } else { 202 | mFormatTexts.add(str); 203 | } 204 | return mFormatTexts; 205 | } 206 | 207 | /** 208 | * 获取列数 209 | * 210 | * @param num 211 | * @return 212 | */ 213 | private int getLineNum(int num) { 214 | if (mLineNum == 0) { 215 | int result = mText.length() % num; 216 | mLineNum = result == 0 ? mText.length() / num : mText.length() / num + 1; 217 | } 218 | return mLineNum; 219 | } 220 | 221 | /** 222 | * 获得一列可容纳的文字数 223 | * 224 | * @return 225 | */ 226 | private int getOneLineTextNum() { 227 | if (mOneLineTextNum == 0) { 228 | int oneLineHeight = mHeight - getPaddingTop() - getPaddingBottom(); 229 | //粗略计算一列可容纳多少字 230 | mOneLineTextNum = (int) (oneLineHeight / getCharHeight()); 231 | while ((int) (mOneLineTextNum * (getCharHeight() + mTopBottomSpacing) - mTopBottomSpacing) > oneLineHeight) { 232 | //有可能加上上下间距后超出可容纳内容高度 233 | mOneLineTextNum--; 234 | } 235 | } 236 | return mOneLineTextNum; 237 | } 238 | 239 | /** 240 | * 给定控件宽高情况下,计算每列可容纳文字数目后得出的上下边距 241 | */ 242 | private void setCalculatePaddingTopBottom() { 243 | if (mCalculatePaddingTopBottom == 0) { 244 | mCalculatePaddingTopBottom = (mHeight - getPaddingTop() - getPaddingBottom() - (getOneLineTextNum() * (getCharHeight() + mTopBottomSpacing) - mTopBottomSpacing)) / 2; 245 | } 246 | } 247 | 248 | /** 249 | * 给定控件宽高情况下,计算列数后得出的左右边距 250 | */ 251 | private void setCalculatePaddingLeftRight() { 252 | if (mCalculatePaddingLeftRight == 0) { 253 | mCalculatePaddingLeftRight = (mWidth - getPaddingLeft() - getPaddingRight() - (getLineNum(getOneLineTextNum()) * (mCharWidth + mLeftRightSpacing) - mLeftRightSpacing)) / 2; 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/vertical_text/VerticalTextActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.vertical_text; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.yazhi1992.practice.R; 7 | 8 | public class VerticalTextActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_vertical_text); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/wave_view/BezierWave.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.wave_view; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Path; 8 | import android.support.annotation.Nullable; 9 | import android.util.AttributeSet; 10 | import android.view.View; 11 | import android.view.animation.LinearInterpolator; 12 | 13 | import com.yazhi1992.practice.R; 14 | 15 | /** 16 | * Created by zengyazhi on 17/4/1. 17 | * 18 | * 使用贝塞尔曲线实现波浪效果 19 | */ 20 | 21 | public class BezierWave extends View { 22 | 23 | private Paint mPaint; 24 | private int mHeight; 25 | private int mWidth; 26 | private Path mPath; 27 | //Y轴偏移量 28 | //波浪高度 29 | private int Y_OFFSET; 30 | private int mX; 31 | private int mY; 32 | private ValueAnimator mValueAnimator; 33 | 34 | public BezierWave(Context context) { 35 | this(context, null); 36 | } 37 | 38 | public BezierWave(Context context, @Nullable AttributeSet attrs) { 39 | this(context, attrs, 0); 40 | } 41 | 42 | public BezierWave(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 43 | super(context, attrs, defStyleAttr); 44 | init(context); 45 | } 46 | 47 | private void init(Context context) { 48 | mPaint = new Paint(); 49 | int color = context.getResources().getColor(R.color.my_color); 50 | mPaint.setAntiAlias(true); 51 | mPaint.setDither(true); 52 | mPaint.setStyle(Paint.Style.FILL); 53 | mPaint.setColor(color); 54 | 55 | mPath = new Path(); 56 | 57 | } 58 | 59 | @Override 60 | protected void onDraw(Canvas canvas) { 61 | super.onDraw(canvas); 62 | mPath.reset(); 63 | mPath.moveTo(0, mHeight); 64 | mPath.lineTo(mX, mY); 65 | 66 | //画屏幕中波浪 67 | mPath.quadTo(mX + mWidth / 4, mY - Y_OFFSET, mX + mWidth / 2, mY); 68 | mPath.quadTo(mX + mWidth * 3 / 4, mY + Y_OFFSET, mX + mWidth, mY); 69 | //再画一部分一模一样的,超出屏幕 70 | mPath.quadTo(mX + mWidth * 5 / 4, mY - Y_OFFSET, mX + mWidth * 3 / 2, mY); 71 | mPath.quadTo(mX + mWidth * 7 / 4, mY + Y_OFFSET, mX + 2 * mWidth, mY); 72 | 73 | mPath.lineTo(mWidth, mHeight); 74 | mPath.lineTo(0, mHeight); 75 | mPath.close(); 76 | canvas.drawPath(mPath, mPaint); 77 | 78 | } 79 | 80 | @Override 81 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 82 | super.onSizeChanged(w, h, oldw, oldh); 83 | mWidth = w; 84 | mHeight = h; 85 | 86 | Y_OFFSET = mHeight / 10; 87 | mX = 0; 88 | mY = mHeight / 2; 89 | 90 | mValueAnimator = ValueAnimator.ofInt(0, -mWidth); 91 | mValueAnimator.setDuration(2000); 92 | mValueAnimator.setRepeatCount(ValueAnimator.INFINITE); 93 | mValueAnimator.setRepeatMode(ValueAnimator.RESTART); 94 | 95 | mValueAnimator.setInterpolator(new LinearInterpolator()); 96 | mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 97 | @Override 98 | public void onAnimationUpdate(ValueAnimator animation) { 99 | //x轴起点不断向右移动,只至移动距离为mWidth,即为完成了一段完整的波浪曲线 100 | mX = (int) animation.getAnimatedValue(); 101 | postInvalidate(); 102 | } 103 | }); 104 | } 105 | 106 | public void start() { 107 | mValueAnimator.start(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/wave_view/SinWave.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.wave_view; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.graphics.Paint; 9 | import android.graphics.Path; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.PorterDuffXfermode; 12 | import android.support.annotation.Nullable; 13 | import android.util.AttributeSet; 14 | import android.view.View; 15 | import android.view.animation.LinearInterpolator; 16 | 17 | import com.yazhi1992.practice.R; 18 | import com.yazhi1992.practice.utils.Utils; 19 | 20 | /** 21 | * Created by zengyazhi on 17/3/31. 22 | *

23 | * 波浪效果进度条 24 | */ 25 | 26 | public class SinWave extends View { 27 | 28 | private Paint mPaint; 29 | private Path mPath; 30 | private int mHeight; 31 | private int mWidth; 32 | private static final int X_OFFSET = 15; 33 | //坡度值,越小曲线越平缓 34 | private double mGrade = 0.2; 35 | //开启动画时x方向的偏移 36 | float mXOffset; 37 | //开启动画时y方向的偏移 38 | float mYOffset; 39 | private Context mContext; 40 | private Bitmap mBitmap; 41 | private AnimatorSet mAnimatorSet; 42 | //波浪高度 43 | private int mWaveHeight; 44 | 45 | public SinWave(Context context) { 46 | this(context, null); 47 | } 48 | 49 | public SinWave(Context context, @Nullable AttributeSet attrs) { 50 | this(context, attrs, 0); 51 | } 52 | 53 | public SinWave(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 54 | super(context, attrs, defStyleAttr); 55 | init(context); 56 | } 57 | 58 | private void init(Context context) { 59 | //关闭GPU硬件加速 60 | setLayerType(View.LAYER_TYPE_SOFTWARE, null); 61 | 62 | mPaint = new Paint(); 63 | mPaint.setColor(context.getResources().getColor(R.color.my_color)); 64 | mPaint.setStyle(Paint.Style.FILL); 65 | mPaint.setAntiAlias(true); 66 | 67 | mPath = new Path(); 68 | 69 | mContext = context; 70 | 71 | } 72 | 73 | @Override 74 | protected void onDraw(Canvas canvas) { 75 | super.onDraw(canvas); 76 | mPaint.setXfermode(null); 77 | if (mBitmap != null) { 78 | //调低背景图片高度,以防进度=0或进度=100时波浪够不到图片,或进度满了波浪没有充满图片 79 | canvas.drawBitmap(mBitmap, mWaveHeight, 2 * mWaveHeight, mPaint); 80 | } 81 | 82 | mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); //只截取前景里和背景重合的部分 83 | canvas.drawPath(getSinPath(), mPaint); 84 | } 85 | 86 | /** 87 | * 获得正弦函数的path 88 | */ 89 | private Path getSinPath() { 90 | mPath.reset(); 91 | //注意,view的原点(0,0)是左上角,向右x轴正方向,向下y轴正方向 92 | mPath.moveTo(0, mHeight); 93 | for (float x = 0; x <= mWidth; x += X_OFFSET) { 94 | float y = (float) ((Math.sin(degreeToRad(mGrade * x + mXOffset)) + 1) * mWaveHeight); 95 | mPath.lineTo(x, y + mYOffset); 96 | } 97 | mPath.lineTo(mWidth, mHeight); 98 | mPath.lineTo(0, mHeight); 99 | mPath.close(); 100 | return mPath; 101 | } 102 | 103 | /** 104 | * 启动动画 105 | */ 106 | public void start() { 107 | mAnimatorSet.start(); 108 | } 109 | 110 | /** 111 | * 角度转弧度 112 | * 113 | * @param degree 114 | * @return 115 | */ 116 | private double degreeToRad(double degree) { 117 | return degree * Math.PI / 180; 118 | } 119 | 120 | @Override 121 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 122 | super.onSizeChanged(w, h, oldw, oldh); 123 | mWidth = w; 124 | mHeight = h; 125 | mWaveHeight = mHeight / 10; 126 | mYOffset = mHeight - mWaveHeight; 127 | 128 | mBitmap = Utils.getBitmap(mContext, mHeight - 2 * mWaveHeight, mHeight - 2 * mWaveHeight, R.mipmap.ic_launcher); 129 | 130 | initAnim(); 131 | } 132 | 133 | private void initAnim() { 134 | ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 3000); 135 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 136 | @Override 137 | public void onAnimationUpdate(ValueAnimator animation) { 138 | mXOffset = (float) animation.getAnimatedValue(); 139 | postInvalidate(); 140 | } 141 | }); 142 | 143 | ValueAnimator valueAnimator2 = ValueAnimator.ofFloat(mYOffset, 0); 144 | valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 145 | @Override 146 | public void onAnimationUpdate(ValueAnimator animation) { 147 | mYOffset = (float) animation.getAnimatedValue(); 148 | postInvalidate(); 149 | } 150 | }); 151 | 152 | mAnimatorSet = new AnimatorSet(); 153 | mAnimatorSet.playTogether(valueAnimator, valueAnimator2); 154 | mAnimatorSet.setDuration(10000); 155 | mAnimatorSet.setInterpolator(new LinearInterpolator()); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/wave_view/WaveViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.wave_view; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | 9 | public class WaveViewActivity extends AppCompatActivity { 10 | 11 | private SinWave mWave; 12 | private BezierWave mBezierWave; 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_wave_view); 18 | 19 | mWave = (SinWave) findViewById(R.id.waveView); 20 | mBezierWave = (BezierWave) findViewById(R.id.bezierWave); 21 | 22 | findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { 23 | @Override 24 | public void onClick(View v) { 25 | mWave.start(); 26 | } 27 | }); 28 | 29 | findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() { 30 | @Override 31 | public void onClick(View v) { 32 | mBezierWave.start(); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/wheel_view/WheelView.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.wheel_view; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | 8 | /** 9 | * Created by zengyazhi on 17/4/21. 10 | * 滚轮 11 | * 参考:https://github.com/Bigkoo/Android-PickerView 12 | */ 13 | 14 | public class WheelView extends View { 15 | public WheelView(Context context) { 16 | this(context, null); 17 | } 18 | 19 | public WheelView(Context context, @Nullable AttributeSet attrs) { 20 | this(context, attrs, 0); 21 | } 22 | 23 | public WheelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | init(); 26 | } 27 | 28 | private void init() { 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/yazhi1992/practice/wheel_view/WheelViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.yazhi1992.practice.wheel_view; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import com.yazhi1992.practice.R; 8 | 9 | public class WheelViewActivity extends AppCompatActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_wheel_view); 15 | 16 | findViewById(R.id.wheelViewBtn).setOnClickListener(new View.OnClickListener() { 17 | @Override 18 | public void onClick(View v) { 19 | 20 | } 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_manage.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_send.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_arthmetic.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |