├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── zieiony
│ │ └── guide
│ │ ├── ActivityAnnotation.java
│ │ ├── ChartViewActivity.java
│ │ ├── FlowLayoutActivity.java
│ │ ├── InvalidEditTextActivity.kt
│ │ ├── LandscapeDrawableActivity.kt
│ │ ├── MainActivity.kt
│ │ ├── MoodToggleActivity.kt
│ │ ├── ProgressTextViewActivity.java
│ │ ├── SampleActivity.kt
│ │ └── SampleItem.kt
│ └── res
│ ├── color
│ ├── color_accent_selector.xml
│ ├── graph_itemcolor.xml
│ └── text_color_selector.xml
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_chartview.xml
│ ├── activity_flowlayout.xml
│ ├── activity_invalidedittext.xml
│ ├── activity_landscapedrawable.xml
│ ├── activity_main.xml
│ ├── activity_moodtoggle.xml
│ ├── activity_progresstextview.xml
│ └── row_sample.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── chartview
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── zieiony
│ │ └── guide
│ │ └── chartview
│ │ ├── ChartView.java
│ │ ├── ChartView2.java
│ │ └── ChartView3.kt
│ └── res
│ └── values
│ └── attrs.xml
├── flowlayout
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── zieiony
│ │ └── guide
│ │ └── flowlayout
│ │ ├── FlowLayout.java
│ │ ├── FlowLayout2.java
│ │ └── FlowLayout3.java
│ └── res
│ └── values
│ └── attrs.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── chartview.png
├── distributionfunction.png
├── flowlayout.png
├── itemdrawable.png
├── landscapedrawable.png
└── moodtoggle.png
├── invalidedittext
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── zieiony
│ │ └── guide
│ │ └── invalidedittext
│ │ └── InvalidEditText.kt
│ └── res
│ └── values
│ └── attrs.xml
├── landscapedrawable
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── github
│ └── zieiony
│ └── guide
│ └── landscapedrawable
│ ├── Cloud.kt
│ ├── ItemDrawable.kt
│ ├── Land.kt
│ ├── LandscapeDrawable.kt
│ ├── LandscapeItem.kt
│ ├── MathUtils.java
│ ├── Sky.kt
│ ├── Stars.kt
│ └── Tree.kt
├── moodtoggle
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── zieiony
│ │ └── guide
│ │ └── moodtoggle
│ │ └── MoodToggle.kt
│ └── res
│ └── drawable
│ ├── ic_sentiment_dissatisfied_black_24dp.xml
│ ├── ic_sentiment_neutral_black_24dp.xml
│ └── ic_sentiment_satisfied_black_24dp.xml
├── progresstextview
├── .gitignore
├── build.gradle
├── result.png
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── zieiony
│ │ └── guide
│ │ └── progresstextview
│ │ ├── ProgressTextView_Bad.kt
│ │ ├── ProgressTextView_Good.kt
│ │ └── ProgressTextView_Medium.kt
│ └── res
│ └── values
│ └── attrs.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GuideToCustomViews
2 | The ultimate guide to Android custom views
3 |
4 | I've been designing, writing and publishing Android custom views for the past 5 years now. I guess it's about time to sum it up and share the results.
5 |
6 | Here you will find the topics related to custom views and a couple of case studies. Code samples will be in Java and Kotlin randomly and the code will be as simple as possible to demonstrate ideas and techniques better. I want this place to be more of a tutorial wiki than a documentation. You can find the documentation on [developer.android.com/](https://developer.android.com/).
7 |
8 | I'll try to add an article or two per week. If there's something missing and you think that I should write about - raise an issue and tell me. If you wish, there's a larger repository with my custom views [here](https://github.com/ZieIony/Carbon).
9 |
10 | ### Topics
11 |
12 | 1. [Basics](https://github.com/ZieIony/GuideToCustomViews/wiki/Basics)
13 | 1. [What to extend](https://github.com/ZieIony/GuideToCustomViews/wiki/What-to-extend)
14 | 1. [Prefix everything](https://github.com/ZieIony/GuideToCustomViews/wiki/Prefix-everything)
15 |
16 | ##### Code
17 |
18 | 1. [Constructors](https://github.com/ZieIony/GuideToCustomViews/wiki/Constructors)
19 | 1. [Measuring](https://github.com/ZieIony/GuideToCustomViews/wiki/Measuring)
20 | 1. [Custom states](https://github.com/ZieIony/GuideToCustomViews/wiki/Custom-states)
21 | 1. [State saving](https://github.com/ZieIony/GuideToCustomViews/wiki/State-saving)
22 | 1. [Edit mode](https://github.com/ZieIony/GuideToCustomViews/wiki/Edit-mode)
23 | 1. [Drawables](https://github.com/ZieIony/GuideToCustomViews/wiki/Drawables)
24 |
25 | ##### Attributes and styles
26 |
27 | 1. [Using attributes](https://github.com/ZieIony/GuideToCustomViews/wiki/Using-attributes)
28 | 1. [Custom attributes](https://github.com/ZieIony/GuideToCustomViews/wiki/Custom-attributes)
29 | 1. [Custom layout attributes](https://github.com/ZieIony/GuideToCustomViews/wiki/Custom-layout-attributes)
30 |
31 | ##### Accessibility
32 |
33 | 1. [Basic accessibility](https://github.com/ZieIony/GuideToCustomViews/wiki/Basic-accessibility)
34 | 1. [Content description and events](https://github.com/ZieIony/GuideToCustomViews/wiki/Content-description-and-events)
35 |
36 | ### Case studies
37 |
38 | ##### ProgressTextView
39 |
40 |
41 |
42 | A progress bar that also has a text. The text should be drawn in one color on the 'done' part of the progress and in another color on the 'remaining' part.
43 |
44 | See: [guide](https://github.com/ZieIony/GuideToCustomViews/wiki/ProgressTextView), [code](https://github.com/ZieIony/GuideToCustomViews/tree/master/progresstextview).
45 |
46 | ##### ChartView
47 |
48 |
49 |
50 | A simple bar chart with color state lists and bar selecting with clicks.
51 |
52 | Topics covered: drawing on canvas, state lists, touch events.
53 |
54 | See: [code](https://github.com/ZieIony/GuideToCustomViews/tree/master/chartview).
55 |
56 | A more complete version of the view can be found [here](https://github.com/ZieIony/Carbon/blob/master/carbon/src/main/java/carbon/beta/ChartView.java)
57 |
58 | ##### FlowLayout
59 |
60 |
61 |
62 | A layout that displays its children in rows, side to side and then in another line.
63 |
64 | Topics covered: measuring, laying out, custom layout attributes, right to left support.
65 |
66 | See: [code](https://github.com/ZieIony/GuideToCustomViews/tree/master/flowlayout).
67 |
68 | ##### InvalidEditText
69 |
70 | An EditText with support for an invalid state attribute.
71 |
72 | Topics covered: custom states.
73 |
74 | See: [guide](https://github.com/ZieIony/GuideToCustomViews/wiki/InvalidEditText), [code](https://github.com/ZieIony/GuideToCustomViews/tree/master/invalidedittext).
75 |
76 | ##### MoodToggle
77 |
78 |
79 |
80 | A very simple toggle with accessibility support.
81 |
82 | Topics covered: basic accessibility.
83 |
84 | See: [guide](https://github.com/ZieIony/GuideToCustomViews/wiki/MoodToggle), [code](https://github.com/ZieIony/GuideToCustomViews/tree/master/moodtoggle).
85 |
86 | ##### LandscapeDrawable
87 |
88 |
89 |
90 | An animated Drawable depicting a landscape with customizable items: trees, clouds, stars, a sun, and fog.
91 |
92 | Topics covered: drawing on Canvas, custom drawables.
93 |
94 | See: [guide](https://github.com/ZieIony/GuideToCustomViews/wiki/LandscapeDrawable), [code](https://github.com/ZieIony/GuideToCustomViews/tree/master/landscapedrawable).
95 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion 29
8 | buildToolsVersion "29.0.2"
9 |
10 | dataBinding {
11 | enabled = true
12 | }
13 | defaultConfig {
14 | applicationId "com.github.zieiony.guide"
15 | minSdkVersion 16
16 | targetSdkVersion 29
17 | versionCode 1
18 | versionName "1.0"
19 | }
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
28 | implementation 'androidx.appcompat:appcompat:1.1.0'
29 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
30 | implementation 'androidx.recyclerview:recyclerview:1.1.0'
31 | implementation 'com.google.android.material:material:1.1.0-beta02'
32 |
33 | implementation 'com.github.ZieIony:randomdata:4e90951628'
34 | implementation 'com.github.ZieIony:carbon:e2aadf6002'
35 |
36 | implementation project(path: ':progresstextview')
37 | implementation project(path: ':chartview')
38 | implementation project(path: ':flowlayout')
39 | implementation project(path: ':invalidedittext')
40 | implementation project(path: ':moodtoggle')
41 | implementation project(path: ':landscapedrawable')
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/ActivityAnnotation.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | @Retention(RetentionPolicy.RUNTIME)
7 | public @interface ActivityAnnotation {
8 | String title();
9 |
10 | int layout();
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/ChartViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Bundle;
5 |
6 | import androidx.annotation.Nullable;
7 |
8 | import com.github.zieiony.guide.chartview.ChartView;
9 | import com.github.zieiony.guide.chartview.ChartView2;
10 | import com.github.zieiony.guide.chartview.ChartView3;
11 |
12 | import tk.zielony.randomdata.RandomData;
13 | import tk.zielony.randomdata.common.FloatGenerator;
14 | import tk.zielony.randomdata.food.StringFruitGenerator;
15 |
16 | @ActivityAnnotation(layout = R.layout.activity_chartview, title = "ChartView")
17 | public class ChartViewActivity extends SampleActivity {
18 |
19 | @SuppressLint("ClickableViewAccessibility")
20 | @Override
21 | protected void onCreate(@Nullable Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 |
24 | RandomData randomData = new RandomData();
25 | randomData.addGenerator(new StringFruitGenerator());
26 | randomData.addGenerator(new FloatGenerator(0, 100).withMatcher(field -> field.getName().equals("value")));
27 |
28 | ChartView.Item[] items = randomData.generateArray(ChartView.Item.class, 10);
29 | final ChartView graphView1 = findViewById(R.id.chartView1);
30 | graphView1.setItems(items);
31 | graphView1.setItemSpacing(4);
32 |
33 | ChartView2.Item[] items2 = randomData.generateArray(ChartView2.Item.class, 10);
34 | final ChartView2 chartView2 = findViewById(R.id.chartView2);
35 | chartView2.setItems(items2);
36 |
37 | ChartView3.Item[] items3 = randomData.generateArray(ChartView3.Item.class, 10);
38 | final ChartView3 chartView3 = findViewById(R.id.chartView3);
39 | chartView3.setItems(items3);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/FlowLayoutActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide;
2 |
3 | @ActivityAnnotation(layout = R.layout.activity_flowlayout, title = "FlowLayout")
4 | public class FlowLayoutActivity extends SampleActivity {
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/InvalidEditTextActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide
2 |
3 | import android.os.Bundle
4 | import android.text.Editable
5 | import android.text.TextWatcher
6 | import kotlinx.android.synthetic.main.activity_invalidedittext.*
7 |
8 | @ActivityAnnotation(layout = R.layout.activity_invalidedittext, title = "InvalidEditText")
9 | class InvalidEditTextActivity : SampleActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 |
13 | invalidEditText.addTextChangedListener(object : TextWatcher {
14 | override fun afterTextChanged(s: Editable) {
15 | invalidEditText.valid = s.length >= 6
16 | }
17 |
18 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
19 | }
20 |
21 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
22 | }
23 | })
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/LandscapeDrawableActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide
2 |
3 | import android.os.Bundle
4 | import com.github.zieiony.guide.landscapedrawable.*
5 | import kotlinx.android.synthetic.main.activity_landscapedrawable.*
6 |
7 | @ActivityAnnotation(layout = R.layout.activity_landscapedrawable, title = "LandscapeDrawable")
8 | class LandscapeDrawableActivity : SampleActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | cloud.setImageDrawable(
12 | ItemDrawable(
13 | Cloud(8, color = resources.getColor(R.color.landscape_cloudColor))
14 | )
15 | )
16 | tree.setImageDrawable(
17 | ItemDrawable(Tree(wind = 1f))
18 | )
19 | sky.setImageDrawable(
20 | ItemDrawable(
21 | Sky(
22 | 4,
23 | cloudSize = resources.getDimension(R.dimen.landscape_cloudSize),
24 | cloudColor = resources.getColor(R.color.landscape_cloudColor),
25 | puffCount = 8,
26 | wind = resources.getDimension(R.dimen.landscape_windStrength)
27 | )
28 | )
29 | )
30 | stars.setImageDrawable(
31 | ItemDrawable(
32 | Stars(
33 | 40,
34 | resources.getDimension(R.dimen.landscape_starSize),
35 | resources.getColor(R.color.landscape_starColor)
36 | )
37 | )
38 | )
39 | land.setImageDrawable(
40 | ItemDrawable(
41 | Land(
42 | resources.getColor(R.color.landscape_landscapeColor),
43 | resources.getColor(R.color.landscape_fogColor),
44 | resources.getDimension(R.dimen.landscape_landscapeHeight) / 4
45 | )
46 | )
47 | )
48 | landscape.background = LandscapeDrawable(
49 | 40,
50 | resources.getDimension(R.dimen.landscape_starSize),
51 | resources.getColor(R.color.landscape_starColor),
52 | 4,
53 | cloudSize = resources.getDimension(R.dimen.landscape_cloudSize),
54 | cloudColor = resources.getColor(R.color.landscape_cloudColor),
55 | fogColor = resources.getColor(R.color.landscape_fogColor),
56 | landscapeColor = resources.getColor(R.color.landscape_landscapeColor),
57 | skyColor = resources.getColor(R.color.landscape_skyColor),
58 | sunColor = resources.getColor(R.color.landscape_sunColor),
59 | skyHeight = resources.getDimension(R.dimen.landscape_skyHeight),
60 | planesCount = 5,
61 | windStrength = resources.getDimension(R.dimen.landscape_windStrength),
62 | sunSize = resources.getDimension(R.dimen.landscape_sunSize),
63 | landscapeHeight = resources.getDimension(R.dimen.landscape_landscapeHeight),
64 | treeHeight = resources.getDimension(R.dimen.landscape_treeHeight),
65 | maxWind = resources.getDimension(R.dimen.landscape_maxWind)
66 | )
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.recyclerview.widget.LinearLayoutManager
7 | import carbon.component.DataBindingComponent
8 | import carbon.component.PaddingItem
9 | import carbon.component.PaddingRow
10 | import carbon.recycler.RowArrayAdapter
11 | import carbon.recycler.RowFactory
12 | import kotlinx.android.synthetic.main.activity_main.*
13 | import java.io.Serializable
14 |
15 | class MainActivity : AppCompatActivity() {
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContentView(R.layout.activity_main)
19 |
20 | val adapter: RowArrayAdapter = RowArrayAdapter(SampleItem::class.java, RowFactory { parent ->
21 | DataBindingComponent(parent, R.layout.row_sample)
22 | })
23 | adapter.addFactory(PaddingItem::class.java) { PaddingRow(it) }
24 | adapter.setOnItemClickedListener(SampleItem::class.java, { view, item, position ->
25 | val activityClass = (item as SampleItem).activityClass
26 | val intent = Intent(view.context, activityClass)
27 | startActivity(intent)
28 | })
29 |
30 | recycler.layoutManager = LinearLayoutManager(this)
31 | recycler.adapter = adapter
32 |
33 | adapter.items = arrayOf(
34 | PaddingItem(resources.getDimensionPixelSize(R.dimen.carbon_paddingHalf)),
35 | SampleItem(ProgressTextViewActivity::class.java),
36 | SampleItem(ChartViewActivity::class.java),
37 | SampleItem(FlowLayoutActivity::class.java),
38 | SampleItem(InvalidEditTextActivity::class.java),
39 | SampleItem(MoodToggleActivity::class.java),
40 | SampleItem(LandscapeDrawableActivity::class.java),
41 | PaddingItem(resources.getDimensionPixelSize(R.dimen.carbon_paddingHalf))
42 | )
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/MoodToggleActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide
2 |
3 | @ActivityAnnotation(layout = R.layout.activity_moodtoggle, title = "MoodToggle")
4 | class MoodToggleActivity : SampleActivity()
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/ProgressTextViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Bundle;
5 |
6 | import androidx.annotation.Nullable;
7 |
8 | import com.github.zieiony.guide.progresstextview.ProgressTextView_Bad;
9 | import com.github.zieiony.guide.progresstextview.ProgressTextView_Good;
10 | import com.github.zieiony.guide.progresstextview.ProgressTextView_Medium;
11 |
12 | @ActivityAnnotation(layout = R.layout.activity_progresstextview, title = "ProgressTextView")
13 | public class ProgressTextViewActivity extends SampleActivity {
14 |
15 | @SuppressLint("ClickableViewAccessibility")
16 | @Override
17 | protected void onCreate(@Nullable Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 |
20 | final ProgressTextView_Bad progress = findViewById(R.id.prgressTextView_bad);
21 | progress.setOnTouchListener((v, event) -> {
22 | progress.setProgress(event.getX() / v.getWidth());
23 | return true;
24 | });
25 |
26 | final ProgressTextView_Medium progress2 = findViewById(R.id.prgressTextView_medium);
27 | progress2.setOnTouchListener((v, event) -> {
28 | progress2.setProgress(event.getX() / v.getWidth());
29 | return true;
30 | });
31 |
32 | final ProgressTextView_Good progress3 = findViewById(R.id.prgressTextView_good);
33 | progress3.setOnTouchListener((v, event) -> {
34 | progress3.setProgress(event.getX() / v.getWidth());
35 | return true;
36 | });
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/SampleActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import carbon.widget.Toolbar
6 |
7 | abstract class SampleActivity : AppCompatActivity() {
8 | override fun onCreate(savedInstanceState: Bundle?) {
9 | super.onCreate(savedInstanceState)
10 |
11 | javaClass.getAnnotation(ActivityAnnotation::class.java)?.let {
12 | setContentView(it.layout)
13 | findViewById(R.id.toolbar)?.title = it.title
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/github/zieiony/guide/SampleItem.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide
2 |
3 | import android.app.Activity
4 | import java.io.Serializable
5 |
6 | open class SampleItem : Serializable {
7 | var activityClass: Class? = null
8 | private set
9 | var name: String? = null
10 | private set
11 | var icon = 0
12 | private set
13 |
14 | @JvmOverloads
15 | constructor(activityClass: Class, icon: Int = 0) : super(
16 | ) {
17 | this.activityClass = activityClass
18 | this.name = activityClass.getAnnotation(ActivityAnnotation::class.java)?.title
19 | this.icon = icon
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/res/color/color_accent_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/color/graph_itemcolor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/color/text_color_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chartview.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
29 |
30 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_flowlayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
25 |
26 |
31 |
32 |
36 |
37 |
42 |
43 |
47 |
48 |
53 |
54 |
58 |
59 |
64 |
65 |
66 |
70 |
71 |
76 |
77 |
81 |
82 |
87 |
88 |
92 |
93 |
98 |
99 |
103 |
104 |
109 |
110 |
111 |
115 |
116 |
121 |
122 |
126 |
127 |
132 |
133 |
138 |
139 |
144 |
145 |
150 |
151 |
157 |
158 |
159 |
163 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_invalidedittext.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_landscapedrawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
29 |
30 |
31 |
41 |
42 |
51 |
52 |
61 |
62 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_moodtoggle.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_progresstextview.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
23 |
24 |
39 |
40 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/row_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
28 |
29 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 | #ffffff
8 | #ffffff
9 | #e0f2f1
10 | #ffe0f2f1
11 | #b2dfdb
12 | #0d47a1
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 100dp
4 | 2dp
5 | 16dp
6 | 112dp
7 | 32dp
8 | 112dp
9 | 1dp
10 | 12dp
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Guide
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.71'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.0.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | maven { url 'https://jitpack.io' }
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/chartview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/chartview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-kapt'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 |
9 | defaultConfig {
10 | minSdkVersion 14
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | compileOptions {
16 | sourceCompatibility JavaVersion.VERSION_1_8
17 | targetCompatibility JavaVersion.VERSION_1_8
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
23 | implementation 'androidx.appcompat:appcompat:1.1.0'
24 | }
25 |
--------------------------------------------------------------------------------
/chartview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chartview/src/main/java/com/github/zieiony/guide/chartview/ChartView.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.chartview;
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.util.AttributeSet;
9 | import android.view.View;
10 |
11 | import androidx.annotation.Nullable;
12 | import androidx.annotation.RequiresApi;
13 |
14 | import java.util.Arrays;
15 |
16 | public class ChartView extends View {
17 | public static class Item {
18 | String name;
19 | float value;
20 |
21 | public Item() {
22 | }
23 |
24 | public Item(String name, float value) {
25 | this.name = name;
26 | this.value = value;
27 | }
28 |
29 | public String getName() {
30 | return name;
31 | }
32 |
33 | public void setName(String name) {
34 | this.name = name;
35 | }
36 |
37 | public float getValue() {
38 | return value;
39 | }
40 |
41 | public void setValue(float value) {
42 | this.value = value;
43 | }
44 | }
45 |
46 | private Item[] items;
47 | private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
48 | private float spacing = 0;
49 |
50 | public ChartView(Context context) {
51 | super(context);
52 | }
53 |
54 | public ChartView(Context context, @Nullable AttributeSet attrs) {
55 | super(context, attrs);
56 | }
57 |
58 | public ChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
59 | super(context, attrs, defStyleAttr);
60 | }
61 |
62 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
63 | public ChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
64 | super(context, attrs, defStyleAttr, defStyleRes);
65 | }
66 |
67 | public void setItems(Item[] items) {
68 | this.items = Arrays.copyOf(items, items.length);
69 | }
70 |
71 | public void setItemSpacing(float spacing) {
72 | this.spacing = spacing;
73 | }
74 |
75 | @Override
76 | protected void onDraw(Canvas canvas) {
77 | super.onDraw(canvas);
78 |
79 | if (items == null)
80 | return;
81 |
82 | paint.setColor(Color.RED);
83 |
84 | float viewportWidth = getWidth() - getPaddingLeft() - getPaddingRight();
85 | float viewportHeight = getHeight() - getPaddingTop() - getPaddingBottom();
86 | float itemWidth = viewportWidth / items.length;
87 | float maxItemHeight = 0;
88 | for (Item item : items)
89 | maxItemHeight = Math.max(maxItemHeight, item.value);
90 |
91 | for (int i = 0; i < items.length; i++) {
92 | Item item = items[i];
93 | canvas.drawRect(
94 | getPaddingLeft() + i * itemWidth + spacing / 2,
95 | getHeight() - getPaddingBottom() - viewportHeight / maxItemHeight * item.value,
96 | getPaddingLeft() + (i + 1) * itemWidth - spacing / 2,
97 | getHeight() - getPaddingBottom(),
98 | paint);
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/chartview/src/main/java/com/github/zieiony/guide/chartview/ChartView2.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.chartview;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.os.Build;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 |
13 | import androidx.annotation.Nullable;
14 | import androidx.annotation.RequiresApi;
15 |
16 | import java.util.Arrays;
17 |
18 | public class ChartView2 extends View {
19 | public static class Item {
20 | String name;
21 | float value;
22 |
23 | public Item() {
24 | }
25 |
26 | public Item(String name, float value) {
27 | this.name = name;
28 | this.value = value;
29 | }
30 |
31 | public String getName() {
32 | return name;
33 | }
34 |
35 | public void setName(String name) {
36 | this.name = name;
37 | }
38 |
39 | public float getValue() {
40 | return value;
41 | }
42 |
43 | public void setValue(float value) {
44 | this.value = value;
45 | }
46 | }
47 |
48 | private Item[] items;
49 | private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
50 | private float spacing = 0;
51 | private ColorStateList itemColor;
52 | private Item selectedItem;
53 |
54 | public ChartView2(Context context) {
55 | super(context);
56 | initChartView(null, 0);
57 | }
58 |
59 | public ChartView2(Context context, @Nullable AttributeSet attrs) {
60 | super(context, attrs);
61 | initChartView(attrs, 0);
62 | }
63 |
64 | public ChartView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
65 | super(context, attrs, defStyleAttr);
66 | initChartView(attrs, defStyleAttr);
67 | }
68 |
69 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
70 | public ChartView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
71 | super(context, attrs, defStyleAttr, defStyleRes);
72 | initChartView(attrs, defStyleAttr);
73 | }
74 |
75 | private void initChartView(@Nullable AttributeSet attrs, int defStyleAttr) {
76 | if (attrs == null)
77 | return;
78 |
79 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ChartView2);
80 |
81 | setItemSpacing(a.getDimension(R.styleable.ChartView2_guide_itemSpacing, 0));
82 | setItemColor(a.getColorStateList(R.styleable.ChartView2_guide_itemColor));
83 |
84 | a.recycle();
85 | }
86 |
87 | public void setItems(Item[] items) {
88 | this.items = Arrays.copyOf(items, items.length);
89 | }
90 |
91 | public void setItemSpacing(float spacing) {
92 | this.spacing = spacing;
93 | }
94 |
95 | public void setItemColor(ColorStateList itemColor) {
96 | this.itemColor = itemColor;
97 | }
98 |
99 | public void setItemColor(int itemColor) {
100 | this.itemColor = ColorStateList.valueOf(itemColor);
101 | }
102 |
103 | @Override
104 | public boolean onTouchEvent(MotionEvent event) {
105 | if (items == null) {
106 | selectedItem = null;
107 | return false;
108 | }
109 |
110 | float viewportWidth = getWidth() - getPaddingLeft() - getPaddingRight();
111 | float itemWidth = viewportWidth / items.length;
112 |
113 | selectedItem = items[(int) Math.max(0, Math.min(Math.floor((event.getX() - getPaddingLeft()) / itemWidth), items.length - 1))];
114 | postInvalidate();
115 |
116 | return true;
117 | }
118 |
119 | @Override
120 | protected void onDraw(Canvas canvas) {
121 | super.onDraw(canvas);
122 |
123 | if (items == null || itemColor == null)
124 | return;
125 |
126 | float viewportWidth = getWidth() - getPaddingLeft() - getPaddingRight();
127 | float viewportHeight = getHeight() - getPaddingTop() - getPaddingBottom();
128 | float itemWidth = viewportWidth / items.length;
129 | float maxItemHeight = 0;
130 | for (Item item : items)
131 | maxItemHeight = Math.max(maxItemHeight, item.value);
132 |
133 | for (int i = 0; i < items.length; i++) {
134 | Item item = items[i];
135 | paint.setColor(itemColor.getColorForState(getDrawableStateSelected(selectedItem == item), itemColor.getDefaultColor()));
136 | canvas.drawRect(
137 | getPaddingLeft() + i * itemWidth + spacing / 2,
138 | getHeight() - getPaddingBottom() - viewportHeight / maxItemHeight * item.value,
139 | getPaddingLeft() + (i + 1) * itemWidth - spacing / 2,
140 | getHeight() - getPaddingBottom(),
141 | paint);
142 | }
143 | }
144 |
145 | private int[] getDrawableStateSelected(boolean selected) {
146 | if (!selected) {
147 | return getDrawableState();
148 | } else {
149 | int[] newState = Arrays.copyOf(getDrawableState(), getDrawableState().length + 1);
150 | newState[newState.length - 1] = android.R.attr.state_selected;
151 | return newState;
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/chartview/src/main/java/com/github/zieiony/guide/chartview/ChartView3.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.chartview
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.graphics.Canvas
6 | import android.graphics.Paint
7 | import android.graphics.Rect
8 | import android.os.Build
9 | import android.os.Bundle
10 | import android.util.AttributeSet
11 | import android.view.KeyEvent
12 | import android.view.MotionEvent
13 | import android.view.View
14 | import androidx.annotation.RequiresApi
15 | import androidx.core.view.ViewCompat
16 | import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
17 | import androidx.customview.widget.ExploreByTouchHelper
18 | import com.github.zieiony.guide.chartview.ChartView3
19 | import java.util.*
20 | import kotlin.math.floor
21 | import kotlin.math.max
22 | import kotlin.math.min
23 |
24 | class ChartView3 : View {
25 | class Item {
26 | lateinit var name: String
27 | var value = 0f
28 |
29 | constructor()
30 | constructor(name: String, value: Float) {
31 | this.name = name
32 | this.value = value
33 | }
34 |
35 | }
36 |
37 | private inner class AccessHelper : ExploreByTouchHelper(this) {
38 | override fun getVirtualViewAt(x: Float, y: Float): Int {
39 | if (x < paddingLeft || y < paddingTop || x > width - paddingRight || y > height - paddingBottom)
40 | return HOST_ID
41 | items?.let { items ->
42 | val viewportWidth = width - paddingLeft - paddingRight.toFloat()
43 | val itemWidth = viewportWidth / items.size
44 | return max(0, min(floor((x - paddingLeft) / itemWidth).toInt(), items.size - 1))
45 | }
46 | return HOST_ID
47 | }
48 |
49 | override fun getVisibleVirtualViews(virtualViewIds: MutableList) {
50 | items?.let {
51 | for (i in it.indices)
52 | virtualViewIds.add(i)
53 | }
54 | }
55 |
56 | override fun onPopulateNodeForVirtualView(virtualViewId: Int, node: AccessibilityNodeInfoCompat) {
57 | items?.let { items ->
58 | node.className = accessibilityClassName
59 | node.contentDescription = "item ${virtualViewId + 1} - ${items[virtualViewId].name}, ${items[virtualViewId].value}"
60 | node.isClickable = true
61 |
62 | val viewportWidth = width - paddingLeft - paddingRight.toFloat()
63 | val viewportHeight = height - paddingTop - paddingBottom.toFloat()
64 | val itemWidth = viewportWidth / items.size
65 | var maxItemHeight = 0f
66 |
67 | for (item in items)
68 | maxItemHeight = max(maxItemHeight, item.value)
69 |
70 | val rect = Rect(
71 | (paddingLeft + virtualViewId * itemWidth + itemSpacing / 2).toInt(),
72 | (height - paddingBottom - viewportHeight / maxItemHeight * items[virtualViewId].value).toInt(),
73 | (paddingLeft + (virtualViewId + 1) * itemWidth - itemSpacing / 2).toInt(),
74 | (height - paddingBottom.toFloat()).toInt()
75 | )
76 |
77 | node.setBoundsInParent(rect)
78 | }
79 | }
80 |
81 | override fun onPerformActionForVirtualView(virtualViewId: Int, action: Int, arguments: Bundle?): Boolean {
82 | items?.let { items ->
83 | if (virtualViewId >= 0 && virtualViewId < items.size)
84 | selectedItem = items[virtualViewId]
85 | return true
86 | }
87 | return false
88 | }
89 | }
90 |
91 | var items: Array- ? = null
92 | private set
93 | private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
94 | var itemSpacing = 0f
95 | var itemColor: ColorStateList? = null
96 | var selectedItem: Item? = null
97 |
98 | private val accessHelper = AccessHelper()
99 |
100 | constructor(context: Context?) : super(context) {
101 | initChartView(null, 0)
102 | }
103 |
104 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
105 | initChartView(attrs, 0)
106 | }
107 |
108 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
109 | initChartView(attrs, defStyleAttr)
110 | }
111 |
112 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
113 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
114 | initChartView(attrs, defStyleAttr)
115 | }
116 |
117 | private fun initChartView(attrs: AttributeSet?, defStyleAttr: Int) {
118 | if (attrs == null)
119 | return
120 |
121 | val a = context.obtainStyledAttributes(attrs, R.styleable.ChartView2)
122 |
123 | itemSpacing = a.getDimension(R.styleable.ChartView2_guide_itemSpacing, 0f)
124 | itemColor = a.getColorStateList(R.styleable.ChartView2_guide_itemColor)
125 |
126 | a.recycle()
127 |
128 | ViewCompat.setAccessibilityDelegate(this, accessHelper)
129 | }
130 |
131 | fun setItems(items: Array
- ) {
132 | this.items = Arrays.copyOf(items, items.size)
133 | }
134 |
135 | fun setItemColor(itemColor: Int) {
136 | this.itemColor = ColorStateList.valueOf(itemColor)
137 | }
138 |
139 | private var prevX = 0f
140 |
141 | override fun onTouchEvent(event: MotionEvent): Boolean {
142 | prevX = event.x
143 | return super.onTouchEvent(event)
144 | }
145 |
146 | override fun performClick(): Boolean {
147 | val items = this.items
148 | if (items == null) {
149 | selectedItem = null
150 | return performClick()
151 | }
152 | val viewportWidth = width - paddingLeft - paddingRight.toFloat()
153 | val itemWidth = viewportWidth / items.size
154 | selectedItem = items[max(0, min(floor((prevX - paddingLeft) / itemWidth).toInt(), items.size - 1))]
155 | postInvalidate()
156 | super.performClick()
157 | return true
158 | }
159 |
160 | override fun onDraw(canvas: Canvas) {
161 | super.onDraw(canvas)
162 |
163 | val items = this.items
164 | val itemColor = this.itemColor
165 | if (items == null || itemColor == null)
166 | return
167 |
168 | val viewportWidth = width - paddingLeft - paddingRight.toFloat()
169 | val viewportHeight = height - paddingTop - paddingBottom.toFloat()
170 | val itemWidth = viewportWidth / items.size
171 | var maxItemHeight = 0f
172 |
173 | for (item in items)
174 | maxItemHeight = max(maxItemHeight, item.value)
175 | for (i in items.indices) {
176 | val item = items[i]
177 | paint.color = itemColor.getColorForState(getDrawableStateSelected(selectedItem === item), itemColor.defaultColor)
178 | canvas.drawRect(
179 | paddingLeft + i * itemWidth + itemSpacing / 2,
180 | height - paddingBottom - viewportHeight / maxItemHeight * item.value,
181 | paddingLeft + (i + 1) * itemWidth - itemSpacing / 2,
182 | height - paddingBottom.toFloat(),
183 | paint
184 | )
185 | }
186 | }
187 |
188 | private fun getDrawableStateSelected(selected: Boolean): IntArray {
189 | return if (!selected) {
190 | drawableState
191 | } else {
192 | val newState = Arrays.copyOf(drawableState, drawableState.size + 1)
193 | newState[newState.size - 1] = android.R.attr.state_selected
194 | newState
195 | }
196 | }
197 |
198 | override fun dispatchHoverEvent(event: MotionEvent): Boolean {
199 | return accessHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event)
200 | }
201 |
202 | override fun dispatchKeyEvent(event: KeyEvent): Boolean {
203 | return accessHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
204 | }
205 |
206 | override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
207 | super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
208 | accessHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
209 | }
210 |
211 | override fun getAccessibilityClassName() = ChartView3::class.java.name
212 |
213 | }
--------------------------------------------------------------------------------
/chartview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/flowlayout/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/flowlayout/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 |
9 |
10 | defaultConfig {
11 | minSdkVersion 14
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0"
15 | }
16 | }
17 |
18 | dependencies {
19 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
20 | implementation 'androidx.appcompat:appcompat:1.1.0'
21 | }
22 |
--------------------------------------------------------------------------------
/flowlayout/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/flowlayout/src/main/java/com/github/zieiony/guide/flowlayout/FlowLayout.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.flowlayout;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.view.Gravity;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import androidx.core.view.GravityCompat;
13 | import androidx.core.view.ViewCompat;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * FlowLayout layouts its children from left to right, top to bottom.
20 | */
21 | public class FlowLayout extends android.widget.FrameLayout {
22 |
23 | private int gravity;
24 |
25 | public FlowLayout(Context context) {
26 | super(context, null, R.attr.guide_flowLayoutStyle);
27 | initFlowLayout(null, R.attr.guide_flowLayoutStyle);
28 | }
29 |
30 | public FlowLayout(Context context, AttributeSet attrs) {
31 | super(context, attrs, R.attr.guide_flowLayoutStyle);
32 | initFlowLayout(attrs, R.attr.guide_flowLayoutStyle);
33 | }
34 |
35 | public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
36 | super(context, attrs, defStyleAttr);
37 | initFlowLayout(attrs, defStyleAttr);
38 | }
39 |
40 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
41 | public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
42 | super(context, attrs, defStyleAttr, defStyleRes);
43 | initFlowLayout(attrs, defStyleAttr);
44 | }
45 |
46 | private void initFlowLayout(AttributeSet attrs, int defStyleAttr) {
47 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout, defStyleAttr, 0);
48 |
49 | gravity = a.getInt(R.styleable.FlowLayout_android_gravity, Gravity.START);
50 |
51 | a.recycle();
52 | }
53 |
54 | public int getGravity() {
55 | return gravity;
56 | }
57 |
58 | public void setGravity(int gravity) {
59 | if (this.gravity != gravity) {
60 | this.gravity = gravity;
61 | requestLayout();
62 | }
63 | }
64 |
65 | private int layoutFlowingViews(int width) {
66 | int gravity = GravityCompat.getAbsoluteGravity(this.gravity, ViewCompat.getLayoutDirection(this));
67 | if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
68 | return layoutFlowingViewsRight(width);
69 | } else {
70 | return layoutFlowingViewsLeft(width);
71 | }
72 | }
73 |
74 | private void relayoutLine(List currentLine) {
75 | if (currentLine.size() < 2)
76 | return;
77 |
78 | int maxY = Integer.MIN_VALUE, minY = currentLine.get(0).getTop() - ((LayoutParams) currentLine.get(0).getLayoutParams()).topMargin;
79 | for (View view : currentLine) {
80 | LayoutParams params = (LayoutParams) view.getLayoutParams();
81 | maxY = Math.max(maxY, view.getBottom() + params.bottomMargin);
82 | }
83 | for (View view : currentLine) {
84 | LayoutParams params = (LayoutParams) view.getLayoutParams();
85 | if ((params.gravity & Gravity.TOP) == Gravity.TOP) {
86 | view.layout(view.getLeft(), minY + params.topMargin, view.getRight(), minY + view.getHeight() + params.topMargin);
87 | } else if ((params.gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
88 | view.layout(view.getLeft(), maxY - view.getHeight() - params.bottomMargin, view.getRight(), maxY - params.bottomMargin);
89 | } else if ((params.gravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
90 | int top = Math.max((maxY + minY) / 2 - view.getHeight() / 2, minY + params.topMargin);
91 | int bottom = top + view.getHeight();
92 | view.layout(view.getLeft(), top, view.getRight(), bottom);
93 | }
94 | }
95 | }
96 |
97 | private int layoutFlowingViewsRight(int width) {
98 | int currentX = width - getPaddingRight();
99 | int currentY = getPaddingTop();
100 | int nextY = getPaddingTop();
101 | List currentLine = new ArrayList<>();
102 | for (int i = 0; i < getChildCount(); i++) {
103 | View child = getChildAt(i);
104 | LayoutParams params = (LayoutParams) child.getLayoutParams();
105 | if (child.getVisibility() != GONE) {
106 | if (currentX != width - getPaddingRight() && currentX - params.leftMargin - child.getMeasuredWidth() - params.rightMargin < getPaddingLeft()) {
107 | currentX = width - getPaddingRight();
108 | currentY = nextY;
109 | relayoutLine(currentLine);
110 | currentLine.clear();
111 | }
112 |
113 | currentLine.add(0, child);
114 | child.layout(currentX - params.rightMargin - child.getMeasuredWidth(), currentY + params.topMargin, currentX - params.rightMargin, currentY + params.topMargin + child.getMeasuredHeight());
115 | currentX -= params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
116 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
117 | }
118 | }
119 | relayoutLine(currentLine);
120 | return nextY + getPaddingBottom();
121 | }
122 |
123 | private int layoutFlowingViewsLeft(int width) {
124 | int currentX = getPaddingLeft();
125 | int currentY = getPaddingTop();
126 | int nextY = getPaddingTop();
127 | List currentLine = new ArrayList<>();
128 | for (int i = 0; i < getChildCount(); i++) {
129 | View child = getChildAt(i);
130 | LayoutParams params = (LayoutParams) child.getLayoutParams();
131 | if (child.getVisibility() != GONE) {
132 | if (currentX != getPaddingLeft() && currentX + params.leftMargin + child.getMeasuredWidth() + params.rightMargin > width - getPaddingRight()) {
133 | currentX = getPaddingLeft();
134 | currentY = nextY;
135 | relayoutLine(currentLine);
136 | currentLine.clear();
137 | }
138 |
139 | currentLine.add(child);
140 | child.layout(currentX + params.leftMargin, currentY + params.topMargin, currentX + params.leftMargin + child.getMeasuredWidth(), currentY + params.topMargin + child.getMeasuredHeight());
141 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
142 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
143 | }
144 | }
145 | relayoutLine(currentLine);
146 | return nextY + getPaddingBottom();
147 | }
148 |
149 | @Override
150 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
151 | super.onLayout(changed, left, top, right, bottom);
152 | layoutFlowingViews(getWidth());
153 | }
154 |
155 |
156 | // -------------------------------
157 | // layout params
158 | // -------------------------------
159 |
160 | @Override
161 | protected LayoutParams generateDefaultLayoutParams() {
162 | return new LayoutParams(super.generateDefaultLayoutParams());
163 | }
164 |
165 | @Override
166 | public LayoutParams generateLayoutParams(AttributeSet attrs) {
167 | return new LayoutParams(getContext(), attrs);
168 | }
169 |
170 | @Override
171 | protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
172 | return new LayoutParams(p);
173 | }
174 |
175 | public static class LayoutParams extends android.widget.FrameLayout.LayoutParams {
176 | public LayoutParams(Context c, AttributeSet attrs) {
177 | super(c, attrs);
178 | }
179 |
180 | public LayoutParams(int w, int h) {
181 | super(w, h);
182 | }
183 |
184 | /**
185 | * {@inheritDoc}
186 | */
187 | public LayoutParams(ViewGroup.LayoutParams source) {
188 | super(source);
189 | }
190 |
191 | /**
192 | * {@inheritDoc}
193 | */
194 | public LayoutParams(ViewGroup.MarginLayoutParams source) {
195 | super(source);
196 | }
197 |
198 | public LayoutParams(android.widget.FrameLayout.LayoutParams source) {
199 | super((MarginLayoutParams) source);
200 | }
201 |
202 | public LayoutParams(LayoutParams source) {
203 | super((MarginLayoutParams) source);
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/flowlayout/src/main/java/com/github/zieiony/guide/flowlayout/FlowLayout2.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.flowlayout;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.view.Gravity;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import androidx.core.view.GravityCompat;
13 | import androidx.core.view.ViewCompat;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * FlowLayout layouts its children from left to right, top to bottom.
20 | */
21 | public class FlowLayout2 extends android.widget.FrameLayout {
22 |
23 | private int gravity;
24 |
25 | public FlowLayout2(Context context) {
26 | super(context, null, R.attr.guide_flowLayoutStyle);
27 | initFlowLayout(null, R.attr.guide_flowLayoutStyle);
28 | }
29 |
30 | public FlowLayout2(Context context, AttributeSet attrs) {
31 | super(context, attrs, R.attr.guide_flowLayoutStyle);
32 | initFlowLayout(attrs, R.attr.guide_flowLayoutStyle);
33 | }
34 |
35 | public FlowLayout2(Context context, AttributeSet attrs, int defStyleAttr) {
36 | super(context, attrs, defStyleAttr);
37 | initFlowLayout(attrs, defStyleAttr);
38 | }
39 |
40 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
41 | public FlowLayout2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
42 | super(context, attrs, defStyleAttr, defStyleRes);
43 | initFlowLayout(attrs, defStyleAttr);
44 | }
45 |
46 | private void initFlowLayout(AttributeSet attrs, int defStyleAttr) {
47 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout2, defStyleAttr, 0);
48 |
49 | gravity = a.getInt(R.styleable.FlowLayout_android_gravity, Gravity.START);
50 |
51 | a.recycle();
52 | }
53 |
54 | public int getGravity() {
55 | return gravity;
56 | }
57 |
58 | public void setGravity(int gravity) {
59 | if (this.gravity != gravity) {
60 | this.gravity = gravity;
61 | requestLayout();
62 | }
63 | }
64 |
65 | private void layoutFlowingViews(int width) {
66 | int gravity = GravityCompat.getAbsoluteGravity(this.gravity, ViewCompat.getLayoutDirection(this));
67 | if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
68 | layoutFlowingViewsRight(width);
69 | } else {
70 | layoutFlowingViewsLeft(width);
71 | }
72 | }
73 |
74 | private void relayoutLine(List currentLine) {
75 | if (currentLine.size() < 2)
76 | return;
77 |
78 | int maxY = Integer.MIN_VALUE, minY = currentLine.get(0).getTop() - ((LayoutParams) currentLine.get(0).getLayoutParams()).topMargin;
79 | for (View view : currentLine) {
80 | LayoutParams params = (LayoutParams) view.getLayoutParams();
81 | maxY = Math.max(maxY, view.getBottom() + params.bottomMargin);
82 | }
83 | for (View view : currentLine) {
84 | LayoutParams params = (LayoutParams) view.getLayoutParams();
85 | if ((params.gravity & Gravity.TOP) == Gravity.TOP) {
86 | view.layout(view.getLeft(), minY + params.topMargin, view.getRight(), minY + view.getHeight() + params.topMargin);
87 | } else if ((params.gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
88 | view.layout(view.getLeft(), maxY - view.getHeight() - params.bottomMargin, view.getRight(), maxY - params.bottomMargin);
89 | } else if ((params.gravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
90 | int top = Math.max((maxY + minY) / 2 - view.getHeight() / 2, minY + params.topMargin);
91 | int bottom = top + view.getHeight();
92 | view.layout(view.getLeft(), top, view.getRight(), bottom);
93 | }
94 | }
95 | }
96 |
97 | private void layoutFlowingViewsRight(int width) {
98 | int currentX = width - getPaddingRight();
99 | int currentY = getPaddingTop();
100 | int nextY = getPaddingTop();
101 | List currentLine = new ArrayList<>();
102 | for (int i = 0; i < getChildCount(); i++) {
103 | View child = getChildAt(i);
104 | LayoutParams params = (LayoutParams) child.getLayoutParams();
105 | if (child.getVisibility() != GONE) {
106 | if (currentX != width - getPaddingRight() && currentX - params.leftMargin - child.getMeasuredWidth() - params.rightMargin < getPaddingLeft()) {
107 | currentX = width - getPaddingRight();
108 | currentY = nextY;
109 | relayoutLine(currentLine);
110 | currentLine.clear();
111 | }
112 |
113 | currentLine.add(0, child);
114 | child.layout(currentX - params.rightMargin - child.getMeasuredWidth(), currentY + params.topMargin, currentX - params.rightMargin, currentY + params.topMargin + child.getMeasuredHeight());
115 | currentX -= params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
116 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
117 | }
118 | }
119 | relayoutLine(currentLine);
120 | }
121 |
122 | private void layoutFlowingViewsLeft(int width) {
123 | int currentX = getPaddingLeft();
124 | int currentY = getPaddingTop();
125 | int nextY = getPaddingTop();
126 | List currentLine = new ArrayList<>();
127 | for (int i = 0; i < getChildCount(); i++) {
128 | View child = getChildAt(i);
129 | LayoutParams params = (LayoutParams) child.getLayoutParams();
130 | if (child.getVisibility() != GONE) {
131 | if (currentX != getPaddingLeft() && currentX + params.leftMargin + child.getMeasuredWidth() + params.rightMargin > width - getPaddingRight()) {
132 | currentX = getPaddingLeft();
133 | currentY = nextY;
134 | relayoutLine(currentLine);
135 | currentLine.clear();
136 | }
137 |
138 | currentLine.add(child);
139 | child.layout(currentX + params.leftMargin, currentY + params.topMargin, currentX + params.leftMargin + child.getMeasuredWidth(), currentY + params.topMargin + child.getMeasuredHeight());
140 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
141 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
142 | }
143 | }
144 | relayoutLine(currentLine);
145 | }
146 |
147 | private int measureWidth(){
148 | int currentX = getPaddingLeft();
149 | for (int i = 0; i < getChildCount(); i++) {
150 | View child = getChildAt(i);
151 | LayoutParams params = (LayoutParams) child.getLayoutParams();
152 | if (child.getVisibility() != GONE) {
153 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
154 | }
155 | }
156 |
157 | return currentX+getPaddingRight();
158 | }
159 |
160 | private int measureHeight(int width){
161 | int currentX = getPaddingLeft();
162 | int currentY = getPaddingTop();
163 | int nextY = getPaddingTop();
164 | for (int i = 0; i < getChildCount(); i++) {
165 | View child = getChildAt(i);
166 | LayoutParams params = (LayoutParams) child.getLayoutParams();
167 | if (child.getVisibility() != GONE) {
168 | if (currentX != getPaddingLeft() && currentX + params.leftMargin + child.getMeasuredWidth() + params.rightMargin > width - getPaddingRight()) {
169 | currentX = getPaddingLeft();
170 | currentY = nextY;
171 | }
172 |
173 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
174 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
175 | }
176 | }
177 | return nextY + getPaddingBottom();
178 | }
179 |
180 | protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
181 | for (int i = 0; i < getChildCount(); ++i) {
182 | final View child = getChildAt(i);
183 | if (child.getVisibility() != GONE)
184 | measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
185 | }
186 | }
187 |
188 | @Override
189 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
190 | super.onLayout(changed, left, top, right, bottom);
191 | layoutFlowingViews(getWidth());
192 | }
193 |
194 | @Override
195 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
196 | measureChildren(widthMeasureSpec, heightMeasureSpec);
197 |
198 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
199 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
200 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
201 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
202 | int width;
203 | int height;
204 |
205 | if (widthMode == MeasureSpec.EXACTLY) {
206 | width = widthSize;
207 | } else {
208 | width = measureWidth();
209 |
210 | width = Math.max(width, getSuggestedMinimumWidth());
211 | if (widthMode == MeasureSpec.AT_MOST)
212 | width = Math.min(widthSize, width);
213 | }
214 |
215 | if (heightMode == MeasureSpec.EXACTLY) {
216 | height = heightSize;
217 | } else {
218 | height = measureHeight(width);
219 |
220 | height = Math.max(height, getSuggestedMinimumHeight());
221 | if (heightMode == MeasureSpec.AT_MOST)
222 | height = Math.min(height, heightSize);
223 | }
224 |
225 | setMeasuredDimension(width, height);
226 | }
227 |
228 | // -------------------------------
229 | // layout params
230 | // -------------------------------
231 |
232 | @Override
233 | protected LayoutParams generateDefaultLayoutParams() {
234 | return new LayoutParams(super.generateDefaultLayoutParams());
235 | }
236 |
237 | @Override
238 | public LayoutParams generateLayoutParams(AttributeSet attrs) {
239 | return new LayoutParams(getContext(), attrs);
240 | }
241 |
242 | @Override
243 | protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
244 | return new LayoutParams(p);
245 | }
246 |
247 | public static class LayoutParams extends android.widget.FrameLayout.LayoutParams {
248 | public LayoutParams(Context c, AttributeSet attrs) {
249 | super(c, attrs);
250 | }
251 |
252 | public LayoutParams(int w, int h) {
253 | super(w, h);
254 | }
255 |
256 | /**
257 | * {@inheritDoc}
258 | */
259 | public LayoutParams(ViewGroup.LayoutParams source) {
260 | super(source);
261 | }
262 |
263 | /**
264 | * {@inheritDoc}
265 | */
266 | public LayoutParams(MarginLayoutParams source) {
267 | super(source);
268 | }
269 |
270 | public LayoutParams(android.widget.FrameLayout.LayoutParams source) {
271 | super((MarginLayoutParams) source);
272 | }
273 |
274 | public LayoutParams(LayoutParams source) {
275 | super((MarginLayoutParams) source);
276 | }
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/flowlayout/src/main/java/com/github/zieiony/guide/flowlayout/FlowLayout3.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.flowlayout;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.view.Gravity;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import androidx.core.view.GravityCompat;
13 | import androidx.core.view.ViewCompat;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * FlowLayout layouts its children from left to right, top to bottom.
20 | */
21 | public class FlowLayout3 extends android.widget.FrameLayout {
22 |
23 | private int gravity;
24 |
25 | public FlowLayout3(Context context) {
26 | super(context, null, R.attr.guide_flowLayoutStyle);
27 | initFlowLayout(null, R.attr.guide_flowLayoutStyle);
28 | }
29 |
30 | public FlowLayout3(Context context, AttributeSet attrs) {
31 | super(context, attrs, R.attr.guide_flowLayoutStyle);
32 | initFlowLayout(attrs, R.attr.guide_flowLayoutStyle);
33 | }
34 |
35 | public FlowLayout3(Context context, AttributeSet attrs, int defStyleAttr) {
36 | super(context, attrs, defStyleAttr);
37 | initFlowLayout(attrs, defStyleAttr);
38 | }
39 |
40 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
41 | public FlowLayout3(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
42 | super(context, attrs, defStyleAttr, defStyleRes);
43 | initFlowLayout(attrs, defStyleAttr);
44 | }
45 |
46 | private void initFlowLayout(AttributeSet attrs, int defStyleAttr) {
47 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout3, defStyleAttr, 0);
48 |
49 | gravity = a.getInt(R.styleable.FlowLayout_android_gravity, Gravity.START);
50 |
51 | a.recycle();
52 | }
53 |
54 | public int getGravity() {
55 | return gravity;
56 | }
57 |
58 | public void setGravity(int gravity) {
59 | if (this.gravity != gravity) {
60 | this.gravity = gravity;
61 | requestLayout();
62 | }
63 | }
64 |
65 | private void layoutFlowingViews(int width) {
66 | int gravity = GravityCompat.getAbsoluteGravity(this.gravity, ViewCompat.getLayoutDirection(this));
67 | if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
68 | layoutFlowingViewsRight(width);
69 | } else {
70 | layoutFlowingViewsLeft(width);
71 | }
72 | }
73 |
74 | private void relayoutLine(List currentLine) {
75 | if (currentLine.size() < 2)
76 | return;
77 |
78 | int maxY = Integer.MIN_VALUE, minY = currentLine.get(0).getTop() - ((LayoutParams) currentLine.get(0).getLayoutParams()).topMargin;
79 | for (View view : currentLine) {
80 | LayoutParams params = (LayoutParams) view.getLayoutParams();
81 | maxY = Math.max(maxY, view.getBottom() + params.bottomMargin);
82 | }
83 | for (View view : currentLine) {
84 | LayoutParams params = (LayoutParams) view.getLayoutParams();
85 | if ((params.gravity & Gravity.TOP) == Gravity.TOP) {
86 | view.layout(view.getLeft(), minY + params.topMargin, view.getRight(), minY + view.getHeight() + params.topMargin);
87 | } else if ((params.gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
88 | view.layout(view.getLeft(), maxY - view.getHeight() - params.bottomMargin, view.getRight(), maxY - params.bottomMargin);
89 | } else if ((params.gravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
90 | int top = Math.max((maxY + minY) / 2 - view.getHeight() / 2, minY + params.topMargin);
91 | int bottom = top + view.getHeight();
92 | view.layout(view.getLeft(), top, view.getRight(), bottom);
93 | }
94 | }
95 | }
96 |
97 | private void layoutFlowingViewsRight(int width) {
98 | int currentX = width - getPaddingRight();
99 | int currentY = getPaddingTop();
100 | int nextY = getPaddingTop();
101 | List currentLine = new ArrayList<>();
102 | for (int i = 0; i < getChildCount(); i++) {
103 | View child = getChildAt(i);
104 | LayoutParams params = (LayoutParams) child.getLayoutParams();
105 | if (child.getVisibility() != GONE) {
106 | if (currentX != width - getPaddingRight() && currentX - params.leftMargin - child.getMeasuredWidth() - params.rightMargin < getPaddingLeft()) {
107 | currentX = width - getPaddingRight();
108 | currentY = nextY;
109 | relayoutLine(currentLine);
110 | currentLine.clear();
111 | }
112 |
113 | currentLine.add(0, child);
114 | int left = params.fill ? getPaddingLeft() + params.leftMargin : currentX - params.rightMargin - child.getMeasuredWidth();
115 | child.layout(left, currentY + params.topMargin, currentX - params.rightMargin, currentY + params.topMargin + child.getMeasuredHeight());
116 | currentX -= params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
117 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
118 |
119 | if (params.fill) {
120 | currentX = width - getPaddingRight();
121 | currentY = nextY;
122 | relayoutLine(currentLine);
123 | currentLine.clear();
124 | }
125 | }
126 | }
127 | relayoutLine(currentLine);
128 | }
129 |
130 | private void layoutFlowingViewsLeft(int width) {
131 | int currentX = getPaddingLeft();
132 | int currentY = getPaddingTop();
133 | int nextY = getPaddingTop();
134 | List currentLine = new ArrayList<>();
135 | for (int i = 0; i < getChildCount(); i++) {
136 | View child = getChildAt(i);
137 | LayoutParams params = (LayoutParams) child.getLayoutParams();
138 | if (child.getVisibility() != GONE) {
139 | if (currentX != getPaddingLeft() && currentX + params.leftMargin + child.getMeasuredWidth() + params.rightMargin > width - getPaddingRight()) {
140 | currentX = getPaddingLeft();
141 | currentY = nextY;
142 | relayoutLine(currentLine);
143 | currentLine.clear();
144 | }
145 |
146 | currentLine.add(child);
147 | int right = params.fill ? width - getPaddingRight() - params.rightMargin : currentX + params.leftMargin + child.getMeasuredWidth();
148 | child.layout(currentX + params.leftMargin, currentY + params.topMargin, right, currentY + params.topMargin + child.getMeasuredHeight());
149 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
150 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
151 |
152 | if (params.fill) {
153 | currentX = getPaddingLeft();
154 | currentY = nextY;
155 | relayoutLine(currentLine);
156 | currentLine.clear();
157 | }
158 | }
159 | }
160 | relayoutLine(currentLine);
161 | }
162 |
163 | private int measureWidth() {
164 | int currentX = getPaddingLeft();
165 | for (int i = 0; i < getChildCount(); i++) {
166 | View child = getChildAt(i);
167 | LayoutParams params = (LayoutParams) child.getLayoutParams();
168 | if (child.getVisibility() != GONE) {
169 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
170 | }
171 | }
172 |
173 | return currentX + getPaddingRight();
174 | }
175 |
176 | private int measureHeight(int width) {
177 | int currentX = getPaddingLeft();
178 | int currentY = getPaddingTop();
179 | int nextY = getPaddingTop();
180 | for (int i = 0; i < getChildCount(); i++) {
181 | View child = getChildAt(i);
182 | LayoutParams params = (LayoutParams) child.getLayoutParams();
183 | if (child.getVisibility() != GONE) {
184 | if (currentX != getPaddingLeft() && currentX + params.leftMargin + child.getMeasuredWidth() + params.rightMargin > width - getPaddingRight()) {
185 | currentX = getPaddingLeft();
186 | currentY = nextY;
187 | }
188 |
189 | currentX += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
190 | nextY = Math.max(nextY, currentY + params.topMargin + child.getMeasuredHeight() + params.bottomMargin);
191 |
192 | if (params.fill) {
193 | currentX = getPaddingLeft();
194 | currentY = nextY;
195 | }
196 | }
197 | }
198 | return nextY + getPaddingBottom();
199 | }
200 |
201 | protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
202 | for (int i = 0; i < getChildCount(); ++i) {
203 | final View child = getChildAt(i);
204 | if (child.getVisibility() != GONE)
205 | measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
206 | }
207 | }
208 |
209 | @Override
210 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
211 | super.onLayout(changed, left, top, right, bottom);
212 | layoutFlowingViews(getWidth());
213 | }
214 |
215 | @Override
216 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
217 | measureChildren(widthMeasureSpec, heightMeasureSpec);
218 |
219 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
220 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
221 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
222 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
223 | int width;
224 | int height;
225 |
226 | if (widthMode == MeasureSpec.EXACTLY) {
227 | width = widthSize;
228 | } else {
229 | width = measureWidth();
230 |
231 | width = Math.max(width, getSuggestedMinimumWidth());
232 | if (widthMode == MeasureSpec.AT_MOST)
233 | width = Math.min(widthSize, width);
234 | }
235 |
236 | if (heightMode == MeasureSpec.EXACTLY) {
237 | height = heightSize;
238 | } else {
239 | height = measureHeight(width);
240 |
241 | height = Math.max(height, getSuggestedMinimumHeight());
242 | if (heightMode == MeasureSpec.AT_MOST)
243 | height = Math.min(height, heightSize);
244 | }
245 |
246 | setMeasuredDimension(width, height);
247 | }
248 |
249 | // -------------------------------
250 | // layout params
251 | // -------------------------------
252 |
253 | @Override
254 | protected LayoutParams generateDefaultLayoutParams() {
255 | return new LayoutParams(super.generateDefaultLayoutParams());
256 | }
257 |
258 | @Override
259 | public LayoutParams generateLayoutParams(AttributeSet attrs) {
260 | return new LayoutParams(getContext(), attrs);
261 | }
262 |
263 | @Override
264 | protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
265 | return new LayoutParams(p);
266 | }
267 |
268 | public static class LayoutParams extends android.widget.FrameLayout.LayoutParams {
269 | private boolean fill = false;
270 |
271 | public LayoutParams(Context c, AttributeSet attrs) {
272 | super(c, attrs);
273 |
274 | TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout3_Layout);
275 |
276 | fill = a.getBoolean(R.styleable.FlowLayout3_Layout_guide_layout_fill, false);
277 |
278 | a.recycle();
279 | }
280 |
281 | public LayoutParams(int w, int h) {
282 | super(w, h);
283 | }
284 |
285 | /**
286 | * {@inheritDoc}
287 | */
288 | public LayoutParams(ViewGroup.LayoutParams source) {
289 | super(source);
290 | }
291 |
292 | /**
293 | * {@inheritDoc}
294 | */
295 | public LayoutParams(MarginLayoutParams source) {
296 | super(source);
297 | }
298 |
299 | public LayoutParams(android.widget.FrameLayout.LayoutParams source) {
300 | super((MarginLayoutParams) source);
301 | }
302 |
303 | public LayoutParams(LayoutParams source) {
304 | super((MarginLayoutParams) source);
305 | fill = source.fill;
306 | }
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/flowlayout/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Aug 18 21:33:59 CEST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/images/chartview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/images/chartview.png
--------------------------------------------------------------------------------
/images/distributionfunction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/images/distributionfunction.png
--------------------------------------------------------------------------------
/images/flowlayout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/images/flowlayout.png
--------------------------------------------------------------------------------
/images/itemdrawable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/images/itemdrawable.png
--------------------------------------------------------------------------------
/images/landscapedrawable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/images/landscapedrawable.png
--------------------------------------------------------------------------------
/images/moodtoggle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/images/moodtoggle.png
--------------------------------------------------------------------------------
/invalidedittext/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/invalidedittext/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 |
9 | defaultConfig {
10 | minSdkVersion 14
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
19 | implementation 'androidx.appcompat:appcompat:1.1.0'
20 | }
21 |
--------------------------------------------------------------------------------
/invalidedittext/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/invalidedittext/src/main/java/com/github/zieiony/guide/invalidedittext/InvalidEditText.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.invalidedittext
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import androidx.appcompat.widget.AppCompatEditText
6 |
7 | class InvalidEditText @JvmOverloads constructor(
8 | context: Context, attrs: AttributeSet? = null
9 | ) : AppCompatEditText(context, attrs) {
10 |
11 | private var _valid: Boolean = false
12 | var valid
13 | get() = _valid
14 | set(value) {
15 | if (_valid == value)
16 | return
17 | _valid = value
18 | refreshDrawableState()
19 | }
20 |
21 | override fun onCreateDrawableState(extraSpace: Int): IntArray {
22 | if (!valid) {
23 | val drawableState = super.onCreateDrawableState(extraSpace + 1)
24 | drawableState[drawableState.size - 1] = R.attr.guide_state_invalid
25 | return drawableState
26 | }
27 | return super.onCreateDrawableState(extraSpace)
28 | }
29 | }
--------------------------------------------------------------------------------
/invalidedittext/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/landscapedrawable/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/landscapedrawable/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 29
6 | buildToolsVersion "29.0.2"
7 |
8 | defaultConfig {
9 | minSdkVersion 14
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | compileOptions {
15 | sourceCompatibility JavaVersion.VERSION_1_8
16 | targetCompatibility JavaVersion.VERSION_1_8
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
22 | implementation 'androidx.appcompat:appcompat:1.1.0'
23 | }
24 |
--------------------------------------------------------------------------------
/landscapedrawable/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/Cloud.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 | import android.graphics.RadialGradient
6 | import android.graphics.Shader
7 | import java.util.*
8 | import kotlin.math.min
9 | import kotlin.math.pow
10 |
11 | class Cloud : LandscapeItem {
12 |
13 | private class Puff(val x: Float, val y: Float, val size: Float, val shader: RadialGradient)
14 |
15 | var z: Float = 0f
16 |
17 | private var _color = 0
18 | var color: Int
19 | get() = _color
20 | set(value) {
21 | _color = value
22 | puffs.clear()
23 | }
24 |
25 | private var _puffCount = 8
26 | var puffCount: Int
27 | get() = _puffCount
28 | set(value) {
29 | _puffCount = value
30 | puffs.clear()
31 | }
32 |
33 | private val random: Random
34 | private val puffs = ArrayList()
35 |
36 | constructor(puffCount: Int, color: Int, random: Random, paint: Paint) : super(paint) {
37 | this.puffCount = puffCount
38 | this.color = color
39 | this.random = random
40 | }
41 |
42 | constructor(puffCount: Int, color: Int) : this(puffCount, color, Random(), Paint(Paint.ANTI_ALIAS_FLAG))
43 |
44 | override fun onSizeChanged() {
45 | puffs.clear()
46 | }
47 |
48 | fun init() {
49 | if (puffs.isEmpty()) {
50 | val maxPuffSize = min(width, height) / 2
51 | for (i in 0 until puffCount) {
52 | val size = random.nextFloat() * maxPuffSize / 2 + maxPuffSize / 2
53 | val x = nextPos() * (width - 2 * size) + size
54 | val y = (nextPos() * (height - 2 * size) + size) / 0.8f
55 | val shader = RadialGradient(x, y - size / 2, size, intArrayOf(-0x1, color), floatArrayOf(0.9f, 1f), Shader.TileMode.CLAMP)
56 | puffs.add(Puff(x, y, size, shader))
57 | }
58 | puffs.sortWith(Comparator { o1, o2 ->
59 | val dist1 = MathUtils.dist(width / 2, height / 2, o2.x, o2.y)
60 | val dist2 = MathUtils.dist(width / 2, height / 2, o1.x, o1.y)
61 | (dist1 - dist2).toInt()
62 | })
63 | }
64 | }
65 |
66 | private fun nextPos(): Float {
67 | return (((random.nextDouble() - 0.5).pow(3) + 0.125) * 4).toFloat()
68 | }
69 |
70 | override fun onDraw(canvas: Canvas) {
71 | init()
72 |
73 | canvas.save()
74 | canvas.scale(1f, 0.8f)
75 | for (p in puffs) {
76 | paint.shader = p.shader
77 | canvas.drawCircle(p.x, p.y, p.size, paint)
78 | }
79 | canvas.restore()
80 | }
81 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/ItemDrawable.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.ColorFilter
5 | import android.graphics.PixelFormat
6 | import android.graphics.Rect
7 | import android.graphics.drawable.Drawable
8 |
9 |
10 | class ItemDrawable(val item: LandscapeItem) : Drawable() {
11 |
12 | override fun onBoundsChange(bounds: Rect) {
13 | super.onBoundsChange(bounds)
14 | item.setSize(bounds.width().toFloat(), bounds.height().toFloat())
15 | }
16 |
17 | override fun draw(canvas: Canvas) {
18 | item.onDraw(canvas)
19 | }
20 |
21 | override fun setAlpha(alpha: Int) {
22 | // not supported
23 | }
24 |
25 | override fun getOpacity() = PixelFormat.TRANSLUCENT
26 |
27 | override fun setColorFilter(colorFilter: ColorFilter?) {
28 | // not supported
29 | }
30 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/Land.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.*
4 | import java.util.*
5 |
6 | class Land : LandscapeItem {
7 | private var _color = 0
8 | var color: Int
9 | get() = _color
10 | set(value) {
11 | _color = value
12 | shader = null
13 | }
14 |
15 | private var _color2 = 0
16 | var color2: Int
17 | get() = _color2
18 | set(value) {
19 | _color2 = value
20 | shader = null
21 | }
22 |
23 | private var _fluctuation = 0.0f
24 | var fluctuation: Float
25 | get() = _fluctuation
26 | set(value) {
27 | _fluctuation = value
28 | path.reset()
29 | trees.clear()
30 | shader = null
31 | }
32 |
33 | private var _treeHeight = 0.0f
34 | var treeHeight: Float
35 | get() = _treeHeight
36 | set(value) {
37 | _treeHeight = value
38 | path.reset()
39 | trees.clear()
40 | shader = null
41 | }
42 |
43 | private var _treeRandom = 0.0f
44 | var treeRandom: Float
45 | get() = _treeRandom
46 | set(value) {
47 | _treeRandom = value
48 | path.reset()
49 | trees.clear()
50 | }
51 |
52 | var wind: Float = 0f
53 |
54 | private val random: Random
55 | private val path = Path()
56 | private val trees = ArrayList()
57 | private var shader: LinearGradient? = null
58 |
59 | constructor(color: Int, color2: Int, fluctuation: Float, treeHeight: Float, treeRandom: Float, random: Random, paint: Paint) : super(paint) {
60 | this.color = color
61 | this.color2 = color2
62 | this.fluctuation = fluctuation
63 | this.treeHeight = treeHeight
64 | this.treeRandom = treeRandom
65 | this.random = random
66 | }
67 |
68 | constructor(color: Int, color2: Int, fluctuation: Float, treeHeight: Float = fluctuation, treeRandom: Float = 0.8f)
69 | : this(color, color2, fluctuation, treeHeight, treeRandom, Random(), Paint(Paint.ANTI_ALIAS_FLAG))
70 |
71 | override fun onSizeChanged() {
72 | path.reset()
73 | trees.clear()
74 | shader = null
75 | }
76 |
77 | fun init() {
78 | if (path.isEmpty) {
79 | path.moveTo(0f, height)
80 | path.lineTo(width, height)
81 | var prevX: Float = width
82 | var prevY = (treeHeight + Math.random() * fluctuation).toFloat()
83 | path.lineTo(prevX, prevY)
84 |
85 | val segments = (width / treeHeight / 3).toInt()
86 | val treeWidth = treeHeight * 2 / 3f
87 | for (i in 0..segments) {
88 | val x: Float = (width * (segments - i) / segments)
89 | val y = (treeHeight + Math.random() * fluctuation).toFloat()
90 | val x33 = MathUtils.lerp(prevX, x, 0.33f)
91 | val x67 = MathUtils.lerp(prevX, x, 0.67f)
92 | path.cubicTo(x33, prevY, x67, y, x, y)
93 | if (random.nextFloat() > treeRandom) {
94 | val tree = Tree(wind, paint)
95 | tree.x = prevX - treeWidth / 2
96 | tree.y = prevY - treeHeight
97 | tree.setSize(treeWidth, treeHeight)
98 | trees.add(tree)
99 | }
100 | if (random.nextFloat() > treeRandom) {
101 | val y33 = MathUtils.lerp(prevY, y, 0.33f)
102 | val tree = Tree(wind, paint)
103 | tree.x = x33 - treeWidth / 2
104 | tree.y = y33 - treeHeight
105 | tree.setSize(treeWidth, treeHeight)
106 | trees.add(tree)
107 | }
108 | if (random.nextFloat() > treeRandom) {
109 | val y67 = MathUtils.lerp(prevY, y, 0.67f)
110 | val tree = Tree(wind, paint)
111 | tree.x = x67 - treeWidth / 2
112 | tree.y = y67 - treeHeight
113 | tree.setSize(treeWidth, treeHeight)
114 | trees.add(tree)
115 | }
116 | if (random.nextFloat() > treeRandom) {
117 | val tree = Tree(wind, paint)
118 | tree.x = x - treeWidth / 2
119 | tree.y = y - treeHeight
120 | tree.setSize(treeWidth, treeHeight)
121 | trees.add(tree)
122 | }
123 | prevX = x
124 | prevY = y
125 | }
126 | path.close()
127 | }
128 |
129 | if (shader == null)
130 | shader = LinearGradient(0f, height, 0f, treeHeight, color, color2, Shader.TileMode.CLAMP)
131 | }
132 |
133 | override fun onDraw(canvas: Canvas) {
134 | init()
135 |
136 | if (wind > 0) {
137 | for (t in trees)
138 | t.wind = wind
139 | }
140 |
141 | paint.color = color2
142 | paint.shader = shader
143 | for (t in trees)
144 | t.draw(canvas)
145 | canvas.drawPath(path, paint)
146 | paint.shader = null
147 | }
148 |
149 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/LandscapeDrawable.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.animation.ArgbEvaluator
4 | import android.graphics.*
5 | import android.graphics.drawable.Drawable
6 | import com.github.zieiony.guide.landscapedrawable.MathUtils.randomForce
7 | import java.util.*
8 |
9 |
10 | class LandscapeDrawable(
11 | var starCount: Int,
12 | var starSize: Float,
13 | var starColor: Int,
14 | var cloudCount: Int,
15 | var cloudSize: Float,
16 | var cloudColor: Int,
17 | var windStrength: Float,
18 | var maxWind: Float,
19 | var sunSize: Float,
20 | var sunColor: Int,
21 | var skyColor: Int,
22 | var skyHeight: Float,
23 | var fogColor: Int,
24 | var landscapeColor: Int,
25 | var planesCount: Int,
26 | var landscapeHeight: Float,
27 | var treeHeight: Float
28 | ) : Drawable() {
29 |
30 | private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
31 | private val random = Random()
32 | private val argbEvaluator = ArgbEvaluator()
33 |
34 | private var landscapes = mutableListOf()
35 |
36 | private val stars = Stars(starCount, starSize, starColor, random, paint)
37 |
38 | private var sunX = 0f
39 | private var sunY = 0f
40 | private var wind = 0f
41 |
42 | private val clouds = Sky(cloudCount, cloudSize, cloudColor, 8, 0f, random, paint)
43 |
44 | private fun init() {
45 | landscapes.clear()
46 | for (i in 0 until planesCount) {
47 | val currTreeHeight = MathUtils.lerp(treeHeight, treeHeight / 2, i / (planesCount - 1f))
48 | val height = landscapeHeight * (i + 1) / (planesCount + 1f)
49 | val fluctuation = height / 2
50 | val color1 = argbEvaluator.evaluate(i.toFloat() / planesCount, landscapeColor, fogColor) as Int
51 | val color2 = argbEvaluator.evaluate((i + 1).toFloat() / planesCount, landscapeColor, fogColor) as Int
52 | val land = Land(color1, color2, fluctuation, currTreeHeight, 0.8f, random, paint)
53 | land.y = bounds.height() - height - currTreeHeight
54 | land.setSize(bounds.width().toFloat(), height + currTreeHeight)
55 | landscapes.add(0, land)
56 | }
57 |
58 | stars.setSize(bounds.width().toFloat(), skyHeight)
59 | clouds.setSize(bounds.width().toFloat(), skyHeight)
60 | sunX = random.nextFloat() * (bounds.width() - sunSize * 2) + sunSize
61 | sunY = random.nextFloat() * (skyHeight - sunSize) + sunSize
62 | }
63 |
64 | override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
65 | super.setBounds(left, top, right, bottom)
66 | init()
67 | }
68 |
69 | override fun draw(canvas: Canvas) {
70 | wind += randomForce(windStrength)
71 | wind = MathUtils.constrain(wind, 0f, maxWind)
72 |
73 | paint.shader = LinearGradient(0f, bounds.height() - landscapes[0].height - landscapes[0].fluctuation, 0f, 0f, fogColor, skyColor, Shader.TileMode.CLAMP)
74 | canvas.drawPaint(paint)
75 | paint.shader = null
76 |
77 | stars.draw(canvas)
78 | paint.color = sunColor
79 | canvas.drawCircle(sunX, sunY, sunSize, paint)
80 |
81 | for (l in landscapes) {
82 | l.wind = wind / 3
83 | l.draw(canvas)
84 | }
85 |
86 | clouds.wind = wind
87 | clouds.draw(canvas)
88 |
89 | if (isVisible)
90 | invalidateSelf()
91 | }
92 |
93 | override fun setAlpha(alpha: Int) {
94 | // not supported
95 | }
96 |
97 | override fun getOpacity() = PixelFormat.OPAQUE
98 |
99 | override fun setColorFilter(colorFilter: ColorFilter?) {
100 | // not supported
101 | }
102 |
103 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/LandscapeItem.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 |
6 |
7 | abstract class LandscapeItem(val paint: Paint) {
8 | var x: Float = 0f
9 | var y: Float = 0f
10 | var width: Float = 0f
11 | var height: Float = 0f
12 |
13 | fun setSize(width: Float, height: Float) {
14 | this.width = width
15 | this.height = height
16 | onSizeChanged()
17 | }
18 |
19 | open fun onSizeChanged() {}
20 |
21 | fun draw(canvas: Canvas) {
22 | canvas.save()
23 | canvas.translate(x, y)
24 | onDraw(canvas)
25 | canvas.restore()
26 | }
27 |
28 | abstract fun onDraw(canvas: Canvas)
29 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/MathUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable;
2 |
3 | import java.util.Random;
4 |
5 | final class MathUtils {
6 |
7 | private MathUtils() {
8 | }
9 |
10 | public static float constrain(float amount, float low, float high) {
11 | return amount < low ? low : (amount > high ? high : amount);
12 | }
13 |
14 | public static float dist(float x1, float y1, float x2, float y2) {
15 | final float x = (x2 - x1);
16 | final float y = (y2 - y1);
17 | return (float) Math.hypot(x, y);
18 | }
19 |
20 | public static float lerp(float start, float stop, float amount) {
21 | return start + (stop - start) * amount;
22 | }
23 |
24 | // TODO: maxStart and maxStop were switched
25 | public static float map(float minStart, float minStop, float maxStart, float maxStop, float value) {
26 | return maxStart + (maxStop - maxStart) * ((value - minStart) / (minStop - minStart));
27 | }
28 |
29 | private static Random random = new Random();
30 |
31 | public static float randomSize(float avgValue) {
32 | return random.nextFloat() * avgValue + avgValue / 2;
33 | }
34 |
35 | public static float randomForce(float maxValue) {
36 | return random.nextFloat() * maxValue * 2 - maxValue;
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/Sky.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 | import java.util.*
6 |
7 | class Sky(
8 | val cloudCount: Int,
9 | val cloudSize: Float,
10 | val cloudColor: Int,
11 | val puffCount: Int,
12 | var wind: Float,
13 | val random: Random,
14 | paint: Paint
15 | ) : LandscapeItem(paint) {
16 |
17 | private val clouds: MutableList = ArrayList()
18 | private var time = System.currentTimeMillis()
19 |
20 | init {
21 | clouds.clear()
22 | for (i in 0 until cloudCount) {
23 | val cloud = Cloud(puffCount, cloudColor, random, paint)
24 | cloud.z = random.nextFloat() * 0.25f + 0.75f
25 | cloud.setSize(cloudSize * cloud.z, cloudSize / 2 * cloud.z)
26 | clouds.add(cloud)
27 | }
28 | }
29 |
30 | constructor(cloudCount: Int = 3, cloudSize: Float, cloudColor: Int, puffCount: Int, wind: Float = 0f)
31 | : this(cloudCount, cloudSize, cloudColor, puffCount, wind, Random(), Paint(Paint.ANTI_ALIAS_FLAG))
32 |
33 | override fun onSizeChanged() {
34 | val cloudHeight = cloudSize / 2
35 | for (cloud in clouds) {
36 | cloud.x = random.nextFloat() * (width + cloudSize) - cloudSize
37 | cloud.y = random.nextFloat() * (height - cloudHeight)
38 | }
39 | }
40 |
41 | override fun onDraw(canvas: Canvas) {
42 | val currentTime = System.currentTimeMillis()
43 | val dt = (currentTime - time) / 1000f
44 | for (cloud in clouds) {
45 | cloud.x += wind * dt * cloud.z
46 | if (cloud.x > width)
47 | cloud.x = -cloud.width
48 | }
49 | time = currentTime
50 |
51 | for (cloud in clouds)
52 | cloud.draw(canvas)
53 | }
54 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/Stars.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 | import java.util.*
6 |
7 |
8 | class Stars(
9 | var starCount: Int,
10 | var starSize: Float,
11 | var starColor: Int,
12 | val random: Random,
13 | paint: Paint
14 | ) : LandscapeItem(paint) {
15 |
16 | class Star(val x: Float, val y: Float, val z: Float)
17 |
18 | private var stars = mutableListOf()
19 |
20 | constructor(starCount: Int, starSize: Float, starColor: Int) : this(starCount, starSize, starColor, Random(), Paint(Paint.ANTI_ALIAS_FLAG))
21 |
22 | override fun onSizeChanged() {
23 | stars.clear()
24 | for (i in 0 until starCount) {
25 | val z = random.nextFloat()
26 | stars.add(Star(random.nextFloat() * width, random.nextFloat() * height, z))
27 | }
28 | }
29 |
30 | override fun onDraw(canvas: Canvas) {
31 | paint.color = starColor
32 | for (s in stars) {
33 | paint.alpha = (255f * s.z).toInt()
34 | canvas.drawCircle(s.x, s.y, s.z * starSize, paint)
35 | }
36 | paint.alpha = 255
37 | }
38 | }
--------------------------------------------------------------------------------
/landscapedrawable/src/main/java/com/github/zieiony/guide/landscapedrawable/Tree.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.landscapedrawable
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 | import android.graphics.Path
6 |
7 | class Tree : LandscapeItem {
8 |
9 | private val path = Path()
10 |
11 | private var _wind = 0f
12 | var wind: Float
13 | get() = _wind
14 | set(value) {
15 | if(_wind!=value)
16 | path.reset()
17 | _wind = value
18 | }
19 |
20 | constructor(wind: Float = 0f, paint: Paint) : super(paint) {
21 | this.wind = wind
22 | }
23 |
24 | constructor(wind: Float = 0f) : this(wind, Paint(Paint.ANTI_ALIAS_FLAG))
25 |
26 | override fun onSizeChanged() {
27 | path.reset()
28 | }
29 |
30 | private fun init() {
31 | if(path.isEmpty) {
32 | val cx = width / 2f
33 | path.addRect(cx - width / 10, height * 5 / 6, cx + width / 10, height * 2, Path.Direction.CCW)
34 | path.moveTo(0f, height * 5 / 6)
35 | path.quadTo(cx - width / 6, height * 5 / 12, cx + wind, 0f)
36 | path.quadTo(cx + width / 6, height * 5 / 12, width, height * 5 / 6)
37 | path.close()
38 | }
39 | }
40 |
41 | override fun onDraw(canvas: Canvas) {
42 | init()
43 | canvas.drawPath(path, paint)
44 | }
45 | }
--------------------------------------------------------------------------------
/moodtoggle/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/moodtoggle/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 29
6 | buildToolsVersion "29.0.2"
7 |
8 | defaultConfig {
9 | minSdkVersion 14
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | compileOptions {
15 | sourceCompatibility JavaVersion.VERSION_1_8
16 | targetCompatibility JavaVersion.VERSION_1_8
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
22 | implementation 'androidx.appcompat:appcompat:1.1.0'
23 | }
24 |
--------------------------------------------------------------------------------
/moodtoggle/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/moodtoggle/src/main/java/com/github/zieiony/guide/moodtoggle/MoodToggle.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.moodtoggle
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.drawable.Drawable
6 | import android.util.AttributeSet
7 | import android.view.View
8 |
9 |
10 | class MoodToggle @JvmOverloads constructor(
11 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
12 | ) : View(context, attrs, defStyleAttr) {
13 |
14 | data class Mood(val name: String, val image: Drawable)
15 |
16 | private val moods = mutableListOf()
17 |
18 | var currentMood: Mood
19 | private set
20 |
21 | init {
22 | moods.add(Mood("satisfied", resources.getDrawable(R.drawable.ic_sentiment_satisfied_black_24dp)))
23 | moods.add(Mood("neutral", resources.getDrawable(R.drawable.ic_sentiment_neutral_black_24dp)))
24 | moods.add(Mood("dissatisfied", resources.getDrawable(R.drawable.ic_sentiment_dissatisfied_black_24dp)))
25 | currentMood = moods[0]
26 | contentDescription = "mood ${currentMood.name}"
27 | isClickable = true
28 | }
29 |
30 | override fun getAccessibilityClassName() = MoodToggle::class.java.simpleName
31 |
32 | override fun performClick(): Boolean {
33 | currentMood = moods[(moods.indexOf(currentMood) + 1) % moods.size]
34 | contentDescription = "mood ${currentMood.name}"
35 | super.performClick()
36 | invalidate()
37 | return true
38 | }
39 |
40 | override fun onDraw(canvas: Canvas) {
41 | super.onDraw(canvas)
42 | currentMood.image.setBounds(0, 0, width, height)
43 | currentMood.image.draw(canvas)
44 | }
45 | }
--------------------------------------------------------------------------------
/moodtoggle/src/main/res/drawable/ic_sentiment_dissatisfied_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/moodtoggle/src/main/res/drawable/ic_sentiment_neutral_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/moodtoggle/src/main/res/drawable/ic_sentiment_satisfied_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/progresstextview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/progresstextview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android-extensions'
3 | apply plugin: 'kotlin-android'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 |
9 |
10 | defaultConfig {
11 | minSdkVersion 16
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0"
15 | }
16 | compileOptions {
17 | sourceCompatibility JavaVersion.VERSION_1_8
18 | targetCompatibility JavaVersion.VERSION_1_8
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 |
25 | implementation 'androidx.appcompat:appcompat:1.1.0'
26 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
27 | }
28 |
29 | repositories {
30 | mavenCentral()
31 | }
32 |
--------------------------------------------------------------------------------
/progresstextview/result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZieIony/GuideToCustomViews/e526d0a3fa7fd3d2aef4bc461d85645f1b7a8ecb/progresstextview/result.png
--------------------------------------------------------------------------------
/progresstextview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/progresstextview/src/main/java/com/github/zieiony/guide/progresstextview/ProgressTextView_Bad.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.progresstextview
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.graphics.Canvas
6 | import android.graphics.Color
7 | import android.graphics.drawable.ColorDrawable
8 | import android.util.AttributeSet
9 | import android.widget.TextView
10 |
11 | // no extending
12 | class ProgressTextView_Bad : TextView {
13 |
14 | private var _progressColor: ColorStateList = DEFAULT_PROGRESSCOLOR
15 | private var _progress: Float = 0.0f
16 | private var _progressTextColor: ColorStateList = DEFAULT_PROGRESSTEXTCOLOR
17 |
18 | @JvmOverloads
19 | constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
20 | // only the two required constructors
21 | attrs?.let {
22 | val a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressTextView_Bad)
23 |
24 | progress = a.getFloat(R.styleable.ProgressTextView_Bad_guide_progress, 0.0f)
25 | progressColor = a.getColorStateList(R.styleable.ProgressTextView_Bad_guide_progressColor)
26 | ?: DEFAULT_PROGRESSCOLOR
27 | progressTextColor = a.getColorStateList(R.styleable.ProgressTextView_Bad_guide_progressTextColor)
28 | ?: DEFAULT_PROGRESSTEXTCOLOR
29 |
30 | a.recycle()
31 | }
32 | }
33 |
34 | // no new accessibility events
35 | override fun getAccessibilityClassName(): CharSequence {
36 | return ProgressTextView_Bad::class.java.name
37 | }
38 |
39 | var progressColor
40 | get() = _progressColor
41 | set(value) {
42 | _progressColor = value
43 | // call invalidate to apply changes
44 | invalidate()
45 | }
46 |
47 | fun setProgressColor(color: Int) {
48 | progressColor = ColorStateList.valueOf(color)
49 | invalidate()
50 | }
51 |
52 | var progress
53 | get() = _progress
54 | set(value) {
55 | _progress = value
56 | invalidate()
57 | }
58 |
59 | var progressTextColor
60 | get() = _progressTextColor
61 | set(value) {
62 | _progressTextColor = value
63 | invalidate()
64 | }
65 |
66 | override fun draw(canvas: Canvas) {
67 | // save current values
68 | val textColor = textColors
69 | val backgroundColor = background
70 |
71 | // set progress colors. This triggers invalidate
72 | setTextColor(progressTextColor)
73 | background = ColorDrawable(progressColor.getColorForState(drawableState, progressColor.defaultColor))
74 |
75 | // draw half of the view using clipping
76 | var saveCount = canvas.save()
77 | canvas.clipRect(0.0f, 0.0f, progress * width, height.toFloat())
78 | super.draw(canvas)
79 | canvas.restoreToCount(saveCount)
80 |
81 | // restore original values
82 | setTextColor(textColor)
83 | background = backgroundColor
84 |
85 | // draw the other half of the view
86 | saveCount = canvas.save()
87 | canvas.clipRect(progress * width, 0.0f, width.toFloat(), height.toFloat())
88 | super.draw(canvas)
89 | canvas.restoreToCount(saveCount)
90 | }
91 |
92 | companion object {
93 | val DEFAULT_PROGRESSCOLOR = ColorStateList.valueOf(Color.WHITE)
94 | val DEFAULT_PROGRESSTEXTCOLOR = ColorStateList.valueOf(Color.BLACK)
95 | }
96 | }
--------------------------------------------------------------------------------
/progresstextview/src/main/java/com/github/zieiony/guide/progresstextview/ProgressTextView_Good.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.progresstextview
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.graphics.Canvas
6 | import android.graphics.Color
7 | import android.text.Layout
8 | import android.text.StaticLayout
9 | import android.text.TextPaint
10 | import android.util.AttributeSet
11 | import android.view.View
12 | import kotlin.math.max
13 | import kotlin.math.min
14 |
15 |
16 | // no extending
17 | class ProgressTextView_Good : View {
18 |
19 | private var _text: String = ""
20 | private var _progressColor: ColorStateList = DEFAULT_PROGRESSCOLOR
21 | private var _progress: Float = 0.0f
22 | private var _progressTextColor: ColorStateList = DEFAULT_PROGRESSTEXTCOLOR
23 | private var _textColor: ColorStateList = DEFAULT_PROGRESSTEXTCOLOR
24 | private var layout: StaticLayout? = null
25 | private var paint = TextPaint(TextPaint.ANTI_ALIAS_FLAG)
26 |
27 | @JvmOverloads
28 | constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
29 | // only the two required constructors
30 | attrs?.let {
31 | val a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressTextView_Good)
32 |
33 | textSize = a.getDimension(R.styleable.ProgressTextView_Good_guide_textSize, DEFAULT_TEXTSIZE)
34 | text = a.getString(R.styleable.ProgressTextView_Good_guide_text) ?: ""
35 | progress = a.getFloat(R.styleable.ProgressTextView_Good_guide_progress, 0.0f)
36 | progressColor = a.getColorStateList(R.styleable.ProgressTextView_Good_guide_progressColor)
37 | ?: DEFAULT_PROGRESSCOLOR
38 | progressTextColor = a.getColorStateList(R.styleable.ProgressTextView_Good_guide_progressTextColor)
39 | ?: DEFAULT_PROGRESSTEXTCOLOR
40 | textColor = a.getColorStateList(R.styleable.ProgressTextView_Good_guide_textColor)
41 | ?: DEFAULT_TEXTCOLOR
42 |
43 | a.recycle()
44 | }
45 | }
46 |
47 | // no new accessibility events
48 | override fun getAccessibilityClassName(): CharSequence {
49 | return ProgressTextView_Good::class.java.name
50 | }
51 |
52 | var progressColor
53 | get() = _progressColor
54 | set(value) {
55 | _progressColor = value
56 | // call invalidate to apply changes
57 | invalidate()
58 | }
59 |
60 | fun setProgressColor(color: Int) {
61 | progressColor = ColorStateList.valueOf(color)
62 | invalidate()
63 | }
64 |
65 | var progress
66 | get() = _progress
67 | set(value) {
68 | _progress = value
69 | invalidate()
70 | }
71 |
72 | var textSize
73 | get() = paint.textSize
74 | set(value) {
75 | paint.textSize = value
76 | requestLayout()
77 | }
78 |
79 | var text
80 | get() = _text
81 | set(value) {
82 | _text = value
83 | requestLayout()
84 | }
85 |
86 | var progressTextColor
87 | get() = _progressTextColor
88 | set(value) {
89 | _progressTextColor = value
90 | invalidate()
91 | }
92 |
93 | var textColor
94 | get() = _textColor
95 | set(value) {
96 | _textColor = value
97 | invalidate()
98 | }
99 |
100 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
101 | super.onLayout(changed, left, top, right, bottom)
102 | layout = StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false)
103 | }
104 |
105 | override fun onDraw(canvas: Canvas) {
106 | super.onDraw(canvas)
107 |
108 | // draw half of the view using clipping
109 | var saveCount = canvas.save()
110 | canvas.clipRect(0.0f, 0.0f, progress * width, height.toFloat())
111 |
112 | paint.color = progressColor.getColorForState(drawableState, progressColor.defaultColor)
113 | canvas.drawRect(0.0f, 0.0f, progress * width, height.toFloat(), paint)
114 |
115 | layout?.let {
116 | canvas.translate(paddingLeft.toFloat(), max(paddingTop.toFloat(), (height - it.height) / 2.0f))
117 | paint.color = progressTextColor.getColorForState(drawableState, progressTextColor.defaultColor)
118 | it.draw(canvas)
119 | }
120 |
121 | canvas.restoreToCount(saveCount)
122 |
123 |
124 | // draw the other half of the view
125 | saveCount = canvas.save()
126 | canvas.clipRect(progress * width, 0.0f, width.toFloat(), height.toFloat())
127 |
128 | layout?.let {
129 | canvas.translate(paddingLeft.toFloat(), max(paddingTop.toFloat(), (height - it.height) / 2.0f))
130 | paint.color = textColor.getColorForState(drawableState, textColor.defaultColor)
131 | it.draw(canvas)
132 | }
133 |
134 | canvas.restoreToCount(saveCount)
135 | }
136 |
137 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
138 | val widthMode = MeasureSpec.getMode(widthMeasureSpec)
139 | val heightMode = MeasureSpec.getMode(heightMeasureSpec)
140 | val widthSize = MeasureSpec.getSize(widthMeasureSpec)
141 | val heightSize = MeasureSpec.getSize(heightMeasureSpec)
142 | var width = paddingLeft + paddingRight
143 | var height = paddingTop + paddingBottom
144 |
145 | if (widthMode == MeasureSpec.EXACTLY) {
146 | width = widthSize
147 | } else {
148 | layout = StaticLayout(text, paint, Integer.MAX_VALUE, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false)
149 | width += (0 until layout!!.lineCount).map { layout!!.getLineWidth(it) }.max()!!.toInt()
150 |
151 | width = max(width, suggestedMinimumWidth)
152 | if (widthMode == MeasureSpec.AT_MOST)
153 | width = min(widthSize, width)
154 | }
155 |
156 | if (heightMode == MeasureSpec.EXACTLY) {
157 | height = heightSize
158 | } else {
159 | layout = StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false)
160 | height += layout!!.height
161 |
162 | height = max(height, suggestedMinimumHeight)
163 | if (heightMode == MeasureSpec.AT_MOST)
164 | height = min(height, heightSize)
165 | }
166 |
167 | setMeasuredDimension(width, height)
168 | }
169 |
170 | companion object {
171 | val DEFAULT_PROGRESSCOLOR = ColorStateList.valueOf(Color.BLUE)
172 | val DEFAULT_PROGRESSTEXTCOLOR = ColorStateList.valueOf(Color.WHITE)
173 | val DEFAULT_TEXTCOLOR = ColorStateList.valueOf(Color.BLACK)
174 | const val DEFAULT_TEXTSIZE = 10.0f
175 | }
176 | }
--------------------------------------------------------------------------------
/progresstextview/src/main/java/com/github/zieiony/guide/progresstextview/ProgressTextView_Medium.kt:
--------------------------------------------------------------------------------
1 | package com.github.zieiony.guide.progresstextview
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.graphics.Canvas
6 | import android.graphics.Color
7 | import android.graphics.Rect
8 | import android.text.TextPaint
9 | import android.util.AttributeSet
10 | import android.view.View
11 | import kotlin.math.max
12 |
13 | // no extending
14 | class ProgressTextView_Medium : View {
15 |
16 | private val bounds = Rect()
17 | private var _text: String = ""
18 | private var _progressColor: ColorStateList = DEFAULT_PROGRESSCOLOR
19 | private var _progress: Float = 0.0f
20 | private var _progressTextColor: ColorStateList = DEFAULT_PROGRESSTEXTCOLOR
21 | private var _textColor: ColorStateList = DEFAULT_PROGRESSTEXTCOLOR
22 | private var paint = TextPaint(TextPaint.ANTI_ALIAS_FLAG)
23 |
24 | @JvmOverloads
25 | constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
26 | // only the two required constructors
27 | attrs?.let {
28 | val a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressTextView_Medium)
29 |
30 | text = a.getString(R.styleable.ProgressTextView_Medium_guide_text) ?: ""
31 | progress = a.getFloat(R.styleable.ProgressTextView_Medium_guide_progress, 0.0f)
32 | progressColor = a.getColorStateList(R.styleable.ProgressTextView_Medium_guide_progressColor)
33 | ?: DEFAULT_PROGRESSCOLOR
34 | progressTextColor = a.getColorStateList(R.styleable.ProgressTextView_Medium_guide_progressTextColor)
35 | ?: DEFAULT_PROGRESSTEXTCOLOR
36 | textColor = a.getColorStateList(R.styleable.ProgressTextView_Medium_guide_textColor)
37 | ?: DEFAULT_TEXTCOLOR
38 | textSize = a.getDimension(R.styleable.ProgressTextView_Medium_guide_textSize, DEFAULT_TEXTSIZE)
39 |
40 | a.recycle()
41 | }
42 | }
43 |
44 | // no new accessibility events
45 | override fun getAccessibilityClassName(): CharSequence {
46 | return ProgressTextView_Medium::class.java.name
47 | }
48 |
49 | var progressColor
50 | get() = _progressColor
51 | set(value) {
52 | _progressColor = value
53 | // call invalidate to apply changes
54 | invalidate()
55 | }
56 |
57 | fun setProgressColor(color: Int) {
58 | progressColor = ColorStateList.valueOf(color)
59 | invalidate()
60 | }
61 |
62 | var progress
63 | get() = _progress
64 | set(value) {
65 | _progress = value
66 | invalidate()
67 | }
68 |
69 | var textSize
70 | get() = paint.textSize
71 | set(value) {
72 | paint.textSize = value
73 | invalidate()
74 | }
75 |
76 | var text
77 | get() = _text
78 | set(value) {
79 | _text = value
80 | invalidate()
81 | }
82 |
83 | var progressTextColor
84 | get() = _progressTextColor
85 | set(value) {
86 | _progressTextColor = value
87 | invalidate()
88 | }
89 |
90 | var textColor
91 | get() = _textColor
92 | set(value) {
93 | _textColor = value
94 | invalidate()
95 | }
96 |
97 | override fun onDraw(canvas: Canvas) {
98 | super.onDraw(canvas)
99 |
100 | paint.getTextBounds(text, 0, text.length, bounds)
101 |
102 | // draw half of the view using clipping
103 | var saveCount = canvas.save()
104 | canvas.clipRect(0.0f, 0.0f, progress * width, height.toFloat())
105 |
106 | paint.color = progressColor.getColorForState(drawableState, progressColor.defaultColor)
107 | canvas.drawRect(0.0f, 0.0f, progress * width, height.toFloat(), paint)
108 |
109 | canvas.translate(paddingLeft.toFloat(), max(paddingTop.toFloat(), (height + bounds.height())/2.0f))
110 | paint.color = progressTextColor.getColorForState(drawableState, progressTextColor.defaultColor)
111 | canvas.drawText(text, 0.0f, 0.0f, paint)
112 |
113 | canvas.restoreToCount(saveCount)
114 |
115 | // draw the other half of the view
116 | saveCount = canvas.save()
117 | canvas.clipRect(progress * width, 0.0f, width.toFloat(), height.toFloat())
118 |
119 | canvas.translate(paddingLeft.toFloat(), max(paddingTop.toFloat(), (height + bounds.height())/2.0f))
120 | paint.color = textColor.getColorForState(drawableState, textColor.defaultColor)
121 | canvas.drawText(text, 0.0f, 0.0f, paint)
122 |
123 | canvas.restoreToCount(saveCount)
124 | }
125 |
126 | companion object {
127 | val DEFAULT_PROGRESSCOLOR = ColorStateList.valueOf(Color.BLUE)
128 | val DEFAULT_PROGRESSTEXTCOLOR = ColorStateList.valueOf(Color.WHITE)
129 | val DEFAULT_TEXTCOLOR = ColorStateList.valueOf(Color.BLACK)
130 | const val DEFAULT_TEXTSIZE = 10.0f
131 | }
132 | }
--------------------------------------------------------------------------------
/progresstextview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':progresstextview', ':chartview', ':flowlayout', ':invalidedittext', ':moodtoggle'
2 | rootProject.name='Guide'
3 | include ':landscapedrawable'
4 |
--------------------------------------------------------------------------------