Android UI Programming
Android UI Programming
Activity
● Android Application Component for UI
– Provides a window for user interaction
– Content area for views and widgets
– Requires lifecycle and state management
Example: HelloWorldActivity (Java)
package com.example.smd.helloworld;
import android.app.Activity;
import android.os.Bundle;
public class HelloWorldActivity extends Activity
{
/** Called when the activity is first created.*/
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Example: HelloWorldActivity (Kotlin)
package com.example.smd.helloworld
import android.app.Activity
import android.os.Bundle
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
<activity
android:name="HelloWorldActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</application>
</manifest>
Activity Lifecycle
● Primary states
– Resumed : Activity in foreground – user can interact
– Paused : At least partially obscured by another activity – activity may be
partially visible but loses focus.
– Stopped : Completely hidden – not visible to user. Activity may exist in
memory, but in low memory situation, can be killed by OS.
Lifecycle and state management
● Need
– Stop animations / video and other actions consuming
CPU
– Release system resources such as handles to sensors
– Commit unsaved changes (if required)
● Method
– onPause() / onResume()
● release / acquire resources, stop / start CPU utilization
– onStop() / onStart() / onRestart()
● save / handle persistent data
– onSaveInstanceState() / onRestoreInstanceState()
● save / load transient data
Basic Activity Relationships
Provides access to
application global state
Useful for looking up assets,
resources and other
application level information
Visual components
android.widget
UI Development Approaches
● Programmatic
– Using Java/Kotlin code
– Dynamic view generation
– Slightly complicated to maintain
● Declarative
– Using XML
– Static views – may be further adjusted through
programmatic approach
– Easier to maintain
Example: Notes App
package com.example.smd; package com.example.smd;
Java Kotlin
package com.example.smd;
import java.util.ArrayList;
Programmatic Approach
import android.app.Activity;
import android.os.Bundle;
...
class MainActivity :Activity() {
public class NotesActivity extends Activity var notes:ArrayList<Note?>? = null
{ var currentNote:Note? = null
ArrayList<Note> notes; var textArea:EditText? = null
Note currentNote;
EditText textArea;
public override fun onCreate(savedInstanceState:Bundle?) {
public void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState)
{ //setContentView(R.layout.main);
super.onCreate(savedInstanceState); createUi()
//setContentView(R.layout.main);
createUi(); notes = ArrayList<Note?>()
notes = new ArrayList<Note>(); }
}
private fun saveNote() {
private void saveNote(){ val content = textArea!!.text.toString()
String content = textArea.getText().toString(); if (currentNote == null) {
if(currentNote == null){
currentNote = new Note(content); currentNote = Note(content)
notes.add(currentNote); notes!!.add(currentNote)
} }
currentNote.setContent(content); currentNote!!.setContent(content)
val text = "Note saved successfully"
String text = "Note saved successfully";
Toast toast = val toast = Toast.m akeText(this,text,Toast.LENGTH_SHORT)
Toast.makeText(this,text,Toast.LENGTH_SHORT); toast.show()
toast.show(); }
}
private fun newNote() {
private void newNote(){ saveNote()
saveNote();
textArea.setText(""); textArea!!.setText("")
currentNote = null; currentNote = null
} }
outerLayout.addView(textArea);
outerLayout.addView(createMenu());
setContentView(outerLayout);
}
layout.addView(saveButton);
layout.addView(newButton);
layout.addView(listButton);
return layout;
}
private fun createUi() { Programmatic Approach (Kotlin)
val outerLayout = LinearLayout(this)
outerLayout.layoutParams =
LinearLayout.LayoutParam s(
LinearLayout.LayoutParam s.MATCH_PARENT,
LinearLayout.LayoutParam s.MATCH_PARENT
)
outerLayout.orientation = LinearLayout.VERTICAL
textArea = EditText(this)
textArea!!.layoutParams = LinearLayout.LayoutParam s(
LinearLayout.LayoutParam s.MATCH_PARENT,
LinearLayout.LayoutParam s.WRAP_CONTENT,
1f
)
textArea!!.hint = "Notes"
textArea!!.gravity = Gravity.TOP
outerLayout.addView(textArea)
outerLayout.addView(createMenu())
setContentView(outerLayout)
}
Design pattern
Makes incompatible interfaces work with each other
Converts the interface to the one client expects
Allows creating decoupled general-purpose
implementations that can work with each other using
adapter
Applicability
Adapters for third-party libraries
public class NoteAdapter extends // create as an inner class of NoteAdapter Java
RecyclerView.Adapter<NoteAdapter.NoteViewHolder> {
private ArrayList<Note> mDataset; public class NoteViewHolder extends
RecyclerView.ViewHolder {
public NoteAdapter(ArrayList<Note> ds) {
mDataset = ds; public TextView title;
} public TextView timestamp;
public Button remove;
public NoteAdapter.NoteViewHolder
onCreateViewHolder(ViewGroup parent, public NoteViewHolder(View v) {
int viewType) { super(v);
title = (TextView) v.findViewById(R.id.title);
View v = LayoutInflater.from(parent.getContext()) timestamp = (TextView)
.inflate(R.layout.note_list_item, parent, false); v.findViewById(R.id.timestamp);
// specify an adapter
mAdapter = new NoteAdapter(dataSet);
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
recyclerView.setAdapter(mAdapter);
}
timestamp =
override fun onBindViewHolder( v.findViewById<View>(R.id.timestamp) as TextView
holder: NoteViewHolder, position: Int) {
remove =
// - get element from your dataset at this position v.findViewById<View>(R.id.button_remove) as Button
// - replace the contents of the view with that element
val content = mDataset[position].content remove.setOnClickListener { v ->
holder.title.text = val pos = v.tag as Int
content!!.substring(0, content.indexOf("\n")) mDataset.removeAt(pos)
holder.timestamp.text = mDataset[position].timeStamp notifyDataSetChanged()
holder.remove.tag = position }
} }
//register
notesLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
//register
notesLauncher = registerForActivityResult<Intent, ActivityResult>(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data
val id = data.getStringExtra("id")
// handle incoming data
}
}
}
● User interaction
– EditText combined with TextWatcher
– Other views e.g. toggle buttons, SearchView, etc.
public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.NoteViewHolder> implements Filterable
{ Java
private Filter filter;
private ArrayList<Note> notes;
private ArrayList<Note> filteredNotes;
...
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if(constraint != null && constraint.length() > 0){
ArrayList<Note> filteredList = new ArrayList<Note>();
for(int i=0; i < notes.size(); i++){
if(notes.get(i).getContent().contains(constraint)){
filteredList.add(notes.get(i));
}
}
results.count = filteredList.size();
results.values = filteredList;
}
else{
results.count = notes.size();
results.values = notes;
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredNotes = (ArrayList<Note>) results.values;
notifyDataSetChanged();
}
}
}
Java
Invoking filter
class NoteAdapter(private val notes: ArrayList<Note>, ls: NoteItemClickListener) :
RecyclerView.Adapter<NoteViewHolder>(), Filterable { Kotlin
private var filteredNotes: ArrayList<Note>
private val listener: NoteItemClickListener
private var filter: Filter? = null
override fun getFilter(): Filter? {
if (filter == null) {
filter = NotesFilter()
}
return filter
}
}
v.setOnClickListener {
val pos = v.tag as Int
listener.onClick(filteredNotes[pos])
}
}
}
}
Kotlin
Invoking filter
Basic state management
public class Note implements java.io.Serializable
{
...
}
try{
savedInstanceState.putSerializable("noteslist",notes);
}
catch(Exception ex){ }
}
public void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
try{
notes = (ArrayList<Note>)
savedInstanceState.getSerializable("noteslist");
}
catch(Exception ex){ }
}
...
Implement
ActionMode.Callback
• Inflate menu
• Handle actions
Initiate action mode
• View's long-click
Recyclerview layout changed listener
to allow selection
• call startActionMode