Campus-Guest
Techiteasy
Loaders and Adapters
#6
Android Academy TLV
11/12/2016
Yossi Segev
Yossi Segev
Crave
Android Academy
Jonathan Yarkoni
Android Developer & Advocate
Ironsource
Android Academy Staff
Yonatan Levin
Google Developer Expert &
Android @ Gett
Britt Barak
Android Lead
Figure8
Yossi Segev
Android Developer
Crave
~ 2000 members Largest Android Active Community
What Do We Do?
●Android Fundamentals
●Android UI / UX
●Community Hackathon
●Android Performance
●Mentors Program
Community Mentors
Betzalel Silver
Today’s agenda
● Part 1: Loaders
○ What are they?
○ Why do we need them?
○ How do we use them?
● Part 2: Adapters
○ View recycling
○ ViewHolder
○ ArrayAdapter
○ CursorAdapter
Code.
Lots of it.
Part 1:
Loaders
Android Main/UI Thread
1.Code runs on the main thread (UI thread).
Android Main/UI Thread
1. Code runs on the main thread (UI thread).
2.Statements are executed in sequence.
Android Main/UI Thread
1. Code runs on the main thread (UI thread).
2. Statements are executed in sequence.
3.Long operation will block the UI thread.
Android Main/UI Thread
1. Code runs on the main thread (UI thread).
2. Statements are executed in sequence.
3. Long operation will block the UI thread.
4.Perform long operation off the UI thread.
Use AsyncTask ?
AsyncTask
✓ Performs background operations off the UI thread.
doInBackground()
@Override
protected String doInBackground(String... params) {
// This code is running on a background thread.
if (!TextUtils.isEmpty(params[0])) {
return "Hello AsyncTask " + params[0];
}
return null;
}
AsyncTask
✓ Performs background operations off the UI thread.
✓ Returns results on the UI thread.
onPostExecute()
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// We're back on the UI thread so we can update the UI from here.
if (!TextUtils.isEmpty(result)) {
mTextView.setText(result);
}
}
AsyncTask
✓ Performs background operations off the UI thread.
✓ Returns results on the UI thread.
✓ Save time.
AsyncTask = Awesome
But there’s a problem…
The AsyncTask
Experiment
Experiment
onCreate() execute()
doInBackground()
Count to 3
.
.
.
onPostExecute()
Activity AsyncTask
Experiment - Logger
public class Logger {
public static void logWithThread(String tag, String message) {
Log.d(tag,
"T:"
+ Thread.currentThread().getId()
+ " | "
+ message);
}
}
D/LogTag: T:1 | The log message.
Experiment - AsyncTask
@Override
protected Void doInBackground(Void... params) {
for (int i = 1 ; i < 4 ; i ++) {
Logger.logWithThread(TAG, "doInBackground: " + i);
try {
Thread.sleep(1000); // Simulates long operation
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
Experiment - AsyncTask
public SimpleAsyncTask() {
Logger.logWithThread(TAG, "SimpleAsyncTask created.");
}
// ...
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Logger.logWithThread(TAG, "onPostExecute()");
}
Experiment - Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Logger.logWithThread(TAG, "onCreate()");
setContentView(R.layout.activity_async_task);
// Creating a new AsyncTask
SimpleAsyncTask simpleAsyncTask = new SimpleAsyncTask();
simpleAsyncTask.execute();
}
Experiment - Activity
@Override
protected void onDestroy() {
super.onDestroy();
Logger.logWithThread(TAG, "onDestroy()");
}
Test #1
Test log #1
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:132 | doInBackground: 1
D/AsyncTask: T:132 | doInBackground: 2
D/AsyncTask: T:132 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Test log #1
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:132 | doInBackground: 1
D/AsyncTask: T:132 | doInBackground: 2
D/AsyncTask: T:132 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
Test log #1
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:132 | doInBackground: 1
D/AsyncTask: T:132 | doInBackground: 2
D/AsyncTask: T:132 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
Test #1 results
Test #2
Test log #2
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 1
D/AsyncTask: T:136 | doInBackground: 2
D/Activity: T:1 | onDestroy()
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 3
D/AsyncTask: T:137 | doInBackground: 1
D/AsyncTask: T:1 | onPostExecute()
D/AsyncTask: T:137 | doInBackground: 2
D/AsyncTask: T:137 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Test log #2
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 1
D/AsyncTask: T:136 | doInBackground: 2
D/Activity: T:1 | onDestroy()
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 3
D/AsyncTask: T:137 | doInBackground: 1
D/AsyncTask: T:1 | onPostExecute()
D/AsyncTask: T:137 | doInBackground: 2
D/AsyncTask: T:137 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
Test log #2
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 1
D/AsyncTask: T:136 | doInBackground: 2
D/Activity: T:1 | onDestroy()
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 3
D/AsyncTask: T:137 | doInBackground: 1
D/AsyncTask: T:1 | onPostExecute()
D/AsyncTask: T:137 | doInBackground: 2
D/AsyncTask: T:137 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
Test log #2
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 1
D/AsyncTask: T:136 | doInBackground: 2
D/Activity: T:1 | onDestroy()
D/Activity: T:1 | onCreate()
D/AsyncTask: T:1 | SimpleAsyncTask created.
D/AsyncTask: T:136 | doInBackground: 3
D/AsyncTask: T:137 | doInBackground: 1
D/AsyncTask: T:1 | onPostExecute()
D/AsyncTask: T:137 | doInBackground: 2
D/AsyncTask: T:137 | doInBackground: 3
D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
AsyncTask #2
✘ No coordination with Activity lifecycle.
✘ Multiple AsyncTasks.
✘ onPostExecute() has no effect.
Test #2 results
Introducing:
Loaders
Loaders
● Loading data asynchronously.
● Coordinating with Activity lifecycle.
● Surviving configuration changes.
● Observing the data source for changes.
Loaders - Where?
Available to every Activity.
First introduced in Android 3.0 (Honeycomb, API 11).
Part of the v4 support library (Compatibility Library).
so,
How Loaders work?
Loader
Loader
Doing background work
● Loading data asynchronously.
● Can monitor the data source.
LoaderCallbacks
LoaderCallBacks
Notify on Loader events
Events:
● New Loader is needed.
● Data is ready.
● Loader is about to reset.
LoaderManager
● Monitor Activity lifecycle.
● Manage Loaders:
➢ Start / Stop / Reset / Retrain
(Activity lifecycle / direct request)
● Invoke LoaderCallBacks.
LoaderManager
Monitor Activity
Manage Loaders
Notify LoaderCallBacks
How Loaders work
Loader
Doing background work
LoaderCallBacks
Notify on Loader events
LoaderManager
Monitor Activity
Manage Loaders
Notify LoaderCallBacks
Create
Let’s create a Loader.
Creating a Loader
Loader<D>
The base class of all Loaders.
AsyncTaskLoader<D>
Subclass of Loader<D>, uses AsyncTask to do its
work in the background.
CursorLoader<D>
Subclass of AsyncTaskLoader<Cursor>, built to
query ContentProviders and monitor their data.
Creating a Loader
1. Create class that extends AsyncTaskLoader<String>.
1. Implement loadInBackground().
2. Override deliverResult().
1. Override onStartLoading().
SimpleLoader.java
public class SimpleLoader extends AsyncTaskLoader<String> {
public SimpleLoader(Context context) {
super(context);
}
}
Creating a Loader
1. Create class that extends AsyncTaskLoader<String>.
1. Implement loadInBackground().
2. Override deliverResult().
1. Override onStartLoading().
SimpleLoader.java
@Override
public String loadInBackground() {
// Simulate long operation
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Return data
return "I'm a data string.";
}
Creating a Loader
1. Create class that extends AsyncTaskLoader<String>.
1. Implement loadInBackground().
2. Override deliverResult().
1. Override onStartLoading().
SimpleLoader.java
private String mCache;
// ...
@Override
public void deliverResult(String data) {
mCache = data;
super.deliverResult(data);
}
Creating a Loader
1. Create class that extends AsyncTaskLoader<String>.
1. Implement loadInBackground().
2. Override deliverResult().
1. Override onStartLoading().
SimpleLoader.java
private String mCache;
// ...
@Override
protected void onStartLoading() {
super.onStartLoading();
if (TextUtils.isEmpty(mCache)) {
forceLoad();
} else {
deliverResult(mCache);
}
}
We have a Loader...
Now preparing our Activity
Preparing our Activity
1. Implement a LoaderCallbacks<String> interface.
2. Create Loader unique ID.
3. Initialize our Loader.
MyActivity.java
public class MyActivity extends Activity implements
LoaderManager.LoaderCallbacks<String> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_myactivity);
mTextView = (TextView) findViewById(R.id.text_view);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
showLoadingIndicator();
//todo init loader
}
LoaderCallbacks
LoaderCallBacks
Notify on Loader events
Events:
● New Loader is needed.
● Loader data is ready.
● Loader is about to reset.
LoaderCallbacks<String>
New Loader is needed
onCreateLoader(int id, Bundle args)
Loader unique ID Optional Bundle
MyActivity.java
// ...
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new SimpleLoader(this);
}
LoaderCallbacks
LoaderCallBacks
Notify on Loader events
Events:
● New Loader is needed.
● Loader data is ready.
● Loader is about to reset.
LoaderCallbacks<String>
Loader data is ready
onLoadFinished(Loader<String> loader, String data)
Finished Loader. Returned data
Check Loader ID:
loader.getId()
MyActivity.java
// ...
@Override
public void onLoadFinished(Loader<String> loader, String data) {
if (!TextUtils.isEmpty(data)) {
showData(data);
}
}
LoaderCallbacks
LoaderCallBacks
Notify on Loader events
Events:
● New Loader is needed.
● Loader data is ready.
● Loader is about to reset.
LoaderCallbacks<String>
Loader is about to reset
onLoaderReset(Loader<String> loader)
Check Loader ID:
loader.getId()
MyActivity.java
// ...
@Override
public void onLoaderReset(Loader<String> loader) {
// Our chance to clear data, reset UI etc...
}
Preparing our Activity
1. Implement a LoaderCallbacks<String> interface.
2. Create Loader unique ID.
3. Initialize our Loader.
Create Loader unique ID
Used to identify the Loader by LoaderManager and the Activity.
private static final int SIMPLE_LOADER_ID = 100;
Preparing our Activity
1. Implement a LoaderCallbacks<String> interface.
2. Create Loader unique ID.
3. Initialize our Loader.
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Getting a reference to the Activity LoaderManager
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Instruct LoaderManager to initialize the Loader
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Unique Loader ID to init
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Optional Bundle
Can be used for Loader creation
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Interface to report on Loader state changes
MyActivity.java
public class MyActivity extends Activity implements
LoaderManager.LoaderCallbacks<String> {
private static final int SIMPLE_LOADER_ID = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_myactivity);
mTextView = (TextView) findViewById(R.id.text_view);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
showLoadingIndicator();
getLoaderManager().initLoader(SIMPLE_LOADER_ID, null, this);
}
initLoader()
LoaderManager
initLoader(LOADER_ID, args, LoaderCallbacks);
No.
onCreateLoader(int id, Bundle
args)
Use the existing Loader
Yes.
Init new Loader
LoaderManager
Init new Loader
LoaderManager
onCreateLoader(int id, Bundle args)
Init new Loader
LoaderManager
onStartLoading()
loadInBackground()
deliverResult(D data)
Init new Loader
LoaderManager
onLoadFinished(Loader<D> loader, D data)
initLoader()
LoaderManager
initLoader(LOADER_ID, args, LoaderCallbacks);
No.
onCreateLoader(int id, Bundle
args)
Use the existing Loader
Yes.
Use existing Loader
deliverResult(D data)
Loader state ?
onLoadFinished(Loader<D> loader, D data)
Remember
● initLoader() - when ready to receive data.
● Need fresh data? Call restartLoader().
Loader vs AsyncTask
Loader vs AsyncTask
Let’s talk about the CursorLoader
CursorLoader
Subclass of AsyncTaskLoader<Cursor>.
Queries ContentProviders.
Monitor data changes using ContentObserver.
Returns a Cursor.
CursorLoader #1
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(this);
loader.setUri(ContactsContract.Contacts.CONTENT_URI);
return loader;
}
CursorLoader #2
// …
Bundle args = new Bundle();
args.putString(ARGS_SEARCH_QUERY, “Yossi”);
getLoaderManager().initLoader(CONTACTS_LOADER_ID, args, this);
CursorLoader #2
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String searchQuery = args.getString(ARGS_SEARCH_QUERY);
String selection = ContactsContract.Contacts.DISPLAY_NAME + " = ?";
String[] selectionArgs = { searchQuery };
return new CursorLoader(
this, // Context
ContactsContract.Contacts.CONTENT_URI, // Table
null, // Projections
selection, // Selection
selectionArgs, // Selection args
null); // Sort order
}
Questions?
Part 2:
Adapters
Adapter
A “bridge” between an AdapterView and it’s underlying data.
Has access to the data items.
Building a View for each item.
Adapter
Data set
ListView (AdapterView)
Adapter
Position Position
Data View
Adapter
Adapter interface
BaseAdapter
Base class of common implementation
for an Adapter
ArrayAdapter<T>
Uses array as a data source
CursorAdapter
Uses Cursor as a data source
Sunshine app ArrayAdapter
// The ArrayAdapter will take data from a source and
// use it to populate the ListView it's attached to.
mForecastAdapter =
new ArrayAdapter<String>(
getActivity(), // The current context (this activity)
R.layout.list_item_forecast, // The name of the layout ID.
R.id.list_item_forecast_textview, // The ID of the textview to populate.
new ArrayList<String>());
Building a custom ArrayAdapter
Custom ArrayAdapter
1. Define the data model.
2. Create a custom XML layout.
3. Create UsersAdapter class.
4. Override getView().
5. Tweak for performance
User.java
public class User {
private String mFullName;
private String mPhoneNumber;
public User(String fullName, String phoneNumber) {
mFullName = fullName;
mPhoneNumber = phoneNumber;
}
public String getFullName() { return mFullName; }
public String getPhoneNumber() { return mPhoneNumber; }
}
Custom ArrayAdapter
1. Define the data model.
2. Create a custom XML layout.
3. Create UsersAdapter class.
4. Override getView().
5. Tweak for performance
XML layout
<LinearLayout 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:orientation="vertical">
<TextView
android:id="@+id/users_adapter_row_fullname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
tools:text="Full Name"/>
<TextView
android:id="@+id/users_adapter_row_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
tools:text="123-4567"/>
</LinearLayout>
Custom ArrayAdapter
1. Define the data model.
2. Create a custom XML layout.
3. Create UsersAdapter class.
4. Override getView().
5. Tweak for performance
UsersAdapter.java
public class UsersAdapter extends ArrayAdapter<User> {
public UsersAdapter(Context context, User[] users) {
// We'll Override getView()
// layout res can be 0.
super(context, 0, users);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
//todo implement this method
}
Custom ArrayAdapter
1. Define the data model.
2. Create a custom XML layout.
3. Create UsersAdapter class.
4. Override getView().
5. Tweak for performance
getView()
Called for every AdapterView position.
getView(int position, View convertView, ViewGroup parent)
getView()
Called for every AdapterView position.
Convert data into a View.
getView(int position, View convertView, ViewGroup parent)
getView()
Called for every AdapterView position.
Convert data into a View.
Reference to current position.
getView(int position, View convertView, ViewGroup parent)
getView()
Called for every AdapterView position.
Convert data into a View.
Reference to current position.
Has access to recycled views.
getView(int position, View convertView, ViewGroup parent)
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
}
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
}
Get data object by position
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
}
Inflate XML (only if needed)
View recycling
getView(int position, View convertView, ViewGroup parent)
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
}
Reference layout Views
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
fullName.setText(user.getFullName());
phoneNumber.setText(user.getPhoneNumber());
}
Bind data to Views
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
fullName.setText(user.getFullName());
phoneNumber.setText(user.getPhoneNumber());
return convertView;
}
Return View
Custom ArrayAdapter
1. Define the data model.
2. Create a custom XML layout.
3. Create UsersAdapter class.
4. Override getView().
5. Tweak for performance
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
fullName.setText(user.getFullName());
phoneNumber.setText(user.getPhoneNumber());
return convertView;
}
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
fullName.setText(user.getFullName());
phoneNumber.setText(user.getPhoneNumber());
return convertView;
}
getView()
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
fullName.setText(user.getFullName());
phoneNumber.setText(user.getPhoneNumber());
return convertView;
}
ViewHolder
Hold references to the Views
Skip findViewById()
ViewHolder
public class UsersAdapter extends ArrayAdapter<User> {
// ...
private static class ViewHolder {
TextView fullName;
TextView phoneNumber;
}
}
ViewHolder
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
ViewHolder
ViewHolder vh;
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
}
ViewHolder
ViewHolder vh;
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
vh = new ViewHolder();
vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
}
ViewHolder
ViewHolder vh;
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
vh = new ViewHolder();
vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
convertView.setTag(vh);
}
ViewHolder
ViewHolder vh;
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
vh = new ViewHolder();
vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
convertView.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
ViewHolder
// ...
vh.fullName.setText(user.getFullName());
vh.phoneNumber.setText(user.getPhoneNumber());
getView() with ViewHolder
@Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
ViewHolder vh;
if (convertView == null) {
convertView = LayoutInflater.from(getContext())
.inflate(R.layout.users_adapter_row, parent, false);
vh = new ViewHolder(); // Create a new ViewHolder
vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname);
vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone);
convertView.setTag(vh); // Store it as a tag
} else {
vh = (ViewHolder) convertView.getTag(); // use the ViewHolder
}
vh.fullName.setText(user.getFullName());
vh.phoneNumber.setText(user.getPhoneNumber());
return convertView;
}
Questions?
CursorAdapter
CursorAdapter
1. Subclass of the abstract BaseAdapter class.
2. Using a Cursor as a data source.
3.getView() is mostly implemented.
CursorAdapter source code
public View getView(int position, View convertView, ViewGroup parent) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
CursorAdapter source code
public View getView(int position, View convertView, ViewGroup parent) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
CursorAdapter
1.newView():
Used to inflate a new view and return it.
2.bindView():
Get Cursor Extract data Bind data to View.
CursorAdapter
public class ContactCursorAdapter extends CursorAdapter {
public ContactCursorAdapter(Context context, Cursor c,
boolean autoRequery) {
super(context, c, autoRequery);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Inflate new view
return LayoutInflater.from(context)
.inflate(R.layout.contacts_adapter_row, parent, false);
}
// ...
}
CursorAdapter
@Override
public void bindView(View view, Context context, Cursor cursor) {
int idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
TextView displayName =
(TextView) view.findViewById(R.id.contacts_adapter_row_displayName);
// Get cursor + extract data
String name = cursor.getString(idx);
// Bind data to the view
displayName.setText(name);
}
CursorAdapter + ViewHolder
CursorAdapter + ViewHolder
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Inflate new view
View view = LayoutInflater.from(context)
.inflate(R.layout.contacts_adapter_row, parent, false);
// Create a new view holder
ViewHolder vh = new ViewHolder();
vh.displayName = (TextView) view.findViewById(R.id.contacts_adapter_row_displayName);
// Save as tag
view.setTag(vh);
return view;
}
CursorAdapter + ViewHolder
@Override
public void bindView(View view, Context context, Cursor cursor) {
int idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
// Get the view holder from the view tag
ViewHolder vh = (ViewHolder) view.getTag();
// Get cursor + extract data
String name = cursor.getString(idx);
// Bind data to the view
vh.displayName.setText(name);
What we didn’t cover today
Multiple layouts
RecyclerView
Questions?
Thank you

Session #6 loaders and adapters

  • 1.
  • 2.
    Loaders and Adapters #6 AndroidAcademy TLV 11/12/2016 Yossi Segev
  • 3.
  • 4.
    Jonathan Yarkoni Android Developer& Advocate Ironsource Android Academy Staff Yonatan Levin Google Developer Expert & Android @ Gett Britt Barak Android Lead Figure8 Yossi Segev Android Developer Crave
  • 5.
    ~ 2000 membersLargest Android Active Community
  • 6.
    What Do WeDo? ●Android Fundamentals ●Android UI / UX ●Community Hackathon ●Android Performance ●Mentors Program
  • 7.
  • 8.
    Today’s agenda ● Part1: Loaders ○ What are they? ○ Why do we need them? ○ How do we use them? ● Part 2: Adapters ○ View recycling ○ ViewHolder ○ ArrayAdapter ○ CursorAdapter
  • 9.
  • 10.
  • 11.
    Android Main/UI Thread 1.Coderuns on the main thread (UI thread).
  • 12.
    Android Main/UI Thread 1.Code runs on the main thread (UI thread). 2.Statements are executed in sequence.
  • 13.
    Android Main/UI Thread 1.Code runs on the main thread (UI thread). 2. Statements are executed in sequence. 3.Long operation will block the UI thread.
  • 14.
    Android Main/UI Thread 1.Code runs on the main thread (UI thread). 2. Statements are executed in sequence. 3. Long operation will block the UI thread. 4.Perform long operation off the UI thread.
  • 15.
  • 16.
    AsyncTask ✓ Performs backgroundoperations off the UI thread.
  • 17.
    doInBackground() @Override protected String doInBackground(String...params) { // This code is running on a background thread. if (!TextUtils.isEmpty(params[0])) { return "Hello AsyncTask " + params[0]; } return null; }
  • 18.
    AsyncTask ✓ Performs backgroundoperations off the UI thread. ✓ Returns results on the UI thread.
  • 19.
    onPostExecute() @Override protected void onPostExecute(Stringresult) { super.onPostExecute(result); // We're back on the UI thread so we can update the UI from here. if (!TextUtils.isEmpty(result)) { mTextView.setText(result); } }
  • 20.
    AsyncTask ✓ Performs backgroundoperations off the UI thread. ✓ Returns results on the UI thread. ✓ Save time.
  • 21.
    AsyncTask = Awesome Butthere’s a problem…
  • 22.
  • 23.
    Experiment onCreate() execute() doInBackground() Count to3 . . . onPostExecute() Activity AsyncTask
  • 24.
    Experiment - Logger publicclass Logger { public static void logWithThread(String tag, String message) { Log.d(tag, "T:" + Thread.currentThread().getId() + " | " + message); } } D/LogTag: T:1 | The log message.
  • 25.
    Experiment - AsyncTask @Override protectedVoid doInBackground(Void... params) { for (int i = 1 ; i < 4 ; i ++) { Logger.logWithThread(TAG, "doInBackground: " + i); try { Thread.sleep(1000); // Simulates long operation } catch (InterruptedException e) { e.printStackTrace(); } } return null; }
  • 26.
    Experiment - AsyncTask publicSimpleAsyncTask() { Logger.logWithThread(TAG, "SimpleAsyncTask created."); } // ... @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Logger.logWithThread(TAG, "onPostExecute()"); }
  • 27.
    Experiment - Activity @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Logger.logWithThread(TAG, "onCreate()"); setContentView(R.layout.activity_async_task); // Creating a new AsyncTask SimpleAsyncTask simpleAsyncTask = new SimpleAsyncTask(); simpleAsyncTask.execute(); }
  • 28.
    Experiment - Activity @Override protectedvoid onDestroy() { super.onDestroy(); Logger.logWithThread(TAG, "onDestroy()"); }
  • 29.
  • 30.
    Test log #1 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:132 | doInBackground: 1 D/AsyncTask: T:132 | doInBackground: 2 D/AsyncTask: T:132 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute()
  • 31.
    Test log #1 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:132 | doInBackground: 1 D/AsyncTask: T:132 | doInBackground: 2 D/AsyncTask: T:132 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute() Activity lifecycle
  • 32.
    Test log #1 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:132 | doInBackground: 1 D/AsyncTask: T:132 | doInBackground: 2 D/AsyncTask: T:132 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute() Activity lifecycle AsyncTask
  • 33.
  • 34.
  • 35.
    Test log #2 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 1 D/AsyncTask: T:136 | doInBackground: 2 D/Activity: T:1 | onDestroy() D/Activity: T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 3 D/AsyncTask: T:137 | doInBackground: 1 D/AsyncTask: T:1 | onPostExecute() D/AsyncTask: T:137 | doInBackground: 2 D/AsyncTask: T:137 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute()
  • 36.
    Test log #2 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 1 D/AsyncTask: T:136 | doInBackground: 2 D/Activity: T:1 | onDestroy() D/Activity: T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 3 D/AsyncTask: T:137 | doInBackground: 1 D/AsyncTask: T:1 | onPostExecute() D/AsyncTask: T:137 | doInBackground: 2 D/AsyncTask: T:137 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute() Activity lifecycle AsyncTask
  • 37.
    Test log #2 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 1 D/AsyncTask: T:136 | doInBackground: 2 D/Activity: T:1 | onDestroy() D/Activity: T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 3 D/AsyncTask: T:137 | doInBackground: 1 D/AsyncTask: T:1 | onPostExecute() D/AsyncTask: T:137 | doInBackground: 2 D/AsyncTask: T:137 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute() Activity lifecycle AsyncTask
  • 38.
    Test log #2 D/Activity:T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 1 D/AsyncTask: T:136 | doInBackground: 2 D/Activity: T:1 | onDestroy() D/Activity: T:1 | onCreate() D/AsyncTask: T:1 | SimpleAsyncTask created. D/AsyncTask: T:136 | doInBackground: 3 D/AsyncTask: T:137 | doInBackground: 1 D/AsyncTask: T:1 | onPostExecute() D/AsyncTask: T:137 | doInBackground: 2 D/AsyncTask: T:137 | doInBackground: 3 D/AsyncTask: T:1 | onPostExecute() Activity lifecycle AsyncTask AsyncTask #2
  • 39.
    ✘ No coordinationwith Activity lifecycle. ✘ Multiple AsyncTasks. ✘ onPostExecute() has no effect. Test #2 results
  • 40.
  • 41.
    Loaders ● Loading dataasynchronously. ● Coordinating with Activity lifecycle. ● Surviving configuration changes. ● Observing the data source for changes.
  • 42.
    Loaders - Where? Availableto every Activity. First introduced in Android 3.0 (Honeycomb, API 11). Part of the v4 support library (Compatibility Library).
  • 43.
  • 44.
    Loader Loader Doing background work ●Loading data asynchronously. ● Can monitor the data source.
  • 45.
    LoaderCallbacks LoaderCallBacks Notify on Loaderevents Events: ● New Loader is needed. ● Data is ready. ● Loader is about to reset.
  • 46.
    LoaderManager ● Monitor Activitylifecycle. ● Manage Loaders: ➢ Start / Stop / Reset / Retrain (Activity lifecycle / direct request) ● Invoke LoaderCallBacks. LoaderManager Monitor Activity Manage Loaders Notify LoaderCallBacks
  • 47.
    How Loaders work Loader Doingbackground work LoaderCallBacks Notify on Loader events LoaderManager Monitor Activity Manage Loaders Notify LoaderCallBacks Create
  • 48.
  • 49.
    Creating a Loader Loader<D> Thebase class of all Loaders. AsyncTaskLoader<D> Subclass of Loader<D>, uses AsyncTask to do its work in the background. CursorLoader<D> Subclass of AsyncTaskLoader<Cursor>, built to query ContentProviders and monitor their data.
  • 50.
    Creating a Loader 1.Create class that extends AsyncTaskLoader<String>. 1. Implement loadInBackground(). 2. Override deliverResult(). 1. Override onStartLoading().
  • 51.
    SimpleLoader.java public class SimpleLoaderextends AsyncTaskLoader<String> { public SimpleLoader(Context context) { super(context); } }
  • 52.
    Creating a Loader 1.Create class that extends AsyncTaskLoader<String>. 1. Implement loadInBackground(). 2. Override deliverResult(). 1. Override onStartLoading().
  • 53.
    SimpleLoader.java @Override public String loadInBackground(){ // Simulate long operation try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // Return data return "I'm a data string."; }
  • 54.
    Creating a Loader 1.Create class that extends AsyncTaskLoader<String>. 1. Implement loadInBackground(). 2. Override deliverResult(). 1. Override onStartLoading().
  • 55.
    SimpleLoader.java private String mCache; //... @Override public void deliverResult(String data) { mCache = data; super.deliverResult(data); }
  • 56.
    Creating a Loader 1.Create class that extends AsyncTaskLoader<String>. 1. Implement loadInBackground(). 2. Override deliverResult(). 1. Override onStartLoading().
  • 57.
    SimpleLoader.java private String mCache; //... @Override protected void onStartLoading() { super.onStartLoading(); if (TextUtils.isEmpty(mCache)) { forceLoad(); } else { deliverResult(mCache); } }
  • 58.
    We have aLoader... Now preparing our Activity
  • 59.
    Preparing our Activity 1.Implement a LoaderCallbacks<String> interface. 2. Create Loader unique ID. 3. Initialize our Loader.
  • 60.
    MyActivity.java public class MyActivityextends Activity implements LoaderManager.LoaderCallbacks<String> { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_myactivity); mTextView = (TextView) findViewById(R.id.text_view); mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); showLoadingIndicator(); //todo init loader }
  • 61.
    LoaderCallbacks LoaderCallBacks Notify on Loaderevents Events: ● New Loader is needed. ● Loader data is ready. ● Loader is about to reset.
  • 62.
    LoaderCallbacks<String> New Loader isneeded onCreateLoader(int id, Bundle args) Loader unique ID Optional Bundle
  • 63.
    MyActivity.java // ... @Override public Loader<String>onCreateLoader(int id, Bundle args) { return new SimpleLoader(this); }
  • 64.
    LoaderCallbacks LoaderCallBacks Notify on Loaderevents Events: ● New Loader is needed. ● Loader data is ready. ● Loader is about to reset.
  • 65.
    LoaderCallbacks<String> Loader data isready onLoadFinished(Loader<String> loader, String data) Finished Loader. Returned data Check Loader ID: loader.getId()
  • 66.
    MyActivity.java // ... @Override public voidonLoadFinished(Loader<String> loader, String data) { if (!TextUtils.isEmpty(data)) { showData(data); } }
  • 67.
    LoaderCallbacks LoaderCallBacks Notify on Loaderevents Events: ● New Loader is needed. ● Loader data is ready. ● Loader is about to reset.
  • 68.
    LoaderCallbacks<String> Loader is aboutto reset onLoaderReset(Loader<String> loader) Check Loader ID: loader.getId()
  • 69.
    MyActivity.java // ... @Override public voidonLoaderReset(Loader<String> loader) { // Our chance to clear data, reset UI etc... }
  • 70.
    Preparing our Activity 1.Implement a LoaderCallbacks<String> interface. 2. Create Loader unique ID. 3. Initialize our Loader.
  • 71.
    Create Loader uniqueID Used to identify the Loader by LoaderManager and the Activity. private static final int SIMPLE_LOADER_ID = 100;
  • 72.
    Preparing our Activity 1.Implement a LoaderCallbacks<String> interface. 2. Create Loader unique ID. 3. Initialize our Loader.
  • 73.
  • 74.
    Init Loader getLoaderManager().initLoader(LOADER_ID, args,LoaderCallbacks); Getting a reference to the Activity LoaderManager
  • 75.
    Init Loader getLoaderManager().initLoader(LOADER_ID, args,LoaderCallbacks); Instruct LoaderManager to initialize the Loader
  • 76.
    Init Loader getLoaderManager().initLoader(LOADER_ID, args,LoaderCallbacks); Unique Loader ID to init
  • 77.
    Init Loader getLoaderManager().initLoader(LOADER_ID, args,LoaderCallbacks); Optional Bundle Can be used for Loader creation
  • 78.
    Init Loader getLoaderManager().initLoader(LOADER_ID, args,LoaderCallbacks); Interface to report on Loader state changes
  • 79.
    MyActivity.java public class MyActivityextends Activity implements LoaderManager.LoaderCallbacks<String> { private static final int SIMPLE_LOADER_ID = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_myactivity); mTextView = (TextView) findViewById(R.id.text_view); mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); showLoadingIndicator(); getLoaderManager().initLoader(SIMPLE_LOADER_ID, null, this); }
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
    Use existing Loader deliverResult(Ddata) Loader state ? onLoadFinished(Loader<D> loader, D data)
  • 87.
    Remember ● initLoader() -when ready to receive data. ● Need fresh data? Call restartLoader().
  • 88.
  • 89.
  • 90.
    Let’s talk aboutthe CursorLoader
  • 91.
    CursorLoader Subclass of AsyncTaskLoader<Cursor>. QueriesContentProviders. Monitor data changes using ContentObserver. Returns a Cursor.
  • 92.
    CursorLoader #1 @Override public Loader<Cursor>onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(this); loader.setUri(ContactsContract.Contacts.CONTENT_URI); return loader; }
  • 93.
    CursorLoader #2 // … Bundleargs = new Bundle(); args.putString(ARGS_SEARCH_QUERY, “Yossi”); getLoaderManager().initLoader(CONTACTS_LOADER_ID, args, this);
  • 94.
    CursorLoader #2 @Override public Loader<Cursor>onCreateLoader(int id, Bundle args) { String searchQuery = args.getString(ARGS_SEARCH_QUERY); String selection = ContactsContract.Contacts.DISPLAY_NAME + " = ?"; String[] selectionArgs = { searchQuery }; return new CursorLoader( this, // Context ContactsContract.Contacts.CONTENT_URI, // Table null, // Projections selection, // Selection selectionArgs, // Selection args null); // Sort order }
  • 95.
  • 96.
  • 97.
    Adapter A “bridge” betweenan AdapterView and it’s underlying data. Has access to the data items. Building a View for each item.
  • 98.
  • 99.
    Adapter Adapter interface BaseAdapter Base classof common implementation for an Adapter ArrayAdapter<T> Uses array as a data source CursorAdapter Uses Cursor as a data source
  • 100.
    Sunshine app ArrayAdapter //The ArrayAdapter will take data from a source and // use it to populate the ListView it's attached to. mForecastAdapter = new ArrayAdapter<String>( getActivity(), // The current context (this activity) R.layout.list_item_forecast, // The name of the layout ID. R.id.list_item_forecast_textview, // The ID of the textview to populate. new ArrayList<String>());
  • 101.
    Building a customArrayAdapter
  • 102.
    Custom ArrayAdapter 1. Definethe data model. 2. Create a custom XML layout. 3. Create UsersAdapter class. 4. Override getView(). 5. Tweak for performance
  • 103.
    User.java public class User{ private String mFullName; private String mPhoneNumber; public User(String fullName, String phoneNumber) { mFullName = fullName; mPhoneNumber = phoneNumber; } public String getFullName() { return mFullName; } public String getPhoneNumber() { return mPhoneNumber; } }
  • 104.
    Custom ArrayAdapter 1. Definethe data model. 2. Create a custom XML layout. 3. Create UsersAdapter class. 4. Override getView(). 5. Tweak for performance
  • 105.
  • 106.
    Custom ArrayAdapter 1. Definethe data model. 2. Create a custom XML layout. 3. Create UsersAdapter class. 4. Override getView(). 5. Tweak for performance
  • 107.
    UsersAdapter.java public class UsersAdapterextends ArrayAdapter<User> { public UsersAdapter(Context context, User[] users) { // We'll Override getView() // layout res can be 0. super(context, 0, users); } @Override public View getView(int position, View convertView, ViewGroup parent) { //todo implement this method }
  • 108.
    Custom ArrayAdapter 1. Definethe data model. 2. Create a custom XML layout. 3. Create UsersAdapter class. 4. Override getView(). 5. Tweak for performance
  • 109.
    getView() Called for everyAdapterView position. getView(int position, View convertView, ViewGroup parent)
  • 110.
    getView() Called for everyAdapterView position. Convert data into a View. getView(int position, View convertView, ViewGroup parent)
  • 111.
    getView() Called for everyAdapterView position. Convert data into a View. Reference to current position. getView(int position, View convertView, ViewGroup parent)
  • 112.
    getView() Called for everyAdapterView position. Convert data into a View. Reference to current position. Has access to recycled views. getView(int position, View convertView, ViewGroup parent)
  • 113.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { }
  • 114.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); } Get data object by position
  • 115.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } } Inflate XML (only if needed)
  • 116.
    View recycling getView(int position,View convertView, ViewGroup parent)
  • 117.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); } Reference layout Views
  • 118.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); fullName.setText(user.getFullName()); phoneNumber.setText(user.getPhoneNumber()); } Bind data to Views
  • 119.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); fullName.setText(user.getFullName()); phoneNumber.setText(user.getPhoneNumber()); return convertView; } Return View
  • 120.
    Custom ArrayAdapter 1. Definethe data model. 2. Create a custom XML layout. 3. Create UsersAdapter class. 4. Override getView(). 5. Tweak for performance
  • 121.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); fullName.setText(user.getFullName()); phoneNumber.setText(user.getPhoneNumber()); return convertView; }
  • 122.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); fullName.setText(user.getFullName()); phoneNumber.setText(user.getPhoneNumber()); return convertView; }
  • 123.
    getView() @Override public View getView(intposition, View convertView, ViewGroup parent) { User user = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); } TextView fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); TextView phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); fullName.setText(user.getFullName()); phoneNumber.setText(user.getPhoneNumber()); return convertView; }
  • 124.
    ViewHolder Hold references tothe Views Skip findViewById()
  • 125.
    ViewHolder public class UsersAdapterextends ArrayAdapter<User> { // ... private static class ViewHolder { TextView fullName; TextView phoneNumber; } }
  • 126.
    ViewHolder if (convertView ==null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); }
  • 127.
    ViewHolder ViewHolder vh; if (convertView== null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); }
  • 128.
    ViewHolder ViewHolder vh; if (convertView== null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); vh = new ViewHolder(); vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); }
  • 129.
    ViewHolder ViewHolder vh; if (convertView== null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); vh = new ViewHolder(); vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); convertView.setTag(vh); }
  • 130.
    ViewHolder ViewHolder vh; if (convertView== null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); vh = new ViewHolder(); vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); convertView.setTag(vh); } else { vh = (ViewHolder) convertView.getTag(); }
  • 131.
  • 132.
    getView() with ViewHolder @Override publicView getView(int position, View convertView, ViewGroup parent) { User user = getItem(position); ViewHolder vh; if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false); vh = new ViewHolder(); // Create a new ViewHolder vh.fullName = (TextView) convertView.findViewById(R.id.users_adapter_row_fullname); vh.phoneNumber = (TextView) convertView.findViewById(R.id.users_adapter_row_phone); convertView.setTag(vh); // Store it as a tag } else { vh = (ViewHolder) convertView.getTag(); // use the ViewHolder } vh.fullName.setText(user.getFullName()); vh.phoneNumber.setText(user.getPhoneNumber()); return convertView; }
  • 133.
  • 134.
  • 135.
    CursorAdapter 1. Subclass ofthe abstract BaseAdapter class. 2. Using a Cursor as a data source. 3.getView() is mostly implemented.
  • 136.
    CursorAdapter source code publicView getView(int position, View convertView, ViewGroup parent) { if (!mDataValid) { throw new IllegalStateException("this should only be called when the cursor is valid"); } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException("couldn't move cursor to position " + position); } View v; if (convertView == null) { v = newView(mContext, mCursor, parent); } else { v = convertView; } bindView(v, mContext, mCursor); return v; }
  • 137.
    CursorAdapter source code publicView getView(int position, View convertView, ViewGroup parent) { if (!mDataValid) { throw new IllegalStateException("this should only be called when the cursor is valid"); } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException("couldn't move cursor to position " + position); } View v; if (convertView == null) { v = newView(mContext, mCursor, parent); } else { v = convertView; } bindView(v, mContext, mCursor); return v; }
  • 138.
    CursorAdapter 1.newView(): Used to inflatea new view and return it. 2.bindView(): Get Cursor Extract data Bind data to View.
  • 139.
    CursorAdapter public class ContactCursorAdapterextends CursorAdapter { public ContactCursorAdapter(Context context, Cursor c, boolean autoRequery) { super(context, c, autoRequery); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // Inflate new view return LayoutInflater.from(context) .inflate(R.layout.contacts_adapter_row, parent, false); } // ... }
  • 140.
    CursorAdapter @Override public void bindView(Viewview, Context context, Cursor cursor) { int idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); TextView displayName = (TextView) view.findViewById(R.id.contacts_adapter_row_displayName); // Get cursor + extract data String name = cursor.getString(idx); // Bind data to the view displayName.setText(name); }
  • 141.
  • 142.
    CursorAdapter + ViewHolder @Override publicView newView(Context context, Cursor cursor, ViewGroup parent) { // Inflate new view View view = LayoutInflater.from(context) .inflate(R.layout.contacts_adapter_row, parent, false); // Create a new view holder ViewHolder vh = new ViewHolder(); vh.displayName = (TextView) view.findViewById(R.id.contacts_adapter_row_displayName); // Save as tag view.setTag(vh); return view; }
  • 143.
    CursorAdapter + ViewHolder @Override publicvoid bindView(View view, Context context, Cursor cursor) { int idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); // Get the view holder from the view tag ViewHolder vh = (ViewHolder) view.getTag(); // Get cursor + extract data String name = cursor.getString(idx); // Bind data to the view vh.displayName.setText(name);
  • 144.
    What we didn’tcover today Multiple layouts RecyclerView
  • 145.
  • 146.