android ui development
DESCRIPTION
Short intro to UI Development on Android platform.TRANSCRIPT
Android UI Development
Jussi Pohjolainen Tampere University of Applied Sciences
UI Overview
• All UI elements are either View or ViewGroup – View = Something on the screen (widget) – ViewGroup = Container for other Views or ViewGroups
• UI is a combinaGon of Views and ViewGroups • Most effecGve way to declare UI is in XML – You can, however, implement everything in code
LAYOUTS
Why Layouts?
• UI is built on top of layout • Android diverse ecosystem – Several different resoluGons and densiGes!
• Layout helps to create layout for different screen sizes
Common Layouts • LinearLayout
– Views in line, either verGcally or horizontally • RelativeLayout
– Define posiGons of each other child view relaGve to each other and screen boundaries
• TableLayout – Rows and Columns
• FrameLayout – FrameLayout is designed to display a single item at a Gme. You can have
mulGple elements within a FrameLayout but each element will be posiGoned based on the top leV of the screen. Elements that overlap will be displayed overlapping.
• AbsoluteLayout (Depricated) – Use coordinates
• And others…
LinearLayout and RelaGveLayout
Width and Height
• Fixed – 42dp, 17px, etc
• Match the parent’s size – “As big as my parent” – MATCH_PARENT (in older version FILL_PARENT)
• Best fit – “As big as needed for my content” – WRAP_CONTENT
Defining Layout
• Portrait mode: – res/layout/main.xml
• Landscape mode: – res/layout-land/main.xml
• Each AcGvity can have it's own layout – setContentView(R.layout.main);
Example of Layout <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width=”match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout>
Common Acributes A)ribute Descrip4on Notes
layout_width Specifies the width of the View or ViewGroup
layout_height Specifies the height of the View or ViewGroup
layout_marginTop Specifies extra space on the top side of the View or ViewGroup
layout_marginBocom Specifies extra space on the bocom side of the View or ViewGroup
layout_marginLeV Specifies extra space on the leV side of the View or ViewGroup
layout_marginRight Specifies extra space on the right side of the View or ViewGroup
layout_gravity Specifies how child Views are posiGoned Only in LinearLayout or TableLayout
layout_weight Specifies the raGo of Views Only in LinearLayout or TableLayout
Example of Layout with Gravity and Weight <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button A" /> <Button android:layout_width="wrap_content" android:layout_height="0dip" android:text="Button B" android:layout_gravity="right" android:layout_weight="0.5" /> <Button android:layout_width="match_parent" android:layout_height="0dip" android:text="Button C" android:layout_weight="0.5" /> </LinearLayout>
Example of Table Layout
<TableLayout 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"
android:stretchColumns="1"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<TableRow>
<TextView android:text="E-mail:" />
<EditText android:id="@+id/email" />
</TableRow>
<TableRow>
<TextView android:text="Password:" />
<EditText android:id="@+id/password" android:password="true" />
</TableRow>
<TableRow>
<TextView />
<CheckBox android:id="@+id/rememberMe" android:text="Remember Me" />
</TableRow>
<TableRow>
<Button android:id="@+id/signIn" android:text="Log In" android:layout_span="2" />
</TableRow>
</TableLayout>
RelaGveLayout
• RelaGveLayout lays out elements based on their relaGonships with one another, and with the parent container.
• It’s possible to put TextView center of the screen and other Views related to that
<RelativeLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/textView" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:textSize="50sp" android:text="Hello World" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/textView2" android:layout_above="@+id/textView" android:layout_centerHorizontal="true" android:text="other text" /> </RelativeLayout>
FrameLayout <?xml version="1.0" encoding="utf-8"?> <FrameLayout
android:layout_width="match_parent" android:layout_height="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <ImageView android:src="@drawable/ic_launcher" android:layout_height="match_parent" android:layout_width="match_parent"/> <TextView android:text="Hello World!" android:textSize="24sp" android:textColor="#000000" android:layout_height="match_parent" android:layout_width="match_parent" android:gravity="center"/>
</FrameLayout>
STYLES
Defining Styles • MulGple Bucons • Define style only once!
The Bucons in layout/main.xml ...
<TableRow android:id="@+id/TableRow01" android:layout_weight="0.2" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/Button07" style="@style/mybutton" android:text="7" />
<Button android:id="@+id/Button08" style="@style/mybutton" android:text="8" />
<Button android:id="@+id/Button09" style="@style/mybutton" android:text="9" />
<Button android:id="@+id/ButtonDivide" style="@style/mybutton" android:text="/" />
</TableRow>
...
Styles in values/styles.xml <?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="mybutton" parent="@android:style/TextAppearance">
<item name="android:textSize">30sp</item>
<item name="android:textColor">#000000</item>
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">fill_parent</item>
<item name="android:layout_weight">1</item>
</style>
</resources>
Styles and Themes
• Way of building formaeng and layout to your app
• Style has formaeng acributes to several elements
• Theme has formaeng acributes to all acGviGes – Create the theme in xml (styles.xml) – Apply the theme in manifest
COMMON WIDGETS
Some Common Widgets • TextView
– Label • EditText
– Editable text • ListView
– View group that manages a group of Views. • Spinner
– TextView associated with ListView. Let's you select an item from a list. • Button
– Standard push bucon • CheckBox
– Standard checkbox • RadioButton
– Standard radiobucon • See documentaGon how to use these!
ListView Example
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp" >
</TextView>
ListAcGvity public class ListViewExample extends ListActivity {
static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", ....
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ArrayAdapter(this, R.layout.list_item, COUNTRIES));
ListView lv = getListView();
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(this, ((TextView) view).getText(),
Toast.LENGTH_SHORT).show();
}
});
}
}
MENUS
About Menus • Common UI component in many types of apps
– Beginning from Android 3.0 (API Level 11) android devices may not hold a dedicated menu – buDon!
• Menus are in ActionBar • Hopefully implemenGng ActionBar is really simple and 99% same than menus
• You can build the menus using Java or XML • There are three different menu types
1. OpGons Menu (AcGonBar) 2. Context Menu 3. Popup Menu
Menus?
• Android no longer requires Menu bucon! • Android 3.0 introduced concept of AcGonBar – No need for physical menu bucon
• You should migrate your designs away from using the Menu bucon
• You can support both menu and acGonbar
AcGonBar
• AcGonBar was introduced in Android 3.0 but also available for 2.1 using Support Library
• If supporGng only API level 11 and higher – import android.app.ActionBar
• If supporGng lower than 11 – import android.support.v7.app.ActionBar
• You must set up a appcombat v7 support library for lower than 11
Menus • Op4ons menu / Ac4onBar
– Compact menu bocom of the screen or acGon bar
• Context Menu – Long press
• Submenus – Submenu opens in a new
window
CreaGng Menus in Java public class MenuExample extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
...
}
// Modify menu items dynamically. Disable/enable menuitems.
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
...
}
// Create your menu here
@Override
public boolean onCreateOptionsMenu(Menu menu) {
...
}
// When menuitem is selected
@Override
public boolean onOptionsItemSelected(MenuItem item) {
...
}
}
CreaGng Menus in Java public class MenuExample extends Activity {
// You can increment this for additional menuitems static final private int MENU_ITEM1 = Menu.FIRST; static final private int MENU_ITEM2 = Menu.FIRST + 1;
// Create your menu here @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu);
// Specify group value to separate Menu Items for batch processing and // ordering. NONE = No group int groupId = Menu.NONE;
// Unique identifier for each menu item (used for event handling) int menuItemId1 = MENU_ITEM1; int menuItemId2 = MENU_ITEM2;
// Order value. Menu.NONE = I don't care int menuItemOrder = Menu.NONE; // Menu Text int menuItemText1 = R.string.menu_item1; int menuItemText2 = R.string.menu_item2;
menu.add(groupId, menuItemId1, menuItemOrder, menuItemText1); menu.add(groupId, menuItemId2, menuItemOrder, menuItemText2);
return true; }
}
Event Handling in Menus public class MenuExample extends Activity { static final private int MENU_ITEM1 = Menu.FIRST; static final private int MENU_ITEM2 = Menu.FIRST + 1; // Event Handling
@Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch (item.getItemId()) { case (R.id.menu_item1):
// Do something
return true; } return false; } ....
}
Changing Menu Dynamically public class MenuExample extends Activity { static final private int MENU_ITEM1 = Menu.FIRST; static final private int MENU_ITEM2 = Menu.FIRST + 1;
@Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem menuItem = menu.findItem(MENU_ITEM1); menuItem.setEnabled(false); return true; }
.... }
CreaGng Menus in XML
• Create new xml-‐file: menu/mymenu.xml • Add Items:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="A" android:showAsAction="ifRoom"
android:id="@+id/a"></item>
<item android:title="B" android:id="@+id/b"></item>
<item android:title="C" android:id="@+id/c"></item>
<item android:title="D" android:id="@+id/d"></item>
</menu>
Submenu
• A sub menu can be added within any menu • Can be defined in XML or in Java • In XML, real easy
<menu> <item title="Reset" /> <item title="Sub menu">
<menu> <item title="Example sub menu item" />
</menu> </item> </menu>
Opening the Menu in Java @Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mymenu, menu);
return true;
}
Context Menu
• Context Menu = Long press • Override two methods on a AcGvity – onCreateContextMenu() – onContextItemSelected()
• Register the context menu to the view – registerForContextMenu(view)
Example public class MyContextMenu extends Activity {
private TextView tv;
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
}
@Override
public boolean onContextItemSelected(MenuItem item) {
}
}
OnCreate @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText("Context Menu!");
registerForContextMenu(tv);
setContentView(tv);
}
onCreateContextMenu @Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
if(v == tv) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mymenu, menu);
}
}
onContextItemSelected @Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
switch (item.getItemId()) {
case R.id.a:
Toast.makeText(this, "A", 1000).show();
return true;
case R.id.b:
Toast.makeText(this, "B", 1000).show();
return true;
}
return false;
}
NOTIFICATING USER Dialogs and NoGficaGons
NoGcaGons
• Toast No4fica4on – Brief message – Toast
• Dialog No4fica4on – AcGvity-‐related noGficaGon – AlertDialog, DatePickerDialog, TimePickerDialog
• Status Bar No4fica4on – Persistent reminder
TOAST
Toast
• Simple Feedback about an operaGon in a small popup – No user response – Also very good for debugging purposes!
How? It's simple… Toast toast = Toast.makeText(this, "Reset", Toast.LENGTH_SHORT); toast.show(); // in one line Toast.makeText(this, "Reset", Toast.LENGTH_SHORT).show();
Dialogs
• A dialog is a small window that prompts the user to make a decision or enter addiGonal informaGon
Dialogs
• You can use subclasses of Dialog: – AlertDialog • dialog with Gtle, up to three bucons, a list of selectable items or a custom layout.
– DatePickerDialog • Predefined layout
– TimePickerDialog • Predefined layout
DialogFragment
• DialogFragment class helps you to manage correctly lifecycle events such as back-‐bu)on pressing – Class also allows reusing of dialog's UI as an embeddable component in larger UI
public void showDialog(View v) {
MyDialogFragment dialog = new MyDialogFragment();
// Tag is a unique name that the system uses to save and restore the
// fragment state when necessary. You can also get a reference to
// the fragment by calling findFragmentByTag()
dialog.show(getFragmentManager(), "fragmentTagName");
}
public static class MyDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("What do you want to do?");
builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
return builder.create();
}
}
Passing Events to Host AcGvity public class MyDialogFragment extends DialogFragment {
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
private NoticeDialogListener host;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// My crash, use try .. catch
host = (NoticeDialogListener) activity;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("What do you want to do?");
builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
host.onDialogPositiveClick(MyDialogFragment.this);
}
});
Passing Events to Host AcGvity
public class MainActivity extends Activity implements MyDialogFragment.NoticeDialogListener {
public void showDialog(View v) {
MyDialogFragment dialog = new MyDialogFragment();
// Tag is a unique name that the system uses to save and restore the
// fragment state when necessary. You can also get a reference to
// the fragment by calling findFragmentByTag()
dialog.show(getFragmentManager(), "fragmentTagName");
}
public void onDialogPositiveClick(DialogFragment dialog) {
this.setTitle("Ok");
}
public void onDialogNegativeClick(DialogFragment dialog) {
this.setTitle("Cancel");
}
To Build an Alert // 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// 2. Chain together various setter methods to set the dialog
// characteristics
builder.setMessage(R.string.dialog_message)
.setTitle(R.string.dialog_title);
// 3. Get the AlertDialog from create()
AlertDialog dialog = builder.create();
List Example @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.pick_color)
.setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position
// of the selected item
}
});
return builder.create();
}
STATUS BAR NOTIFICATIONS
NoGficaGons
• Message for user outside of your app's normal UI
• NoGficaGon shows in no4fica4on area
• More details can be seen when user opens no4fica4on drawer
Normal View
• Normal view appears in area up to 64dp tall • Holds 1) Content Gtle, 2) Large icon, 3) Content text, 4) Content info, 5) Small icon, 6) Time noGficaGon was issued
Big View
• Big View appears when noGficaGon is expanded • Available 4.1 -‐> • Difference: 7) Details area
The Basics
• Specify your noficaGon using NotificationCompat.Builder object
• Use NotificationCompat.Builder.build() for building the noGficaGon object
• call notify() when needed • NoGficaGon must contain: 1) small icon, 2) Gtle and 3) detail text
• Service can launch status bar no4fica4on!
Building the NoGficaGon // Building the notification
Notification.Builder mBuilder =
new Notification.Builder(this);
// Drag icon.png to drawable/ folder
// Small icon, content title and text are minimum.
mBuilder.setSmallIcon(R.drawable.icon);
mBuilder.setContentTitle("Notification Title");
mBuilder.setContentText("Notification Text");
// You should create also a pending intent
// createPendingIntent is a method that you have to implement.
// PendingIntent pi = createPendingIntent();
// mBuilder.setContentIntent(pi);
Notification notification = mBuilder.build();
// Displaying the notification using notification manager
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(this.NOTIFICATION_SERVICE);
int mId = 1;
// mId allows you to update the notification later on.
mNotificationManager.notify(mId , notification);
NoGficaGon AcGons • Although opGonal, you should provide one acGon to noGficaGon – Allows user to go to your app from the noGficaGon – StarGng an acGvity when the user clicks the noGficaGon is the most common scenario
• NoGficaGonManager is different app from your app and it needs special permissions to open your acGvity. – Instead of using Intent, you use PendingIntent.
• See: hcp://developer.android.com/guide/topics/ui/noGfiers/noGficaGons.html
CUSTOM COMPONENTS AND DRAWING
Basic Approach
• CreaGng custom views is really easy: 1. Extend exiGng View – class 2. Override some of the View – classes methods • onDraw(), onMeasure(), onKeyDown() ..
3. Use your class in XML – layout file
class MyCustomWidget extends View {
// You will get here information implemented in the xml – file
// This is mandatory
public MyCustomWidget(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// How to draw? Paint. Where to draw? Canvas
// It would be wise to initialize the Paint one time instead
// of every draw iteration
Paint p = new Paint();
p.setTextSize(20);
p.setColor(0xFFFF0000);
p.setAntiAlias(true);
canvas.drawText("Hello World", 0, 20, p);
}
}
XML <fi.mycompany.MyCustomWidget
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
Define own Custom Acributes
• You can define your own custom acributes • Create custom acribute in values/acrs.xml • Add the acribute to the view in layout.xml • Read the acribute in View's constructor
values/acrs.xml <?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomView">
<attr name="textInLabel" format="string" />
</declare-styleable>
</resources>
layout.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fi.company.project.CustomView android:layout_width="fill_parent" android:layout_height="wrap_content" custom:textInLabel="Hello World" /> </LinearLayout>
CustomView public class CustomView extends View { private String text; public CustomView(Context context, AttributeSet attrs) { super(context, attrs); text = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "textInLabel"); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint p = new Paint(); p.setTextSize(20); p.setColor(0xFFFF0000); p.setAntiAlias(true); canvas.drawText(text, 0, 20, p); } }