Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
37 views

Android UI Programming

The document discusses Android activity components and user interface programming. It describes activities as components that provide windows for user interaction and require lifecycle and state management. It provides Java and Kotlin code examples of a basic HelloWorld activity, including the activity class and manifest declaration. It outlines the primary activity states of resumed, paused, and stopped. It also discusses the activity lifecycle methods for handling state changes and describes common UI development approaches using either programmatic or declarative XML layouts.

Uploaded by

Edward Mid
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views

Android UI Programming

The document discusses Android activity components and user interface programming. It describes activities as components that provide windows for user interaction and require lifecycle and state management. It provides Java and Kotlin code examples of a basic HelloWorld activity, including the activity class and manifest declaration. It outlines the primary activity states of resumed, paused, and stopped. It also discusses the activity lifecycle methods for handling state changes and describes common UI development approaches using either programmatic or declarative XML layouts.

Uploaded by

Edward Mid
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 43

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

class HelloWorldActivity : Activity() {


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
}
}
Example Manifest declaration
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.smd.helloworld"
android:versionCode="1"
android:versionName="1.0" >

<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" />

<category android:name="android.intent.category.LAUNCHER" />


</intent-filter>
</activity>

</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

A container for storing map data


Useful for state management
Views and Widgets
android.view

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;

import java.util.Date; import java.util.Date;


import java.util.UUID; import java.util.UUID;

public class Note{ class Note {


private var id:String? = null
private String id; private var content:String? = null
private String content; private var creationDateTim e:Date? = null
private Date creationDateTime;

public Note(){ constructor() {


init(); init()
} }

public Note(String content){ constructor(content:String?) {


init(); init()
this.content = content; this.content = content
} }
private void init(){
private fun init() {
this.id = UUID.randomUUID().toString();
this.creationDateTime = new Date(); id = UUID.random UUID().toString()
} creationDateTim e = Date()
}
public void setContent(String content){
this.content = content; fun setContent(content:String?) {
} this.content = content
}
} }

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
} }

private void listNotes(){ private fun listNotes() {


String text = "Total " + notes.size() + " notes"; val text = "Total " + notes!!.size + " notes"
Toast toast = Toast.makeText(this,text,Toast.LENGTH_LONG);
toast.show(); val toast = Toast.m akeText(this,text,Toast.LENGTH_LONG)
} toast.show()
}
private void createUi(){ ... }
private void createMenu(){ ... } private fun createUI() { ...}
}
private fun createMenu() { ...}
}
private void createUi(){
LinearLayout outerLayout = new LinearLayout(this);
Programmatic Approach (Java)
outerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
outerLayout.setOrientation(LinearLayout.VERTICAL);

textArea = new EditText(this);


textArea.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT,1f));
textArea.setHint("Notes");
textArea.setGravity(Gravity.TOP);

outerLayout.addView(textArea);
outerLayout.addView(createMenu());
setContentView(outerLayout);
}

private ViewGroup createMenu(){


LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.CENTER);

LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);

Button saveButton = new Button(this);


saveButton.setLayoutParams(params);
saveButton.setText("Save");
saveButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
saveNote();
}
});
Button newButton = new Button(this);
newButton.setLayoutParams(params);
newButton.setText("New");
newButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
newNote();
}
});
Button listButton = new Button(this);
listButton.setLayoutParams(params);
listButton.setText("List");
listButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
listNotes();
}
});

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)
}

private fun createMenu():ViewGroup {


val layout = LinearLayout(this)
layout.layoutParams =
LinearLayout.LayoutParam s(
LinearLayout.LayoutParam s.MATCH_PARENT,
LinearLayout.LayoutParam s.WRAP_CONTENT
)
layout.orientation = LinearLayout.HORIZONTAL
layout.gravity = Gravity.CENTER
val param s = LinearLayout.LayoutParam s(
LinearLayout.LayoutParam s.WRAP_CONTENT,
LinearLayout.LayoutParam s.WRAP_CONTENT
)
val saveButton = Button(this)
saveButton.layoutParams = param s
saveButton.text = "Save"
saveButton.setOnClickListener { saveNote() }
val newButton = Button(this)
newButton.layoutParams = param s
newButton.text = "New"
newButton.setOnClickListener { newNote() }
val listButton = Button(this)
listButton.layoutParams = param s
listButton.text = "List"
listButton.setOnClickListener { listNotes() }
layout.addView(saveButton)
layout.addView(newButton)
layout.addView(listButton)
Declarative Approach
<?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"
>
<EditText
android:id="@+id/text_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Notes"
android:gravity="top"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
>
<Button
android:id="@+id/button_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save"
android:onClick="buttonClick"
/>
<Button
android:id="@+id/button_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New"
android:onClick="buttonClick"
/>
<Button
android:id="@+id/button_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="List"
android:onClick="buttonClick"
/>
</LinearLayout>
</LinearLayout>
package com.example.smd;

import java.util.ArrayList; Declarative Approach


import android.app.Activity;
...
public class NotesActivity extends Activity class NotesActivity :Activity() {
{ private var notes:ArrayList<Note>? = null
ArrayList<Note> notes; private var currentNote:Note? = null
Note currentNote; private var textArea:EditText? = null
EditText textArea;
public override fun onCreate(savedInstanceState:Bundle?) {
public void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState)
{ setContentView(R.layout.main_linear)
super.onCreate(savedInstanceState); textArea = findViewById<View>(R.id.text_area) as EditText
setContentView(R.layout.main); notes = ArrayList()
textArea = (EditText) findViewById(R.id.text_area);
}
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 = Note(content)
currentNote = new Note(content); notes!!.add(currentNote!!)
notes.add(currentNote); }
} currentNote!!.setContent(content)
currentNote.setContent(content); . ..
... }
}
private fun newNote() {
private void newNote(){ saveNote()
saveNote(); textArea!!.setText("")
textArea.setText(""); currentNote = null
currentNote = null;
}
}

private void listNotes(){ private fun listNotes() {


String text = "Total " + notes.size() + " notes"; val text = "Total " + notes!!.size + " notes"
Toast toast = Toast.makeText(this,text,Toast.LENGTH_LONG); val toast = Toast.m akeText(this,text,Toast.LENGTH_LONG)
toast.show(); toast.show()
} }

public void buttonClick(View v){ fun buttonClick(v:View) {


if(v.getId() == R.id.button_save){ if (v.id == R.id.button_save) {
saveNote(); saveNote()
} else if(v.getId() == R.id.button_new){ }
newNote(); if (v.id == R.id.button_new) {
} else if(v.getId() == R.id.button_list){ newNote()
listNotes(); }
} if (v.id == R.id.button_list) {
} listNotes()
}
}
}
}
Resource
● Additional files (other than code) used by app
● Types
– layout
● defines UI layouts and components
– drawables
● bitmaps and other graphics used in app
– values
● string constants
● colors
● styles, etc
– other resources
● menu, animations, etc
Strings

<?xml version="1.0" encoding="utf-8"?>


<resources>
<string name="app_name">Notes</string>
<string name="new">New</string>
<string name="save">Save</string>
<string name="list">List</string>
<string name="notes">Notes</string>
</resources>
UI Pattern: Lists and Grids
● Presents large data sets in form of lists or grids
● Features
– Scrollable
– Customization of UI as well as data source
– Recycling support
● Implementation
– Consider Adapter design pattern
Example: RecyclerView
Adapter Design Pattern


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);

NoteViewHolder vh = new NoteViewHolder(v); remove = (Button)


return vh; v.findViewById(R.id.button_remove);
}
remove.setOnClickListener(
public void new View.OnClickListener() {
onBindViewHolder(NoteViewHolder holder,
int position) { public void onClick(View v) {
Note note = mDataset.get(position); int pos = (int) v.getTag();
String content = note.getContent(); mDataset.remove(pos);
String ts = note.getTimeStamp();
String title = content.substring(0,content.indexOf("\n")); //adapter method
holder.title.setText(title); notifyDataSetChanged();
holder.timestamp.setText(ts); }
holder.remove.setTag(position);
} });
}
public int getItemCount() {
return mDataset.size(); } // end of view holder class
}
} // end of adapter class
public class MainActivity extends AppCompatActivity {
Java
private RecyclerView recyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager layoutManager;
ArrayList<Note> dataSet = new ArrayList<Note>();

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.list);
recyclerView = (RecyclerView) findViewById(R.id.list);
recyclerView.setHasFixedSize(true);

// use a linear layout manager


layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);

// specify an adapter
mAdapter = new NoteAdapter(dataSet);
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
recyclerView.setAdapter(mAdapter);
}

public void clickHandler(View v){


if (v.getId() == R.id.button_new){
Note note = new Note("Note " + (dataSet.size()+1) + "\nLorem ipsum ...");
dataSet.add(note);
mAdapter.notifyDataSetChanged();
}
}
}
class NoteAdapter(private val mDataset: ArrayList<Note>) : // create as an inner class of NoteAdapter Kotlin
RecyclerView.Adapter<NoteViewHolder>() {
inner class NoteViewHolder(v: View) :
override fun onCreateViewHolder( RecyclerView.ViewHolder(v) {
parent: ViewGroup,
viewType: Int var title: TextView
) : NoteViewHolder { var timestamp: TextView
// create a new view var remove: Button
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.note_list_item, parent, false) init {
return NoteViewHolder(v) title = v.findViewById<View>(R.id.title) as
} TextView

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 }
} }

// return the size of your dataset } // end of inner class


// (invoked by the layout manager)
override fun getItemCount(): Int {
return mDataset.size } // end of adapter class
}
class MainActivity : AppCompatActivity() {
Kotlin
private var recyclerView: RecyclerView? = null
private var mAdapter: RecyclerView.Adapter<*>? = null
private var layoutManager: RecyclerView.LayoutManager? = null
var dataSet = ArrayList<Note>()

override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)
setContentView(R.layout.list)
recyclerView = findViewById<View>(R.id.list) as RecyclerView
recyclerView!!.setHasFixedSize(true)

// use a linear layout manager


layoutManager = LinearLayoutManager(this)
recyclerView!!.layoutManager = layoutManager

// specify an adapter (see also next example)


mAdapter = NoteAdapter(dataSet)
recyclerView!!.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
recyclerView!!.adapter = mAdapter
}

fun clickHandler(v: View) {


if (v.id == R.id.button_new) {
val note = Note(
""" Note ${dataSet.size + 1}
Lorem ipsum … """.trimIndent() )
dataSet.add(note)
mAdapter!!.notifyDataSetChanged()
}
}
}
Launching another Activity

Using an Intent
 An action intended for the android platform
 One of the uses is to start another activity
 Specify Activity class to be invoked
 Use ActivityResultLauncher to launch a child activity
returning a result back to parent
» startActivityForResult used in older APIs is deprecated
public class MainActivity extends AppCompatActivity { Java
// other attributes ...
ActivityResultLauncher<Intent> notesLauncher;

protected void onCreate(Bundle savedInstanceState) {


// other UI creation related tasks
// ....

//register
notesLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {

public void onActivityResult(ActivityResult result) {


if(result.getResultCode() == RESULT_OK){
Intent data = result.getData();
String id = data.getStringExtra("id");
// handle incoming data from child
}
}
});

public void clickHandler(View v){


if (v.getId() == R.id.button_new){
Intent intent = new Intent(this,NotesActivity.class);
intent.putExtra("id","");
notesLauncher.launch(intent);
}
}
}
public class NotesActivity extends Activity Java
{
EditText textArea;
String noteId;

public void onCreate(Bundle savedInstanceState)


{
super.onCreate(savedInstanceState);
setContentView(R.layout.note);
textArea = (EditText) findViewById(R.id.text_area);

Intent intent = getIntent();


String content = intent.getStringExtra("content");
textArea.setText(content);
noteId = intent.getStringExtra("id");
}

public void saveNote(View v){


Intent result = new Intent();
result.putExtra("content",textArea.getText().toString());
result.putExtra("id",noteId);
setResult(RESULT_OK,result);
finish();
}

public void cancelNote(View v){


Intent result = new Intent();
setResult(RESULT_CANCELED,result);
finish();
}
}
class MainActivity : AppCompatActivity() { Kotlin
// other attributes ...
ActivityResultLauncher<Intent>? = null

override fun onCreate(savedInstanceState?: Bundle) {


// other UI creation related tasks
// ....

//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
}
}
}

fun clickHandler(v: View) {


if (v.id == R.id.button_new) {
val intent = Intent(this, NotesActivity::class.java)
intent.putExtra("id", "")
notesLauncher!!.launch(intent)
}
}
}
class NotesActivity : Activity() { Kotlin
var textArea: EditText? = null
var noteId: String? = null

public override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)
setContentView(R.layout.note)
textArea = findViewById<View>(R.id.text_area) as EditText
val intent = intent
val content = intent.getStringExtra("content")
textArea!!.setText(content)
noteId = intent.getStringExtra("id")
}

private fun saveNote() {


val result = Intent()
result.putExtra("content", textArea!!.text.toString())
result.putExtra("id", noteId)
setResult(RESULT_OK, result)
finish()
}

private fun cancelNote() {


val result = Intent()
setResult(RESULT_CANCELED, result)
finish()
}
}
UI Pattern: Searching and Filter
● Filter
– A convenient widget for filtering result sets
– Performs complex processing in a worker thread
– Return result to UI thread

● 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;
...

public Filter getFilter() {


if (filter == null){
filter = new NotesFilter();
}
return filter;
}

public class NoteViewHolder extends RecyclerView.ViewHolder {


...
public NoteViewHolder(View v) {
super(v);
...
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int pos = (int) v.getTag();
listener.onClick(filteredNotes.get(pos));
}
});
}
}

public NoteAdapter(ArrayList<Note> ds, NoteItemClickListener ls) {


notes = ds;
filteredNotes = ds;
listener = ls;
}

public NoteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {


...
}

public void onBindViewHolder(NoteViewHolder holder, int position) {


String content = filteredNotes.get(position).getContent();
...
}

public int getItemCount() {


return filteredNotes.size();
}
...
}
Java

Declared private as generally implemented as an inner class

private class NotesFilter extends Filter{

@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

search = (EditText) findViewById(R.id.search);


search.addTextChangedListener(new TextWatcher() {

public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {


}

public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {


filterable.getFilter().filter(search.getText().toString());
}

public void afterTextChanged(Editable editable) {


}
});

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
}

inner class NoteViewHolder(v: View) : RecyclerView.ViewHolder(v) {


...
init {
...
remove.setOnClickListener { v ->
val pos = v.tag as Int
val note = filteredNotes[pos]
filteredNotes.remove(note)
...

}
v.setOnClickListener {
val pos = v.tag as Int
listener.onClick(filteredNotes[pos])
}
}
}

// Provide a suitable constructor (depends on the kind of dataset)


init {
filteredNotes = notes
listener = ls
}

override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): NoteViewHolder {


...
}

override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {


...
holder.timestamp.text = filteredNotes[position].timeStamp
...
}

override fun getItemCount(): Int {


return filteredNotes.size
}
Kotlin

Declared private as generally implemented as an inner class

private inner class NotesFilter : Filter() {


override fun performFiltering(constraint: CharSequence): FilterResults {
val results = FilterResults()
if (constraint != null && constraint.length > 0) {
val filteredList = ArrayList<Note>()
for (i in notes.indices) {
if (notes[i].content!!.contains(constraint)) {
filteredList.add(notes[i])
}
}
results.count = filteredList.size
results.values = filteredList
} else {
results.count = notes.size
results.values = notes
}
return results
}

override fun publishResults(constraint: CharSequence, results: FilterResults) {


filteredNotes = results.values as ArrayList<Note>
notifyDataSetChanged()
}
}

}
Kotlin

search = findViewById<View>(R.id.search) as EditText


search!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int,
i2: Int) {}

override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {


filterable!!.filter.filter(search!!.text.toString())
}

override fun afterTextChanged(editable: Editable) {}


})

Invoking filter
Basic state management
public class Note implements java.io.Serializable
{
...
}

public class MainActivity extends Activity


{
...
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);

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){ }
}

...

Bundle available to onSaveInstanceState : suitable for lightweight transient data


View Model
● In-memory storage structure for UI state
● Associated with Activity's life-cycle
● Survives screen rotations but not system-initiated death

public class UsersViewModel extends ViewModel {


private List<User> users;
public List<User> getUsers() {
if (users == null) {
users = new ArrayList<User>();
// other related operations ...
}
return users;
}
}
UI Pattern: Menus
● Options Menu
– Primary collection of menu items for an activity
– Manages global navigation for activity / application
– Activities can enable / disable / change menu items
– Application-wide generalization can be done through an abstract
base Activity and a Template Method design pattern
● Context Menu
– Floating menu that appears on long-click of an element
– Provides options for manipulating a selected item
– Contextual Action Mode can be used for manipulating multiple items
simultaneously
Options Menu
Menu item fixed

App Bar Overflow menu

Create Menu Items


menu resource file
Override
onCreateOptionsMen
u
Inflate menu using
MenuInflater

Handle click events


Override
onOptionsItemSelecte
d
Contextual Menu and Action Mode
Contextual action bar
as an action mode


Implement
ActionMode.Callback
• Inflate menu

• Handle actions


Initiate action mode
• View's long-click
Recyclerview layout changed listener
to allow selection
• call startActionMode

You might also like