Un fragment :
Activity
(ne peut exister seul),Lorsqu’une activité est démarrée, elle peut ajouter, remplacer, supprimer des fragments à son interface.
build.gradle
:dependencies {
implementation "androidx.fragment:fragment:1.4.1"
}
import androidx.fragment.app.Fragment;
class ExampleFragment extends Fragment {
public ExampleFragment() {
super(R.layout.example_fragment);
}
}
… associée à un layout res/layout/example_fragment.xml
.
Tout ceci est automatisé dans Android Studio.
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://..."
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.ExampleFragment" />
res/layout/example_activity.xml
Uniquement dans le layout
FragmentContainerView
dans le layout de l’activitéandroid:name
indique la classe du fragment à insérer.public class ExampleActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null)
getSupportFragmentManager()
.beginTransaction()
.setReorderingAllowed(true)
.add(R.id.fragment_container_view, ExampleFragment.class, null)
.commit();
}
}
Avec layout + code
FragmentContainerView
dans le layout de l’activité sans spécifier l’attribut android:name
.FragmentManager
pour ajouter/remplacer/supprimer un fragment dans ce layout.Fragment
dans la méthode onCreate
de l’activité.FragmentManager
get[Support|Parent|Child]FragmentManager()
,FragmentTransaction
pour manipuler les fragments,FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragmentContainer, FragmentExample.class, null);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(null);
transaction.commit();
...
fragmentManager.popBackStack();
replace( containerId, fragmentClass, bundle )
containerId
par une nuovelle instance d’un fragmentClass
en lui transmettant un bundle
.
setTransition(id)
Ajoute un effet visuel au changement de fragment.
commit()
Exécute la transaction dès que le thread principal est prêt.
addToBackStack(name)
popBackStack(name)
Rejoue la dernière transaction à l’envers.
Les ViewModel
permettent de faire persister des données des activités et des fragments en dehors de leur cycle de vie.
Un ViewModel
:
ViewModelProvider
,LiveData<T>
,public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() { ... }
}
public class MyActivity extends Activity {
public void onCreate(Bundle state) {
MyViewModel model =
new ViewModelProvider(this)
.get(MyViewModel.class);
model.getUsers()
.observe(this, users -> {
// update UI
});
}
}
LiveData<T>
observe()
) pour réagir lorsque ses données sont modifiées.
ViewModelProvider(scope).get(Class)
scope
un objet de type Class
, sous-classe de ViewModel
. Le second appel de cette méthode d’une même classe pour un même scope retourne l’instance pré-existante.
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() { return selected; }
}
public class ListFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model =
new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}
Les SharedPreferences
stockent des associations clé-valeur
dans un fichier.
SharedPreferences
MODE_PRIVATE
depuis la v17,SharedPreferences prefs = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("high-score", newHighScore);
editor.apply();
Editor
ajoute/modifie des entrées avec putInt(key, value)
, putString(key,value)
, putFloat(key,value)
etc.apply()
modifie les préférences en mémoire mais ne les écrit pas immédiatement dans le fichier. Utiliser commit()
pour cela.int highScore = prefs.getInt("high-score", 0);
getInt(key,defaultValue)
, getString(key,defautlValue)
, etc.getString(id)
de Context
pour y accéder.
int highScore = prefs.getInt(getString(R.id.pref_high_score), 0);
onStop()
)onCreate()
)En effet, lorsque l’application est lancée, les données sont gérées de préférences dans un ViewModel
.
Un processus est composé d’un ou plusieurs threads
qui s’exécutent de manière concurrente.
Il y a plusieurs pages de la documentation sur les Threads :
Dans la suite, nous allons voir deux façon de faire des requêtes HTTP :
URL url = new URL("http://...");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setRequestMethod("GET");
connection.connect();
InputStream in = new BufferedInputStream(connection.getInputStream());
readStream(in);
}
finally {
connection.disconnect();
}
Il y a plusieurs étapes à suivre pour effectuer une requête HTTP :
setDoOutput(true)
et écrire sur getOutputStream()
1 - Attention, pour qu’une application accède à internet, elle doit déclarer qu’elle a besoin d’y accéder dans le fichier AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET" />
2 - Exemple de requête GET pour obtenir des données JSON :
URL url = new URL('http://...');
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
InputStream in = new BufferedInputStream(connection.getInputStream());
StringBuilder sb = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
sb.append(line + "\n");
}
in.close();
JSONObject json = new JSONObject(sb.toString());
C’est manuel, mais on a vite fait une méthode qui encapsule tout ça.
Volley est une bibliothèque Android qui permet de faire des requêtes HTTP simplement et rapidement.
En particulier, Volley gère une file d’attente des requêtes et permet d’abandonner des requêtes à tout moment.
dependencies {
...
implementation 'com.android.volley:volley:1.2.1'
}
Déclaration de la dépendance dans build.gradle
.
TextView textView = (TextView) findViewById(R.id.text);
StringRequest stringRequest = new StringRequest(
Request.Method.GET, "http://...",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
textView.setText("Response is: "+ response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
textView.setText("That didn't work!");
}
}
);
RequestQueue queue = Volley.newRequestQueue(this);
queue.add(stringRequest);
JSONObjectRequest
, JSONArrayRequest
, ImageRequest
.Construct(method,url,listener,errorListener)
RequestQueue
.Une RequestQueue
ayant besoin d’une Context
, il est d’usage de l’encapsuler dans un singleton.
public class MyRequestQueue {
private static MyRequestQueue instance = null;
private RequestQueue requestQueue;
private MyRequestQueue(Context context) {
requestQueue = Volley.newRequestQueue(context.getApplicationContext());
}
public static synchronized MyRequestQueue getInstance(Context context) {
if (instance == null)
instance = new MyRequestQueue(context);
return instance;
}
public static synchronized MyRequestQueue getInstance() {
return instance;
}
public <T> void add(Request<T> req) {
requestQueue.add(req);
}
}
MySingleton.getInstance(this.getApplicationContext());
MySingleton.getInstance().add(request);
Voir documentation pour exemple plus complet.
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.GET, "http://...", null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
textView.setText("Response: " + response.toString());
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) { ... }
}
);
MyRequestQueue.getInstance().add(request);
Il y a des constructeurs alternatifs, en particulier :
Construct(url,listener,errorListener)
où la méthode par défaut est GET
.Construct(method,url,jsonRequest,listener,errorListener)
où jsonRequest
peut être null ou contenir des données POST.