Niestandardowe okno dialogowe

Niestandardowy układ widoku okna dialogowego przygotowuje się na tych samych zasadach co dowolny układ widoku zapisany w pliku XML. Aby podpiąć taki widok do okna modalnego klasy AlerDialog korzysta się z metody tej klasy- setView(). Parametrem tej metody jest przygotowany układ widoku (layout).

Celem jest utworzenie dwóch niestandardowych okien komunikatów. Okna mają mieć inny kształt ramki oraz inny kształt klawiszy. Patrz poniższa ilustracja.

niestandardowe okno dialogowe Kotlin Android Studio

Treścią komunikatu jest licznik kliknięć.

Przygotowanie projektu rozwiązania

Utwórz pusty projekt. Wybierz język Kotlin. W głównym widoku aplikacji osadź jedną kontrolkę typu TextView i cztery kontrolki typu Button. Wprowadź odpowiedni tekst.

układ okna niestandardowe okno dialogowe Kotlin Android Studio

Zawartość pliku XML proponowanego głównego układu widoku podaję poniżej.

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="92dp"
        android:text="Licznik kliknięć"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="162dp"
        android:layout_marginTop="40dp"
        android:layout_marginEnd="162dp"
        android:text="Klikaj"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Pokaż komunikat (wersja 1)"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="36dp"
        android:text="Pokaż komunikat (wersja 2)"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="52dp"
        android:text="Zamknij"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.501"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button3" />

</androidx.constraintlayout.widget.ConstraintLayout>

Aby zmiany kształtu klawiszy były widoczne w tworzonej aplikacji musimy zmienić główny typ motywu (theme) aplikacji. Przejdź do pliku res/ values/ themes/ themes.xml i zmień rodzica (parent) motywu na przykład na Theme.AppCompat.Light.NoActionBar

Wskazówka:


<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <!--<style name="Base.Theme.OknoDialogoweNiestandardowe"
	parent="Theme.Material3.DayNight.NoActionBar">-->
    <style name="Base.Theme.OknoDialogoweNiestandardowe"
	parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your light theme here. -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
    </style>
    <style name="Theme.OknoDialogoweNiestandardowe"
	parent="Base.Theme.OknoDialogoweNiestandardowe" />
</resources>

W podanym przykładzie nazwa tworzonego motywu to Theme.OknoDialogoweNiestandardowe. Można przypisać swoją inną. Sprawdź czy w pliku manifestu ta nazwa się pojawia.

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        
        android:theme="@style/Theme.OknoDialogoweNiestandardowe"
        
    </application>

</manifest>

Funkcja licznika kliknięć i zamknięcia aplikacji

Tworzony projekt bazuje na projekcie opisanym pod tym linkiem zobacz. Musimy utworzyć zmienną do przechowywania licznika kliknięć i dwie funkcje. Jedna zwiększająca licznik, a druga zamykająca aplikację.

W głównym pliku tworzonego rozwiązania zapiszemy obie funkcje oraz zmienną. Kliknięcia podpinamy pod obsługę pierwszego klawisza. Patrz kod poniżej.

Wskazówka:


class MainActivity : AppCompatActivity() {
    var licznik=0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //zliczaj klikniecia
        val bt1=findViewById<Button>(R.id.button1)
        bt1.setOnClickListener{
            licznik=Licznik(licznik)
            val tv=findViewById<TextView>(R.id.textView)
            tv.setText("Licznik kliknięć: "+licznik)
        }
    }
    private fun Zamknij(){
        this.finish()
        System.exit(0)
    }

    private fun Licznik(i:Int):Int{
        return i+1
    }

}

Skompiluj projekt i sprawdź zliczanie klikania.

licznik kliknięć Kotlin Android Studio

Niestandardowy kształt ramki okna dialogowego

Do projektu dodamy plik XML, w którym zdefiniujemy niestandardowy kształt ramki okna. Ramkę okna przygotujemy z zaokrąglonymi rogami, gradientowym tłem i cienka obwódką wokół ramki. W lokalizacji res/ drawable tworzymy plik zasobów

kształt ramki okno dialogowe Kotlin Android Studio

Nazwę pliku podajemy jako ksztalt_okno_dialogowe.xml, Root element ustawiamy na shape. Definiujemy właściwości ramki.

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle"
        >
        <!--rogi, prawy dolny mniejszy-->
        <corners
            android:radius="40dp"
            android:bottomRightRadius="10dp"
            />
        <!-- gradientowe tło-->
        <gradient
            android:angle="45"
            android:centerX="200"
            android:centerY="200"
            android:startColor="#0189ff"
            android:endColor="#01f1fa"
            android:gradientRadius="100"
            android:type="linear"
            />
        <!--biała obwódka-->
        <stroke
            android:width="3dp"
            android:color="#FFFFFF"
            />
</shape>

Układ widoku okna komunikatu pierwszego

Do folderu res/ layout dodajemy plik XML układu widoku okna komunikatu. W widoku osadzamy jedną kontrolkę TextView (dla treści komunikatu) i dwie kontroli typu Button.

ukjład kształt ramki okno dialogowe Kotlin Android Studio

Dla prezentowanego widoku przypisano poniższe właściwości

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="2dp"
        android:background="@color/czerwony"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="Treść komunikatu"
        android:textColor="@color/white"
        android:textSize="24sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button5"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="48dp"
        android:layout_marginBottom="48dp"
        android:text="Zamknij okno"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2"
        app:layout_constraintVertical_bias="0.494" />

    <Button
        android:id="@+id/button6"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:layout_marginEnd="24dp"
        android:layout_marginBottom="48dp"
        android:text="Zakończ aplikację"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

</androidx.constraintlayout.widget.ConstraintLayout>

Na tym etapie tworzenia aplikacji wszystko jest już przygotowane aby wywołać komunikat. W momencie wywoływania okna komunikatu podmieniamy w kodzie standardowy układ okna na przygotowany przez nas.

Funkcja tworzenia niestandardowego okna komunikatu

W tworzonym rozwiązaniu napiszemy jedną funkcję tworzącą niestandardowe okno komunikatu. Ta jedną funkcją obsłużymy dwie różne wersje okna komunikatu. Funkcja będzie posiadać parametr widoku układu okna komunikatu. Tworzenie okna jest dwuetapowe.

Zmienna BudowaOknaAlertu=AlertDialog.Builder() służy do ustawiania parametrów ramki i umożliwia dostęp do osadzonych kontrolek w widoku okna.

Zmienna oknoAlertu=BudowaOknaAlertu.create() pozwala skorzystać z metod pokazania okna (show) czy też programowego zamknięcia okna (cancel) jak i kilkunastu innych.

Patrz poniższy kod

Wskazówka:


private fun Komunikat(idUklad:Int){
    val BudowaOknaAlertu=AlertDialog.Builder(this,R.style.UstawieniaAlertDialog)
    BudowaOknaAlertu.setTitle("TYTUŁ OKNA")
    val widok=layoutInflater.inflate(idUklad,null)
    //ustaw niestandardowy styl układu
    BudowaOknaAlertu.setView(widok)
    //wyłącz zamknięcie okna przez klikniecie w dowolne miejsce ekranu
    BudowaOknaAlertu.setCancelable(false)
    //wyslij tekst komunikatu
    val tv=widok.findViewById<TextView>(R.id.textView2)
    tv.setText("Licznik kliknięć: "+licznik)
    //utwórz zmodyfikowane okno dialogowe alertu
    val oknoAlertu=BudowaOknaAlertu.create()
    //obsłuz klawisz zamykania okna dialogowego
    val btZamknijOkno=widok.findViewById<Button>(R.id.button5)
    btZamknijOkno.setOnClickListener{
            //zamknij tylko okno dialogowe
            oknoAlertu.cancel()
        }
    //obsłuz klawisz zamykania całej aplikacji
    val btZakonczAplikacje=widok.findViewById<Button>(R.id.button6)
    btZakonczAplikacje.setOnClickListener{
         Zamknij()
        }
    oknoAlertu.show();
}

W głównym pliku aplikacji wywołamy obsługę klawisza pokazującego okno komunikatu dla wersji pierwszej. Patrz poniższy kod

Wskazówka:


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //zliczaj klikniecia
    val bt1=findViewById<Button>(R.id.button1)
    bt1.setOnClickListener{
        licznik=Licznik(licznik)
        val tv=findViewById<TextView>(R.id.textView)
        tv.setText("Licznik kliknięć: "+licznik)
    }
    //obsługa klawisza pokaz komunikat wersja 1
    val bt2=findViewById<Button>(R.id.button2)
    bt2.setOnClickListener{
        Komunikat(R.layout.okno_dialogowe_wersja_1)
    }
}

Skompiluj program i sprawdź działanie. Aplikacja powinna wyświetlać stan licznika i reagować na wybór klawisza okna dialogowego.

obsługa okno dialogowe Kotlin Android Studio

Zmiana kształtu klawiszy niestandardowego okna dialogowego

Na podstawie tematu opisanego pod linkiem zobacz zmienimy kształty klawiszy okna dialogowego. Do projektu dodajemy plik XML w lokalizacji res/ drawable/ kształt_przycik_okragly.xml. Właściwości kształtu przypiszemy na owalne z gradientowym tłem i białą obwódką. Dodamy dwa stany aktywności klawisza: stan normalny i stan wciśnięty. Wciśnięcie klawisza zaakcentujemy zmianą koloru tła gradientu. Poniżej zawartość utworzonego pliku

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="oval"
            >
            <!--okrag o promieniu 34dp-->
            <corners android:radius="34dp"/>
            <!-- gradientowe tło-->
            <gradient
                android:angle="45"
                android:centerX="0"
                android:centerY="0"
                android:startColor="#B4EC74"
                android:endColor="#15AF1B"
                android:gradientRadius="100"
                android:type="linear"
                />
            <!--biała obwódka-->
            <stroke
                android:width="5dp"
                android:color="#FFFFFF"
                />
        </shape>
    </item>
    <!-- stan normalny-->
    <item>
        <shape
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="oval"
            >
            <!--okrag oi promieniu 40dp-->
            <corners android:radius="40dp"/>
            <!-- gradientowe tło-->
            <gradient
                android:angle="45"
                android:centerX="0"
                android:centerY="0"
                android:startColor="#BA0600"
                android:endColor="#F6673A"
                android:gradientRadius="100"
                android:type="linear"
                />
            <!--biała obwódka-->
            <stroke
                android:width="3dp"
                android:color="#FFFFFF"
                />
        </shape>
    </item>
</selector>

Zmiana stylu klawiszy niestandardowego okna dialogowego

W wersji drugiej okna komunikatu oprócz kształtu klawisza zmienimy jego styl. Dodamy czcionkę pogrubioną z cieniowaniem. Styl możemy zdefiniować w osobnym pliku XML lub dopisać jako kolejny w pliku już istniejącym. Wybieramy drugą możliwość.

Otwieramy plik res/ values/ themes/ themes.xml i dopisujemy nowy styl o nazwie styl klawiszy. Patrz poniżej

Wskazówka:


<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <!--<style name="Base.Theme.OknoDialogoweNiestandardowe" 
	parent="Theme.Material3.DayNight.NoActionBar">-->
    <style name="Base.Theme.OknoDialogoweNiestandardowe" 
		parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your light theme here. -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
    </style>
    <style name="Theme.OknoDialogoweNiestandardowe" 
	parent="Base.Theme.OknoDialogoweNiestandardowe" />
    <!--USTAWIENIE KSZTAŁTU OKNA DIALOGOWEGO-->
    <style name="UstawieniaAlertDialog"
           parent="Theme.AppCompat.Light.Dialog.Alert">
      <item name="android:windowBackground">
          @drawable/ksztalt_okno_dialogowe
      </item>
    </style>
    <style name="stylKlawiszy">
        <item name="android:textColor">#FFFFFF</item>
        <item name="android:gravity">center</item>
        <item name="android:layout_margin">10dp</item>
        <item name="android:textSize">25sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:shadowColor">#AD0A0A0A</item>
        <item name="android:shadowDx">5</item>
        <item name="android:shadowDy">5</item>
        <item name="android:shadowRadius">2</item>
    </style>
</resources>

Układ widoku drugiego okna komunikatu

Dodajemy nowy plik XML przeznaczony na układ widoku drugiego okna komunikatu. W lokalizacji tworzymy plik res/ layout/ okno_dialogowe_wersja_2.xml. Do pliku kopiujemy zawartość poprzedniego pliku widoku okna komunikatu wersji pierwszej. Przy kopiowaniu zostawiamy takie same identyfikatory jak w oknie komunikatu w wersji pierwszej. Pozostawienie tych samych identyfikatorów umożliwi wykorzystanie tej samej funkcji wywołującej okno komunikatu.

Po skopiowaniu dodaj do parametrów kontrolek Button te właściwości

Wskazówka:


android:background="@drawable/ksztalt_przycisk_okragly"
style="@style/stylKlawiszy"

Zawartość pliku okno_dialogowe_wersja_2.xml

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="2dp"
        android:background="@color/czerwony"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="Treść komunikatu"
        android:textColor="@color/white"
        android:textSize="24sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button5"
        style="@style/stylKlawiszy"
        android:layout_width="164dp"
        android:layout_height="162dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="48dp"
        android:layout_marginBottom="48dp"
        android:background="@drawable/ksztalt_przycisk_okragly"
        android:text="Zamknij okno"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <Button
        android:id="@+id/button6"
        style="@style/stylKlawiszy"
        android:layout_width="167dp"
        android:layout_height="158dp"
        android:layout_marginTop="48dp"
        android:layout_marginEnd="24dp"
        android:layout_marginBottom="48dp"
        android:background="@drawable/ksztalt_przycisk_okragly"
        android:text="Zakończ aplikację"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

</androidx.constraintlayout.widget.ConstraintLayout>

Na podglądzie widoku układu okna komunikatu w wersji drugiej pokaże się zmieniony kształt klawiszy

kształt ramki okno dialogowe Kotlin Android Studio

Dodajemy obsługę klawisza wywołującego komunikat w wersji drugiej. W głównym pliku aplikacji w metodzie onCreate() wprowadzamy kod obsługi kliknięcia w kolejny przycisk (tu button3). Zwróć uwagę, że zmieniono identyfikator okna układu komunikatu w funkcji Komunikat()

Wskazówka:


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //zliczaj klikniecia
    val bt1=findViewById<Button>(R.id.button1)
    bt1.setOnClickListener{
        licznik=Licznik(licznik)
        val tv=findViewById<TextView>(R.id.textView)
        tv.setText("Licznik kliknięć: "+licznik)
    }
    //obsługa klawisza pokaz komunikat wersja 1
    val bt2=findViewById<Button>(R.id.button2)
    bt2.setOnClickListener{
        Komunikat(R.layout.okno_dialogowe_wersja_1)
    }
    //obsługa klawisza pokaz komunikat wersja 2
    val bt3=findViewById<Button>(R.id.button3)
    bt3.setOnClickListener{
        Komunikat(R.layout.okno_dialogowe_wersja_2)
    }
}

Uruchom aplikację i sprawdź zachowanie klawiszy okna komunikatu dla wersji drugiej.

kształt ramki i klawiszy okno dialogowe Kotlin Android Studio

Pełny kod pliku MainActivity.kt

Wskazówka:


import androidx.appcompat.app.AlertDialog
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    var licznik=0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //zliczaj klikniecia
        val bt1=findViewById<Button>(R.id.button1)
        bt1.setOnClickListener{
            licznik=Licznik(licznik)
            val tv=findViewById<TextView>(R.id.textView)
            tv.setText("Licznik kliknięć: "+licznik)
        }
        //obsługa klawisza pokaz komunikat wersja 1
        val bt2=findViewById<Button>(R.id.button2)
        bt2.setOnClickListener{
            Komunikat(R.layout.okno_dialogowe_wersja_1)
        }
        //obsługa klawisza pokaz komunikat wersja 2
        val bt3=findViewById<Button>(R.id.button3)
        bt3.setOnClickListener{
            Komunikat(R.layout.okno_dialogowe_wersja_2)
        }
    }
    private fun Zamknij(){
        this.finish()
        System.exit(0)
    }

    private fun Licznik(i:Int):Int{
        return i+1
    }

    private fun Komunikat(idUklad:Int){
        val BudowaOknaAlertu=AlertDialog.Builder(this,R.style.UstawieniaAlertDialog)
        BudowaOknaAlertu.setTitle("TYTUŁ OKNA")
        val widok=layoutInflater.inflate(idUklad,null)
        //ustaw niestandardowy styl układu
        BudowaOknaAlertu.setView(widok)
        //wyłącz zamknięcie okna przez klikniecie w dowolne miejsce ekranu
        BudowaOknaAlertu.setCancelable(false)
        //wyslij tekst komunikatu
        val tv=widok.findViewById<TextView>(R.id.textView2)
        tv.setText("Licznik kliknięć: "+licznik)
        //utwórz zmodyfikowane okno dialogowe alertu
        val oknoAlertu=BudowaOknaAlertu.create()
        //obsłuz klawisz zamykania okna dialogowego
        val btZamknijOkno=widok.findViewById<Button>(R.id.button5)
        btZamknijOkno.setOnClickListener{
                //zamknij tylko okno dialogowe
                oknoAlertu.cancel()
            }
        //obsłuz klawisz zamykania całej aplikacji
        val btZakonczAplikacje=widok.findViewById<Button>(R.id.button6)
        btZakonczAplikacje.setOnClickListener{
             Zamknij()
            }
        oknoAlertu.show();
    }

}

Pełny kod manifestu aplikacji

Wskazówka:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.OknoDialogoweNiestandardowe"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Układ okresowy- kod qr
Układ okresowy

Układ okresowy pierwiastków- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
Alkomat- wirtualny test kod qr
Alkomat- wirtualny test

Alkomat- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
Taklarz- olinowanie stałe kod qr
Olinowanie stałe- kalkulator średnic

Olinowanie stałe- darmowa aplikacja na Androida

Pobierz ze sklepu Google Play
przepis na gogfry

Przepis na gofry

zobacz
przepis na bitą śmietanę

Przepis na bitą śmietanę

zobacz