Advertisement
tuomasvaltanen

Untitled

May 8th, 2023 (edited)
162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.05 KB | None | 0 0
  1. // Edistynyt mobiiliohjelmointi 8.5.2023
  2.  
  3. // säädetään ulkoasun ja Viewien kokoja CustomViewTesterFragmentissa, jotta myöhemmin on tilaa myös compound controlille:
  4.  
  5. <?xml version="1.0" encoding="utf-8"?>
  6. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  7. xmlns:tools="http://schemas.android.com/tools"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"
  10. xmlns:app="http://schemas.android.com/apk/res-auto"
  11. android:orientation="vertical"
  12. tools:context=".CustomViewTesterFragment">
  13.  
  14. <com.github.anastr.speedviewlib.SpeedView
  15. android:id="@+id/speedView"
  16. app:sv_withTremble="false"
  17. app:sv_minSpeed="-50"
  18. app:sv_maxSpeed="50"
  19. app:sv_unit="℃"
  20. android:layout_width="wrap_content"
  21. android:layout_height="wrap_content"
  22. android:layout_margin="10dp" />
  23.  
  24. <com.example.edistynytandroid2023verkko.CustomTemperatureView
  25. android:layout_width="140dp"
  26. android:layout_height="140dp"
  27. android:layout_margin="10dp" />
  28.  
  29. </LinearLayout>
  30.  
  31. // säädetään tekstin kokoa ja lisätään lihavointi
  32.  
  33. init
  34. {
  35. // this is constructor of your component, all initializations go here
  36. // define the colors!
  37. paint.color = Color.BLUE
  38. textPaint.color = Color.WHITE
  39. textPaint.textSize = 100f
  40. textPaint.textAlign = Paint.Align.CENTER
  41.  
  42. // asetetaan myös lihavointi
  43. textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD))
  44. }
  45.  
  46. // jotta CustomView voi näyttää tilanteesta riippuen eri lämpötilan, tarvitaan muuttuja joka pitää kirjaa lämpötilasta:
  47.  
  48. // muuttuja, joka pitää kirjaa aktiivisesta lämpötila
  49. private var temperature : Int = 0
  50.  
  51.  
  52. // kytketään muuttuja onDrawissa niin että tulostetaan muuttujan sisältö:
  53.  
  54. // parameters: content, x, y, color
  55. canvas.drawText("${temperature}℃", width.toFloat() / 2, width.toFloat() / 2 + 30, textPaint);
  56.  
  57.  
  58. // tehdään myös apufunktio/metodi, jotta lämpötilaa voi vaihtaa myös fragmentista käsin:
  59.  
  60. // tämän funktion/metodin kautta voimme muuttaa aktiivista lämpötilaa
  61. // esim. fragmentista käsin
  62. fun changeTemperature(temp : Int) {
  63. temperature = temp
  64. }
  65.  
  66. // annetaan CustomViewille id fragmentissa:
  67.  
  68. <com.example.edistynytandroid2023verkko.CustomTemperatureView
  69. android:id="@+id/customTemperatureView_test"
  70. android:layout_width="140dp"
  71. android:layout_height="140dp"
  72. android:layout_margin="10dp" />
  73.  
  74. // kokeillaan CustomViewTesterFragmentissa vaihtaa lämpötilaa lennosta:
  75.  
  76. binding.customTemperatureViewTest.changeTemperature(14)
  77.  
  78. // päivitetään changeTemperature, jotta taustaväri
  79. // vaihtuu lämpötilan pohjalta
  80.  
  81. // tämän funktion/metodin kautta voimme muuttaa aktiivista lämpötilaa
  82. // esim. fragmentista käsin
  83. fun changeTemperature(temp : Int) {
  84. temperature = temp
  85.  
  86. // muutetaan ympyrän taustaväri
  87. // riipuen lämpötilasta
  88. // tähän voi tehdä niin monimutkaisen
  89. // if-elif-else -rakenteen kuin itse vain haluaa
  90. if(temperature > 0) {
  91. paint.color = Color.RED
  92. }
  93. else {
  94. paint.color = Color.BLUE
  95. }
  96. }
  97.  
  98. // lisätään testiluontoinen nappi fragmentiin, ja kokeillaan vaihtaa lämpötilaa napin kautta:
  99.  
  100. <Button
  101. android:id="@+id/button_set_random_temperature"
  102. android:layout_width="match_parent"
  103. android:layout_height="wrap_content"
  104. android:text="CHANGE TEMPERATURE" />
  105.  
  106. // napin logiikka:
  107.  
  108. // kun nappia painetaan, vaihdetaan lämpötila
  109. binding.buttonSetRandomTemperature.setOnClickListener {
  110. val randomTemperature : Int = (-40..40).random()
  111. binding.customTemperatureViewTest.changeTemperature(randomTemperature)
  112. Log.d("ADVTECH", randomTemperature.toString())
  113. }
  114.  
  115.  
  116. // jotta Custom View päivittyy myös napin kautta, pitää changeTemperature-funktiota muokata siten että Androidille ilmoitetaan että CustomView pitää piirtää uusiksi:
  117.  
  118. fun changeTemperature(temp : Int) {
  119. temperature = temp
  120.  
  121. // muutetaan ympyrän taustaväri
  122. // riipuen lämpötilasta
  123. // tähän voi tehdä niin monimutkaisen
  124. // if-elif-else -rakenteen kuin itse vain haluaa
  125. if(temperature > 0) {
  126. paint.color = Color.RED
  127. }
  128. else {
  129. paint.color = Color.BLUE
  130. }
  131.  
  132. // Android ei oletuksena piirrä CustomViewiä uusiksi
  133. // siltä varalta jos data taustalla muuttuu.
  134. // tämän takia meidän pitää itse ilmoittaa Androidille
  135. // että view pitää piirtää uusiksi uudella lämpötilalla
  136. invalidate()
  137. requestLayout()
  138. }
  139.  
  140.  
  141. // Osa 3: Compound Controlit eli valmiista Vieweistä rakennettu oma Custom View!
  142.  
  143. // uusi tiedosto projektiin: LatestDataView.kt (Kotlin-class)
  144. // tehdään tyhjä pohja Compound Controlille:
  145.  
  146. class LatestDataView @JvmOverloads constructor(
  147. context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
  148. ) : LinearLayout(context, attrs, defStyleAttr) {
  149.  
  150. // alkuvaiheessa tämä riittää kun käytetään compound controlia
  151. // huom: onDraw ja onMeasure ym. ovat jo valmiiksi tehty LinearLayoutissa
  152. init {
  153.  
  154. }
  155. }
  156.  
  157. // lisätään CustomViewTesterin ulkoasuun:
  158. // huom: paketin nimi on projektissasi eri, viot aloittaa kirjoittamaan
  159. // <LatestDataView, jolloin Android Studio tunnistaa oman Custom Viewisi (compound control)
  160. <com.example.edistynytandroid2023verkko.LatestDataView
  161. android:layout_width="match_parent"
  162. android:layout_height="wrap_content" />
  163.  
  164.  
  165. // kokeillaan muokata LatestDataView niin, että siihen voi lisätä TextViewejä lennosta:
  166.  
  167. class LatestDataView @JvmOverloads constructor(
  168. context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
  169. ) : LinearLayout(context, attrs, defStyleAttr) {
  170.  
  171. // alkuvaiheessa tämä riittää kun käytetään compound controlia
  172. // huom: onDraw ja onMeasure ym. ovat jo valmiiksi tehty LinearLayoutissa
  173. init {
  174. // vaihdetaan LinearLayoutin orientaation pystysuuntaiseksi,
  175. // jotta TextViewit rakentuvat allekkain
  176. this.orientation = VERTICAL
  177.  
  178. addData("Testiviesti 1!")
  179. addData("Testiviesti 2!")
  180. }
  181.  
  182. // apufunktio/metodi, joka lisää LinearLayoutiin uuden TextViewin lennosta
  183. fun addData(message : String)
  184. {
  185. var newTextView : TextView = TextView(context) as TextView
  186. newTextView.setText(message)
  187. newTextView.setBackgroundColor(Color.BLACK)
  188. newTextView.setTextColor(Color.YELLOW)
  189. this.addView(newTextView)
  190. }
  191. }
  192.  
  193. // pienennetään muita Viewejä CustomViewTesterin ulkoasussa, että mahtuu paremmin testaamaan LatestDataViewiä:
  194.  
  195. <?xml version="1.0" encoding="utf-8"?>
  196. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  197. xmlns:tools="http://schemas.android.com/tools"
  198. android:layout_width="match_parent"
  199. android:layout_height="match_parent"
  200. xmlns:app="http://schemas.android.com/apk/res-auto"
  201. android:orientation="vertical"
  202. tools:context=".CustomViewTesterFragment">
  203.  
  204. <com.github.anastr.speedviewlib.SpeedView
  205. android:id="@+id/speedView"
  206. app:sv_withTremble="false"
  207. app:sv_minSpeed="-50"
  208. app:sv_maxSpeed="50"
  209. app:sv_unit="℃"
  210. android:layout_width="200dp"
  211. android:layout_height="wrap_content"
  212. android:layout_margin="10dp" />
  213.  
  214. <com.example.edistynytandroid2023verkko.CustomTemperatureView
  215. android:id="@+id/customTemperatureView_test"
  216. android:layout_width="140dp"
  217. android:layout_height="140dp"
  218. android:layout_margin="10dp" />
  219.  
  220. <Button
  221. android:id="@+id/button_set_random_temperature"
  222. android:layout_width="match_parent"
  223. android:layout_height="wrap_content"
  224. android:text="CHANGE TEMPERATURE" />
  225.  
  226. <com.example.edistynytandroid2023verkko.LatestDataView
  227. android:layout_width="match_parent"
  228. android:layout_height="wrap_content"
  229. android:layout_margin="10dp" />
  230.  
  231. </LinearLayout>
  232.  
  233.  
  234. // tehdään kustomoitu reunus XML:n avulla.
  235.  
  236. // uusi tiedosto kansioon res -> drawable , -> customborder.xml:
  237.  
  238. <?xml version="1.0" encoding="UTF-8"?>
  239. <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
  240. <padding android:left="4dp" android:right="4dp" android:top="4dp" android:bottom="4dp"/>
  241. <stroke android:width="4dp" android:color="#52ED6C"/>
  242. </shape>
  243.  
  244. // tämän jälkeen voimme käyttää "taustakuvana" tätä reunusta fragmentissa, esim:
  245.  
  246. <com.example.edistynytandroid2023verkko.LatestDataView
  247. android:layout_width="match_parent"
  248. android:layout_height="wrap_content"
  249. android:layout_margin="10dp"
  250. android:background="@drawable/customborder"/>
  251.  
  252. // lisätään Button ulkoasuun, jotta voidaan testata paremmin LatestDataViewiä:
  253.  
  254.  
  255. <Button
  256. android:id="@+id/button_add_test_data"
  257. android:layout_width="match_parent"
  258. android:layout_height="wrap_content"
  259. android:text="ADD DATA" />
  260.  
  261. // lisätään LatestDataViewille myös id:
  262.  
  263. <com.example.edistynytandroid2023verkko.LatestDataView
  264. android:id="@+id/latestdataviewtest"
  265. android:layout_width="match_parent"
  266. android:layout_height="wrap_content"
  267. android:layout_margin="10dp"
  268. android:background="@drawable/customborder" />
  269.  
  270.  
  271. // Buttonilla voidaan lisätä testiarvo LatestDataViewiin näin:
  272.  
  273. binding.buttonAddTestData.setOnClickListener {
  274. val randomValue : Int = (1..100).random()
  275. binding.latestdataviewtest.addData("Testing " + randomValue.toString())
  276. }
  277.  
  278.  
  279. // jotta LatestDataView ei lisää liikaa TextViewejä napista, korjataan addData-funktiota:
  280.  
  281. // apufunktio/metodi, joka lisää LinearLayoutiin uuden TextViewin lennosta
  282. fun addData(message : String)
  283. {
  284. // ennen kuin lisätään uusi TextView, huolehditaan siitä
  285. // että LinearLayoutissa ei ole ylimääräisiä TextViewejö (max 5 kpl)
  286.  
  287. // niin kauan kuin lukumäärä on liian suuri -> poista vanhin TextView
  288. while(this.childCount >= maxRows) {
  289. this.removeViewAt(0)
  290. }
  291.  
  292. // lisätään uusi TextView
  293. var newTextView : TextView = TextView(context) as TextView
  294. newTextView.setText(message)
  295. newTextView.setBackgroundColor(Color.BLACK)
  296. newTextView.setTextColor(Color.YELLOW)
  297. this.addView(newTextView)
  298. }
  299.  
  300.  
  301. // jos halutaan että LinearLayoutin korkeus on oikea heti ohjelman alussa, voidaan tehdä tällä tavalla:
  302.  
  303. var maxRows = 5
  304.  
  305. // alkuvaiheessa tämä riittää kun käytetään compound controlia
  306. // huom: onDraw ja onMeasure ym. ovat jo valmiiksi tehty LinearLayoutissa
  307. init {
  308. // vaihdetaan LinearLayoutin orientaation pystysuuntaiseksi,
  309. // jotta TextViewit rakentuvat allekkain
  310. this.orientation = VERTICAL
  311.  
  312. // jotta aloituskorkeus saadaan heti alussa oikeaksi, voidaan tehdä seuraavaa:
  313.  
  314. // idea:
  315. // tehdään koodin muistiin uusi TextView (jota ei käytetä missään),
  316. // ja otetaan siitä yhden TextViewin korkeus näytöllä
  317. // kerrotaan tämä luku maxRows-muuttujalla (eli 5)
  318. // lisätään päälle mahdolliset LinearLayouting omat lisäkorkeudet (padding ym.)
  319. // lasketaan kaikki yhteen => tarvittava korkeus LinearLayoutille
  320.  
  321. // tehdään uusi TextView muistiin, ja käsketään Androidia mittaamaan se tällä näytöllä
  322. var someTextView : TextView = TextView(context)
  323. someTextView.measure(0,0)
  324. var rowHeight = someTextView.measuredHeight
  325.  
  326. // mitataan myös itse LinearLayout (paddingit ym.)
  327. this.measure(0, 0)
  328.  
  329. // lasketaan kaikki yhteen ja asetetaan korkeus LinearLayoutissa
  330. var additionalHeight = this.measuredHeight + (maxRows * rowHeight)
  331. this.minimumHeight = additionalHeight
  332. }
  333.  
  334.  
  335. // kokeillaan fade-in animaatiota, tehdään res-kansioon uusi kansio: anim
  336.  
  337. // lisätään siihen uusi tiedosto : customfade.xml:
  338.  
  339. <?xml version="1.0" encoding="utf-8"?>
  340. <set xmlns:android="http://schemas.android.com/apk/res/android"
  341. android:interpolator="@android:anim/linear_interpolator">
  342. <alpha
  343. android:duration="2000"
  344. android:fromAlpha="0.1"
  345. android:toAlpha="1.0" />
  346. </set>
  347.  
  348. // LatestDataViewin addDatan lopussa voidaan käyttää animaatiota:
  349.  
  350. // fade-in animaatio päälle
  351. val animation = AnimationUtils.loadAnimation(context, R.anim.customfade)
  352. // starting the animation
  353. newTextView.startAnimation(animation)
  354.  
  355.  
  356. // kokeillaan vielä WeatherStation-datan kanssa:
  357.  
  358.  
  359. val sdf = SimpleDateFormat("HH:mm:ss")
  360. val currentDate = sdf.format(Date())
  361.  
  362. var dataText : String = "${currentDate} - Temperature: ${temperature}℃ - Humidity: ${humidity}%"
  363.  
  364. // koska MQTT-plugin ajaa koodia ja käsittelee dataa
  365. // tausta-ajolla omassa säikeessään eli threadissa
  366. // joudumme laittamaan ulkoasuun liittyvän koodin runOnUiThread-blokin
  367. // sisälle. Muutoin tulee virhe että koodit toimivat eri säikeissä.
  368. activity?.runOnUiThread {
  369.  
  370. // id pitää olla sama kuin fragmentin ulkoasussa LatestDataViewin id
  371. binding.latestdataviewtestWeatherstation.addData(dataText)
  372. }
  373.  
  374.  
  375. // testi: robottianimaatio: animaatiotiedostot:
  376.  
  377. https://opengameart.org/content/animated-robot-set-png
  378.  
  379. // muista uudelleennimetä tiedostot siten, ettei niissä ole välilyöntejä, esim. robo1.png, robo2.png jne.
  380.  
  381. // tehdään drawable-kansioon uusi tiedosto: roboanimation.xml:
  382.  
  383. <?xml version="1.0" encoding="utf-8"?>
  384. <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
  385. android:oneshot="false">
  386.  
  387. <item
  388. android:duration="100"
  389. android:drawable="@drawable/robo1"/>
  390.  
  391. <item
  392. android:duration="100"
  393. android:drawable="@drawable/robo2"/>
  394.  
  395. .... loput framet tähän ....
  396.  
  397.  
  398. </animation-list>
  399.  
  400.  
  401. // lisätään haluttuun fragmentiin ImageView:
  402.  
  403. <ImageView
  404. android:background="@drawable/robo1"
  405. android:id="@+id/imageView_robot"
  406. android:layout_width="200dp"
  407. android:layout_height="360dp"
  408. app:layout_constraintBottom_toBottomOf="parent"
  409. app:layout_constraintEnd_toEndOf="parent"
  410. app:layout_constraintStart_toStartOf="parent"
  411. app:layout_constraintTop_toTopOf="parent" />
  412.  
  413.  
  414. // animaation käynnistäminen koodissa:
  415.  
  416. // käynnistetään robotin animaatio XML:n pohjalta
  417. binding.imageViewRobot.setBackgroundResource(R.drawable.roboanimation)
  418. val frameAnimation = binding.imageViewRobot.background as AnimationDrawable
  419. frameAnimation.start()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement