session #6 loaders and adapters
TRANSCRIPT
Campus-GuestTechiteasy
Loaders and Adapters#6
Android Academy TLV11/12/2016Yossi Segev
Yossi Segev
Crave
Android Academy
Jonathan Yarkoni
Android Developer & Advocate Ironsource
Android Academy Staff
Yonatan LevinGoogle Developer
Expert & Android @ Gett
Britt BarakAndroid Lead
Figure8
Yossi SegevAndroid Developer
Crave
~ 2000 members Largest Android Active Community
What Do We Do?
●Android Fundamentals
●Android UI / UX
●Community Hackathon
●Android Performance
●Mentors Program●Active community
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()@Overrideprotected 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()@Overrideprotected 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 = AwesomeBut there’s a problem…
The AsyncTaskExperiment
Experiment
onCreate() execute()
doInBackground()Count to 3
.
.
.
onPostExecute()
Activity AsyncTask
Experiment - Loggerpublic 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@Overrideprotected 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 - AsyncTaskpublic SimpleAsyncTask() { Logger.logWithThread(TAG, "SimpleAsyncTask created.");}
// ...
@Overrideprotected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Logger.logWithThread(TAG, "onPostExecute()");}
Experiment - Activity@Overrideprotected 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@Overrideprotected 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: 1D/AsyncTask: T:132 | doInBackground: 2D/AsyncTask: T:132 | doInBackground: 3D/AsyncTask: T:1 | onPostExecute()
Test log #1
D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:132 | doInBackground: 1D/AsyncTask: T:132 | doInBackground: 2D/AsyncTask: T:132 | doInBackground: 3D/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: 1D/AsyncTask: T:132 | doInBackground: 2D/AsyncTask: T:132 | doInBackground: 3D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
Test #1 results
Test #2
Test log #2D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 1D/AsyncTask: T:136 | doInBackground: 2D/Activity: T:1 | onDestroy()D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 3D/AsyncTask: T:137 | doInBackground: 1D/AsyncTask: T:1 | onPostExecute()D/AsyncTask: T:137 | doInBackground: 2D/AsyncTask: T:137 | doInBackground: 3D/AsyncTask: T:1 | onPostExecute()
Test log #2D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 1D/AsyncTask: T:136 | doInBackground: 2D/Activity: T:1 | onDestroy()D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 3D/AsyncTask: T:137 | doInBackground: 1D/AsyncTask: T:1 | onPostExecute()D/AsyncTask: T:137 | doInBackground: 2D/AsyncTask: T:137 | doInBackground: 3D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
Test log #2D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 1D/AsyncTask: T:136 | doInBackground: 2D/Activity: T:1 | onDestroy()D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 3D/AsyncTask: T:137 | doInBackground: 1D/AsyncTask: T:1 | onPostExecute()D/AsyncTask: T:137 | doInBackground: 2D/AsyncTask: T:137 | doInBackground: 3D/AsyncTask: T:1 | onPostExecute()
Activity lifecycle
AsyncTask
Test log #2D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 1D/AsyncTask: T:136 | doInBackground: 2D/Activity: T:1 | onDestroy()D/Activity: T:1 | onCreate()D/AsyncTask: T:1 | SimpleAsyncTask created.D/AsyncTask: T:136 | doInBackground: 3D/AsyncTask: T:137 | doInBackground: 1D/AsyncTask: T:1 | onPostExecute()D/AsyncTask: T:137 | doInBackground: 2D/AsyncTask: T:137 | doInBackground: 3D/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
LoaderDoing background work
● Loading data asynchronously.
● Can monitor the data source.
LoaderCallbacks
LoaderCallBacksNotify 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.
LoaderManagerMonitor Activity
Manage Loaders
Notify LoaderCallBacks
How Loaders work
LoaderDoing background work
LoaderCallBacksNotify on Loader events
LoaderManagerMonitor Activity
Manage Loaders
Notify LoaderCallBacks
InvokeNotify
Manag
eCreate
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>.
2. Implement loadInBackground().
3. Override deliverResult().
4. Override onStartLoading().
SimpleLoader.javapublic class SimpleLoader extends AsyncTaskLoader<String> { public SimpleLoader(Context context) { super(context); }}
Creating a Loader
1. Create class that extends AsyncTaskLoader<String>.
2. Implement loadInBackground().
3. Override deliverResult().
4. Override onStartLoading().
SimpleLoader.java@Overridepublic 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>.
2. Implement loadInBackground().
3. Override deliverResult().
4. Override onStartLoading().
SimpleLoader.javaprivate String mCache;
// ...
@Overridepublic void deliverResult(String data) { mCache = data; super.deliverResult(data);}
Creating a Loader
1. Create class that extends AsyncTaskLoader<String>.
2. Implement loadInBackground().
3. Override deliverResult().
4. Override onStartLoading().
SimpleLoader.javaprivate String mCache;
// ...
@Overrideprotected 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.javapublic class MyActivity extends Activity implements LoaderManager.LoaderCallbacks<String> {
@Overrideprotected 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
LoaderCallBacksNotify on Loader events
Events:● New Loader is needed.● Loader data is ready.● Loader is about to reset.
LoaderCallbacks<String>
New Loader is neededonCreateLoader(int id, Bundle args)
Loader unique ID
Optional Bundle
MyActivity.java// ...
@Overridepublic Loader<String> onCreateLoader(int id, Bundle args) { return new SimpleLoader(this);}
LoaderCallbacks
LoaderCallBacksNotify on Loader events
Events:● New Loader is needed.● Loader data is ready.● Loader is about to reset.
LoaderCallbacks<String>
Loader data is readyonLoadFinished(Loader<String> loader, String data)
Finished Loader.
Returned data
Check Loader ID:
loader.getId()
MyActivity.java// ...
@Overridepublic void onLoadFinished(Loader<String> loader, String data) { if (!TextUtils.isEmpty(data)) { showData(data); }}
LoaderCallbacks
LoaderCallBacksNotify on Loader events
Events:● New Loader is needed.● Loader data is ready.● Loader is about to reset.
LoaderCallbacks<String>
Loader is about to resetonLoaderReset(Loader<String> loader)
Check Loader ID:
loader.getId()
MyActivity.java// ...
@Overridepublic 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 BundleCan be used for Loader creation
Init Loader
getLoaderManager().initLoader(LOADER_ID, args, LoaderCallbacks);
Interface to report on Loader state changes
MyActivity.javapublic class MyActivity extends Activity implements LoaderManager.LoaderCallbacks<String> {
private static final int SIMPLE_LOADER_ID = 100;
@Overrideprotected 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);
Do I know
this ID?
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
LoaderManageronStartLoading()
loadInBackground()
deliverResult(D data)
Init new Loader
LoaderManager
onLoadFinished(Loader<D> loader, D data)
initLoader()
LoaderManager
initLoader(LOADER_ID, args, LoaderCallbacks);
Do I know
this ID?
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@Overridepublic 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@Overridepublic 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
BaseAdapterBase class of common implementation
for an Adapter
ArrayAdapter<T>Uses array as a data source
CursorAdapterUses 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.javapublic 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.javapublic class UsersAdapter extends ArrayAdapter<User> {
public UsersAdapter(Context context, User[] users) { // We'll Override getView() // layout res can be 0. super(context, 0, users);}
@Overridepublic 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()@Overridepublic View getView(int position, View convertView, ViewGroup parent) {
}
getView()@Overridepublic View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
}
Get data object by position
getView()@Overridepublic 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()@Overridepublic 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()@Overridepublic 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()@Overridepublic 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()@Overridepublic 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()@Overridepublic 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()@Overridepublic 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()
ViewHolderpublic 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);}
ViewHolderViewHolder vh;
if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(R.layout.users_adapter_row, parent, false);}
ViewHolderViewHolder 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);
}
ViewHolderViewHolder 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);
}
ViewHolderViewHolder 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@Overridepublic 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 codepublic 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 codepublic 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.
CursorAdapterpublic class ContactCursorAdapter extends CursorAdapter {
public ContactCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);}
@Overridepublic View newView(Context context, Cursor cursor, ViewGroup parent) { // Inflate new view return LayoutInflater.from(context)
.inflate(R.layout.contacts_adapter_row, parent, false);}
// ...}
CursorAdapter@Overridepublic 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@Overridepublic 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@Overridepublic 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 layoutsRecyclerView
Questions ?
Thank you