Sunday, May 30, 2010

Another exercise of SurfaceView, in a FrameLaout inside another LinearLayout

It's another exercise of using FurfaceView in Android.

In the previous exercises "Android SurfaceView" and "Android SurfaceView, run in Thread with sleep()", the layout was simple a FrameLayout composed of a SurfaceView only, setup using programming code setContentView(R.layout.main).

In this exercise, it's a FrameLayout inside a LinearLayout, together with two buttons. The FrameLayout is composed of a SurfaceView. And the layout is defined in main.xml. Both the MySurfaceView(extends SurfaceView) and the MySurfaceThread(extends Thread) are implemented as separated class. When the application is running, there are two buttons over the FrameLayout with SurfaceView, a dot bounce inside the SurfaceView. User can click the first button to make the another button invisible, to change the dimension of the FrameLayout in run-time, and the SurfaceView change also accordingly.



main.xml
<?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">
<Button
android:id="@+id/showhide"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Toggle The Another Button Show/Hide" />
<Button
android:id="@+id/dummy"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="a Button" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.exercise.AndroidMergeSurfaceView.MySurfaceView
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
</LinearLayout>


MySurfaceView.java
package com.exercise.AndroidMergeSurfaceView;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

private MySurfaceThread thread;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
int cx, cy, offx, offy;

public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init();
}

public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}

public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init();
}

private void init(){
getHolder().addCallback(this);
thread = new MySurfaceThread(getHolder(), this);

setFocusable(true); // make sure we get key events

paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.WHITE);

cx = 0;
cy = 0;
offx = 10;
offy = 10;

}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
thread.setRunning(true);
thread.start();

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
}
catch (InterruptedException e) {
}
}
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
canvas.drawRGB(0, 0, 0);
canvas.drawCircle(cx, cy, 3, paint);
cx += offx;
if (cx > getWidth() || (cx < 0)){
offx *= -1;
cx += offx;
}

cy += offy;
if (cy > getHeight() || (cy < 0)){
offy *= -1;
cy += offy;
}
}
}


MySurfaceThread.java
package com.exercise.AndroidMergeSurfaceView;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class MySurfaceThread extends Thread {
private SurfaceHolder myThreadSurfaceHolder;
private MySurfaceView myThreadSurfaceView;
private boolean myThreadRun = false;

public MySurfaceThread(SurfaceHolder surfaceHolder, MySurfaceView surfaceView) {
myThreadSurfaceHolder = surfaceHolder;
myThreadSurfaceView = surfaceView;
}

public void setRunning(boolean b) {
myThreadRun = b;
}

@Override
public void run() {
// TODO Auto-generated method stub
while(myThreadRun){
Canvas c = null;

try{
c = myThreadSurfaceHolder.lockCanvas(null);
synchronized (myThreadSurfaceHolder){
myThreadSurfaceView.onDraw(c);
}
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
myThreadSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}


AndroidMergeSurfaceView.java
package com.exercise.AndroidMergeSurfaceView;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class AndroidMergeSurfaceView extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Button buttonShowHide = (Button)findViewById(R.id.showhide);
final Button buttonDummy = (Button)findViewById(R.id.dummy);

buttonShowHide.setOnClickListener(
new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
if(buttonDummy.getVisibility()==View.VISIBLE){
buttonDummy.setVisibility(View.GONE);
}
else{
buttonDummy.setVisibility(View.VISIBLE);
}
}

}
);

}
}


Download the files.

Further exercise on SurfaceView: SurfaceView overlap with a LinearLayout









It's a bug here!


Pls. refer to the article
"IllegalThreadStateException in LunarLander"
for details.


I'm Sorry about that! ()



Tuesday, May 25, 2010

Two view in one FrameLayout

In my previous exercise "Draw a bitmap on View", "Draw something on a Canvas", "Android FaceDetector" and "Custom View with User Interaction", everything is drawn on ONE view. In this exercise, it will be separated in two views. The un-changed bitmap is drawn on View1, and user interaction is drawn on View2. Such that, everytime VIew2.onDraw() will redraw the interactive part only, no need to re-draw the bitmap.

Two view in one FrameLayout

The layout file, main.xml, is modified to use a FrameLayout with two View, cover whole of the screen.

- place a bitmap of 320x480 in /res/drawable/ folder, or using the bitmap in the downloaded files.

- Create two view class extends View

View1.java
package com.exercise.AndroidTwoView;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

public class View1 extends View {

public View1(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public View1(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

public View1(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.googlelogo320x480);
canvas.drawBitmap(myBitmap, 0, 0, null);

}

}


View2.java
package com.exercise.AndroidTwoView;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class View2 extends View {

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
float cx = 0, cy =0;
boolean draw = false;

public View2(Context context) {
super(context);
init();
}

public View2(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public View2(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

private void init(){
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.BLUE);
setFocusable(true);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
if (draw){
canvas.drawCircle(cx, cy, 3, paint);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();

if (action==MotionEvent.ACTION_DOWN){
cx = event.getX();
cy = event.getY();
draw = true;
invalidate();
}

return true;
}

}


- Modify main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.exercise.AndroidTwoView.View1
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<com.exercise.AndroidTwoView.View2
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</FrameLayout>


- AndroidTwoView.java actually no change from the auto-generated.

Download the files.

Android SurfaceView, run in Thread with sleep()

Further works on the last exercise "Android SurfaceView", a call to sleep(500) is introduced in run() of the thread, such that the onDraw() will be called in half second.

In this exercise, there is a point on the screen. When user touch on screen, the point will run from the current position to the touched position, in speed of half the distance per second.

Android SurfaceView, run in Thread with sleep()

AndroidSurfaceViewUI.java
package com.exercise.AndroidSurfaceViewUI;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class AndroidSurfaceViewUI extends Activity {

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

private float initX, initY;
private float targetX, targetY;
private boolean drawing = true;

public class MySurfaceThread extends Thread {

private SurfaceHolder myThreadSurfaceHolder;
private MySurfaceView myThreadSurfaceView;
private boolean myThreadRun = false;

public MySurfaceThread(SurfaceHolder surfaceHolder, MySurfaceView surfaceView) {
myThreadSurfaceHolder = surfaceHolder;
myThreadSurfaceView = surfaceView;
}

public void setRunning(boolean b) {
myThreadRun = b;
}

@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
while (myThreadRun) {
Canvas c = null;
try {
c = myThreadSurfaceHolder.lockCanvas(null);
synchronized (myThreadSurfaceHolder) {
myThreadSurfaceView.onDraw(c);
}

sleep(500);

} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
myThreadSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

private MySurfaceThread thread;

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
//super.onDraw(canvas);
if(drawing){
canvas.drawRGB(0, 0, 0);
canvas.drawCircle(initX, initY, 3, paint);
if ((initX==targetX) && (initY==targetY)){
drawing = false;
}
else{
initX = (initX + targetX)/2;
initY = (initY + targetY)/2;
}
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
//return super.onTouchEvent(event);
int action = event.getAction();

if (action==MotionEvent.ACTION_DOWN){
targetX = event.getX();
targetY = event.getY();
drawing = true;
}

return true;
}

public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init();
}

public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}

public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init();
}

private void init(){
getHolder().addCallback(this);
thread = new MySurfaceThread(getHolder(), this);

setFocusable(true); // make sure we get key events

paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.WHITE);

initX = targetX = 0;
initY = targetY = 0;

}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
thread.setRunning(true);
thread.start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);

MySurfaceView mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
}



Download the file.

Sunday, May 23, 2010

Android SurfaceView

It's a SurfaceView variation from the last exercise "Custom View with User Interaction".

Android SurfaceView

- Implement a new class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback.

- Implement the surfaceCreated(), surfaceDestroyed(), surfaceChanged() methods for SurfaceHolder.Callback.

- Implement a Thread for our SurfaceView.

- In SurfaceView's constructor, add the SurfaceView to SurfaceHolder for a callback, and create the Thread.
getHolder().addCallback(this);
thread = new MySurfaceThread(getHolder(), this);

- Modify the run() method of the Thread

package com.exercise.AndroidSurfaceViewUI;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class AndroidSurfaceViewUI extends Activity {

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

private float initX, initY, radius;
private boolean drawing = false;

public class MySurfaceThread extends Thread {

private SurfaceHolder myThreadSurfaceHolder;
private MySurfaceView myThreadSurfaceView;
private boolean myThreadRun = false;

public MySurfaceThread(SurfaceHolder surfaceHolder, MySurfaceView surfaceView) {
myThreadSurfaceHolder = surfaceHolder;
myThreadSurfaceView = surfaceView;
}

public void setRunning(boolean b) {
myThreadRun = b;
}

@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
while (myThreadRun) {
Canvas c = null;
try {
c = myThreadSurfaceHolder.lockCanvas(null);
synchronized (myThreadSurfaceHolder) {
myThreadSurfaceView.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
myThreadSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

private MySurfaceThread thread;

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
//super.onDraw(canvas);
if(drawing){
canvas.drawCircle(initX, initY, radius, paint);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
//return super.onTouchEvent(event);
int action = event.getAction();
if (action==MotionEvent.ACTION_MOVE){
float x = event.getX();
float y = event.getY();
radius = (float) Math.sqrt(Math.pow(x-initX, 2) + Math.pow(y-initY, 2));
}
else if (action==MotionEvent.ACTION_DOWN){
initX = event.getX();
initY = event.getY();
radius = 1;
drawing = true;
}
else if (action==MotionEvent.ACTION_UP){
drawing = false;
}

return true;
}

public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init();
}

public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}

public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init();
}

private void init(){
getHolder().addCallback(this);
thread = new MySurfaceThread(getHolder(), this);

setFocusable(true); // make sure we get key events

paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.WHITE);
}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
thread.setRunning(true);
thread.start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);

MySurfaceView mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
}


(In the View version in "Custom View with User Interaction", the view is forced to re-draw by invalidate() method, so the screen will be cleared. In this SurfaceView version, the onDraw() method is called inside thread's Run() method, so the drawing on the original canvas will not be cleared.)

Download the file.


next: Android SurfaceView, run in Thread with sleep()







It's a bug here!


Pls. refer to the article
"IllegalThreadStateException in LunarLander"
for details.


I'm Sorry about that! ()



Saturday, May 22, 2010

Custom View with User Interaction

In my previous exercise "Draw a bitmap on View", "Draw something on a Canvas" and even "Android FaceDetector", the application output is drawn on View in onDraw method in application starting once only; without any interaction with user. In this exercise, a custom View with user interaction will be implemented. When user touch on the screen and move, a circle will be drawn.

Custom View with User Interaction

There is a customized MyView class, extends from View. The onTouchEvent(MotionEvent event) method is used to handle the user touch events; such as ACTION_MOVE, ACTION_DOWN and ACTION_UP. Then it calls invalidate() to force the view to be redraw, onDraw(Canvas canvas) will be called in turn.

AndroidViewUI.java
package com.exercise.AndroidViewUI;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class AndroidViewUI extends Activity {
public class MyView extends View {

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

private float initX, initY, radius;
private boolean drawing = false;

public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init();
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}

public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init();
}

private void init(){
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.WHITE);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
if(drawing){
canvas.drawCircle(initX, initY, radius, paint);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub

int action = event.getAction();
if (action==MotionEvent.ACTION_MOVE){
float x = event.getX();
float y = event.getY();

radius = (float) Math.sqrt(Math.pow(x-initX, 2) + Math.pow(y-initY, 2));

}
else if (action==MotionEvent.ACTION_DOWN){
initX = event.getX();
initY = event.getY();
radius = 1;
drawing = true;
}
else if (action==MotionEvent.ACTION_UP){
drawing = false;
}
invalidate();
return true;
}

}



/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);

MyView myView = new MyView(this);
setContentView(myView);
}


}


Download the file.

Related Article: Instance two object from the same custom view class



Thursday, May 20, 2010

Android 2.2 platform available for the Android SDK

The Android 2.2 platform is now available for the Android SDK, along with new tools, documentation, and a new NDK.

Android 2.2 is a minor platform release including user features, developer features, API changes, and bug fixes. For information on developer features and API changes, see the Framework API section.

Android 2.2
Android 2.2

For developers, the Android 2.2 platform is available as a downloadable component for the Android SDK. The downloadable platform includes a fully compliant Android library and system image, as well as a set of emulator skins, sample applications, and more. The downloadable platform includes no external libraries.

You can refer to my article to know how to Install Android SDK on Eclipse 3.5 Galileo, in Ubuntu 9.10. Or, if you have install Android 1.6 SDK or later version, you can refer to another article to upgrade Android SDK using Android SDK and AVD Manager.





Wednesday, May 19, 2010

Android FaceDetector

Android provide a class android.media.FaceDetector to identify the faces of people in a Bitmap graphic object.

Android FaceDetector

It's a simple exercise of face detection on Android. You have to place your own photo (in 320x480) into /res/drawable folder, or use the attached photos from the download link on the bottom of the text.

package com.exercise.AndroidFaceDetector;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;
import android.os.Bundle;
import android.view.View;

public class AndroidFaceDetector extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        setContentView(new myView(this));
    }
    
    private class myView extends View{
     
     private int imageWidth, imageHeight;
     private int numberOfFace = 5;
     private FaceDetector myFaceDetect; 
     private FaceDetector.Face[] myFace;
     float myEyesDistance;
     int numberOfFaceDetected;
     
     Bitmap myBitmap;

  public myView(Context context) {
   super(context);
   // TODO Auto-generated constructor stub
   
   BitmapFactory.Options BitmapFactoryOptionsbfo = new BitmapFactory.Options();
   BitmapFactoryOptionsbfo.inPreferredConfig = Bitmap.Config.RGB_565; 
   myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.face5, BitmapFactoryOptionsbfo);
   imageWidth = myBitmap.getWidth();
   imageHeight = myBitmap.getHeight();
   myFace = new FaceDetector.Face[numberOfFace];
   myFaceDetect = new FaceDetector(imageWidth, imageHeight, numberOfFace);
   numberOfFaceDetected = myFaceDetect.findFaces(myBitmap, myFace); 
   
  }

  @Override
  protected void onDraw(Canvas canvas) {
   // TODO Auto-generated method stub
   
            canvas.drawBitmap(myBitmap, 0, 0, null);
            
            Paint myPaint = new Paint();
            myPaint.setColor(Color.GREEN);
            myPaint.setStyle(Paint.Style.STROKE); 
            myPaint.setStrokeWidth(3);
            
            for(int i=0; i < numberOfFaceDetected; i++)
            {
             Face face = myFace[i];
             PointF myMidPoint = new PointF();
             face.getMidPoint(myMidPoint);
    myEyesDistance = face.eyesDistance();
             canvas.drawRect(
               (int)(myMidPoint.x - myEyesDistance),
               (int)(myMidPoint.y - myEyesDistance),
               (int)(myMidPoint.x + myEyesDistance),
               (int)(myMidPoint.y + myEyesDistance),
               myPaint);
            }
  }
    }
}



Download the files.

Related:
- Face detection for Camera

Updated:
- With the release of Google Play services 7.8, new Mobile Vision APIs was added, to include a new Face API that finds human faces in images and video. Read more: Face Detection with Google Play services, Mobile Vision API (with demo APK)


Friday, May 14, 2010

Draw something on a Canvas

Simple draw something (drawRect) on a Canvas over a bitmap on a View.



Modify from the previous article "Draw a bitmap on View". In order to draw something, a object of Paint class have to been create, and set some parameter; such as color, style, Strok.

Modify the code:
package com.exercise.AndroidPaint;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;

public class AndroidPaint extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
setContentView(new myView(this));
}

private class myView extends View{

public myView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.googlelogo320x480);
canvas.drawBitmap(myBitmap, 0, 0, null);

Paint myPaint = new Paint();
myPaint.setColor(Color.GREEN);
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeWidth(3);
canvas.drawRect(10, 10, 100, 100, myPaint);
}
}
}


Download the files.

Next: Android FaceDetector
Next: Custom View with User Interaction



Wednesday, May 12, 2010

Draw a bitmap on View

It's a simple example to draw a bitmap on screen in View. The content view is set to a View, and the drawing is achieved in onDraw method.

Draw a bitmap on View

To make it simple, a bitmap of 320x480 (the size of HVGA) was prepared. It's in /res/drawable/ folder, and will be loaded using the following codes:

Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.googlelogo320x480);

canvas.drawBitmap(myBitmap, 0, 0, null);

package com.exercise.AndroidPaint;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.View;

public class AndroidPaint extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
setContentView(new myView(this));
}

private class myView extends View{

public myView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.googlelogo320x480);
canvas.drawBitmap(myBitmap, 0, 0, null);
}
}
}


Download the files.

Next: Draw something on a Canvas.

Monday, May 10, 2010

A simple RSS reader III, show details once item clicked.

Last article "A simple RSS reader II, implement with RSSFeed & RSSItem", only the titles shown in a List. Here, we are going to start another activity to show the details once any item in the list clicked.

A simple RSS reader III, show details once item clicked.

Most of the works from the last article "A simple RSS reader II, implement with RSSFeed & RSSItem", with the changes lsited below.

Implement /res/layout/details.xml, it's the layout of the activity ShowDetails.java
<?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="RSS Details:-" />
<TextView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/detailstitle" />
<TextView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/detailsdescription" />
<TextView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:autoLink="web"
  android:id="@+id/detailslink" />
<TextView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/detailspubdate" />
</LinearLayout>


Implement a new Activity /src/com.exercise.AndroidRssReader/ShowDetails.java, it will be started once any item in the list clicked.
package com.exercise.AndroidRssReader;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ShowDetails extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
 // TODO Auto-generated method stub
 super.onCreate(savedInstanceState);
 setContentView(R.layout.details);
 TextView detailsTitle = (TextView)findViewById(R.id.detailstitle);
 TextView detailsDescription = (TextView)findViewById(R.id.detailsdescription);
 TextView detailsPubdate = (TextView)findViewById(R.id.detailspubdate);
 TextView detailsLink = (TextView)findViewById(R.id.detailslink);

 Bundle bundle = this.getIntent().getExtras();
    
      detailsTitle.setText(bundle.getString("keyTitle"));
      detailsDescription.setText(bundle.getString("keyDescription"));
      detailsPubdate.setText(bundle.getString("keyPubdate"));
      detailsLink.setText(bundle.getString("keyLink"));
    
}
}


Modify /src/com.exercise.AndroidRssReader/AndroidRssReader.java to start new activity once any list item clicked.
package com.exercise.AndroidRssReader;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class AndroidRssReader extends ListActivity {

private RSSFeed myRssFeed = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
    
      try {
  URL rssUrl = new URL("http://www.gov.hk/en/about/rss/govhkrss.data.xml");
  SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
  SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
  XMLReader myXMLReader = mySAXParser.getXMLReader();
  RSSHandler myRSSHandler = new RSSHandler();
  myXMLReader.setContentHandler(myRSSHandler);
  InputSource myInputSource = new InputSource(rssUrl.openStream());
  myXMLReader.parse(myInputSource);
 
  myRssFeed = myRSSHandler.getFeed();
 
 } catch (MalformedURLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (ParserConfigurationException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (SAXException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }

 if (myRssFeed!=null)
 {
  TextView feedTitle = (TextView)findViewById(R.id.feedtitle);
  TextView feedDescribtion = (TextView)findViewById(R.id.feeddescribtion);
  TextView feedPubdate = (TextView)findViewById(R.id.feedpubdate);
  TextView feedLink = (TextView)findViewById(R.id.feedlink);
  feedTitle.setText(myRssFeed.getTitle());
  feedDescribtion.setText(myRssFeed.getDescription());
  feedPubdate.setText(myRssFeed.getPubdate());
  feedLink.setText(myRssFeed.getLink());
 
  ArrayAdapter<RSSItem> adapter =
   new ArrayAdapter<RSSItem>(this,
     android.R.layout.simple_list_item_1,myRssFeed.getList());
  setListAdapter(adapter);
 
 }
  }

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
 // TODO Auto-generated method stub
 Intent intent = new Intent(this,ShowDetails.class);
 Bundle bundle = new Bundle();
 bundle.putString("keyTitle", myRssFeed.getItem(position).getTitle());
 bundle.putString("keyDescription", myRssFeed.getItem(position).getDescription());
 bundle.putString("keyLink", myRssFeed.getItem(position).getLink());
 bundle.putString("keyPubdate", myRssFeed.getItem(position).getPubdate());
 intent.putExtras(bundle);
      startActivity(intent);
}
}


Modify AndroidManifest.xml to include the new activity.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.exercise.AndroidRssReader"
    android:versionCode="1"
    android:versionName="1.0">
  <application android:icon="@drawable/icon" android:label="@string/app_name">
      <activity android:name=".AndroidRssReader"
                android:label="@string/app_name">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
 <activity android:name=".ShowDetails"></activity>
  </application>
  <uses-sdk android:minSdkVersion="4" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>


Download the files.

next:
- Apply custom adapter to ListView for RSS Reader
- A simple RSS reader IV, start browser to open the selected feed



Saturday, May 8, 2010

A simple RSS reader II, implement with RSSFeed & RSSItem

In the previous article "A simple RSS reader, in ListView", I just show a very simple RSS reader, with a very limited handling on the RSSHandle events. Here, I will show in more details, also in better orgination.

A simple RSS reader II, implement with RSSFeed & RSSItem

First of all, the source of the feed is changed to http://www.gov.hk/en/about/rss/govhkrss.data.xml, it's a feed from Hong Kong Government; because the original blogger's feed is too complicated for me to demo here!

Here, RSSHandler, RSSFeed and RSSItems are implemented as separated class for better organization. The application have a single object of RSSFeed class, which consist a list of RSSItem object. More details (Title, PubDate, Description and Link) of the feed and individual items are saved in the structure, which can be retrieved later.

AndroidManifest.xml to grant "android.permission.INTERNET" to the application. (Refer to last article "A simple RSS reader, using Android's org.xml.sax package")

Keep the rsslist.xml as before
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rowtext"
android:layout_width="fill_parent"
android:layout_height="25px"
android:textSize="10sp" />


Modify main.xml
<?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" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/feedtitle" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/feeddescribtion" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/feedpubdate" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:id="@+id/feedlink" />
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="No Data" />
</LinearLayout>


Implement /src/com.exercise.AndroidRssReader/RSSItem.java
package com.exercise.AndroidRssReader;

public class RSSItem {

private String title = null;
private String description = null;
private String link = null;
private String pubdate = null;

RSSItem(){
}

void setTitle(String value)
{
title = value;
}
void setDescription(String value)
{
description = value;
}
void setLink(String value)
{
link = value;
}
void setPubdate(String value)
{
pubdate = value;
}

String getTitle()
{
return title;
}
String getDescription()
{
return description;
}
String getLink()
{
return link;
}
String getPubdate()
{
return pubdate;
}

@Override
public String toString() {
// TODO Auto-generated method stub
return title;
}
}


Implement /src/com.exercise.AndroidRssReader/RSSFeed.java
package com.exercise.AndroidRssReader;

import java.util.List;
import java.util.Vector;

public class RSSFeed {
private String title = null;
private String description = null;
private String link = null;
private String pubdate = null;
private List<RSSItem> itemList;

RSSFeed(){
itemList = new Vector<RSSItem>(0);
}

void addItem(RSSItem item){
itemList.add(item);
}

RSSItem getItem(int location){
return itemList.get(location);
}

List<RSSItem> getList(){
return itemList;
}

void setTitle(String value)
{
title = value;
}
void setDescription(String value)
{
description = value;
}
void setLink(String value)
{
link = value;
}
void setPubdate(String value)
{
pubdate = value;
}

String getTitle()
{
return title;
}
String getDescription()
{
return description;
}
String getLink()
{
return link;
}
String getPubdate()
{
return pubdate;
}

}


Implemenet /src/com.exercise.AndroidRssReader/RSSHandler.java
package com.exercise.AndroidRssReader;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class RSSHandler extends DefaultHandler {

final int state_unknown = 0;
final int state_title = 1;
final int state_description = 2;
final int state_link = 3;
final int state_pubdate = 4;
int currentState = state_unknown;

RSSFeed feed;
RSSItem item;

boolean itemFound = false;

RSSHandler(){
}

RSSFeed getFeed(){
return feed;
}

@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
feed = new RSSFeed();
item = new RSSItem();

}

@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
}

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub

if (localName.equalsIgnoreCase("item")){
itemFound = true;
item = new RSSItem();
currentState = state_unknown;
}
else if (localName.equalsIgnoreCase("title")){
currentState = state_title;
}
else if (localName.equalsIgnoreCase("description")){
currentState = state_description;
}
else if (localName.equalsIgnoreCase("link")){
currentState = state_link;
}
else if (localName.equalsIgnoreCase("pubdate")){
currentState = state_pubdate;
}
else{
currentState = state_unknown;
}

}

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if (localName.equalsIgnoreCase("item")){
feed.addItem(item);
}
}

@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub

String strCharacters = new String(ch,start,length);

if (itemFound==true){
// "item" tag found, it's item's parameter
switch(currentState){
case state_title:
item.setTitle(strCharacters);
break;
case state_description:
item.setDescription(strCharacters);
break;
case state_link:
item.setLink(strCharacters);
break;
case state_pubdate:
item.setPubdate(strCharacters);
break;
default:
break;
}
}
else{
// not "item" tag found, it's feed's parameter
switch(currentState){
case state_title:
feed.setTitle(strCharacters);
break;
case state_description:
feed.setDescription(strCharacters);
break;
case state_link:
feed.setLink(strCharacters);
break;
case state_pubdate:
feed.setPubdate(strCharacters);
break;
default:
break;
}
}

currentState = state_unknown;
}


}


Modify /src/com.exercise.AndroidRssReader/AndroidRssReader.java
package com.exercise.AndroidRssReader;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class AndroidRssReader extends ListActivity {

private RSSFeed myRssFeed = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

try {
URL rssUrl = new URL("http://www.gov.hk/en/about/rss/govhkrss.data.xml");
SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
XMLReader myXMLReader = mySAXParser.getXMLReader();
RSSHandler myRSSHandler = new RSSHandler();
myXMLReader.setContentHandler(myRSSHandler);
InputSource myInputSource = new InputSource(rssUrl.openStream());
myXMLReader.parse(myInputSource);

myRssFeed = myRSSHandler.getFeed();

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

if (myRssFeed!=null)
{
TextView feedTitle = (TextView)findViewById(R.id.feedtitle);
TextView feedDescribtion = (TextView)findViewById(R.id.feeddescribtion);
TextView feedPubdate = (TextView)findViewById(R.id.feedpubdate);
TextView feedLink = (TextView)findViewById(R.id.feedlink);
feedTitle.setText(myRssFeed.getTitle());
feedDescribtion.setText(myRssFeed.getDescription());
feedPubdate.setText(myRssFeed.getPubdate());
feedLink.setText(myRssFeed.getLink());

ArrayAdapter<RSSItem> adapter =
new ArrayAdapter<RSSItem>(this,
android.R.layout.simple_list_item_1,myRssFeed.getList());
setListAdapter(adapter);
}
}
}


Download the files.

Next: A simple RSS reader III, show details once item clicked.

Tuesday, May 4, 2010

How to install sun-java6-jdk on Ubuntu 10.04 LTS?

For the just released Ubuntu 10.04 LTS, the sun-java6-jdk packages have been removed from the Multiverse section of the Ubuntu archive. So you cannot install it using the command sudo apt-get install sun-java6-jdk!!!

In order to install sun-java6-jdk on Ubuntu 10.04 LTS, You can configure Ubuntu 10.04 LTS to use Canonical Partner Repository via command-line:

sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"

Then, Update the source list:

sudo apt-get update

Finally, you can install sun-java6-jdk using the command:

sudo apt-get install sun-java6-jdk