11package me.ibrahimsn.particle
22
33import android.content.Context
4- import android.graphics.Canvas
5- import android.graphics.Color
6- import android.graphics.Paint
7- import android.graphics.Path
4+ import android.graphics.*
85import android.util.AttributeSet
9- import android.view.View
6+ import android.view.SurfaceHolder
7+ import android.view.SurfaceView
108import android.view.ViewTreeObserver
119import kotlin.random.Random
1210
13- class ParticleView : View {
11+ class ParticleView : SurfaceView , SurfaceHolder . Callback {
1412
15- private lateinit var particles: Array <Particle >
13+ private lateinit var particles: Array <Particle ?>
14+
15+ private var surfaceViewThread: SurfaceViewThread ? = null
1616
1717 private var count = 20
1818 private var minRadius = 5
1919 private var maxRadius = 10
20+ private var hasSurface: Boolean = false
2021
22+ private var background = Color .BLACK
2123 private var color = Color .WHITE
2224 private val path = Path ()
2325
@@ -28,75 +30,144 @@ class ParticleView : View {
2830 }
2931
3032 constructor (context: Context ) : super (context)
31- constructor (context: Context , attrs: AttributeSet ? ) : super (context, attrs) {
33+ constructor (context: Context , attrs: AttributeSet ) : super (context, attrs) {
3234 val a = context.obtainStyledAttributes(attrs, R .styleable.ParticleView , 0 , 0 )
3335
3436 count = a.getInt(R .styleable.ParticleView_particleCount , count)
3537 minRadius = a.getInt(R .styleable.ParticleView_minParticleRadius , minRadius)
3638 maxRadius = a.getInt(R .styleable.ParticleView_maxParticleRadius , maxRadius)
3739 color = a.getColor(R .styleable.ParticleView_particleColor , color)
40+ background = a.getColor(R .styleable.ParticleView_backgroundColor , background)
41+ a.recycle()
3842
43+ particles = arrayOfNulls(count)
3944 paint.color = color
4045
41- if (count > 50 )
42- count = 50
46+ if (count > 50 ) count = 50
47+ if (minRadius <= 0 ) minRadius = 1
48+ if (maxRadius <= minRadius) maxRadius = minRadius + 1
49+
50+ if (holder != null )
51+ holder.addCallback(this )
4352
44- if (minRadius <= 0 )
45- minRadius = 1
53+ hasSurface = false
54+ }
4655
47- if (maxRadius <= minRadius)
48- maxRadius = minRadius + 1
56+ override fun surfaceCreated ( holder : SurfaceHolder ) {
57+ hasSurface = true
4958
50- a.recycle()
59+ if (surfaceViewThread == null )
60+ surfaceViewThread = SurfaceViewThread ()
61+
62+ surfaceViewThread!! .start()
5163 }
5264
53- init {
54- viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver .OnPreDrawListener {
55- override fun onPreDraw (): Boolean {
56- if (viewTreeObserver.isAlive)
57- viewTreeObserver.removeOnPreDrawListener(this )
65+ fun resume () {
66+ if (surfaceViewThread == null ) {
67+ surfaceViewThread = SurfaceViewThread ()
5868
59- val array = arrayOfNulls<Particle >(count)
69+ if (hasSurface)
70+ surfaceViewThread!! .start()
71+ }
72+ }
6073
61- for (i in 0 until count)
62- array[i] = Particle (
63- Random .nextInt(minRadius, maxRadius).toFloat(),
64- Random .nextInt(0 , width).toFloat(),
65- Random .nextInt(0 , height).toFloat(),
66- Random .nextInt(- 2 , 2 ),
67- Random .nextInt(- 2 , 2 ),
68- Random .nextInt(150 , 255 ))
74+ fun pause () {
75+ if (surfaceViewThread != null ) {
76+ surfaceViewThread!! .requestExitAndWait()
77+ surfaceViewThread = null
78+ }
79+ }
6980
70- particles = array as Array <Particle >
81+ override fun surfaceDestroyed (holder : SurfaceHolder ) {
82+ hasSurface = false
7183
72- return true
73- }
74- })
84+ if (surfaceViewThread != null ) {
85+ surfaceViewThread!! .requestExitAndWait()
86+ surfaceViewThread = null
87+ }
7588 }
7689
77- override fun onDraw (canvas : Canvas ) {
78- for (i in 0 until count) {
79- particles[i].x + = particles[i].vx
80- particles[i].y + = particles[i].vy
90+ override fun surfaceChanged (holder : SurfaceHolder , format : Int , w : Int , h : Int ) {
8191
82- if (particles[i].x < 0 )
83- particles[i].x = width.toFloat()
84- else if (particles[i].x > width)
85- particles[i].x = 0F
92+ }
93+
94+ private inner class SurfaceViewThread : Thread () {
95+
96+ private var running: Boolean = true
8697
87- if (particles[i].y < 0 )
88- particles[i].y = height.toFloat()
89- else if (particles[i].y > height)
90- particles[i].y = 0F
98+ init {
99+ running = true
91100
92- for (j in 0 until count)
93- linkParticles(canvas, particles[i], particles[j])
101+ viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver .OnPreDrawListener {
102+ override fun onPreDraw (): Boolean {
103+ if (viewTreeObserver.isAlive)
104+ viewTreeObserver.removeOnPreDrawListener(this )
94105
95- paint.alpha = particles[i].alpha
96- canvas.drawCircle(particles[i].x, particles[i].y, particles[i].radius, paint)
106+ for (i in 0 until count)
107+ particles[i] = Particle (
108+ Random .nextInt(minRadius, maxRadius).toFloat(),
109+ Random .nextInt(0 , width).toFloat(),
110+ Random .nextInt(0 , height).toFloat(),
111+ Random .nextInt(- 2 , 2 ),
112+ Random .nextInt(- 2 , 2 ),
113+ Random .nextInt(150 , 255 ))
114+
115+ return true
116+ }
117+ })
97118 }
98119
99- postInvalidateDelayed(25 )
120+ override fun run () {
121+ while (running) {
122+ var canvas: Canvas ? = null
123+
124+ try {
125+ canvas = holder.lockCanvas()
126+
127+ synchronized (holder) {
128+ canvas.drawColor(background)
129+
130+ for (i in 0 until count) {
131+ particles[i]!! .x + = particles[i]!! .vx
132+ particles[i]!! .y + = particles[i]!! .vy
133+
134+ if (particles[i]!! .x < 0 )
135+ particles[i]!! .x = width.toFloat()
136+ else if (particles[i]!! .x > width)
137+ particles[i]!! .x = 0F
138+
139+ if (particles[i]!! .y < 0 )
140+ particles[i]!! .y = height.toFloat()
141+ else if (particles[i]!! .y > height)
142+ particles[i]!! .y = 0F
143+
144+ for (j in 0 until count)
145+ linkParticles(canvas, particles[i]!! , particles[j]!! )
146+
147+ paint.alpha = particles[i]!! .alpha
148+ canvas.drawCircle(particles[i]!! .x, particles[i]!! .y, particles[i]!! .radius, paint)
149+ }
150+ }
151+
152+ } catch (e: Exception ) {
153+ e.printStackTrace()
154+ } finally {
155+ if (canvas != null ) {
156+ holder.unlockCanvasAndPost(canvas)
157+ }
158+ }
159+ }
160+ }
161+
162+ fun requestExitAndWait () {
163+ running = false
164+
165+ try {
166+ join()
167+ } catch (ignored: InterruptedException ) {
168+
169+ }
170+ }
100171 }
101172
102173 private fun linkParticles (canvas : Canvas , p1 : Particle , p2 : Particle ) {
0 commit comments