Share
Embed
Google Mobile Design Codelab.pdf
JasonNi
October 03, 2013
Transcript
139 slides
-
Developer Resources Find out more: + Android Developers Android Developers
on YouTube Android Developers Blog http://developer.android.com -
DevByte Series: “Making Apps Beautiful” “Making Apps Beautiful” DevBytes series
on +Android Developers http://bit.ly/14xbcD0 -
Codelab Format Slides, Worksheet, Code 6 Checkpoints Ask our friendly
Teaching Assistants Material: http://bit.ly/18EhXuo -
Our Agenda 0: Get Set up 1: Modernising and ActionBarCompat
2: Branding 3: Spacing and Typography 4: Tablet UI 5: Bitmap Performance 6: Threading 7: Image Caching -
Required Downloads IDE for Android Development: http://developer.android.com/sdk •ADT Bundle (Eclipse
+ Android SDK) •Bonus: Android Studio Android SDK, API 18 Support Library (already included in codelab archive) -
Checkpoint 0 Worksheet: Checkpoint 0 http://bit.ly/18EhXuo What you will do:
•Setup IDE and environment •Import Project •Run the app -
EXAMPLE TREATMENT FOR DEVELOPERS AndroidManifest.xml: android:targetSdkVersion = 18 <?xml version="1.0"
encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xyzreader" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="18" /> ... </manifest> -
EXAMPLE TREATMENT FOR DEVELOPERS AndroidManifest.xml: android:targetSdkVersion = 18 Set your
target SDK <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xyzreader" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="18" /> ... </manifest> -
EXAMPLE TREATMENT FOR DEVELOPERS AndroidManifest.xml: android:targetSdkVersion = 18 Set your
target SDK <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xyzreader" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="18" /> ... </manifest> -
EXAMPLE TREATMENT FOR DEVELOPERS AndroidManifest.xml: android:targetSdkVersion = 18 <?xml version="1.0"
encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xyzreader" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="18" /> ... </manifest> -
EXAMPLE TREATMENT FOR DEVELOPERS Extend ActionBarActivity Use ActionBarCompat public class
ArticleDetailActivity extends ActionBarActivity FragmentActivity { ... } public class ArticleListActivity extends ActionBarActivity FragmentActivity { ... } -
EXAMPLE TREATMENT FOR DEVELOPERS ActionBar options in ActionBarCompat: Use support-callbacks
Use ActionBarCompat @Override public void onCreateSupportOptionsMenu(Menu menu, MenuInflater inflater) { ... } @Override public boolean onSupportOptionsItemSelected(MenuItem item) { ... } -
EXAMPLE TREATMENT FOR DEVELOPERS Menus in ActionBarCompat: Need to define
your own namespace Use ActionBarCompat In your menu.xml, add the following namespace: <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="@+id/refresh" android:title="@string/refresh" android:icon="@drawable/ic_refresh" app:showAsAction="always|withText" /> <item android:id="@+id/settings" app:showAsAction="never" android:title="@string/settings"/> </menu> -
EXAMPLE TREATMENT FOR DEVELOPERS Menus in ActionBarCompat: Need to define
your own namespace Use ActionBarCompat In your menu.xml, add the following namespace: <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="@+id/refresh" android:title="@string/refresh" android:icon="@drawable/ic_refresh" app:showAsAction="always|withText" /> <item android:id="@+id/settings" app:showAsAction="never" android:title="@string/settings"/> </menu> -
EXAMPLE TREATMENT FOR DEVELOPERS Menus in ActionBarCompat: Need to define
your own namespace In your menu.xml, add the following namespace: <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="@+id/refresh" android:title="@string/refresh" android:icon="@drawable/ic_refresh" app:showAsAction="always|withText" /> <item android:id="@+id/settings" app:showAsAction="never" android:title="@string/settings"/> </menu> -
EXAMPLE TREATMENT FOR DEVELOPERS android:theme = Theme.Holo.Light.DarkActionBar <resources> <style name="AppBaseTheme"
parent="Theme.AppCompat.Light.DarkActionBar"> </style> </resources> -
EXAMPLE TREATMENT FOR DEVELOPERS android:theme = Theme.Holo.Light.DarkActionBar Holo Theme +
Action Bar <resources> <style name="AppBaseTheme" parent="Theme.AppCompat.Light.DarkActionBar"> </style> </resources> -
EXAMPLE TREATMENT FOR DEVELOPERS android:theme = Theme.Holo.Light.DarkActionBar <resources> <style name="AppBaseTheme"
parent="Theme.AppCompat.Light.DarkActionBar"> </style> </resources> -
EXAMPLE TREATMENT FOR DEVELOPERS Application theme MUST inherit from Theme.AppCompat.*
Styling your ActionBar <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="Theme.Styled" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item> <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item> </style> <style name="Widget.Styled.ActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse"> <item name="background">@drawable/bg_striped</item> <item name="android:background">@drawable/bg_striped</item> </style> ... </resources> -
EXAMPLE TREATMENT FOR DEVELOPERS Application theme MUST inherit from Theme.AppCompat.*
Use ActionBarCompat Styling your ActionBar <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="Theme.Styled" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item> <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item> </style> <style name="Widget.Styled.ActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse"> <item name="background">@drawable/bg_striped</item> <item name="android:background">@drawable/bg_striped</item> </style> ... </resources> -
EXAMPLE TREATMENT FOR DEVELOPERS Application theme MUST inherit from Theme.AppCompat.*
Styling your ActionBar <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="Theme.Styled" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item> <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item> </style> <style name="Widget.Styled.ActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse"> <item name="background">@drawable/bg_striped</item> <item name="android:background">@drawable/bg_striped</item> </style> ... </resources> -
Checkpoint 1 Worksheet: Checkpoint 1 http://bit.ly/18EhXuo What you will do:
•Set the targetSDK •Add ActionBarCompat -
EXAMPLE TREATMENT FOR DEVELOPERS Search Google for “Action Bar Style
Generator”: Android Action Bar Style Generator by Jeff Gilfelt Style Compatibility AppCompat -
EXAMPLE TREATMENT Branded Color Scheme + App Icon FOR DEVELOPERS
Search Google for “Action Bar Style Generator”: Android Action Bar Style Generator by Jeff Gilfelt Style Compatibility AppCompat -
EXAMPLE TREATMENT FOR DEVELOPERS Search Google for “Action Bar Style
Generator”: Android Action Bar Style Generator by Jeff Gilfelt Style Compatibility AppCompat -
Checkpoint 2 Worksheet: Checkpoint 2 http://bit.ly/18EhXuo What you will do:
•(Generate your own style) •Apply custom theme •Update link color -
EXAMPLE TREATMENT MORE INFO 48dp high elements: better touch targets,
information density -
EXAMPLE TREATMENT 48dp Pattern MORE INFO 48dp high elements: better
touch targets, information density -
EXAMPLE TREATMENT MORE INFO 48dp high elements: better touch targets,
information density -
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="thumbnail_width">80dp</dimen> <dimen name="thumbnail_height">48dp</dimen> <dimen name="photo_height">200dp</dimen>
<dimen name="list_item_vert_margin">8dp</dimen> <dimen name="detail_horiz_margin">16dp</dimen> </resources> EXAMPLE TREATMENT FOR DEVELOPERS 48dp Rhythm set margins in dimens.xml dimens.xml -
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="thumbnail_width">80dp</dimen> <dimen name="thumbnail_height">48dp</dimen> <dimen name="photo_height">200dp</dimen>
<dimen name="list_item_vert_margin">8dp</dimen> <dimen name="detail_horiz_margin">16dp</dimen> </resources> EXAMPLE TREATMENT FOR DEVELOPERS 48dp Rhythm set margins in dimens.xml Whitespace (Margins + Spacing) dimens.xml -
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="thumbnail_width">80dp</dimen> <dimen name="thumbnail_height">48dp</dimen> <dimen name="photo_height">200dp</dimen>
<dimen name="list_item_vert_margin">8dp</dimen> <dimen name="detail_horiz_margin">16dp</dimen> </resources> EXAMPLE TREATMENT FOR DEVELOPERS 48dp Rhythm set margins in dimens.xml Whitespace (Margins + Spacing) dimens.xml -
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="thumbnail_width">80dp</dimen> <dimen name="thumbnail_height">48dp</dimen> <dimen name="photo_height">200dp</dimen>
<dimen name="list_item_vert_margin">8dp</dimen> <dimen name="detail_horiz_margin">16dp</dimen> </resources> EXAMPLE TREATMENT FOR DEVELOPERS 48dp Rhythm set margins in dimens.xml dimens.xml -
EXAMPLE TREATMENT FOR DEVELOPERS android:scaleType = centerCrop <ImageView android:id="@+id/photo" android:layout_width="match_parent"
android:layout_height="@dimen/photo_height" android:layout_marginLeft="@dimen/detail_horiz_margin" android:layout_marginRight="@dimen/detail_horiz_margin" android:scaleType="centerCrop" /> -
EXAMPLE TREATMENT FOR DEVELOPERS android:scaleType = centerCrop Thumbnail + Photo
Treatment <ImageView android:id="@+id/photo" android:layout_width="match_parent" android:layout_height="@dimen/photo_height" android:layout_marginLeft="@dimen/detail_horiz_margin" android:layout_marginRight="@dimen/detail_horiz_margin" android:scaleType="centerCrop" /> -
EXAMPLE TREATMENT FOR DEVELOPERS android:scaleType = centerCrop Thumbnail + Photo
Treatment <ImageView android:id="@+id/photo" android:layout_width="match_parent" android:layout_height="@dimen/photo_height" android:layout_marginLeft="@dimen/detail_horiz_margin" android:layout_marginRight="@dimen/detail_horiz_margin" android:scaleType="centerCrop" /> -
EXAMPLE TREATMENT FOR DEVELOPERS android:scaleType = centerCrop <ImageView android:id="@+id/photo" android:layout_width="match_parent"
android:layout_height="@dimen/photo_height" android:layout_marginLeft="@dimen/detail_horiz_margin" android:layout_marginRight="@dimen/detail_horiz_margin" android:scaleType="centerCrop" /> -
EXAMPLE TREATMENT Custom Font More spacing Ellipsized text Limited, Fixed
number of lines All caps Deemphasized colour for sub headings -
EXAMPLE TREATMENT Custom Font More spacing Ellipsized text Limited, Fixed
number of lines All caps Deemphasized colour for sub headings Typography -
EXAMPLE TREATMENT Custom Font More spacing Ellipsized text Limited, Fixed
number of lines All caps Deemphasized colour for sub headings -
EXAMPLE TREATMENT <TextView android:id="@+id/article_title" style="?android:textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#c000" android:fontFamily="sans-serif-condensed" android:textStyle="bold"
android:ellipsize="end" android:maxLines="2" android:lineSpacingMultiplier="0.85" android:paddingBottom="4dp" /> <TextView android:id="@+id/article_subtitle" style="?android:textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#6000" android:textAllCaps="true" android:maxLines="1" android:singleLine="true" android:ellipsize="end" /> FOR DEVELOPERS android:fontFamily = sans-serif-condensed (or -light) android:lineSpacingMultiplier android:textStyle android:ellipsize = end android:maxLines android:singleLine = true android:textAllCaps = true -
EXAMPLE TREATMENT Typography <TextView android:id="@+id/article_title" style="?android:textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#c000" android:fontFamily="sans-serif-condensed"
android:textStyle="bold" android:ellipsize="end" android:maxLines="2" android:lineSpacingMultiplier="0.85" android:paddingBottom="4dp" /> <TextView android:id="@+id/article_subtitle" style="?android:textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#6000" android:textAllCaps="true" android:maxLines="1" android:singleLine="true" android:ellipsize="end" /> FOR DEVELOPERS android:fontFamily = sans-serif-condensed (or -light) android:lineSpacingMultiplier android:textStyle android:ellipsize = end android:maxLines android:singleLine = true android:textAllCaps = true -
EXAMPLE TREATMENT Typography <TextView android:id="@+id/article_title" style="?android:textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#c000" android:fontFamily="sans-serif-condensed"
android:textStyle="bold" android:ellipsize="end" android:maxLines="2" android:lineSpacingMultiplier="0.85" android:paddingBottom="4dp" /> <TextView android:id="@+id/article_subtitle" style="?android:textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#6000" android:textAllCaps="true" android:maxLines="1" android:singleLine="true" android:ellipsize="end" /> FOR DEVELOPERS android:fontFamily = sans-serif-condensed (or -light) android:lineSpacingMultiplier android:textStyle android:ellipsize = end android:maxLines android:singleLine = true android:textAllCaps = true -
EXAMPLE TREATMENT <TextView android:id="@+id/article_title" style="?android:textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#c000" android:fontFamily="sans-serif-condensed" android:textStyle="bold"
android:ellipsize="end" android:maxLines="2" android:lineSpacingMultiplier="0.85" android:paddingBottom="4dp" /> <TextView android:id="@+id/article_subtitle" style="?android:textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#6000" android:textAllCaps="true" android:maxLines="1" android:singleLine="true" android:ellipsize="end" /> FOR DEVELOPERS android:fontFamily = sans-serif-condensed (or -light) android:lineSpacingMultiplier android:textStyle android:ellipsize = end android:maxLines android:singleLine = true android:textAllCaps = true -
EXAMPLE TREATMENT FOR DEVELOPERS New link color, setting color within
TextView Custom Highlight + Font -
EXAMPLE TREATMENT FOR DEVELOPERS android:textColorLink = @color/bacon Custom code @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_article_detail, container, false); if (mItem != null) { ((TextView) rootView.findViewById(R.id.article_title)) .setText(mItem.title); // Custom highlight the author ((TextView) rootView.findViewById(R.id.article_byline)) .setText(Html.fromHtml(mItem.time.toUpperCase() + " BY <font color='" + getResources().getString(R.string.author_font_color) + "'>" + mItem.author.toUpperCase() + "</a>")); // Use custom font ((TextView) rootView.findViewById(R.id.article_body)) .setTypeface(Typeface.createFromAsset( getResources().getAssets(), "Rosario-Regular.ttf")); ... } return rootView; } -
EXAMPLE TREATMENT FOR DEVELOPERS android:textColorLink = @color/bacon Custom code Custom
Highlight + Font @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_article_detail, container, false); if (mItem != null) { ((TextView) rootView.findViewById(R.id.article_title)) .setText(mItem.title); // Custom highlight the author ((TextView) rootView.findViewById(R.id.article_byline)) .setText(Html.fromHtml(mItem.time.toUpperCase() + " BY <font color='" + getResources().getString(R.string.author_font_color) + "'>" + mItem.author.toUpperCase() + "</a>")); // Use custom font ((TextView) rootView.findViewById(R.id.article_body)) .setTypeface(Typeface.createFromAsset( getResources().getAssets(), "Rosario-Regular.ttf")); ... } return rootView; } -
EXAMPLE TREATMENT FOR DEVELOPERS android:textColorLink = @color/bacon Custom code Custom
Highlight + Font @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_article_detail, container, false); if (mItem != null) { ((TextView) rootView.findViewById(R.id.article_title)) .setText(mItem.title); // Custom highlight the author ((TextView) rootView.findViewById(R.id.article_byline)) .setText(Html.fromHtml(mItem.time.toUpperCase() + " BY <font color='" + getResources().getString(R.string.author_font_color) + "'>" + mItem.author.toUpperCase() + "</a>")); // Use custom font ((TextView) rootView.findViewById(R.id.article_body)) .setTypeface(Typeface.createFromAsset( getResources().getAssets(), "Rosario-Regular.ttf")); ... } return rootView; } -
EXAMPLE TREATMENT FOR DEVELOPERS android:textColorLink = @color/bacon Custom code @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_article_detail, container, false); if (mItem != null) { ((TextView) rootView.findViewById(R.id.article_title)) .setText(mItem.title); // Custom highlight the author ((TextView) rootView.findViewById(R.id.article_byline)) .setText(Html.fromHtml(mItem.time.toUpperCase() + " BY <font color='" + getResources().getString(R.string.author_font_color) + "'>" + mItem.author.toUpperCase() + "</a>")); // Use custom font ((TextView) rootView.findViewById(R.id.article_body)) .setTypeface(Typeface.createFromAsset( getResources().getAssets(), "Rosario-Regular.ttf")); ... } return rootView; } -
Checkpoint 3 Worksheet: Checkpoint 3 http://bit.ly/18EhXuo What you will do:
•Update whitespace (padding and margins) •Fix image thumbnails and photos •Update Typography •Custom highlighting and font -
EXAMPLE TREATMENT FOR DEVELOPERS Guides: “Designing for Multiple Screens” and
“Building a Dynamic UI with Fragments” -
EXAMPLE TREATMENT FOR DEVELOPERS Guides: “Designing for Multiple Screens” and
“Building a Dynamic UI with Fragments” Two-pane UI -
EXAMPLE TREATMENT FOR DEVELOPERS Guides: “Designing for Multiple Screens” and
“Building a Dynamic UI with Fragments” -
EXAMPLE TREATMENT FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Reusable references:
refs.xml <resources> <!-- For more on layout aliases, see: http://developer.android.com/training/multiscreen/ screensizes.html#TaskUseAliasFilters --> <item name="activity_article_list" type="layout"> @layout/activity_article_twopane </item> </resources> refs.xml -
EXAMPLE TREATMENT FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Reusable references:
refs.xml Two-pane UI <resources> <!-- For more on layout aliases, see: http://developer.android.com/training/multiscreen/ screensizes.html#TaskUseAliasFilters --> <item name="activity_article_list" type="layout"> @layout/activity_article_twopane </item> </resources> refs.xml -
EXAMPLE TREATMENT FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Reusable references:
refs.xml <resources> <!-- For more on layout aliases, see: http://developer.android.com/training/multiscreen/ screensizes.html#TaskUseAliasFilters --> <item name="activity_article_list" type="layout"> @layout/activity_article_twopane </item> </resources> refs.xml -
EXAMPLE TREATMENT <?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="thumbnail_width">96dp</dimen> <dimen name="photo_height">400dp</dimen>
<dimen name="list_item_vert_margin">16dp</dimen> <dimen name="detail_horiz_margin">80dp</dimen> </resources> dimens.xml FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Tablet-specific dimensions: dimens.xml -
EXAMPLE TREATMENT Two-pane UI with better spacing <?xml version="1.0" encoding="utf-8"?>
<resources> <dimen name="thumbnail_width">96dp</dimen> <dimen name="photo_height">400dp</dimen> <dimen name="list_item_vert_margin">16dp</dimen> <dimen name="detail_horiz_margin">80dp</dimen> </resources> dimens.xml FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Tablet-specific dimensions: dimens.xml -
EXAMPLE TREATMENT Two-pane UI with better spacing <?xml version="1.0" encoding="utf-8"?>
<resources> <dimen name="thumbnail_width">96dp</dimen> <dimen name="photo_height">400dp</dimen> <dimen name="list_item_vert_margin">16dp</dimen> <dimen name="detail_horiz_margin">80dp</dimen> </resources> dimens.xml FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Tablet-specific dimensions: dimens.xml -
EXAMPLE TREATMENT <?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="thumbnail_width">96dp</dimen> <dimen name="photo_height">400dp</dimen>
<dimen name="list_item_vert_margin">16dp</dimen> <dimen name="detail_horiz_margin">80dp</dimen> </resources> dimens.xml FOR DEVELOPERS Landscape tablet resources: values-sw600dp-land Tablet-specific dimensions: dimens.xml -
Checkpoint 4 Worksheet: Checkpoint 4 http://bit.ly/18EhXuo What you will do:
•Add tablet layout •Tablet-optimized whitespace -
EXAMPLE TREATMENT FOR DEVELOPERS BitmapFactory.Options: options.inSampleSize = 16 (or use
smaller images) @Override public View getView(int position, View convertView, ViewGroup container) { if (convertView == null) { convertView = LayoutInflater.from(getActivity()) .inflate(R.layout.list_item_article, container, false); } ... BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 16; Bitmap b = BitmapFactory.decodeResource(getResources(), item.photoResId, options); ((ImageView) convertView.findViewById(R.id.thumbnail)).setImageBitmap(b); return convertView; } -
EXAMPLE TREATMENT FOR DEVELOPERS BitmapFactory.Options: options.inSampleSize = 16 (or use
smaller images) Bitmap Performance Tuning @Override public View getView(int position, View convertView, ViewGroup container) { if (convertView == null) { convertView = LayoutInflater.from(getActivity()) .inflate(R.layout.list_item_article, container, false); } ... BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 16; Bitmap b = BitmapFactory.decodeResource(getResources(), item.photoResId, options); ((ImageView) convertView.findViewById(R.id.thumbnail)).setImageBitmap(b); return convertView; } -
EXAMPLE TREATMENT FOR DEVELOPERS BitmapFactory.Options: options.inSampleSize = 16 (or use
smaller images) Bitmap Performance Tuning @Override public View getView(int position, View convertView, ViewGroup container) { if (convertView == null) { convertView = LayoutInflater.from(getActivity()) .inflate(R.layout.list_item_article, container, false); } ... BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 16; Bitmap b = BitmapFactory.decodeResource(getResources(), item.photoResId, options); ((ImageView) convertView.findViewById(R.id.thumbnail)).setImageBitmap(b); return convertView; } -
EXAMPLE TREATMENT FOR DEVELOPERS BitmapFactory.Options: options.inSampleSize = 16 (or use
smaller images) @Override public View getView(int position, View convertView, ViewGroup container) { if (convertView == null) { convertView = LayoutInflater.from(getActivity()) .inflate(R.layout.list_item_article, container, false); } ... BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 16; Bitmap b = BitmapFactory.decodeResource(getResources(), item.photoResId, options); ((ImageView) convertView.findViewById(R.id.thumbnail)).setImageBitmap(b); return convertView; } -
EXAMPLE TREATMENT FOR DEVELOPERS options.inJustDecodeBounds = true; options.inSampleSize = 16
(or use smaller images) public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } -
EXAMPLE TREATMENT FOR DEVELOPERS options.inJustDecodeBounds = true; options.inSampleSize = 16
(or use smaller images) Dynamically calculate inSampleSize public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } -
EXAMPLE TREATMENT FOR DEVELOPERS options.inJustDecodeBounds = true; options.inSampleSize = 16
(or use smaller images) Dynamically calculate inSampleSize public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } -
EXAMPLE TREATMENT FOR DEVELOPERS options.inJustDecodeBounds = true; options.inSampleSize = 16
(or use smaller images) public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } -
EXAMPLE TREATMENT FOR DEVELOPERS calculate optimal sample size for required
size public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 16; if (reqWidth == 0 || reqHeight == 0) return inSampleSize; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } -
EXAMPLE TREATMENT FOR DEVELOPERS calculate optimal sample size for required
size Dynamically calculate inSampleSize public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 16; if (reqWidth == 0 || reqHeight == 0) return inSampleSize; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } -
EXAMPLE TREATMENT FOR DEVELOPERS calculate optimal sample size for required
size public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 16; if (reqWidth == 0 || reqHeight == 0) return inSampleSize; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } -
Checkpoint 5 Worksheet: Checkpoint 5 http://bit.ly/18EhXuo What you will do:
•Load bitmaps using ‘sampleSize’ •Set ‘inJustDecodeBounds’ parameter -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask class BitmapWorkerTask extends AsyncTask<Integer, Void,
Bitmap> { private final WeakReference<ImageView> imageViewReference; private int resId = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can // be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { resId = params[0]; ImageView thumbnail = imageViewReference.get(); Bitmap bitmap = decodeSampledBitmapFromResource( getResources(), resId, thumbnail.getWidth(), thumbnail.getHeight()); addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); return bitmap; } ... -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask Load images concurrently off the
Main(UI) Thread class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private int resId = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can // be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { resId = params[0]; ImageView thumbnail = imageViewReference.get(); Bitmap bitmap = decodeSampledBitmapFromResource( getResources(), resId, thumbnail.getWidth(), thumbnail.getHeight()); addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); return bitmap; } ... -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask Load images concurrently off the
Main(UI) Thread class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private int resId = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can // be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { resId = params[0]; ImageView thumbnail = imageViewReference.get(); Bitmap bitmap = decodeSampledBitmapFromResource( getResources(), resId, thumbnail.getWidth(), thumbnail.getHeight()); addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); return bitmap; } ... -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask class BitmapWorkerTask extends AsyncTask<Integer, Void,
Bitmap> { private final WeakReference<ImageView> imageViewReference; private int resId = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can // be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { resId = params[0]; ImageView thumbnail = imageViewReference.get(); Bitmap bitmap = decodeSampledBitmapFromResource( getResources(), resId, thumbnail.getWidth(), thumbnail.getHeight()); addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); return bitmap; } ... -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask ... // Once complete, see
if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } } -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask Load images concurrently off the
Main(UI) Thread ... // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } } -
EXAMPLE TREATMENT FOR DEVELOPERS AsyncTask ... // Once complete, see
if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } } -
Checkpoint 6 Worksheet: Checkpoint 6 http://bit.ly/18EhXuo What you will do:
•Add AsyncTask for loading images -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache private LruCache<String, Bitmap> mMemoryCache; //
Instantiate in the onCreate() method public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache Use Image Cache private LruCache<String,
Bitmap> mMemoryCache; // Instantiate in the onCreate() method public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache Use Image Cache private LruCache<String,
Bitmap> mMemoryCache; // Instantiate in the onCreate() method public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache private LruCache<String, Bitmap> mMemoryCache; //
Instantiate in the onCreate() method public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache calculate cache size in sizeOf(..)
// Get max available VM memory in kilobytes, exceeding this amount // will throw an OutOfMemory exception. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size in kilobytes rather than if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount() / 1024; } else { return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } } }; -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache calculate cache size in sizeOf(..)
Use Image Cache // Get max available VM memory in kilobytes, exceeding this amount // will throw an OutOfMemory exception. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size in kilobytes rather than if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount() / 1024; } else { return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } } }; -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache calculate cache size in sizeOf(..)
Use Image Cache // Get max available VM memory in kilobytes, exceeding this amount // will throw an OutOfMemory exception. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size in kilobytes rather than if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount() / 1024; } else { return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } } }; -
EXAMPLE TREATMENT FOR DEVELOPERS LruCache calculate cache size in sizeOf(..)
// Get max available VM memory in kilobytes, exceeding this amount // will throw an OutOfMemory exception. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size in kilobytes rather than if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount() / 1024; } else { return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } } }; -
Checkpoint 6 Worksheet: Checkpoint 6 http://bit.ly/18EhXuo What you will do:
•Add LruCache to cache images Faster -
Thank you! A few simple techniques: Backwards Compatibility using Support
Library Design, Spacing, Typography Improve app performance Interested to find out more? developer.android.com +Android Developers