Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
android_20250623
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Patryk Czarnik
android_20250623
Commits
8dd7f102
Commit
8dd7f102
authored
Jun 27, 2025
by
Patryk Czarnik
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Projekt 4 - dodanie przeliczania walut !
parent
dc6720d9
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
194 additions
and
15 deletions
+194
-15
gradle.xml
Projekt4/.idea/gradle.xml
+1
-0
misc.xml
Projekt4/.idea/misc.xml
+0
-1
README.md
Projekt4/README.md
+11
-0
FirstFragment.java
...app/src/main/java/com/example/projekt4/FirstFragment.java
+26
-6
SecondFragment.java
...pp/src/main/java/com/example/projekt4/SecondFragment.java
+70
-1
SingletonPobranaTabela.java
.../main/java/com/example/waluty/SingletonPobranaTabela.java
+28
-0
fragment_second.xml
Projekt4/app/src/main/res/layout/fragment_second.xml
+58
-7
No files found.
Projekt4/.idea/gradle.xml
View file @
8dd7f102
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<project
version=
"4"
>
<component
name=
"GradleMigrationSettings"
migrationVersion=
"1"
/>
<component
name=
"GradleSettings"
>
<component
name=
"GradleSettings"
>
<option
name=
"linkedExternalProjectsSettings"
>
<option
name=
"linkedExternalProjectsSettings"
>
<GradleProjectSettings>
<GradleProjectSettings>
...
...
Projekt4/.idea/misc.xml
View file @
8dd7f102
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<project
version=
"4"
>
<component
name=
"ExternalStorageConfigurationManager"
enabled=
"true"
/>
<component
name=
"ExternalStorageConfigurationManager"
enabled=
"true"
/>
<component
name=
"ProjectRootManager"
version=
"2"
languageLevel=
"JDK_21"
default=
"true"
project-jdk-name=
"jbr-21"
project-jdk-type=
"JavaSDK"
>
<component
name=
"ProjectRootManager"
version=
"2"
languageLevel=
"JDK_21"
default=
"true"
project-jdk-name=
"jbr-21"
project-jdk-type=
"JavaSDK"
>
...
...
Projekt4/README.md
View file @
8dd7f102
...
@@ -9,9 +9,20 @@ W tym projektcie:
...
@@ -9,9 +9,20 @@ W tym projektcie:
-
większość napisów jest umieszczona w
`strings.xml`
-
większość napisów jest umieszczona w
`strings.xml`
-
w podziale na pliki oparliśmy się o wzór z inicjalnego projektu → dwa fragmenty
-
w podziale na pliki oparliśmy się o wzór z inicjalnego projektu → dwa fragmenty
Nie traktujcie tego projektu jako wzoru do naśladowania w kontekście wykorzystania fragmentów
– nie robię tu tego w zalecany sposób.
Bardziej zależało mi na pokazaniu pozostałych elementów, a gotowy wzorzec, o który oparliśmy się,
akurat używa techniki fragmentów.
## Waluty
## Waluty
Aplikacja na żądanie pobiera kursy walut w formacie XML z serwera NBP i wyświetla pobrane dane.
Aplikacja na żądanie pobiera kursy walut w formacie XML z serwera NBP i wyświetla pobrane dane.
Pobieranie jest wykonywane w tle przez wątek / executor.
Pobieranie jest wykonywane w tle przez wątek / executor.
Aby aplikacja miała uprawnienie „internet”, trzeba było dodać stosowny wpis do manifestu.
Aby aplikacja miała uprawnienie „internet”, trzeba było dodać stosowny wpis do manifestu.
**Uzupełnione po zajęciach:**
Na drugim ekranie (
*Second Fragment*
) dodałem wybór waluty z rozwijanej listy (
`Spinner`
)
i możliwość przeliczania kwot poprzez wpisanie kwoty w górne lub dolne pole i zatwierdzenie (na komputerze po prostu Enter).
Aby oba ekrany (oddzielne klasy w Javie) miały dostęp do wspólnych danych, użyłem prymitywnego
lecz skutecznego rozwiązania - klasy typu
*Singleton*
.
Projekt4/app/src/main/java/com/example/projekt4/FirstFragment.java
View file @
8dd7f102
...
@@ -16,12 +16,24 @@ import com.example.projekt4.databinding.FragmentFirstBinding;
...
@@ -16,12 +16,24 @@ import com.example.projekt4.databinding.FragmentFirstBinding;
import
com.example.waluty.ExchangeRatesTable
;
import
com.example.waluty.ExchangeRatesTable
;
import
com.example.waluty.ObslugaNBP
;
import
com.example.waluty.ObslugaNBP
;
import
com.example.waluty.Rate
;
import
com.example.waluty.Rate
;
import
com.example.waluty.SingletonPobranaTabela
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.Executors
;
/* Ważnym aspektem tego przykładu jest pobieranie danych z sieci, które trzeba wykonać w tle,
aby nie blokować głównego wątku obsługującego interfejs użytkownika.
Gdybyśmy pobieranie wykonali bezpośrednio w procedurze obsługi zdarzenia, aplikacja
byłaby nieresponsywna w trakcie tego pobierania.
Android sam wykrywa taką sytuację i przerywa pobieranie wyjątkiem.
Na potrzeby pobierania w tle przygotowujemy dodatkowy wątek w formie tzw. Executora
- jest to rozwiązanie, które można łatwo skalować zwiększając liczbę wątków w puli.
U nas będzie to jednak pojedynczy dodatkowy wątek.
*/
public
class
FirstFragment
extends
Fragment
{
public
class
FirstFragment
extends
Fragment
{
private
ExecutorService
watkiPobierajace
=
Executors
.
newSingleThreadExecutor
();
// albo fixedThreadPool
private
final
ExecutorService
watkiPobierajace
=
Executors
.
newSingleThreadExecutor
();
// private ExecutorService watkiPobierajace = Executors.newFixedThreadPool(4);
private
FragmentFirstBinding
binding
;
private
FragmentFirstBinding
binding
;
...
@@ -42,20 +54,28 @@ public class FirstFragment extends Fragment {
...
@@ -42,20 +54,28 @@ public class FirstFragment extends Fragment {
NavHostFragment
.
findNavController
(
FirstFragment
.
this
)
NavHostFragment
.
findNavController
(
FirstFragment
.
this
)
.
navigate
(
R
.
id
.
action_FirstFragment_to_SecondFragment
)
.
navigate
(
R
.
id
.
action_FirstFragment_to_SecondFragment
)
);
);
// jeśli waluty były już pobierane - wykorzystaj dane zapisane w pamięci
ExchangeRatesTable
table
=
SingletonPobranaTabela
.
getInstance
().
getTable
();
if
(
table
!=
null
)
{
odswiezWidok
(
table
);
}
// do przycisku "Pobierz bieżące" przypisujemy akcję pobierania
binding
.
buttonPobierzBiezace
.
setOnClickListener
(
v
->
{
binding
.
buttonPobierzBiezace
.
setOnClickListener
(
v
->
{
wykonajCalePobieranie
();
wykonajCalePobieranie
();
});
});
// TODO dla chętnych - dodaj operację pobierania kursów archiwalnych w oparciu o datę
}
}
private
void
wykonajCalePobieranie
()
{
private
void
wykonajCalePobieranie
()
{
Log
.
d
(
"Waluty"
,
"
pobieranie rozpoczęt
e"
);
Log
.
d
(
"Waluty"
,
"
zlecam pobierani
e"
);
//
komunikacji sieciowej nie wolno robić w wątku głównym
//
zlecam zadanie do wykonania w tle
watkiPobierajace
.
submit
(()
->
{
watkiPobierajace
.
submit
(()
->
{
Log
.
d
(
"Waluty"
,
"
a kuku 1
"
);
Log
.
d
(
"Waluty"
,
"
executor: początek pobierania
"
);
ExchangeRatesTable
table
=
ObslugaNBP
.
pobierzBiezaceKursy
();
ExchangeRatesTable
table
=
ObslugaNBP
.
pobierzBiezaceKursy
();
Log
.
d
(
"Waluty"
,
"a kuku 2"
);
SingletonPobranaTabela
.
getInstance
().
setTable
(
table
);
// aby SecondFragment też mógł korzystać...
Log
.
d
(
"Waluty"
,
"executor: koniec pobierania"
);
// jednak zmiany w wyglądzie UI powinny być wykonane przez wątek główny
// jednak zmiany w wyglądzie UI powinny być wykonane przez wątek główny
// - zlecam wątkowi głównemu wykonanie "gdy znajdzie na to czas"
this
.
getActivity
().
runOnUiThread
(()
->
{
this
.
getActivity
().
runOnUiThread
(()
->
{
odswiezWidok
(
table
);
odswiezWidok
(
table
);
});
});
...
...
Projekt4/app/src/main/java/com/example/projekt4/SecondFragment.java
View file @
8dd7f102
...
@@ -4,16 +4,29 @@ import android.os.Bundle;
...
@@ -4,16 +4,29 @@ import android.os.Bundle;
import
android.view.LayoutInflater
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.view.ViewGroup
;
import
android.widget.AdapterView
;
import
android.widget.ArrayAdapter
;
import
android.widget.Spinner
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.NonNull
;
import
androidx.fragment.app.Fragment
;
import
androidx.fragment.app.Fragment
;
import
androidx.navigation.fragment.NavHostFragment
;
import
androidx.navigation.fragment.NavHostFragment
;
import
com.example.projekt4.databinding.FragmentSecondBinding
;
import
com.example.projekt4.databinding.FragmentSecondBinding
;
import
com.example.waluty.ExchangeRatesTable
;
import
com.example.waluty.Rate
;
import
com.example.waluty.SingletonPobranaTabela
;
public
class
SecondFragment
extends
Fragment
{
import
java.math.BigDecimal
;
public
class
SecondFragment
extends
Fragment
{
private
FragmentSecondBinding
binding
;
private
FragmentSecondBinding
binding
;
private
String
[]
codes
=
{};
private
KtoraKwota
ktoraKwotaBylaWpsiana
=
null
;
private
ExchangeRatesTable
table
;
private
Rate
selectedRate
;
private
enum
KtoraKwota
{
WALUTA
,
PLN
};
@Override
@Override
public
View
onCreateView
(
public
View
onCreateView
(
...
@@ -29,10 +42,66 @@ public class SecondFragment extends Fragment {
...
@@ -29,10 +42,66 @@ public class SecondFragment extends Fragment {
public
void
onViewCreated
(
@NonNull
View
view
,
Bundle
savedInstanceState
)
{
public
void
onViewCreated
(
@NonNull
View
view
,
Bundle
savedInstanceState
)
{
super
.
onViewCreated
(
view
,
savedInstanceState
);
super
.
onViewCreated
(
view
,
savedInstanceState
);
table
=
SingletonPobranaTabela
.
getInstance
().
getTable
();
codes
=
table
!=
null
?
table
.
getCodes
()
:
new
String
[
0
];
Spinner
spinner
=
binding
.
spinnerWyborWalut
;
ArrayAdapter
<
String
>
adapter
=
new
ArrayAdapter
<>(
this
.
getContext
(),
android
.
R
.
layout
.
simple_spinner_item
,
codes
);
adapter
.
setDropDownViewResource
(
android
.
R
.
layout
.
simple_spinner_dropdown_item
);
spinner
.
setAdapter
(
adapter
);
binding
.
buttonSecond
.
setOnClickListener
(
v
->
binding
.
buttonSecond
.
setOnClickListener
(
v
->
NavHostFragment
.
findNavController
(
SecondFragment
.
this
)
NavHostFragment
.
findNavController
(
SecondFragment
.
this
)
.
navigate
(
R
.
id
.
action_SecondFragment_to_FirstFragment
)
.
navigate
(
R
.
id
.
action_SecondFragment_to_FirstFragment
)
);
);
binding
.
spinnerWyborWalut
.
setOnItemSelectedListener
(
new
AdapterView
.
OnItemSelectedListener
()
{
@Override
public
void
onItemSelected
(
AdapterView
<?>
parent
,
View
view
,
int
position
,
long
id
)
{
selectedRate
=
table
.
find
(
codes
[
position
]);
odwiezWidok
();
}
@Override
public
void
onNothingSelected
(
AdapterView
<?>
parent
)
{
selectedRate
=
null
;
odwiezWidok
();
}
});
// zdarzenia po wpisaniu liczby w górnym lub dolnym polu i zatwierdzeniu
// (na komputerze to "naciśnięcie enter")
binding
.
editTextKwota1
.
setOnClickListener
(
v
->
{
ktoraKwotaBylaWpsiana
=
KtoraKwota
.
WALUTA
;
odwiezWidok
();
});
binding
.
editTextKwota2
.
setOnClickListener
(
v
->
{
ktoraKwotaBylaWpsiana
=
KtoraKwota
.
PLN
;
odwiezWidok
();
});
}
private
void
odwiezWidok
()
{
binding
.
textViewCurrencyName
.
setText
(
selectedRate
==
null
?
""
:
selectedRate
.
getCurrency
());
binding
.
labelKwota1
.
setText
(
selectedRate
==
null
?
"Kwota"
:
(
"Kwota "
+
selectedRate
.
getCode
()));
if
(
selectedRate
==
null
||
ktoraKwotaBylaWpsiana
==
null
)
{
binding
.
editTextKwota1
.
setText
(
""
);
binding
.
editTextKwota2
.
setText
(
""
);
return
;
}
switch
(
ktoraKwotaBylaWpsiana
)
{
case
WALUTA:
{
BigDecimal
kwota1
=
new
BigDecimal
(
binding
.
editTextKwota1
.
getText
().
toString
().
replace
(
','
,
'.'
));
BigDecimal
kwota2
=
selectedRate
.
przeliczNaZlote
(
kwota1
);
binding
.
editTextKwota2
.
setText
(
String
.
valueOf
(
kwota2
));
break
;
}
case
PLN:
{
BigDecimal
kwota2
=
new
BigDecimal
(
binding
.
editTextKwota2
.
getText
().
toString
().
replace
(
','
,
'.'
));
BigDecimal
kwota1
=
selectedRate
.
przeliczNaWalute
(
kwota2
);
binding
.
editTextKwota1
.
setText
(
String
.
valueOf
(
kwota1
));
break
;
}
}
}
}
@Override
@Override
...
...
Projekt4/app/src/main/java/com/example/waluty/SingletonPobranaTabela.java
0 → 100644
View file @
8dd7f102
package
com
.
example
.
waluty
;
/* Ta klasa działa jako singleton, tzn w pamięci jest utrzymywany pojedynczy obiekt tej klasy,
dostępny za pomocą getInstance. Dostęp do niego mają różne klasy aplikacji
- w tym przypadku FirstFragment i SenondFragment.
Rozwiązanie przypomina stosowanie zmiennych globalnych w innych językach programowania,
dlatego przez wiele osób jest uważane za niezbyt porządne.
Ale jego zaletą jest prostota i łatwość dostępu do wspólnych danych przez różne fragmenty aplikacji.
*/
public
class
SingletonPobranaTabela
{
private
static
SingletonPobranaTabela
instance
;
private
ExchangeRatesTable
table
;
public
synchronized
static
SingletonPobranaTabela
getInstance
()
{
if
(
instance
==
null
)
{
instance
=
new
SingletonPobranaTabela
();
}
return
instance
;
}
public
synchronized
ExchangeRatesTable
getTable
()
{
return
this
.
table
;
};
public
synchronized
void
setTable
(
ExchangeRatesTable
table
)
{
this
.
table
=
table
;
};
}
Projekt4/app/src/main/res/layout/fragment_second.xml
View file @
8dd7f102
...
@@ -16,20 +16,70 @@
...
@@ -16,20 +16,70 @@
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"@string/previous"
android:text=
"@string/previous"
app:layout_constraintBottom_toTopOf=
"@id/textview_second"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
app:layout_constraintTop_toTopOf=
"parent"
/>
<Spinner
android:id=
"@+id/spinnerWyborWalut"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"64dp"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/button_second"
/>
<TextView
<TextView
android:id=
"@+id/text
view_second
"
android:id=
"@+id/text
ViewCurrencyName
"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"16dp"
android:text=
""
android:text=
"@string/lorem_ipsum"
android:layout_marginStart=
"16dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/spinnerWyborWalut"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintBaseline_toBaselineOf=
"@+id/spinnerWyborWalut"
/>
<TextView
android:id=
"@+id/labelKwota1"
android:labelFor=
"@id/editTextKwota1"
android:layout_width=
"80dp"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"64dp"
android:text=
"Kwota ___"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/spinnerWyborWalut"
/>
<EditText
android:id=
"@+id/editTextKwota1"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:autofillHints=
"0"
android:ems=
"10"
android:inputType=
"numberDecimal"
app:layout_constraintStart_toEndOf=
"@id/labelKwota1"
app:layout_constraintBaseline_toBaselineOf=
"@+id/labelKwota1"
/>
<TextView
android:id=
"@+id/labelKwota2"
android:labelFor=
"@id/editTextKwota2"
android:layout_width=
"80dp"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"24dp"
android:text=
"Kwota PLN"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/button_second"
/>
app:layout_constraintTop_toBottomOf=
"@+id/labelKwota1"
/>
<EditText
android:id=
"@+id/editTextKwota2"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:autofillHints=
"0"
android:ems=
"10"
android:inputType=
"numberDecimal"
app:layout_constraintStart_toEndOf=
"@id/labelKwota2"
app:layout_constraintBaseline_toBaselineOf=
"@+id/labelKwota2"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.core.widget.NestedScrollView>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment