Skip to content

Commit 87aa1ca

Browse files
GerardGerard
authored andcommitted
Added xml attributes
View attributes can now be set in XML.
1 parent 43867fa commit 87aa1ca

File tree

4 files changed

+108
-15
lines changed

4 files changed

+108
-15
lines changed

app/src/main/java/com/gerardbradshaw/cloudview/MainActivity.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ class MainActivity : AppCompatActivity() {
3535

3636
minSizeView.text = cloudView.minCloudSize.toString()
3737
maxSizeView.text = cloudView.maxCloudSize.toString()
38-
39-
cloudView.startAnimation()
4038
}
4139

4240
private fun locateViews() {

app/src/main/res/layout/activity_main.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
android:id="@+id/cloud_view"
1414
android:layout_width="match_parent"
1515
android:layout_height="match_parent"
16-
android:background="#03A9F4"/>
16+
app:basePassTimeMs="1000"
17+
app:passTimeVarianceMs="500"/>
1718

1819
<LinearLayout
1920
android:layout_width="match_parent"

library/src/main/java/com/gerardbradshaw/library/CloudView.kt

Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,80 @@ import android.widget.ImageView
1616
import androidx.core.animation.doOnEnd
1717
import androidx.core.animation.doOnStart
1818
import androidx.core.view.updateLayoutParams
19+
import java.lang.Exception
1920
import java.util.*
2021
import kotlin.math.max
2122
import kotlin.math.min
2223
import kotlin.random.Random
2324

2425
class CloudView : FrameLayout {
2526
constructor(context: Context) : super(context)
26-
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
27-
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
27+
28+
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
29+
if (attrs != null) saveAttrs(attrs)
30+
}
31+
32+
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
33+
if (attrs != null) saveAttrs(attrs, defStyleAttr)
34+
}
2835

2936
private var isDrawn = false
3037
private var isAnimating = false
31-
private var requestedSizeRange = 300..500
38+
private var requestedSizeRange = DEFAULT_MINIMUM_CLOUD_SIZE..DEFAULT_MAXIMUM_CLOUD_SIZE
3239
private val imageViews = HashSet<ImageView>()
3340

3441
private var imageResId: Int? = R.drawable.ic_cloud
3542
private var imageBitmap: Bitmap? = null
3643
private var imageDrawable: Drawable? = null
3744

3845

46+
private fun saveAttrs(attrs: AttributeSet?, defStyleAttr: Int = 0) {
47+
context.theme.obtainStyledAttributes(
48+
attrs, R.styleable.CloudView, defStyleAttr, 0).apply {
49+
try {
50+
if (getBoolean(R.styleable.CloudView_isAnimating, true)) startAnimation()
51+
52+
getResourceId(R.styleable.CloudView_cloudImageSrc, -1).apply {
53+
if (this != -1) imageResId = this
54+
}
55+
56+
getDimensionPixelSize(R.styleable.CloudView_minCloudSize, DEFAULT_MINIMUM_CLOUD_SIZE).apply {
57+
minCloudSize = this
58+
}
59+
60+
getDimensionPixelSize(R.styleable.CloudView_maxCloudSize, DEFAULT_MAXIMUM_CLOUD_SIZE).apply {
61+
maxCloudSize = this
62+
}
63+
64+
getInteger(R.styleable.CloudView_cloudCount, DEFAULT_CLOUD_COUNT).apply {
65+
cloudCount = this
66+
}
67+
68+
getInteger(R.styleable.CloudView_basePassTimeMs, DEFAULT_PASS_TIME_MS).apply {
69+
basePassTimeMs = this
70+
}
71+
72+
getInteger(R.styleable.CloudView_passTimeVarianceMs, DEFAULT_PASS_TIME_VARIANCE_MS).apply{
73+
passTimeVarianceMs = this
74+
}
75+
76+
getBoolean(R.styleable.CloudView_fadeInEnabled, false).apply {
77+
isFadeInEnabled = this
78+
}
79+
80+
getInteger(R.styleable.CloudView_fadeInTimeMs, DEFAULT_FADE_IN_TIME_MS).apply {
81+
82+
}
83+
84+
getColor(R.styleable.CloudView_skyColor, Color.parseColor("#03A9F4")).apply {
85+
setBackgroundColor(this)
86+
}
87+
} finally {
88+
recycle()
89+
}
90+
}
91+
}
92+
3993
/**
4094
* Sets all values to their defaults. This is not necessary as a first step if no values have
4195
* been changed.
@@ -47,16 +101,16 @@ class CloudView : FrameLayout {
47101
imageResId = R.drawable.ic_cloud
48102
imageBitmap = null
49103
imageDrawable = null
50-
requestedSizeRange = 300..500
51-
setCloudCount(10)
104+
requestedSizeRange = DEFAULT_MINIMUM_CLOUD_SIZE..DEFAULT_MAXIMUM_CLOUD_SIZE
105+
setCloudCount(DEFAULT_CLOUD_COUNT)
52106

53107
if (wasAnimating) startAnimation()
54108
}
55109

56110
/**
57111
* The maximum number of clouds seen in the view at once. Note clouds are redrawn on change.
58112
*/
59-
var cloudCount: Int = 10
113+
var cloudCount: Int = DEFAULT_CLOUD_COUNT
60114
set(value) {
61115
field = value
62116
if (isDrawn) respawnClouds()
@@ -118,7 +172,7 @@ class CloudView : FrameLayout {
118172
* will pass the entire view in basePassTimeMs + [passTimeVarianceMs]. Note that clouds currently
119173
* crossing the view are not updated.
120174
*/
121-
var basePassTimeMs: Int = 10000
175+
var basePassTimeMs: Int = DEFAULT_PASS_TIME_MS
122176
set(value) {
123177
if (value <= 0) throw IllegalArgumentException()
124178
else field = value
@@ -129,7 +183,7 @@ class CloudView : FrameLayout {
129183
* entire view. A variance of 0 means all clouds will move at the same speed. Note that clouds
130184
* currently crossing the view are not updated.
131185
*/
132-
var passTimeVarianceMs: Int = 2000
186+
var passTimeVarianceMs: Int = DEFAULT_PASS_TIME_VARIANCE_MS
133187
set(value) {
134188
if (value < 0) throw IllegalArgumentException()
135189
else field = value
@@ -147,7 +201,6 @@ class CloudView : FrameLayout {
147201
var isFadeInEnabled = false
148202

149203
init {
150-
setBackgroundColor(Color.TRANSPARENT)
151204
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
152205
override fun onGlobalLayout() {
153206
if (isDrawn) {
@@ -294,11 +347,31 @@ class CloudView : FrameLayout {
294347
}
295348
}
296349

350+
/**
351+
* The time taken for a cloud to fade in when entering the view. Note that [isFadeInEnabled] must
352+
* be true in order to enable this effect. Exceeding [basePassTimeMs] or [passTimeVarianceMs] may
353+
* cause clouds to cross the entire view before fully fading in.
354+
*/
355+
var fadeInTimeMs: Int = DEFAULT_FADE_IN_TIME_MS
356+
set(value) {
357+
if (value < 0) throw IllegalArgumentException()
358+
359+
field = value
360+
361+
if (value > basePassTimeMs) {
362+
Log.i(TAG, "setFadeIntTimeMs(): Warning: Fade in time (${value} ms) exceeds base " +
363+
"pass time (${basePassTimeMs} ms). Clouds may cross the entire view before fully " +
364+
"fading in.")
365+
}
366+
}
367+
297368
private fun animateCloud(cloud: ImageView) {
298369
cloud.animation = null
299370

300-
cloud.x = width.toFloat()
301-
cloud.y = height * Random.nextFloat()
371+
val cloudHeight = cloud.layoutParams.width.toFloat()
372+
373+
cloud.x = width.toFloat() - paddingRight
374+
cloud.y = paddingTop + ((height - paddingBottom - paddingTop - cloudHeight) * Random.nextFloat())
302375

303376
val cloudWidth = cloud.layoutParams.width.toFloat()
304377

@@ -310,7 +383,7 @@ class CloudView : FrameLayout {
310383
doOnStart {
311384
if (isFadeInEnabled) {
312385
with(AlphaAnimation(0.0f, 1.0f)) {
313-
this.duration = 1000
386+
this.duration = fadeInTimeMs.toLong()
314387
cloud.startAnimation(this)
315388
}
316389
}
@@ -352,5 +425,11 @@ class CloudView : FrameLayout {
352425

353426
companion object {
354427
private const val TAG = "GGG CloudView"
428+
const val DEFAULT_PASS_TIME_MS = 10000
429+
const val DEFAULT_PASS_TIME_VARIANCE_MS = 2000
430+
const val DEFAULT_MINIMUM_CLOUD_SIZE = 300
431+
const val DEFAULT_MAXIMUM_CLOUD_SIZE = 500
432+
const val DEFAULT_CLOUD_COUNT = 10
433+
const val DEFAULT_FADE_IN_TIME_MS = 1000
355434
}
356435
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<declare-styleable name="CloudView">
4+
<attr name="isAnimating" format="boolean"/>
5+
<attr name="cloudImageSrc" format="reference"/>
6+
<attr name="minCloudSize" format="dimension"/>
7+
<attr name="maxCloudSize" format="dimension"/>
8+
<attr name="cloudCount" format="integer"/>
9+
<attr name="basePassTimeMs" format="integer"/>
10+
<attr name="passTimeVarianceMs" format="integer"/>
11+
<attr name="fadeInEnabled" format="boolean"/>
12+
<attr name="skyColor" format="color"/>
13+
<attr name="fadeInTimeMs" format="integer"/>
14+
</declare-styleable>
15+
</resources>

0 commit comments

Comments
 (0)