I am trying to implement face detection using Google Mobile Vision in an Android app. In the app, I have an Imageview and a button with text, "Process".
When the button is clicked, the code connects with Google's Vision API and detects the face. After detecting the face, I am trying to draw a rectangle around the face. For that purpose I am using "Canvas" available in Android.
The error (not exactly) is I am unable to see the rectangle around the face. Here is the code in my MainActivity.java file:
package com.startertutorials.googlefacedetect;
import android.app.Activity;
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.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.face.Face;
import com.google.android.gms.vision.face.FaceDetector;
import com.google.android.gms.vision.face.Landmark;
public class MainActivity extends Activity {
ImageView imageView;
Button btnProcess;
Bitmap myBitmap;
Canvas canvas;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView)findViewById(R.id.imageView);
btnProcess = (Button)findViewById(R.id.btnProcess);
myBitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.me);
//imageView.setImageBitmap(myBitmap);
Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth(), myBitmap.getHeight(), myBitmap.getConfig());
canvas = new Canvas(tempBitmap);
btnProcess.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FaceDetector faceDetector = new FaceDetector.Builder(getApplicationContext())
.setTrackingEnabled(false)
.setLandmarkType(FaceDetector.ALL_LANDMARKS)
.setMode(FaceDetector.FAST_MODE)
.build();
if (!faceDetector.isOperational()) {
Toast.makeText(MainActivity.this, "Face Detector Setup Failed!", Toast.LENGTH_SHORT).show();
return;
}
Frame frame = new Frame.Builder().setBitmap(myBitmap).build();
SparseArray<Face> sparseArray = faceDetector.detect(frame);
for (int i = 0; i < sparseArray.size(); i++) {
Face face = sparseArray.valueAt(i);
detectLandmarks(face);
}
}
});
}
private void detectLandmarks(Face face) {
float height = face.getHeight();
float width = face.getWidth();
PointF p = face.getPosition();
float cx = p.x;
float cy = p.y;
drawOnImageView(cx, cy, height, width);
/*
for(Landmark landmark:face.getLandmarks())
{
int cx = (int)landmark.getPosition().x;
int cy = (int)landmark.getPosition().y;
drawOnImageView(landmark.getType(),cx,cy);
}
*/
}
private void drawOnImageView(float cx, float cy, float height, float width) {
Paint myPaint = new Paint();
myPaint.setColor(Color.GREEN);
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeWidth(2);
canvas.drawBitmap(myBitmap, 0, 0, null);
canvas.drawRect(0, 0, 200, 200, myPaint);
imageView.setImageBitmap(myBitmap);
Log.d("Success", "Face detected successfully");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
The output after I click the "Process" button in the app is as follows:
In the logcat of Android studio, I can see the message "Face detected successfully". So, I am assuming that all my code is running. But I am unable to see the rectangle.
Please help me with displaying a rectangle first. Later I will adjust it to display around the face.
You have to extend the Detector class:
https://developers.google.com/android/reference/com/google/android/gms/vision/Detector
Defining your RectangleDetector class. The code to detect rectangles would be implemented by overriding the detect() method. You'd need to implement this yourself, since there isn't already code for detecting rectangles in mobile vision.
When you have this, you'd be able to use it with CameraSource and other parts of the mobile vision API.
Related
i completed the drawing text on ImageView and i saved the final image in sd card. My problem is, when i am touching the screen to draw the text, my image in the ImageView is disappearing.
Code
import java.io.File;
import java.io.FileOutputStream;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class DrawTextOnImgActivity extends Activity {
private Button btn_save, btn_resume;
private ImageView iv_canvas;
private Bitmap baseBitmap;
private Canvas canvas;
private Paint paint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_draw_text_on_img);
// The initialization of a brush, brush width is 5, color is red
paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
iv_canvas = (ImageView) findViewById(R.id.imageView1);
btn_save = (Button) findViewById(R.id.btn_save);
btn_resume = (Button) findViewById(R.id.btn_resume);
btn_save.setOnClickListener(click);
btn_resume.setOnClickListener(click);
iv_canvas.setOnTouchListener(touch);
}
private View.OnTouchListener touch = new OnTouchListener() {
// Coordinate definition finger touch
float startX;
float startY;
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
// Press the user action
case MotionEvent.ACTION_DOWN:
// The first drawing initializes the memory image, specify the background is white
if (baseBitmap == null) {
baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(),
iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(baseBitmap);
canvas.drawColor(Color.WHITE);
}
// Recording began to touch point coordinate
startX = event.getX();
startY = event.getY();
break;
// The user's finger on the screen of mobile action
case MotionEvent.ACTION_MOVE:
// Record the coordinates of the mobile point
float stopX = event.getX();
float stopY = event.getY();
//According to the coordinates of the two points, drawing lines
canvas.drawLine(startX, startY, stopX, stopY, paint);
// Update start point
startX = event.getX();
startY = event.getY();
// The pictures to the ImageView
iv_canvas.setImageBitmap(baseBitmap);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
};
private View.OnClickListener click = new OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_save:
saveBitmap();
break;
case R.id.btn_resume:
resumeCanvas();
break;
default:
break;
}
}
};
/**
* Save the image to the SD card.
*/
protected void saveBitmap() {
try {
// Save the image to the SD card.
File folder = new File(Environment.getExternalStorageDirectory() + "/DrawTextOnImg");
if (!folder.exists()) {
folder.mkdir();
}
File file = new File(Environment.getExternalStorageDirectory()+"/DrawTextOnImg/tempImg.png");
FileOutputStream stream = new FileOutputStream(file);
baseBitmap.compress(CompressFormat.PNG, 100, stream);
Toast.makeText(DrawTextOnImgActivity.this, "Save the picture of success", Toast.LENGTH_SHORT).show();
// Android equipment Gallery application will only at boot time scanning system folder
// The simulation of a media loading broadcast, for the preservation of images can be viewed in Gallery
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
sendBroadcast(intent);
} catch (Exception e) {
Toast.makeText(DrawTextOnImgActivity.this, "Save failed", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
/**
* Clear the drawing board
*/
protected void resumeCanvas() {
// Manually clear the drawing board, to create a drawing board
if (baseBitmap != null) {
baseBitmap = Bitmap.createBitmap(iv_canvas.getWidth(), iv_canvas.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(baseBitmap);
canvas.drawColor(Color.WHITE);
iv_canvas.setImageBitmap(baseBitmap);
Toast.makeText(DrawTextOnImgActivity.this, "Clear the drawing board, can be re started drawing", Toast.LENGTH_SHORT).show();
}
}
}
Manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<protected-broadcast android:name="android.intent.action.MEDIA_MOUNTED" />
By using this code i am writing the text on ImageView but image in the ImageView is disappearing. So, please help me to write the text on image of ImageView
I achieved it by fallowing this code
DrawTextOnImgActivity
import java.io.File;
import java.io.FileOutputStream;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore.Images;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
public class DrawTextOnImgActivity extends Activity {
ImageView ivDrawImg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_draw_text_on_img);
ivDrawImg = (ImageView) findViewById(R.id.iv_draw);
Button btnSave = (Button) findViewById(R.id.btn_save);
Button btnResume = (Button) findViewById(R.id.btn_resume);
btnSave.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
saveImg();
}
});
btnResume.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
private void saveImg(){
Bitmap image = Bitmap.createBitmap(ivDrawImg.getWidth(), ivDrawImg.getHeight(), Bitmap.Config.RGB_565);
ivDrawImg.draw(new Canvas(image));
String uri = Images.Media.insertImage(getContentResolver(), image, "title", null);
Log.e("uri", uri);
try {
// Save the image to the SD card.
File folder = new File(Environment.getExternalStorageDirectory() + "/DrawTextOnImg");
if (!folder.exists()) {
folder.mkdir();
//folder.mkdirs(); //For creating multiple directories
}
File file = new File(Environment.getExternalStorageDirectory()+"/DrawTextOnImg/tempImg.png");
FileOutputStream stream = new FileOutputStream(file);
image.compress(CompressFormat.PNG, 100, stream);
Toast.makeText(DrawTextOnImgActivity.this, "Picture saved", Toast.LENGTH_SHORT).show();
// Android equipment Gallery application will only at boot time scanning system folder
// The simulation of a media loading broadcast, for the preservation of images can be viewed in Gallery
/*Intent intent = new Intent();
intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
sendBroadcast(intent);*/
} catch (Exception e) {
Toast.makeText(DrawTextOnImgActivity.this, "Save failed", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}
DrawView
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
public class DrawView extends ImageView {
private int color = Color.parseColor("#0000FF");
private float width = 4f;
private List<Holder> holderList = new ArrayList<Holder>();
private class Holder {
Path path;
Paint paint;
Holder(int color, float width) {
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
}
public DrawView(Context context) {
super(context);
init();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
holderList.add(new Holder(color, width));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Holder holder : holderList) {
canvas.drawPath(holder.path, holder.paint);
}
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
holderList.add(new Holder(color,width));
holderList.get(holderList.size() - 1).path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
holderList.get(holderList.size() - 1).path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
public void resetPaths() {
for (Holder holder : holderList) {
holder.path.reset();
}
invalidate();
}
public void setBrushColor(int color) {
this.color = color;
}
public void setWidth(float width) {
this.width = width;
}
}
Layout (activity_draw_text_on_img)
<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"
android:background="#FFFFFF" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:background="#FFFFFF" >
<com.app.drawtextonimg.DrawView
android:id="#+id/iv_draw"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:src="#drawable/rajamouli" />
</RelativeLayout>
<Button
android:id="#+id/btn_save"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:background="#000000"
android:text="#string/save"
android:textColor="#FFFFFF" />
<Button
android:id="#+id/btn_resume"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#000000"
android:text="#string/resume"
android:textColor="#FFFFFF" />
</RelativeLayout>
Manifest file
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Project link
If I understand your question correctly, I think that you should remove this line from your Case.ACTION_MOVE.
iv_canvas.setImageBitmap(baseBitmap);
By doing this you are resetting your ImageView every time you move on the screen which will cause the ImageView to be just a blank
I want to write a program which creates a button, whenever I'm touching the screen, the button should be created on the place where the screen was touched.
I wrote a program which creates circles, whenever and where the screen is touched, can anybody explain me how to make buttons instead of circles?
Thx.
Main Activity
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View.OnTouchListener;
public class SingleTouchActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SingleTouchEventView(this, null));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.single_touch, menu);
return true;
}
}
Touch Activity
import java.util.ArrayList; //not all imports are necessary
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.view.View.OnClickListener;
public class SingleTouchEventView extends RelativeLayout {
private Paint paint = new Paint();
List<Point> points = new ArrayList<Point>();
public SingleTouchEventView(Context context, AttributeSet attrs) {
super(context, attrs);}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: // a pointer was moved
case MotionEvent.ACTION_UP:
Point p = new Point();
p.x = (int)event.getX();
p.y = (int)event.getY();
points.add(p);
Button button = new Button(getContext());
int buttonHeight = 50;
int buttonWidth = 50;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(buttonWidth, buttonHeight);
params.leftMargin = p.x;
params.topMargin = p.y;
addView(button, params);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
//DO SOMETHING! {RUN SOME FUNCTION ... DO CHECKS... ETC}
}
});
case MotionEvent.ACTION_CANCEL: {
break;
}
}
invalidate();
return true;
}
// Do something
}
First make your class extends a ViewGroup like RelativeLayout and FrameLayout in your case.
Then, on the touch event, create a Button (or a ImageView and setOnClickListener()), adjust the position of the button with margins on the view LayoutParams and add the button to the layout via addView().
Edit: The button creation will be like this on onTouchEvent():
Point p = new Point();
p.x = (int)event.getX();
p.y = (int)event.getY();
Button button = new Button(context);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(buttonWidth, buttonHeight);
params.leftMargin = p.x;
params.topMargin = p.y;
addView(button, params);
Also change this to be inside ACTION_UP to prevent creating multiple buttons on long pressing.
This is not possible. You can draw a circle because that is a graphic. If you were to make button appear then you would need to add it to XML. I assume you are making a game that requires elements to randomly appear. I suggest if this is the case to use and engine a spawn random circles and when touched do what you need. Hope this helps
I'm learning Android programming.
So I managed to implement a simple app that rolls a ball over the screen if you tilt yout phone. But right now it is as simple as:
if roll > 0 then xpos++ else xpos-- end and the same for ypos.
So I want to calculate a more exact direction and also I would like the ball to roll faster the more the phone is tilting.
So if I know the tilt in the roll direction and the pitch direction, how do I calculate the direction and speed of the ball?
Here is how:
package benyamephrem.tiltball;
import android.graphics.Point;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Handler;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.SensorEventListener;
import android.widget.Toast;
public class TiltBall extends ActionBarActivity {
BallView mBallView = null;
Handler RedrawHandler = new Handler(); //so redraw occurs in main thread
Timer mTmr = null;
TimerTask mTsk = null;
int mScrWidth, mScrHeight;
android.graphics.PointF mBallPos, mBallSpd;
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE); //hide title bar
//set app to full screen and keep screen on
getWindow().setFlags(0xFFFFFFFF, LayoutParams.FLAG_FULLSCREEN | LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tilt_ball);
//create pointer to main screen
final FrameLayout mainView = (android.widget.FrameLayout) findViewById(R.id.main_view);
//get screen dimensions
Display display = getWindowManager().getDefaultDisplay();
mScrWidth = display.getWidth();
mScrHeight = display.getHeight();
mBallPos = new android.graphics.PointF();
mBallSpd = new android.graphics.PointF();
//create variables for ball position and speed
mBallPos.x = mScrWidth / 2;
mBallPos.y = mScrHeight / 5;
mBallSpd.x = 0;
mBallSpd.y = 0;
//create initial ball
mBallView = new BallView(this, mBallPos.x, mBallPos.y, 75);
mainView.addView(mBallView); //add ball to main screen
mBallView.invalidate(); //call onDraw in BallView
//listener for accelerometer, use anonymous class for simplicity
((SensorManager) getSystemService(Context.SENSOR_SERVICE)).registerListener(
new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent event) {
//set ball speed based on phone tilt (ignore Z axis)
//***Change speed here by multiplying event values by bigger numbers***
mBallSpd.x = -event.values[0] * (30/10);
mBallSpd.y = event.values[1] * (30/10);
//timer event will redraw ball
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
} //ignore
},
((SensorManager) getSystemService(Context.SENSOR_SERVICE))
.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0),
SensorManager.SENSOR_DELAY_NORMAL);
//listener for touch event
mainView.setOnTouchListener(new android.view.View.OnTouchListener() {
public boolean onTouch(android.view.View v, android.view.MotionEvent e) {
//set ball position based on screen touch
mBallPos.x = e.getX();
mBallPos.y = e.getY();
//timer event will redraw ball
return true;
}
});
} //OnCreate
//listener for menu button on phone
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("Exit"); //only one menu item
return super.onCreateOptionsMenu(menu);
}
//listener for menu item clicked
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
if (item.getTitle() == "Exit") //user clicked Exit
finish(); //will call onPause
return super.onOptionsItemSelected(item);
}
//For state flow see http://developer.android.com/reference/android/app/Activity.html
#Override
public void onPause() //app moved to background, stop background threads
{
mTmr.cancel(); //kill\release timer (our only background thread)
mTmr = null;
mTsk = null;
super.onPause();
}
#Override
public void onResume() //app moved to foreground (also occurs at app startup)
{
//create timer to move ball to new position
mTmr = new Timer();
mTsk = new TimerTask() {
public void run() {
//if debugging with external device,
// a log cat viewer will be needed on the device
Log.d("TiltBall", "Timer Hit - " + mBallPos.x + ":" + mBallPos.y);
//move ball based on current speed
mBallPos.x += mBallSpd.x;
mBallPos.y += mBallSpd.y;
//if ball goes off screen, reposition to opposite side of screen
if (mBallPos.x > mScrWidth) mBallPos.x = 0;
if (mBallPos.y > mScrHeight) mBallPos.y = 0;
if (mBallPos.x < 0) mBallPos.x = mScrWidth;
if (mBallPos.y < 0) mBallPos.y = mScrHeight;
//update ball class instance
mBallView.x = mBallPos.x;
mBallView.y = mBallPos.y;
//redraw ball. Must run in background thread to prevent thread lock.
RedrawHandler.post(new Runnable() {
public void run() {
mBallView.invalidate();
}
});
}
}; // TimerTask
mTmr.schedule(mTsk, 10, 10); //start timer
super.onResume();
} // onResume
#Override
public void onDestroy() //main thread stopped
{
super.onDestroy();
//wait for threads to exit before clearing app
System.runFinalizersOnExit(true);
//remove app from memory
android.os.Process.killProcess(android.os.Process.myPid());
}
//listener for config change.
//This is called when user tilts phone enough to trigger landscape view
// we want our app to stay in portrait view, so bypass event
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
} //TiltBallActivity
Here is the BallView class;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class BallView extends View {
public float x;
public float y;
private final int r;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//construct new ball object
public BallView(Context context, float x, float y, int r) {
super(context);
//color hex is [transparncy][red][green][blue]
mPaint.setColor(0xFF1325E0); //not transparent. color is blue
this.x = x;
this.y = y;
this.r = r; //radius
}
//qcalled by invalidate()
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, r, mPaint);
}
public int getRadius(){
return r;
}
}
This is not my code but I forgot its source so credits to whoever made it. I am using it for a current project I'm working on.
I am developing a game for android. Between stages, i want to show a part of a map with a route, and move it from a city to city (stage to stage).
First i want to do it on my phone, this a Samsung Galaxy Y, 240x320 Qvga ldpi.
So i have the map file in jpg format. This picture is 2463x602, this is a world map.
I did it, everything is done except one thing. This "animation" is slow for me.
When i start with this, i thought, it will be so fast, and i will handle the speed with a Thread.sleep(); but the matter is, it is not fast.
How can i make it more faster this?
Here is my code:
package hu.mycompany.myproject;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MapActivity extends Activity {
DrawView drawView;
SoundPool soundPool;
int soundId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
soundId = soundPool.load(this, R.raw.airplane, 1);
drawView = new DrawView(this);
setContentView(drawView);
}
public void playSound() {
soundPool.play(soundId, 1f, 1f, 1, 0, 1f);
}
#Override
public void onResume() {
super.onResume();
playSound();
drawView.resume();
}
#Override
public void onPause() {
super.onPause();
drawView.pause();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_map, menu);
return true;
}
public class DrawView extends SurfaceView implements Runnable {
Bitmap gameMap = null;
Thread gameLoop = null;
SurfaceHolder surface;
Rect rect;
volatile boolean running;
volatile boolean moved;
public DrawView(Context context) {
super(context);
moved = false;
surface = getHolder();
gameMap = BitmapFactory.decodeResource(getResources(),
R.drawable.map);
}
public void resume() {
running = true;
gameLoop = new Thread(this);
gameLoop.start();
}
public void pause() {
running = false;
while (true) {
try {
gameLoop.join();
} catch (InterruptedException e) {
}
}
}
#Override
public void run() {
Rect canvasSize = new Rect(0, 0, 240, 320);
Paint paint = new Paint();
paint.setAntiAlias(true);
while (running) {
if (!surface.getSurface().isValid()) {
continue;
}
if (!moved) {
int i;
for (i = 80; i <= 830; i++) {
Canvas canvas = surface.lockCanvas();
rect = new Rect(i, 250, (i + 240), 570);
canvas.drawBitmap(gameMap, rect, canvasSize, paint);
surface.unlockCanvasAndPost(canvas);
}
moved = true;
}
}
}
}
}
1-try to reduce picture size by exporting it save for web in photoshop and select png as extension type
2- You should create two class one for handling thread and other for rendering and game logic. The thread will manage it by locking canvas when system is rendering. Take a look at this link
http://android-er.blogspot.com/2010/05/android-surfaceview.html
I have a simple application, where I created a SurfaceView class, then in the main activity, I created an object of the class, then added this SurfaceView to a relative layout, and set the content view to the relative layout.
package com.my.game;
import com.google.ads.Ad;
import com.google.ads.AdListener;
import com.google.ads.AdRequest;
import com.google.ads.AdRequest.ErrorCode;
import com.google.ads.AdSize;
import com.google.ads.AdView;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.RelativeLayout;
public class ShootLetters extends Activity {
private Panel panel;
private int newWidth;
private int newHeight;
private AdView adView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bitmap bg = BitmapFactory.decodeResource(this.getResources(),
R.drawable.shooting_background);
;
int width = bg.getWidth();
int height = bg.getHeight();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
newWidth = metrics.widthPixels;
newHeight = metrics.heightPixels;
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBackGround = Bitmap.createBitmap(bg, 0, 0, width, height,
matrix, false);
Window win = getWindow();
Drawable d = new BitmapDrawable(resizedBackGround);
win.setBackgroundDrawable(d);
panel = new Panel(this, newWidth, newHeight);
panel.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
panel.setClickX(event.getX());
panel.setClickY(event.getY());
}
return true;
}
});
adView = new AdView(this, AdSize.BANNER, "admobidxxx222");
RelativeLayout rl = new RelativeLayout(this);
rl.addView(panel);
rl.addView(adView);
setContentView(rl);
// Initiate a generic request to load it with an ad
adView.loadAd(new AdRequest());
}
#Override
public void onDestroy() {
if (adView != null) {
adView.destroy();
}
panel.stopThread();
panel = null;
super.onDestroy();
}
}
This works fine for starting and stopping the application.
The problem is when another activity comes on top of this one, it behaves in an unstable way (no need for details at this stage as sometimes it crashes and sometimes it works with missing layout objects).
I read that I should implement onPause() & onResume() methods to handle this.
I only need pause the activity or resume it on resume. No persistent data required.
What should I put in which methods in this case?
Thanks
I guess its your Panel that is the surfaceview? I see you are doing something with its thread in onDestroy but that is not sufficient. You had a correct hunch about implementing onPause() and onResume() Now my idea(without being able to see all of your code) is that you are not dealing with the thread correctly. In your Panel class which extends surfaceview you should have something like these two functions:
public void pause(){
Loop = false;
while (true){
try{
panelThread.join();//end it
}catch (InterruptedException e){
e.printStackTrace();
}
break;
}
panelThread = null;
}
public void resume(){
Loop = true;
panelThread = new Thread(this);
panelThread.start();
}
Ignore the Loop part if you are not using a loop in your run() method.
Then, in your activity's onPause() and onResume() methods you call panel.pause() and panel.resume() respectively. This makes sure that the surface view thread is being handled in a safe way.