IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Menus

Els menús són un component d'interfície d'usuari molt comuns en molts tipus d'aplicacions. Android proporciona una API de menús que permet presentar accions d'usuari d'una forma familiar i consistent. Els menús es gestionen principalment a partir de la Vista Android:

 android.view.Menu

Des de la versió Android 3.0 (API level 11), ja no és necessari proveir d'un botó de menú dedicat. Cal tenir en compte que a més s'imposa el paradigma de disseny basat en Action Bar. Ja no és necessari proveir d'un menú amb totes les opcions sinó que es poden destacar les opcions de menú més importants posant-les a l'action bar.


Els menús d'accions es poden veure com una col·lecció Java, és a dir un grup d'objectes. Cada objecte del grup s'anomena menu item. Els menús tenen les accions més importants d'una activitat android: buscar, configuració, crear un email, etc...

Per mostrar el menú a l'usuari tenim dos opcions:

  • Android 2.3 o anterior: cal prémer el botó de menú
  • Android 3.0 o superior: El menú d'opcions es mostra en dos parts:
  • On-screen actions: Són les opcions de menú (menu items) que apareixen a l'Action Bar. Representen les accions més importants.
  • Overflow options: conté els ítems de menú secundaris. El boto de menú es considera deprecated

Hi a diferents tipus de menú:

Options Menu

El menú d'opcions és on hem d'incloure accions i altres opcions que són rellevants de l'activitat sobre la que estem treballant, com serien "Cerca", "Composar correu", i "Configuració".

Els items que apareixeran dintre del menú d'opcions dependrà de la versió que s'hagi utilitzat per desenvolupar l'aplicació:

Si hem desenvolupat l'aplicació per a Android 2.3.x (API level 10) o menor, els continguts del menú d'opcions apareixeran a baix de la pantalla quan l'usuari prem el botó de menú, com mostrem a la figura 1. Quan s'ha obert, la primera part visible es una porció de l'icono menu, el qual soporta un total de sis botons. Si el menú inclou més botons, Android posa el sisé item i la resta dintre del menú overflow, el qual l'usuari pot obrir clicant l'opció "More".

Si estem desenvolupant amb Android 3.0 (API level 11) i superior, els items del menu opcions estan disponibles a la barra d'accions. Per defecte, el sistema posa tots els items dintre el botó overflow, el qual l'usuari pot desplegar amb el botó overflow a la dreta de la barra d'accions (o prement el botó menu del nostre dispositiu Android, si està disponible).

Hugolucas actionbarAntic.jpeg

Figure 1. Options menu in the Browser, on Android 2.3.

Hugolucas actionbarNou.jpeg

Figure 2. Action bar from the Honeycomb Gallery app, showing navigation tabs and a camera action item (plus the action overflow button).

Contextual menu

A contextual menu offers actions that affect a specific item or context frame in the UI. You can provide a context menu for any view, but they are most often used for items in a ListView, GridView, or other view collections in which the user can perform direct actions on each item.

There are two ways to provide contextual actions:

In a floating context menu. A menu appears as a floating list of menu items (similar to a dialog) when the user performs a long-click (press and hold) on a view that declares support for a context menu. Users can perform a contextual action on one item at a time.

In the contextual action mode. This mode is a system implementation of ActionMode that displays a contextual action bar at the top of the screen with action items that affect the selected item(s). When this mode is active, users can perform an action on multiple items at once (if your app allows it).

Hugolucas contextualmenu.jpeg

Popup menu

A PopupMenu is a modal menu anchored to a View. It appears below the anchor view if there is room, or above the view otherwise. It's useful for:

Providing an overflow-style menu for actions that relate to specific content (such as Gmail's email headers, shown in figure 4).

Note: This is not the same as a context menu, which is generally for actions that affect selected content. For actions that affect selected content, use the contextual action mode or floating context menu.

Providing a second part of a command sentence (such as a button marked "Add" that produces a popup menu with different "Add" options).

Providing a drop-down similar to Spinner that does not retain a persistent selection.

NOTA: PopupMenu is available with API level 11 and higher.

Hugolucas popupmenu.jpeg

Action Bar

Hugolucas actionbar.jpeg

1. App icon

The app icon establishes your app's identity. It can be replaced with a different logo or branding if you wish. Important: If the app is currently not displaying the top-level screen, be sure to display the Up caret to the left of the app icon, so the user can navigate up the hierarchy. For more discussion of Up navigation, see the Navigation pattern.

2. View control

If your app displays data in different views, this segment of the action bar allows users to switch views. Examples of view-switching controls are drop-down menus or tab controls. If your app doesn't support different views, you can also use this space to display non-interactive content, such as an app title or longer branding information.

3. Action buttons

Show the most important actions of your app in the actions section. Actions that don't fit in the action bar are moved automatically to the action overflow. Long-press on an icon to view the action's name.

4. Action overflow

Move less often used actions to the action overflow.

Fragments

Un fragment representa un comportament o una porció de la interfície d'usuari d'una Activitat Android. Es poden combinar múltiples fragments en una sola activitat per tal de crear una User Interface (aka UI) multi-pane i reutilitzar els fragments en múltiples activitats. Es pot pensar en un fragment com una secció modular d'una activitat.

Els fragments són com activitats anidades dins d'altres Activitats i que tenen el seu propi layout i cicle de vida.
Hugolucas fragments.png

"Són com subactivitats que es poden reutilitzar amb altres activitats."

Un fragment sempre ha de estar dins d'una activitat i el cicle de vida de l'activitat està associat al cicle de vida de l'activitat, per exemple si es destrueix una activitat també es destrueixen tots els fragments que conté. Cal tenir en compte però que quan una activitat esta executant-se, cada fragment es pot manipular de forma independent, com per exemple es pot afegir o eliminar. A aquestes accions les anomenen fragment transactions, per exemple es poden afegir a una back stack.

Quan s'afegeix un fragment al layout d'una activitat, s'ha de fer dins d'un ViewGroup i el fragment defineix el seu propi layout. S'utilitza l'element XML:

<fragment>

NOTA: Es poden utilitzar fragment que no formin part del layout de l'activitat, s'utilitzen com a invisible workers de l'activitat Android

Les qüestions que cal tenir en compte són:

  • Com mantenir l'estat dels fragments al afegir-los al back stack de l'activitat.
  • Com compartir esdeveniments amb l'activitat pare i altres fragments.
  • Com contribuir a l'Action bar de l'activitat pare.

En resum, el que cal tenir en compte dels Fragments és:

  • Descomposen la funcionalitat i el UI de les aplicacions en mòduls
  • s'afegeixen a una pantall per evitar la commutació entre activitats.
  • tenen el seu propi cicle de vida i back stack
  • Requereixen l'API 11 o superior.

Vegeu també Fragments

Recursos:

Afegir una interfície d'usuari a un fragment

La principal diferència respecte a com s'afegeixen els layous a una activitat, és que no ho farem al mètode onCreate sinó al mètode:

onCreateView()

que és el mètode que el sistema operatiu Android crida quan és el moment de que el fragment sigui dibuixat per pantalla. El mètode ha de tornar una vista (View) que sigui l'arrel del layout del fragment.

NOTA: si parlem de ListFragment, aleshores es retorna ListView

Es pot obtenir l'arrel del layout inflant-la a partir d'un layout resource definit en XML. Vegem un exemple de com utilitzar el fitxer example_fragment.xml com a layout d'un fragment.

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

Afegir un fragment a una activitat

Hi ha dos formes de fer-ho:

  • Estàtica: Declarar el fragment dins del layout de l'actvitat.
  • Dinàmica: per codi afegir el Fragment a un ViewGroup existent.

Estàtica

Vegem un exemple:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <fragment android:name="com.example.news.ArticleListFragment"
                android:id="@+id/list"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
        <fragment android:name="com.example.news.ArticleReaderFragment"
                android:id="@+id/viewer"
                android:layout_weight="2"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
    </LinearLayout>

On

android:name

indica la classe Fragment.


NOTA: Each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted (and which you can use to capture the fragment to perform transactions, such as remove it). There are three ways to provide an ID for a fragment: Supply the android:id attribute with a unique ID. Supply the android:tag attribute with a unique string. If you provide neither of the previous two, the system uses the ID of the container view.

Dinàmica

S'utilitza l'API FragmentTransaction:

 FragmentManager fragmentManager = getFragmentManager()
 FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

Per afegir un fragment.

    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();

ListView

L'objecte android.widget.ListView [1] té la següent jerarquia:

java.lang.Object
  ↳ 	android.view.Viewandroid.view.ViewGroupandroid.widget.AdapterView<T extends android.widget.Adapter>
 	  	  	   ↳ 	android.widget.AbsListView
 	  	  	  	   ↳ 	android.widget.ListView

Per tant podem veure com es tracta d'un Widget Android, i també com és un conjunt de vistes o viewGroup i que a més és de tipus AdapterView, és a dir una llista que treballa amb Adaptadors.

Un exemple de layout XML amb una ViewList:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/mylist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout> 

Adapters

Els adaptadors s'utilitzen per proveir de dades al objecte ListView (o si utilitzem fragments els ListFragment). També defineixen com es mostra cada fila a la ListView.

Els adaptadors s'assignen a ListView o ListFragment utilitzant el mètode:

setAdapter [2]

Un adaptador és una classe filla de:

BaseAdapter [3]

Android us proporcionar una sèrie de adaptadors per defecte com:

  • ArrayAdapter: permet crear adaptadors a partir de dades basades en l'estructura de dades array o java.util.List.
  • CursorAdapter: La classe SimpleCursorAdapter s'utilitza per fer llistes a partir de dades relacionades amb bases de dades.

TODO

WrapperListAdapter

Sets the data behind this ListView. The adapter passed to this method may be wrapped by a WrapperListAdapter, depending on the ListView features currently in use. For instance, adding headers and/or footers will cause the adapter to be wrapped.

Adaptadors a mida

Per controlar la forma en que l'adaptador assigna les dades d'origen als diferents elements de la UI a mida d'un afila cal crear un implementació propia de Adapter (subclase de BaseAdapter).

El mètode que cal sobrescriure és:

getView()

Que és el que utilitza la ListView o el ListFragment per obtenir la vista de la fila. El root del layout. Exemple:

package de.vogella.android.listactivity;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MySimpleArrayAdapter extends ArrayAdapter<String> {
  private final Context context;
  private final String[] values;

  public MySimpleArrayAdapter(Context context, String[] values) {
    super(context, R.layout.rowlayout, values);
    this.context = context;
    this.values = values;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View rowView = inflater.inflate(R.layout.rowlayout, parent, false);
    TextView textView = (TextView) rowView.findViewById(R.id.label);
    ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
    textView.setText(values[position]);
    // Change the icon for Windows and iPhone
    String s = values[position];
    if (s.startsWith("iPhone")) {
      imageView.setImageResource(R.drawable.no);
    } else {
      imageView.setImageResource(R.drawable.ok);
    }

    return rowView;
  }
} 

Within the getView() method you would inflate an XML based layout and then set the values of the individual Views in the layout. For inflating an XML layout you can use the system service LayoutInflator. This service can get accessed via the Activity or via the context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) method call.

The individual elements in the layout can be found via the findViewById() method call.

Exemples

ArrayAdapter

Vegem un exemple de com utilitzar un array de valors per a omplir una llista:

ListView listView = (ListView) findViewById(R.id.mylist);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
  "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
  "Linux", "OS/2" };

// Define a new Adapter
// First parameter - Context
// Second parameter - Layout for the row
// Third parameter - ID of the TextView to which the data is written
// Forth - the Array of data

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
  android.R.layout.simple_list_item_1, android.R.id.text1, values);


// Assign adapter to ListView
listView.setAdapter(adapter); 

El més important és tenir en compte que:

android.R.layout.simple_list_item1

Consulteu android.R.layout

http://developer.android.com/reference/android/R.layout.html

és un recurs predefinit que ens proporciona la plataforma Android (no cal definir-lo en els nostres layouts, ja ve predefinit.)

  • simple_list_item_1: contains only a TextView
  • simple_list_item_2: has two inside a subclass of RelativeLayout.

These are both taken from Jelly Bean.

ArrayAdapter amb ListFragment

package de.vogella.android.fragments;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.app.ListFragment;

public class MyListFragment extends ListFragment {

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
        "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
        "Linux", "OS/2" };
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
        android.R.layout.simple_list_item_1, values);
    setListAdapter(adapter);
  }

  @Override
  public void onListItemClick(ListView l, View v, int position, long id) {
    // Do something with the data

  }

} 

Exemple agafant les dades de la llista d'un XML strin-array resource

ArrayAdapter customitzat

Volem fer una llista amb l'estil:

TODO: image
XC7SMdAbAx.png

El primer que cal fer és definir un objecte contenidor de les files:

package app.arsviator;

public class ABEntry {
    private String name;
    private String phoneNo;
    private int photo;
   
    public ABEntry(String _name, String _pn, int _photo) {
        this.name = _name;
        this.phoneNo = _pn;
        this.photo = _photo;
    }
   
    public String getName() {
        return name;
    }

    public String getPhoneNo() {
        return phoneNo;
    }

    public int getPhotoId() {
        return photo;
    }
}

Ara definim el layout d'una fila:

Xb2iSY0xm8.png

En XML ( /res/layout/entry.xml )

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:paddingTop="5px"
    android:paddingBottom="5px"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >
  <ImageView android:id="@+id/ePhoto"
    android:layout_width="48px"
    android:layout_height="48px"
    android:src="@drawable/nophoto" />
  <LinearLayout android:orientation="vertical"
      android:paddingLeft="10px"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:id="@+id/eName"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="22sp" />
    <TextView android:id="@+id/ePhoneNo"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="16sp" />
  </LinearLayout>
  <TextView android:id="@+id/eNull"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content" />
</LinearLayout>

I ara creem un objecte fill de ArrayAdapter i implemente getView:

private class ABArrayAdapter extends ArrayAdapter<ABEntry> {
        private ArrayList<ABEntry> items;
        private int rsrc;
       
        public ABArrayAdapter(Context ctx, int rsrcId, int txtId, ArrayList<ABEntry> data) {
            super(ctx, rsrcId, txtId, data);
            this.items = data;
            this.rsrc = rsrcId;
        }
       
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = li.inflate(rsrc, null);
            }
            ABEntry e = items.get(position);
            if (e != null) {
                ((TextView)v.findViewById(R.id.eName)).setText(e.getName());
                ((TextView)v.findViewById(R.id.ePhoneNo)).setText(e.getPhoneNo());
                if (e.getPhotoId() != -1) {
 ((ImageView)v.findViewById(R.id.ePhoto)).setImageResource(e.getPhotoId());               
                } else {
 ((ImageView)v.findViewById(R.id.ePhoto)).setImageResource(R.drawable.nophoto); 
                }
            }
            return v;
        }
    }
}

Ara el Listview seria:

package app.arsviator;

import java.util.ArrayList;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class CustomAA extends ListActivity {
    ArrayAdapter<ABEntry> abAdapter;
    final ArrayList<ABEntry> abList = new ArrayList<ABEntry>();
    Context ctx = this;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
               
        ABEntry ne1 = new ABEntry("Mia","01034567890", R.drawable.photo1);
        abList.add(ne1);
        ABEntry ne2 = new ABEntry("Aki","01062954351", R.drawable.photo2);
        abList.add(ne2);
        ABEntry ne3 = new ABEntry("Snoopy","01091534510", -1);
        abList.add(ne3);
        ABEntry ne4 = new ABEntry("Jenny","01034964501", R.drawable.photo3);
        abList.add(ne4);
        ABEntry ne5 = new ABEntry("Kim","01043956103", -1);
        abList.add(ne5);

        abAdapter = new ABArrayAdapter(this, R.layout.entry, R.id.eName, abList);
        setListAdapter(abAdapter);       
    }
   
    public void onListItemClick(ListView parent, View v, int pos, long id) {
        final int index = pos;
        final ABEntry ent = abAdapter.getItem(pos);
        final String pn = ent.getPhoneNo();
        final int _photo = ent.getPhotoId()!=-1?ent.getPhotoId():R.drawable.nophoto;       
       
        new AlertDialog.Builder(this).setTitle(ent.getName())
        .setMessage(pn)
        .setIcon(ent.getPhotoId())
        .setNegativeButton("Delete", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dlg, int sumthin) {
                abAdapter.remove(ent);
                abAdapter.notifyDataSetChanged();
            }           
        })
        .setNeutralButton("Call", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dlg, int sumthin) {
                    Intent i = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+pn));
                    startActivity(i);               
                }
        })
        .show();
    }
   
    private class ABArrayAdapter extends ArrayAdapter<ABEntry> {
        private ArrayList<ABEntry> items;
        private int rsrc;
       
        public ABArrayAdapter(Context ctx, int rsrcId, int txtId, ArrayList<ABEntry> data) {
            super(ctx, rsrcId, txtId, data);
            this.items = data;
            this.rsrc = rsrcId;
        }
       
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = li.inflate(rsrc, null);
            }
            ABEntry e = items.get(position);
            if (e != null) {
                ((TextView)v.findViewById(R.id.eName)).setText(e.getName());
                ((TextView)v.findViewById(R.id.ePhoneNo)).setText(e.getPhoneNo());
                if (e.getPhotoId() != -1) {
 ((ImageView)v.findViewById(R.id.ePhoto)).setImageResource(e.getPhotoId());               
                } else {
 ((ImageView)v.findViewById(R.id.ePhoto)).setImageResource(R.drawable.nophoto);           
                }
            }
            return v;
        }
    }
}

Recursos