Monday, July 30, 2012
Introducing Google Fiber: The Next Chapter of the Internet
Google Fiber starts with Internet speeds 100 times faster than what most Americans have today. In this video, we see the evolution of the Internet represented in three different stages. It started with Dial-Up, grew with Broadband -- and with Google Fiber, the possibilities are endless. Pre-register now at http://www.google.com/fiber
About the Video:
The cars and model installation were built by hand, and filmed at Agua Dolce airport in Santa Clarita, CA. The installation now resides in the Fiber Space in Kansas City. For more info about the Fiber Space, please visit http://google.com/fiber/fiberspace
Apply LruCache on GridView
The example "Caching Bitmaps with LruCache" provide a example to Apply LruCache on Gallery. This exercise is GridView version using LruCache.
Reference: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#memory-cache
To using android.util.LruCache in the code, targetSdkVersion of "13" is needed to be specified in AndroidManifest.xml.
The layout file, refer to the post "GridView loading photos from SD Card".
Download the files.
Reference: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#memory-cache
To using android.util.LruCache in the code, targetSdkVersion of "13" is needed to be specified in AndroidManifest.xml.
The layout file, refer to the post "GridView loading photos from SD Card".
package com.example.androidgridview;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
public class ImageAdapter extends BaseAdapter {
private Context mContext;
ArrayList<String> itemList = new ArrayList<String>();
public ImageAdapter(Context c) {
mContext = c;
}
void add(String path){
itemList.add(path);
}
@Override
public int getCount() {
return itemList.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
//Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);
// Use the path as the key to LruCache
final String imageKey = itemList.get(position);
final Bitmap bm = getBitmapFromMemCache(imageKey);
if (bm == null){
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(imageKey);
};
imageView.setImageBitmap(bm);
return imageView;
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView)imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
ImageAdapter myImageAdapter;
private LruCache<String, Bitmap> mMemoryCache;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridview = (GridView) findViewById(R.id.gridview);
myImageAdapter = new ImageAdapter(this);
gridview.setAdapter(myImageAdapter);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myImageAdapter.add(file.getAbsolutePath());
}
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass
= ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
.getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return (Bitmap) mMemoryCache.get(key);
}
}
Download the files.
Sunday, July 29, 2012
Factory images of Jelly Bean for Galaxy Nexus and Nexus S released
Factory Images for Nexus Devices updated with Factory images of Jelly Bean for Galaxy Nexus and Nexus S.
You will find these files useful if you have used the Android Open-Source Project, flashed custom builds on your device, and wish to return that device to its factory state.
You will find these files useful if you have used the Android Open-Source Project, flashed custom builds on your device, and wish to return that device to its factory state.
Gallery-like single column GridView
by changing android:numColumns="1", the last exercise of "GridView" can be modified to a Gallery-like single column GridView.
<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="horizontal">
<GridView
android:id="@+id/gridview"
android:layout_width="130dp"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="1"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"
android:background="@android:color/background_dark"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/background_light">
</LinearLayout>
</LinearLayout>
GridView loading photos from SD Card
Previous exercises described how to implement "Gallery-like HorizontalScrollView" and "Vertical Gallery-like ScrollView". Alternatively, it can be displayed in GridView.
Add a <GridView> in layout.
Main code:
Download the files.
Next:
- Gallery-like single column GridView
- Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method
- Implement OnItemClickListener for GridView
Suggested:
- Scale bitmap Efficiently
Add a <GridView> in 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">
<GridView
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"/>
</LinearLayout>
Main code:
package com.example.androidgridview;
import java.io.File;
import java.util.ArrayList;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
public class ImageAdapter extends BaseAdapter {
private Context mContext;
ArrayList<String> itemList = new ArrayList<String>();
public ImageAdapter(Context c) {
mContext = c;
}
void add(String path){
itemList.add(path);
}
@Override
public int getCount() {
return itemList.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);
imageView.setImageBitmap(bm);
return imageView;
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
ImageAdapter myImageAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridview = (GridView) findViewById(R.id.gridview);
myImageAdapter = new ImageAdapter(this);
gridview.setAdapter(myImageAdapter);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myImageAdapter.add(file.getAbsolutePath());
}
}
}
Download the files.
Next:
- Gallery-like single column GridView
- Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method
- Implement OnItemClickListener for GridView
Suggested:
- Scale bitmap Efficiently
Saturday, July 21, 2012
Vertical Gallery-like ScrollView
With the custom LinearLayout (MyHorizontalLayout.java) in last exercise "Implement custom LinearLayout for Gallery-like HorizontalScrollView", it can be implement vertical Gallery-like ScrollView also.
Keep both MainActivity.java and MyHorizontalLayout.java of last exercise no change.
Modify the layout.
Related:
- GridView loading photos from SD Card
Keep both MainActivity.java and MyHorizontalLayout.java of last exercise no change.
Modify the 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">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="fill_parent" >
<com.example.androidhorizontalscrollviewgallery.MyHorizontalLayout
android:id="@+id/mygallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
/>
</ScrollView>
</LinearLayout>
Related:
- GridView loading photos from SD Card
Friday, July 20, 2012
Implement custom LinearLayout for Gallery-like HorizontalScrollView
Last exercise explain the basic to "implement Gallery-like HorizontalScrollView". In this post, we are going to implement our custom LinearLayout for Gallery-like HorizontalScrollView.
MyHorizontalLayout.java, our custom LinearLayout for Gallery-like HorizontalScrollView.
Modify layout to include MyHorizontalLayout.
See how simple is the main code:
Download the files.
Next:
- Vertical Gallery-like ScrollView
- Handle onClick for our custom LinearLayout for Gallery-like HorizontalScrollView
Related:
- GridView loading photos from SD Card
MyHorizontalLayout.java, our custom LinearLayout for Gallery-like HorizontalScrollView.
package com.example.androidhorizontalscrollviewgallery;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class MyHorizontalLayout extends LinearLayout {
Context myContext;
ArrayList<String> itemList = new ArrayList<String>();
public MyHorizontalLayout(Context context) {
super(context);
myContext = context;
}
public MyHorizontalLayout(Context context, AttributeSet attrs) {
super(context, attrs);
myContext = context;
}
public MyHorizontalLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
myContext = context;
}
void add(String path){
int newIdx = itemList.size();
itemList.add(path);
addView(getImageView(newIdx));
}
ImageView getImageView(int i){
Bitmap bm = null;
if (i < itemList.size()){
bm = decodeSampledBitmapFromUri(itemList.get(i), 220, 220);
}
ImageView imageView = new ImageView(myContext);
imageView.setLayoutParams(new LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
return imageView;
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
Modify layout to include MyHorizontalLayout.
<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">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.example.androidhorizontalscrollviewgallery.MyHorizontalLayout
android:id="@+id/mygallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
</HorizontalScrollView>
</LinearLayout>
See how simple is the main code:
package com.example.androidhorizontalscrollviewgallery;
import java.io.File;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.widget.Toast;
public class MainActivity extends Activity {
MyHorizontalLayout myHorizontalLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myHorizontalLayout = (MyHorizontalLayout)findViewById(R.id.mygallery);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myHorizontalLayout.add(file.getAbsolutePath());
}
}
}
Download the files.
Next:
- Vertical Gallery-like ScrollView
- Handle onClick for our custom LinearLayout for Gallery-like HorizontalScrollView
Related:
- GridView loading photos from SD Card
Implement Gallery-like HorizontalScrollView
As mentioned in the post "Implement Android Gallery widget" - android.widget.Gallery is no longer supported. Other horizontally scrolling widgets include HorizontalScrollView and ViewPager from the support library.
It's a example to implement Gallery-like view using HorizontalScrollView. Please note that in this example, the bitmaps in HorizontalScrollView will not be removed even not in screen. So if too much bitmaps loaded, error of java.lang.OutOfMemoryError will be thrown!
Add HorizontalScrollView in layout:
Main Java code:
Download the files.
Next:
- Implement custom LinearLayout for Gallery-like HorizontalScrollView
- Handle onClick for our custom LinearLayout for Gallery-like HorizontalScrollView
Remark@2015-11-13:
It's a old example. With current Material Design on Android, it's suggested to consider using " RecyclerView + CardView".
Coresponding "Gallery-like RecyclerView + CardView example".
It's a example to implement Gallery-like view using HorizontalScrollView. Please note that in this example, the bitmaps in HorizontalScrollView will not be removed even not in screen. So if too much bitmaps loaded, error of java.lang.OutOfMemoryError will be thrown!
Add HorizontalScrollView in 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">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="@+id/mygallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
</HorizontalScrollView>
</LinearLayout>
Main Java code:
package com.example.androidhorizontalscrollviewgallery;
import java.io.File;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
public class MainActivity extends Activity {
LinearLayout myGallery;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myGallery = (LinearLayout)findViewById(R.id.mygallery);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myGallery.addView(insertPhoto(file.getAbsolutePath()));
}
}
View insertPhoto(String path){
Bitmap bm = decodeSampledBitmapFromUri(path, 220, 220);
LinearLayout layout = new LinearLayout(getApplicationContext());
layout.setLayoutParams(new LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);
ImageView imageView = new ImageView(getApplicationContext());
imageView.setLayoutParams(new LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
layout.addView(imageView);
return layout;
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
Download the files.
Next:
- Implement custom LinearLayout for Gallery-like HorizontalScrollView
- Handle onClick for our custom LinearLayout for Gallery-like HorizontalScrollView
Remark@2015-11-13:
It's a old example. With current Material Design on Android, it's suggested to consider using " RecyclerView + CardView".
Coresponding "Gallery-like RecyclerView + CardView example".
Thursday, July 19, 2012
Dialog Animation using windowAnimations
In this exercise, we are going to apply slide-in and slide-out animation on dialog, using windowAnimations.
Create/modify /res/values/styles.xml to add animation style of DialogAnimation, using build-in animation of slide_in_left and slide_out_right.
Implement our dialog layout, /res/layout/dialoglayout.xml.
MainActivity.java
Main layout:
Download the files.
Create/modify /res/values/styles.xml to add animation style of DialogAnimation, using build-in animation of slide_in_left and slide_out_right.
<resources>
<style name="AppTheme" parent="android:Theme.Light" />
<style name="DialogAnimation">
<item name="android:windowEnterAnimation">@android:anim/slide_in_left</item>
<item name="android:windowExitAnimation">@android:anim/slide_out_right</item>
</style>
</resources>
Implement our dialog layout, /res/layout/dialoglayout.xml.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
<Button
android:id="@+id/dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dismiss"/>
</LinearLayout>
MainActivity.java
package com.example.animationdialog;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.app.Activity;
import android.app.Dialog;
public class MainActivity extends Activity {
Button btnOpenDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOpenDialog = (Button)findViewById(R.id.opendialog);
btnOpenDialog.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
openDialog();
}});
}
private void openDialog(){
final Dialog dialog = new Dialog(MainActivity.this);
dialog.setTitle("Animation Dialog");
dialog.setContentView(R.layout.dialoglayout);
dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
Button btnDismiss = (Button)dialog.getWindow().findViewById(R.id.dismiss);
btnDismiss.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
dialog.dismiss();
}});
dialog.show();
}
}
Main layout:
<RelativeLayout 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" >
<Button
android:id="@+id/opendialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="Open Dialog"
tools:context=".MainActivity" />
</RelativeLayout>
Download the files.
Android SDK Tools Revision 20.0.1, NDK revision 8b, and ADT 20.0.1 released
Android SDK Tools Revision 20.0.1, full SDK for Android 4.1, released. You can now develop and publish applications against API level 16 using new Jelly Bean APIs. The new update can be downloaded through SDK Manager. Also updated are NDK revision 8b and ADT Plugin 20.0.1.
Update SDK on Eclipse:
Please note that note that the SDK Tools r20.0.1 is designed for use with ADT 20.0.1 and later. To update ADT in Eclipse, click Help -> Check for updates, to update ADT.
After updated ADT, click Window -> Android SDK Manager to install updated components.
Update SDK on Eclipse:
Please note that note that the SDK Tools r20.0.1 is designed for use with ADT 20.0.1 and later. To update ADT in Eclipse, click Help -> Check for updates, to update ADT.
After updated ADT, click Window -> Android SDK Manager to install updated components.
Tuesday, July 17, 2012
Implement GestureDetector/OnGestureListener to detect Fling, and ObjectAnimator for ImageView
In this exercise, I will implement GestureDetector with OnGestureListener for a ImageView of icon. Once the user fling on the icon, it will be moved (animated using ObjectAnimator).
In order to use android.animation.ObjectAnimator, minSdkVersion="11" is needed.
Download the files.
package com.example.androidanimatefling;
import android.os.Bundle;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity {
TextView info;
ImageView flingObj;
FrameLayout mainScreen;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
info = (TextView)findViewById(R.id.info);
flingObj = (ImageView)findViewById(R.id.flingobject);
mainScreen = (FrameLayout)findViewById(R.id.mainscreen);
final GestureDetector myGesture = new GestureDetector(this, new MyOnGestureListener());
flingObj.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
return myGesture.onTouchEvent(event);
}});
flingObj.setClickable(true);
}
class MyOnGestureListener implements OnGestureListener{
int MIN_DIST = 100;
@Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
float e1X = e1.getX();
float e1Y = e1.getY();
float e2X = e2.getX();
float e2Y = e2.getY();
float distX = e2X - e1X;
float distY = e2Y - e1Y;
info.setText(
"e1X e1Y : " + String.valueOf(e1X) + " : " + String.valueOf(e1Y) + "\n" +
"e2X e2Y : " + String.valueOf(e2X) + " : " + String.valueOf(e2Y) + "\n" +
"velocityX : " + String.valueOf(velocityX) + "\n" +
"velocityY : " + String.valueOf(velocityY));
//Get the Y OFfset
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();
int[] location = new int[2];
flingObj.getLocationOnScreen(location);
float orgX = location[0];
float orgY = location[1] - offsetY;
float stopX = orgX + distX;
float stopY = orgY + distY;
if (distX > MIN_DIST) {
//Fling Right
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationX", orgX, stopX);
flingAnimator.setDuration(1000);
flingAnimator.start();
}else if(distX < - MIN_DIST){
//Fling Left
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationX", orgX, stopX);
flingAnimator.setDuration(1000);
flingAnimator.start();
}else if (distY > MIN_DIST) {
//Fling Down
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationY", orgY, stopY);
flingAnimator.setDuration(1000);
flingAnimator.start();
}else if(distY < - MIN_DIST){
//Fling Up
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationY", orgY, stopY);
flingAnimator.setDuration(1000);
flingAnimator.start();
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}};
}
<FrameLayout 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:id="@+id/mainscreen">
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/flingobject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium"
android:src="@drawable/ic_launcher"
tools:context=".MainActivity" />
</FrameLayout>
In order to use android.animation.ObjectAnimator, minSdkVersion="11" is needed.
Download the files.
Error of getLocationInWindow() and getLocationOnScreen()
The View class provide the methods to computes the coordinates:
In my experience, the returned x location is correct, but the y location is always error with a fixed offset. The offset various depends on devices and configuration.
To correct it, we can get the offset using the code, after view displayed:
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();
- getLocationInWindow (int[] location): Computes the coordinates of the view in its window.
- getLocationOnScreen (int[] location): Computes the coordinates of the view on the screen.
In my experience, the returned x location is correct, but the y location is always error with a fixed offset. The offset various depends on devices and configuration.
To correct it, we can get the offset using the code, after view displayed:
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();
package com.example.androidoffsetgetlocation;
import android.os.Bundle;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
LinearLayout mainScreen;
ImageView object;
TextView textOnCreate, textOnWindowFocusChanged;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainScreen = (LinearLayout)findViewById(R.id.mainscreen);
object = (ImageView)findViewById(R.id.object);
textOnCreate = (TextView)findViewById(R.id.textview1);
textOnWindowFocusChanged = (TextView)findViewById(R.id.textview2);
readLocation(textOnCreate, "onCreate()");
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
readLocation(textOnWindowFocusChanged, "onWindowFocusChanged()");
}
private void readLocation(TextView tv, String status){
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetX = displayMetrics.widthPixels - mainScreen.getMeasuredWidth();
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();
int[] locationInWindow = new int[2];
object.getLocationInWindow(locationInWindow);
int[] locationOnScreen = new int[2];
object.getLocationOnScreen(locationOnScreen);
tv.setText(
"\n" + status +"\n"
+ "getLocationInWindow() - " + locationInWindow[0] + " : " + locationInWindow[1] + "\n"
+ "getLocationOnScreen() - " + locationOnScreen[0] + " : " + locationOnScreen[1] + "\n"
+ "Offset x: y - " + offsetX + " : " + offsetY);
}
}
<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"
android:id="@+id/mainscreen">
<ImageView
android:id="@+id/object"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/textview1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/textview2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Monday, July 16, 2012
Complete list of all new features of Android 4.1 Jelly Bean
Android 4.1, Jelly Bean, is the fastest and smoothest version of Android yet. Jelly Bean improves on the simplicity and beauty of Android 4.0, and introduces a new Google search experience on Android.
To read what's new in Android 4.1 Jelly Bean, visit: http://www.android.com/about/jelly-bean/.
- Everything in Jelly Bean feels fast, fluid, and smooth. Moving between home screens and switching between apps is effortless, like turning pages in a book.
- Jelly Bean features improved performance throughout the system, including faster orientation changes, faster responses when switching between recent apps, and smoother and more consistent rendering across the system through vsync and triple buffering.
- Jelly Bean has more reactive and uniform touch responses, and makes your device even more responsive by boosting your device's CPU instantly when you touch the screen, and turns it down when you don't need it to improve battery life.
To read what's new in Android 4.1 Jelly Bean, visit: http://www.android.com/about/jelly-bean/.
Implement grouped CheckBox on Action Menu
Example of Action Menu with grouped CheckBox.
Create /res/menu/activity_main.xml to define action menu.
The checked status will not be updated automatically. We can change it in onOptionsItemSelected() callback method.
Create /res/menu/activity_main.xml to define action menu.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_settings"
android:title="@string/menu_settings"
android:orderInCategory="100"
android:showAsAction="never" />
<group android:checkableBehavior="single">
<item android:id="@+id/selecta"
android:title="Selection A" android:checked="true"/>
<item android:id="@+id/selectb"
android:title="Selection B" />
<item android:id="@+id/selectc"
android:title="Selection C" />
</group>
</menu>
The checked status will not be updated automatically. We can change it in onOptionsItemSelected() callback method.
package com.example.androidactionbar;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.selecta:
item.setChecked(true);
Toast.makeText(getApplicationContext(),
"A Selected",
Toast.LENGTH_LONG).show();
return true;
case R.id.selectb:
item.setChecked(true);
Toast.makeText(getApplicationContext(),
"B Selected",
Toast.LENGTH_LONG).show();
return true;
case R.id.selectc:
item.setChecked(true);
Toast.makeText(getApplicationContext(),
"C Selected",
Toast.LENGTH_LONG).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Sunday, July 15, 2012
LruCache with different size
Last exercise show Gallery with cached bitmaps using LruCache.
There is no specific size or formula that suits all applications, it's up to you to analyze your usage and come up with a suitable solution. A cache that is too small causes additional overhead with no benefit, a cache that is too large can once again cause java.lang.OutOfMemory exceptions and leave the rest of your app little memory to work with.
In this exercise, there are two Gallery widgets on screen, both with cached bitmaps using LruCache. The upper one with smaller cache size and the lower one with bigger cache size. There are more than 200 photos in /test/ directory. The upper small cache size gallery always re-load the cache. The lower one with bigger size of cache can keep bitmaps in cache, but when the cached memory get large, it crash the app!.
modify AndroidManifest.xml to have minSdkVersion and targetSdkVersion of "13".
Download the files.
There is no specific size or formula that suits all applications, it's up to you to analyze your usage and come up with a suitable solution. A cache that is too small causes additional overhead with no benefit, a cache that is too large can once again cause java.lang.OutOfMemory exceptions and leave the rest of your app little memory to work with.
In this exercise, there are two Gallery widgets on screen, both with cached bitmaps using LruCache. The upper one with smaller cache size and the lower one with bigger cache size. There are more than 200 photos in /test/ directory. The upper small cache size gallery always re-load the cache. The lower one with bigger size of cache can keep bitmaps in cache, but when the cached memory get large, it crash the app!.
package com.example.androidgallery;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
public abstract class CacheGalleryBaseAdapter extends BaseAdapter {
ArrayList<String> GalleryFileList;
Context context;
CacheGalleryBaseAdapter(Context cont){
context = cont;
GalleryFileList = new ArrayList<String>();
}
abstract Bitmap abstractGetBitmapFromMemCache(String key);
abstract BitmapWorkerTask abstractStartBitmapWorkerTask(ImageView imageView);
@Override
public int getCount() {
return GalleryFileList.size();
}
@Override
public Object getItem(int position) {
return GalleryFileList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(context);
//---
// Use the path as the key to LruCache
final String imageKey = GalleryFileList.get(position);
final Bitmap bm = abstractGetBitmapFromMemCache(imageKey);
if (bm == null){
BitmapWorkerTask task = abstractStartBitmapWorkerTask(imageView);
task.execute(imageKey);
}
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);
imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
layout.addView(imageView);
return layout;
}
public void add(String newitem){
GalleryFileList.add(newitem);
}
}
public class CacheGalleryBaseAdapter_SmallSize extends CacheGalleryBaseAdapter{
CacheGalleryBaseAdapter_SmallSize(Context cont) {
super(cont);
}
@Override
Bitmap abstractGetBitmapFromMemCache(String key) {
return getBitmapFromMemCache_SmallSize(key);
}
@Override
BitmapWorkerTask abstractStartBitmapWorkerTask(ImageView imageView) {
return (new BitmapWorkerTask_SmallSize(imageView));
}
}
public class CacheGalleryBaseAdapter_BigSize extends CacheGalleryBaseAdapter{
CacheGalleryBaseAdapter_BigSize(Context cont) {
super(cont);
}
@Override
Bitmap abstractGetBitmapFromMemCache(String key) {
return getBitmapFromMemCache_BigSize(key);
}
@Override
BitmapWorkerTask abstractStartBitmapWorkerTask(ImageView imageView) {
return(new BitmapWorkerTask_BigSize(imageView));
}
}
CacheGalleryBaseAdapter_SmallSize myFastGallery1BaseAdapter;
CacheGalleryBaseAdapter_BigSize myFastGallery2BaseAdapter;
Gallery myFastGallery1, myFastGallery2;
private LruCache<String, Bitmap> mMemoryCache_SmallSize;
private LruCache<String, Bitmap> mMemoryCache_BigSize;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myFastGallery1 = (Gallery)findViewById(R.id.fastgallery1);
myFastGallery2 = (Gallery)findViewById(R.id.fastgallery2);
myFastGallery1BaseAdapter = new CacheGalleryBaseAdapter_SmallSize(this);
myFastGallery2BaseAdapter = new CacheGalleryBaseAdapter_BigSize(this);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myFastGallery1BaseAdapter.add(file.getPath());
myFastGallery2BaseAdapter.add(file.getPath());
}
myFastGallery1.setAdapter(myFastGallery1BaseAdapter);
myFastGallery2.setAdapter(myFastGallery2BaseAdapter);
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
final int cacheSize_SmallSize = 1024 * 1024 * memClass / 12;
mMemoryCache_SmallSize = new LruCache_customSize(cacheSize_SmallSize);
final int cacheSize_BigSize = 1024 * 1024 * memClass;
mMemoryCache_BigSize = new LruCache_customSize(cacheSize_BigSize);
}
class LruCache_customSize extends LruCache<String, Bitmap>{
public LruCache_customSize(int maxSize) {
super(maxSize);
// TODO Auto-generated constructor stub
}
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
}
abstract class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView)imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
public Bitmap getBitmapFromMemCache_SmallSize(String key) {
return (Bitmap) mMemoryCache_SmallSize.get(key);
}
public Bitmap getBitmapFromMemCache_BigSize(String key) {
return (Bitmap) mMemoryCache_BigSize.get(key);
}
class BitmapWorkerTask_SmallSize extends BitmapWorkerTask{
public BitmapWorkerTask_SmallSize(ImageView imageView) {
super(imageView);
// TODO Auto-generated constructor stub
}
@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);
if( getBitmapFromMemCache_SmallSize(String.valueOf(params[0])) == null){
mMemoryCache_SmallSize.put(String.valueOf(params[0]), bitmap);
}
return bitmap;
}
}
class BitmapWorkerTask_BigSize extends BitmapWorkerTask{
public BitmapWorkerTask_BigSize(ImageView imageView) {
super(imageView);
// TODO Auto-generated constructor stub
}
@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);
if( getBitmapFromMemCache_BigSize(String.valueOf(params[0])) == null){
mMemoryCache_BigSize.put(String.valueOf(params[0]), bitmap);
}
return bitmap;
}
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
<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:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery with Small Cache Size" />
<Gallery
android:id="@+id/fastgallery1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery with Big Cache Size" />
<Gallery
android:id="@+id/fastgallery2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
modify AndroidManifest.xml to have minSdkVersion and targetSdkVersion of "13".
Download the files.
Saturday, July 14, 2012
Caching Bitmaps with LruCache
In the last exercise of "Implement Android Gallery widget with scale-down bitmap", the bitmap will be loaded and scaled every-time getView() of GalleryBaseAdapter called. The side-effect is the gallery widget stop a moment whenever a new a new item come-out.
The LruCache class (also available in the Support Library for use back to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently referenced objects in a strong referenced LinkedHashMap and evicting the least recently used member before the cache exceeds its designated size. ~ reference: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#memory-cache.
In this exercise, two Gallery widget were implemented. In the video shown below, the upper one is original gallery, the lower one is gallery with cached bitmaps. It can be noted that the cached gallery need more time to be loaded in first loading, but swipe much more smooth.
Main activity.
Modify the layout to have two gallery.
android.util.LruCache is needed in the code, modify AndroidManifest.xml to have minSdkVersion and targetSdkVersion of "13".
Download the files.
Please note that this approach have it's own trade-off: if you request cacheSize too much, it will make your app causing java.lang.OutOfMemory exceptions, if too small, it will cause additional overhead. In my own openion, if you have predictable images (or resources) needed for caching, it's a good choice.
Read more: LruCache with different size
Related:
- Apply LruCache on GridView
The LruCache class (also available in the Support Library for use back to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently referenced objects in a strong referenced LinkedHashMap and evicting the least recently used member before the cache exceeds its designated size. ~ reference: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#memory-cache.
In this exercise, two Gallery widget were implemented. In the video shown below, the upper one is original gallery, the lower one is gallery with cached bitmaps. It can be noted that the cached gallery need more time to be loaded in first loading, but swipe much more smooth.
Main activity.
package com.example.androidgallery;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
public class GalleryBaseAdapter extends BaseAdapter {
ArrayList<String> GalleryFileList;
Context context;
GalleryBaseAdapter(Context cont){
context = cont;
GalleryFileList = new ArrayList<String>();
}
@Override
public int getCount() {
return GalleryFileList.size();
}
@Override
public Object getItem(int position) {
return GalleryFileList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Bitmap bm = decodeSampledBitmapFromUri(GalleryFileList.get(position), 200, 200);
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);
ImageView imageView = new ImageView(context);
imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
layout.addView(imageView);
return layout;
}
public void add(String newitem){
GalleryFileList.add(newitem);
}
}
public class CacheGalleryBaseAdapter extends GalleryBaseAdapter{
CacheGalleryBaseAdapter(Context cont) {
super(cont);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(context);
//---
// Use the path as the key to LruCache
final String imageKey = GalleryFileList.get(position);
final Bitmap bm = getBitmapFromMemCache(imageKey);
if (bm == null){
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(imageKey);
}
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);
imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
layout.addView(imageView);
return layout;
}
}
GalleryBaseAdapter myGalleryBaseAdapter;
CacheGalleryBaseAdapter myCacheGalleryBaseAdapter;
Gallery myPhotoGallery, myFastGallery;
private LruCache<String, Bitmap> mMemoryCache;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myPhotoGallery = (Gallery)findViewById(R.id.photogallery);
myFastGallery = (Gallery)findViewById(R.id.fastgallery);
myGalleryBaseAdapter = new GalleryBaseAdapter(this);
myCacheGalleryBaseAdapter = new CacheGalleryBaseAdapter(this);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myGalleryBaseAdapter.add(file.getPath());
myCacheGalleryBaseAdapter.add(file.getPath());
}
myPhotoGallery.setAdapter(myGalleryBaseAdapter);
myFastGallery.setAdapter(myCacheGalleryBaseAdapter);
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView)imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return (Bitmap) mMemoryCache.get(key);
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
Modify the layout to have two gallery.
<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:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery without Cache" />
<Gallery
android:id="@+id/photogallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery with Cache" />
<Gallery
android:id="@+id/fastgallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
android.util.LruCache is needed in the code, modify AndroidManifest.xml to have minSdkVersion and targetSdkVersion of "13".
Download the files.
Please note that this approach have it's own trade-off: if you request cacheSize too much, it will make your app causing java.lang.OutOfMemory exceptions, if too small, it will cause additional overhead. In my own openion, if you have predictable images (or resources) needed for caching, it's a good choice.
Read more: LruCache with different size
Related:
- Apply LruCache on GridView
Implement Android Gallery widget with scale-down bitmap
In the exercise of "Android Gallery widget", the bitmap are loaded in Gallery widget without re-size. If you load with big picture, error of java.lang.OutOfMemoryError will be thrown.
Last post "Scale bitmap Efficiently" explain how to scale-down bitmap. It will be apply in the Gallery.
The layout refer "Android Gallery widget".
Download the files.
Related:
- Caching Bitmaps with LruCache
Last post "Scale bitmap Efficiently" explain how to scale-down bitmap. It will be apply in the Gallery.
package com.example.androidgallery;
import java.io.File;
import java.util.ArrayList;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Gravity;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
public class MainActivity extends Activity {
public class GalleryBaseAdapter extends BaseAdapter {
ArrayList<String> GalleryFileList;
Context context;
GalleryBaseAdapter(Context cont){
context = cont;
GalleryFileList = new ArrayList<String>();
}
@Override
public int getCount() {
return GalleryFileList.size();
}
@Override
public Object getItem(int position) {
return GalleryFileList.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Bitmap bm = BitmapFactory.decodeFile(GalleryFileList.get(position));
Bitmap bm = decodeSampledBitmapFromUri(GalleryFileList.get(position), 200, 200);
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);
ImageView imageView = new ImageView(context);
imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
layout.addView(imageView);
return layout;
}
public void add(String newitem){
GalleryFileList.add(newitem);
}
}
GalleryBaseAdapter myGalleryBaseAdapter;
Gallery myPhotoGallery;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myPhotoGallery = (Gallery)findViewById(R.id.photogallery);
myGalleryBaseAdapter = new GalleryBaseAdapter(this);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myGalleryBaseAdapter.add(file.getPath());
}
myPhotoGallery.setAdapter(myGalleryBaseAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
The layout refer "Android Gallery widget".
Download the files.
Related:
- Caching Bitmaps with LruCache
Scale bitmap Efficiently
Remark: I have a exercise "Display Gallery selected image using BitmapFactory" before. The original idea is to select image using Android build-in app Gallery, and display on a ImageView, FOR SMALL IMAGE. Recently found that it cannot display large image, such as original photos from camera ~ but I don't know why no error reported!!!
It may be due to the Bitmap is too large for the ImageView. To scale-down the bitmap before display, Android Developer Site have a good article to describe "Loading Large Bitmaps Efficiently".
The old exercise is modified with scale-down bitmap and re-posted here:
Main Code:
Layout:
Download the files.
It may be due to the Bitmap is too large for the ImageView. To scale-down the bitmap before display, Android Developer Site have a good article to describe "Loading Large Bitmaps Efficiently".
The old exercise is modified with scale-down bitmap and re-posted here:
Main Code:
package com.example.androidselectimage;
import java.io.FileNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
TextView textTargetUri;
ImageView targetImage;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonLoadImage = (Button)findViewById(R.id.loadimage);
textTargetUri = (TextView)findViewById(R.id.targeturi);
targetImage = (ImageView)findViewById(R.id.targetimage);
buttonLoadImage.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Intent intent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, 0);
}});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK){
Uri targetUri = data.getData();
textTargetUri.setText(targetUri.toString());
Toast.makeText(getApplicationContext(),
"ImageView: " + targetImage.getWidth() + " x " + targetImage.getHeight(),
Toast.LENGTH_LONG).show();
Bitmap bitmap;
bitmap = decodeSampledBitmapFromUri(
targetUri,
targetImage.getWidth(), targetImage.getHeight());
if(bitmap == null){
Toast.makeText(getApplicationContext(), "the image data could not be decoded", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getApplicationContext(),
"Decoded Bitmap: " + bitmap.getWidth() + " x " + bitmap.getHeight(),
Toast.LENGTH_LONG).show();
targetImage.setImageBitmap(bitmap);
}
}
}
/*
* How to "Loading Large Bitmaps Efficiently"?
* Refer: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
*/
public Bitmap decodeSampledBitmapFromUri(Uri uri, int reqWidth, int reqHeight) {
Bitmap bm = null;
try{
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
return bm;
}
public 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 = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/loadimage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Load Image" />
<TextView
android:id="@+id/targeturi"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/targetimage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="centerCrop" />
</LinearLayout>
Download the files.
Friday, July 13, 2012
Implement Android Gallery widget
Please note: android.widget.Gallery is no longer supported. Other horizontally scrolling widgets include HorizontalScrollView and ViewPager from the support library. (Refer Implement Gallery-like HorizontalScrollView)
~ But in some case, I still can't find a suitable alternative, so I still post about Gallery here.
In this exercise, the image files in "test/" folder under SD Card will be loaded in Gallery.
layout:
Download the files.
Next:
- Implement Android Gallery widget with scale-down bitmap
Related:
- Implement Gallery-like HorizontalScrollView.
~ But in some case, I still can't find a suitable alternative, so I still post about Gallery here.
In this exercise, the image files in "test/" folder under SD Card will be loaded in Gallery.
package com.example.androidgallery;
import java.io.File;
import java.util.ArrayList;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Gravity;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
public class MainActivity extends Activity {
public class GalleryBaseAdapter extends BaseAdapter {
ArrayList<String> GalleryFileList;
Context context;
GalleryBaseAdapter(Context cont){
context = cont;
GalleryFileList = new ArrayList<String>();
}
@Override
public int getCount() {
return GalleryFileList.size();
}
@Override
public Object getItem(int position) {
return GalleryFileList.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Bitmap bm = BitmapFactory.decodeFile(GalleryFileList.get(position));
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);
ImageView imageView = new ImageView(context);
imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);
layout.addView(imageView);
return layout;
}
public void add(String newitem){
GalleryFileList.add(newitem);
}
}
GalleryBaseAdapter myGalleryBaseAdapter;
Gallery myPhotoGallery;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myPhotoGallery = (Gallery)findViewById(R.id.photogallery);
myGalleryBaseAdapter = new GalleryBaseAdapter(this);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/test/";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myGalleryBaseAdapter.add(file.getPath());
}
myPhotoGallery.setAdapter(myGalleryBaseAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
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" >
<Gallery
android:id="@+id/photogallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Download the files.
Next:
- Implement Android Gallery widget with scale-down bitmap
Related:
- Implement Gallery-like HorizontalScrollView.
Thursday, July 12, 2012
Android Accessory Development Kit (ADK) for 2012
The Android Accessory Development Kit (ADK) for 2012 is the latest reference implementation of an Android Open Accessory device, designed to help Android hardware accessory builders and software developers create accessories for Android.
Google I/O 2012 - ADK 2.0
Introducing the new APIs and capabilities in ADK 2.0, with demos.
The ADK 2012 is based on the Arduino open source electronics prototyping platform and is an open hardware design. The hardware design files and firmware source code are included with the ADK software download. The ADK contains two main physical hardware components:
- Main processing board containing the microprocessor, USB connections, power connector and input/output pins. This board can be removed and used separately from the rest of the hardware.
- Shield containing sensors, LEDs, input controls, audio amplifier and speaker output, contained in a custom, polygon box enclosure.
The main hardware features of the ADK are as follows:
- An ARM 32-bit Cortex M3 micro-processor
- Separate USB connections for an Android device and computer connection for programming and debugging
- Sensors for light, color, proximity, temperature, humidity, barometric pressure, and acceleration
- Micro SD Card slot
- Bluetooth support
The ADK comes preloaded with an alarm clock firmware program that you can use immediately. A companion Android application, ADK 2012, is available on Google Play. The source code for both the Android application and the ADK firmware (an Arduino sketch) can be downloaded from this page.
The ADK 2012 also comes with additional parts to help you develop accessories with it, including:
- AC power adapter
- USB A to Micro USB B connector cable
- Micro USB B to Micro USB AB connector (small, rectangular plug)
- Micro SD Card, preinstalled in the ADK SD Card socket
Google I/O 2012 - ADK 2.0
Introducing the new APIs and capabilities in ADK 2.0, with demos.