Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Edistynyt mobiiliohjelmointi 8.5.2023
- // säädetään ulkoasun ja Viewien kokoja CustomViewTesterFragmentissa, jotta myöhemmin on tilaa myös compound controlille:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- tools:context=".CustomViewTesterFragment">
- <com.github.anastr.speedviewlib.SpeedView
- android:id="@+id/speedView"
- app:sv_withTremble="false"
- app:sv_minSpeed="-50"
- app:sv_maxSpeed="50"
- app:sv_unit="℃"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="10dp" />
- <com.example.edistynytandroid2023verkko.CustomTemperatureView
- android:layout_width="140dp"
- android:layout_height="140dp"
- android:layout_margin="10dp" />
- </LinearLayout>
- // säädetään tekstin kokoa ja lisätään lihavointi
- init
- {
- // this is constructor of your component, all initializations go here
- // define the colors!
- paint.color = Color.BLUE
- textPaint.color = Color.WHITE
- textPaint.textSize = 100f
- textPaint.textAlign = Paint.Align.CENTER
- // asetetaan myös lihavointi
- textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD))
- }
- // jotta CustomView voi näyttää tilanteesta riippuen eri lämpötilan, tarvitaan muuttuja joka pitää kirjaa lämpötilasta:
- // muuttuja, joka pitää kirjaa aktiivisesta lämpötila
- private var temperature : Int = 0
- // kytketään muuttuja onDrawissa niin että tulostetaan muuttujan sisältö:
- // parameters: content, x, y, color
- canvas.drawText("${temperature}℃", width.toFloat() / 2, width.toFloat() / 2 + 30, textPaint);
- // tehdään myös apufunktio/metodi, jotta lämpötilaa voi vaihtaa myös fragmentista käsin:
- // tämän funktion/metodin kautta voimme muuttaa aktiivista lämpötilaa
- // esim. fragmentista käsin
- fun changeTemperature(temp : Int) {
- temperature = temp
- }
- // annetaan CustomViewille id fragmentissa:
- <com.example.edistynytandroid2023verkko.CustomTemperatureView
- android:id="@+id/customTemperatureView_test"
- android:layout_width="140dp"
- android:layout_height="140dp"
- android:layout_margin="10dp" />
- // kokeillaan CustomViewTesterFragmentissa vaihtaa lämpötilaa lennosta:
- binding.customTemperatureViewTest.changeTemperature(14)
- // päivitetään changeTemperature, jotta taustaväri
- // vaihtuu lämpötilan pohjalta
- // tämän funktion/metodin kautta voimme muuttaa aktiivista lämpötilaa
- // esim. fragmentista käsin
- fun changeTemperature(temp : Int) {
- temperature = temp
- // muutetaan ympyrän taustaväri
- // riipuen lämpötilasta
- // tähän voi tehdä niin monimutkaisen
- // if-elif-else -rakenteen kuin itse vain haluaa
- if(temperature > 0) {
- paint.color = Color.RED
- }
- else {
- paint.color = Color.BLUE
- }
- }
- // lisätään testiluontoinen nappi fragmentiin, ja kokeillaan vaihtaa lämpötilaa napin kautta:
- <Button
- android:id="@+id/button_set_random_temperature"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="CHANGE TEMPERATURE" />
- // napin logiikka:
- // kun nappia painetaan, vaihdetaan lämpötila
- binding.buttonSetRandomTemperature.setOnClickListener {
- val randomTemperature : Int = (-40..40).random()
- binding.customTemperatureViewTest.changeTemperature(randomTemperature)
- Log.d("ADVTECH", randomTemperature.toString())
- }
- // jotta Custom View päivittyy myös napin kautta, pitää changeTemperature-funktiota muokata siten että Androidille ilmoitetaan että CustomView pitää piirtää uusiksi:
- fun changeTemperature(temp : Int) {
- temperature = temp
- // muutetaan ympyrän taustaväri
- // riipuen lämpötilasta
- // tähän voi tehdä niin monimutkaisen
- // if-elif-else -rakenteen kuin itse vain haluaa
- if(temperature > 0) {
- paint.color = Color.RED
- }
- else {
- paint.color = Color.BLUE
- }
- // Android ei oletuksena piirrä CustomViewiä uusiksi
- // siltä varalta jos data taustalla muuttuu.
- // tämän takia meidän pitää itse ilmoittaa Androidille
- // että view pitää piirtää uusiksi uudella lämpötilalla
- invalidate()
- requestLayout()
- }
- // Osa 3: Compound Controlit eli valmiista Vieweistä rakennettu oma Custom View!
- // uusi tiedosto projektiin: LatestDataView.kt (Kotlin-class)
- // tehdään tyhjä pohja Compound Controlille:
- class LatestDataView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
- ) : LinearLayout(context, attrs, defStyleAttr) {
- // alkuvaiheessa tämä riittää kun käytetään compound controlia
- // huom: onDraw ja onMeasure ym. ovat jo valmiiksi tehty LinearLayoutissa
- init {
- }
- }
- // lisätään CustomViewTesterin ulkoasuun:
- // huom: paketin nimi on projektissasi eri, viot aloittaa kirjoittamaan
- // <LatestDataView, jolloin Android Studio tunnistaa oman Custom Viewisi (compound control)
- <com.example.edistynytandroid2023verkko.LatestDataView
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- // kokeillaan muokata LatestDataView niin, että siihen voi lisätä TextViewejä lennosta:
- class LatestDataView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
- ) : LinearLayout(context, attrs, defStyleAttr) {
- // alkuvaiheessa tämä riittää kun käytetään compound controlia
- // huom: onDraw ja onMeasure ym. ovat jo valmiiksi tehty LinearLayoutissa
- init {
- // vaihdetaan LinearLayoutin orientaation pystysuuntaiseksi,
- // jotta TextViewit rakentuvat allekkain
- this.orientation = VERTICAL
- addData("Testiviesti 1!")
- addData("Testiviesti 2!")
- }
- // apufunktio/metodi, joka lisää LinearLayoutiin uuden TextViewin lennosta
- fun addData(message : String)
- {
- var newTextView : TextView = TextView(context) as TextView
- newTextView.setText(message)
- newTextView.setBackgroundColor(Color.BLACK)
- newTextView.setTextColor(Color.YELLOW)
- this.addView(newTextView)
- }
- }
- // pienennetään muita Viewejä CustomViewTesterin ulkoasussa, että mahtuu paremmin testaamaan LatestDataViewiä:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- tools:context=".CustomViewTesterFragment">
- <com.github.anastr.speedviewlib.SpeedView
- android:id="@+id/speedView"
- app:sv_withTremble="false"
- app:sv_minSpeed="-50"
- app:sv_maxSpeed="50"
- app:sv_unit="℃"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:layout_margin="10dp" />
- <com.example.edistynytandroid2023verkko.CustomTemperatureView
- android:id="@+id/customTemperatureView_test"
- android:layout_width="140dp"
- android:layout_height="140dp"
- android:layout_margin="10dp" />
- <Button
- android:id="@+id/button_set_random_temperature"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="CHANGE TEMPERATURE" />
- <com.example.edistynytandroid2023verkko.LatestDataView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp" />
- </LinearLayout>
- // tehdään kustomoitu reunus XML:n avulla.
- // uusi tiedosto kansioon res -> drawable , -> customborder.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <padding android:left="4dp" android:right="4dp" android:top="4dp" android:bottom="4dp"/>
- <stroke android:width="4dp" android:color="#52ED6C"/>
- </shape>
- // tämän jälkeen voimme käyttää "taustakuvana" tätä reunusta fragmentissa, esim:
- <com.example.edistynytandroid2023verkko.LatestDataView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:background="@drawable/customborder"/>
- // lisätään Button ulkoasuun, jotta voidaan testata paremmin LatestDataViewiä:
- <Button
- android:id="@+id/button_add_test_data"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="ADD DATA" />
- // lisätään LatestDataViewille myös id:
- <com.example.edistynytandroid2023verkko.LatestDataView
- android:id="@+id/latestdataviewtest"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:background="@drawable/customborder" />
- // Buttonilla voidaan lisätä testiarvo LatestDataViewiin näin:
- binding.buttonAddTestData.setOnClickListener {
- val randomValue : Int = (1..100).random()
- binding.latestdataviewtest.addData("Testing " + randomValue.toString())
- }
- // jotta LatestDataView ei lisää liikaa TextViewejä napista, korjataan addData-funktiota:
- // apufunktio/metodi, joka lisää LinearLayoutiin uuden TextViewin lennosta
- fun addData(message : String)
- {
- // ennen kuin lisätään uusi TextView, huolehditaan siitä
- // että LinearLayoutissa ei ole ylimääräisiä TextViewejö (max 5 kpl)
- // niin kauan kuin lukumäärä on liian suuri -> poista vanhin TextView
- while(this.childCount >= maxRows) {
- this.removeViewAt(0)
- }
- // lisätään uusi TextView
- var newTextView : TextView = TextView(context) as TextView
- newTextView.setText(message)
- newTextView.setBackgroundColor(Color.BLACK)
- newTextView.setTextColor(Color.YELLOW)
- this.addView(newTextView)
- }
- // jos halutaan että LinearLayoutin korkeus on oikea heti ohjelman alussa, voidaan tehdä tällä tavalla:
- var maxRows = 5
- // alkuvaiheessa tämä riittää kun käytetään compound controlia
- // huom: onDraw ja onMeasure ym. ovat jo valmiiksi tehty LinearLayoutissa
- init {
- // vaihdetaan LinearLayoutin orientaation pystysuuntaiseksi,
- // jotta TextViewit rakentuvat allekkain
- this.orientation = VERTICAL
- // jotta aloituskorkeus saadaan heti alussa oikeaksi, voidaan tehdä seuraavaa:
- // idea:
- // tehdään koodin muistiin uusi TextView (jota ei käytetä missään),
- // ja otetaan siitä yhden TextViewin korkeus näytöllä
- // kerrotaan tämä luku maxRows-muuttujalla (eli 5)
- // lisätään päälle mahdolliset LinearLayouting omat lisäkorkeudet (padding ym.)
- // lasketaan kaikki yhteen => tarvittava korkeus LinearLayoutille
- // tehdään uusi TextView muistiin, ja käsketään Androidia mittaamaan se tällä näytöllä
- var someTextView : TextView = TextView(context)
- someTextView.measure(0,0)
- var rowHeight = someTextView.measuredHeight
- // mitataan myös itse LinearLayout (paddingit ym.)
- this.measure(0, 0)
- // lasketaan kaikki yhteen ja asetetaan korkeus LinearLayoutissa
- var additionalHeight = this.measuredHeight + (maxRows * rowHeight)
- this.minimumHeight = additionalHeight
- }
- // kokeillaan fade-in animaatiota, tehdään res-kansioon uusi kansio: anim
- // lisätään siihen uusi tiedosto : customfade.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/linear_interpolator">
- <alpha
- android:duration="2000"
- android:fromAlpha="0.1"
- android:toAlpha="1.0" />
- </set>
- // LatestDataViewin addDatan lopussa voidaan käyttää animaatiota:
- // fade-in animaatio päälle
- val animation = AnimationUtils.loadAnimation(context, R.anim.customfade)
- // starting the animation
- newTextView.startAnimation(animation)
- // kokeillaan vielä WeatherStation-datan kanssa:
- val sdf = SimpleDateFormat("HH:mm:ss")
- val currentDate = sdf.format(Date())
- var dataText : String = "${currentDate} - Temperature: ${temperature}℃ - Humidity: ${humidity}%"
- // koska MQTT-plugin ajaa koodia ja käsittelee dataa
- // tausta-ajolla omassa säikeessään eli threadissa
- // joudumme laittamaan ulkoasuun liittyvän koodin runOnUiThread-blokin
- // sisälle. Muutoin tulee virhe että koodit toimivat eri säikeissä.
- activity?.runOnUiThread {
- // id pitää olla sama kuin fragmentin ulkoasussa LatestDataViewin id
- binding.latestdataviewtestWeatherstation.addData(dataText)
- }
- // testi: robottianimaatio: animaatiotiedostot:
- https://opengameart.org/content/animated-robot-set-png
- // muista uudelleennimetä tiedostot siten, ettei niissä ole välilyöntejä, esim. robo1.png, robo2.png jne.
- // tehdään drawable-kansioon uusi tiedosto: roboanimation.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="false">
- <item
- android:duration="100"
- android:drawable="@drawable/robo1"/>
- <item
- android:duration="100"
- android:drawable="@drawable/robo2"/>
- .... loput framet tähän ....
- </animation-list>
- // lisätään haluttuun fragmentiin ImageView:
- <ImageView
- android:background="@drawable/robo1"
- android:id="@+id/imageView_robot"
- android:layout_width="200dp"
- android:layout_height="360dp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- // animaation käynnistäminen koodissa:
- // käynnistetään robotin animaatio XML:n pohjalta
- binding.imageViewRobot.setBackgroundResource(R.drawable.roboanimation)
- val frameAnimation = binding.imageViewRobot.background as AnimationDrawable
- frameAnimation.start()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement