I need to create a android application which is for recording voice while showing the voice(sound) level visualization.
I already created an audio recording application but I can not add sound level visualization. How can I do it?
please someone help me a giving suggestion or a sample tutorials link or code.
Create a xml activity_recording.xml like this.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_alignParentBottom="true"
android:background="#231f20" >
<ali.visualiser.VisualizerView
android:id="#+id/visualizer"
android:layout_width="220dp"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_margin="5dp" />
<TextView
android:id="#+id/txtRecord"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="25dp"
android:gravity="center"
android:text="Start Recording"
android:textColor="#android:color/white"
android:textSize="30sp" />
</RelativeLayout>
Create a custom visualizerView as given below.
package ali.visualiser;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class VisualizerView extends View {
private static final int LINE_WIDTH = 1; // width of visualizer lines
private static final int LINE_SCALE = 75; // scales visualizer lines
private List<Float> amplitudes; // amplitudes for line lengths
private int width; // width of this View
private int height; // height of this View
private Paint linePaint; // specifies line drawing characteristics
// constructor
public VisualizerView(Context context, AttributeSet attrs) {
super(context, attrs); // call superclass constructor
linePaint = new Paint(); // create Paint for lines
linePaint.setColor(Color.GREEN); // set color to green
linePaint.setStrokeWidth(LINE_WIDTH); // set stroke width
}
// called when the dimensions of the View change
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w; // new width of this View
height = h; // new height of this View
amplitudes = new ArrayList<Float>(width / LINE_WIDTH);
}
// clear all amplitudes to prepare for a new visualization
public void clear() {
amplitudes.clear();
}
// add the given amplitude to the amplitudes ArrayList
public void addAmplitude(float amplitude) {
amplitudes.add(amplitude); // add newest to the amplitudes ArrayList
// if the power lines completely fill the VisualizerView
if (amplitudes.size() * LINE_WIDTH >= width) {
amplitudes.remove(0); // remove oldest power value
}
}
// draw the visualizer with scaled lines representing the amplitudes
#Override
public void onDraw(Canvas canvas) {
int middle = height / 2; // get the middle of the View
float curX = 0; // start curX at zero
// for each item in the amplitudes ArrayList
for (float power : amplitudes) {
float scaledHeight = power / LINE_SCALE; // scale the power
curX += LINE_WIDTH; // increase X by LINE_WIDTH
// draw a line representing this item in the amplitudes ArrayList
canvas.drawLine(curX, middle + scaledHeight / 2, curX, middle
- scaledHeight / 2, linePaint);
}
}
}
Create RecordingActivity class as given below.
package ali.visualiser;
import java.io.File;
import java.io.IOException;
import android.app.Activity;
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class RecordingActivity extends Activity {
public static final String DIRECTORY_NAME_TEMP = "AudioTemp";
public static final int REPEAT_INTERVAL = 40;
private TextView txtRecord;
VisualizerView visualizerView;
private MediaRecorder recorder = null;
File audioDirTemp;
private boolean isRecording = false;
private Handler handler; // Handler for updating the visualizer
// private boolean recording; // are we currently recording?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording);
visualizerView = (VisualizerView) findViewById(R.id.visualizer);
txtRecord = (TextView) findViewById(R.id.txtRecord);
txtRecord.setOnClickListener(recordClick);
audioDirTemp = new File(Environment.getExternalStorageDirectory(),
DIRECTORY_NAME_TEMP);
if (audioDirTemp.exists()) {
deleteFilesInDir(audioDirTemp);
} else {
audioDirTemp.mkdirs();
}
// create the Handler for visualizer update
handler = new Handler();
}
OnClickListener recordClick = new OnClickListener() {
#Override
public void onClick(View v) {
if (!isRecording) {
// isRecording = true;
txtRecord.setText("Stop Recording");
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audioDirTemp + "/audio_file"
+ ".mp3");
OnErrorListener errorListener = null;
recorder.setOnErrorListener(errorListener);
OnInfoListener infoListener = null;
recorder.setOnInfoListener(infoListener);
try {
recorder.prepare();
recorder.start();
isRecording = true; // we are currently recording
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
handler.post(updateVisualizer);
} else {
txtRecord.setText("Start Recording");
releaseRecorder();
}
}
};
private void releaseRecorder() {
if (recorder != null) {
isRecording = false; // stop recording
handler.removeCallbacks(updateVisualizer);
visualizerView.clear();
recorder.stop();
recorder.reset();
recorder.release();
recorder = null;
}
}
public static boolean deleteFilesInDir(File path) {
if( path.exists() ) {
File[] files = path.listFiles();
if (files == null) {
return true;
}
for(int i=0; i<files.length; i++) {
if(files[i].isDirectory()) {
}
else {
files[i].delete();
}
}
}
return true;
}
#Override
protected void onDestroy() {
super.onDestroy();
releaseRecorder();
}
// updates the visualizer every 50 milliseconds
Runnable updateVisualizer = new Runnable() {
#Override
public void run() {
if (isRecording) // if we are already recording
{
// get the current amplitude
int x = recorder.getMaxAmplitude();
visualizerView.addAmplitude(x); // update the VisualizeView
visualizerView.invalidate(); // refresh the VisualizerView
// update in 40 milliseconds
handler.postDelayed(this, REPEAT_INTERVAL);
}
}
};
}
Result
This is how it looks:
https://www.youtube.com/watch?v=BoFG6S02GH0
When it reaches the end, the animation continues as expected: erasing the beginning of the graph.
I like Ali's answer, but here's a simpler version that performs much better. The real speed comes from making the view class's onDraw method as fast as possible. Store the correct values in memory first by doing any computations not required for drawing outside the draw loop, and pass fully populated structures to draw routines to allow the hardware to optimize drawing many lines.
I launched my RecordingActivity and set it full screen, but you can create a layout resource or add the view anywhere.
Actvity:
public class RecordingActivity extends Activity {
private VisualizerView visualizerView;
private MediaRecorder recorder = new MediaRecorder();
private Handler handler = new Handler();
final Runnable updater = new Runnable() {
public void run() {
handler.postDelayed(this, 1);
int maxAmplitude = recorder.getMaxAmplitude();
if (maxAmplitude != 0) {
visualizerView.addAmplitude(maxAmplitude);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording);
visualizerView = (VisualizerView) findViewById(R.id.visualizer);
try {
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile("/dev/null");
recorder.prepare();
recorder.start();
} catch (IllegalStateException | IOException ignored) {
}
}
#Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(updater);
recorder.stop();
recorder.reset();
recorder.release();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
handler.post(updater);
}
}
View:
public class VisualizerView extends View {
private static final int MAX_AMPLITUDE = 32767;
private float[] amplitudes;
private float[] vectors;
private int insertIdx = 0;
private Paint pointPaint;
private Paint linePaint;
private int width;
private int height;
public VisualizerView(Context context, AttributeSet attrs) {
super(context, attrs);
linePaint = new Paint();
linePaint.setColor(Color.GREEN);
linePaint.setStrokeWidth(1);
pointPaint = new Paint();
pointPaint.setColor(Color.BLUE);
pointPaint.setStrokeWidth(1);
}
#Override
protected void onSizeChanged(int width, int h, int oldw, int oldh) {
this.width = width;
height = h;
amplitudes = new float[this.width * 2]; // xy for each point across the width
vectors = new float[this.width * 4]; // xxyy for each line across the width
}
/**
* modifies draw arrays. cycles back to zero when amplitude samples reach max screen size
*/
public void addAmplitude(int amplitude) {
invalidate();
float scaledHeight = ((float) amplitude / MAX_AMPLITUDE) * (height - 1);
int ampIdx = insertIdx * 2;
amplitudes[ampIdx++] = insertIdx; // x
amplitudes[ampIdx] = scaledHeight; // y
int vectorIdx = insertIdx * 4;
vectors[vectorIdx++] = insertIdx; // x0
vectors[vectorIdx++] = 0; // y0
vectors[vectorIdx++] = insertIdx; // x1
vectors[vectorIdx] = scaledHeight; // y1
// insert index must be shorter than screen width
insertIdx = ++insertIdx >= width ? 0 : insertIdx;
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawLines(vectors, linePaint);
canvas.drawPoints(amplitudes, pointPaint);
}
}
If you're using the MediaRecorder class and visualization based on peak amplitude is ok you can use the getMaxAmplitude() method to continuously poll for the "maximum absolute amplitude that was sampled since the last call".
Scale that amplitude down into an index that determines how many of your app's graphical volume bars to light up and you're set.
My approach to this is based on activedecay's and Ali's answers, and I added a display DPI scale, since dp is scaled by the screen density so, 1 pixel in 320 dpi is not 1 pixel in 420 dpi. I had that problem that the visualizer was not moving at the same rate in different screens.
I also haven't found out, why the canvas doesn't start drawing from the beginning of the view API 28 only. But is not looking bad in any way.
Info about dpi scaling:
Android Developers/Support different pixel densities
package com.example.mediarecorderdemo.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import static com.example.mediarecorderdemo.RecordingActivity.DEBUG;
public class VisualizerView extends View {
private static final int MAX_AMPLITUDE = 32767;
private ArrayList<Float> amplitudes;
private Paint linePaint;
private int width;
private int height;
private int density;
private float stroke;
public VisualizerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
density = this.getResources().getDisplayMetrics().densityDpi; //Get the display DPI
linePaint = new Paint();
linePaint.setColor(Color.GREEN);
linePaint.setAntiAlias(true); //Add AntiAlias for displaying strokes that are less than 1
}
#Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
width = w;
height = h;
amplitudes = new ArrayList<>(width * 2);
stroke =(width * ((float)density / 160)) / 1000; //Calculate actual pixel size for the view based on view width and dpi
linePaint.setStrokeWidth(stroke);
}
/**
* Add a new value of int to the visualizer array
* #param amplitude Int value
*/
public void addAmplitude(int amplitude){
invalidate();
float scaledHeight = ((float) amplitude / MAX_AMPLITUDE) * (height -1);
amplitudes.add(scaledHeight);
}
/**
* Clears Visualization
*/
public void clear(){
amplitudes.clear();
}
#Override
protected void onDraw(Canvas canvas) {
int middle = height / 2; // get the middle of the View
float curX = 0; // start curX at zero
// for each item in the amplitudes ArrayList
for (float power : amplitudes) {
// draw a line representing this item in the amplitudes ArrayList
canvas.drawLine(curX, middle + power / 2, curX, middle
- power / 2, linePaint);
curX += stroke; // increase X by line width
}
}
}
Related
I have had this issue for a few days already and I'm not sure how to fix it. I have a code to open the camera and display the output in full screen. but the displied image is not full. a part of it is not there. I added this function and it fixed the image but the display is not in full screen anymore.
here are the two images before and after adding the function
Before
After
the function I added
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mTextureView || null == mPreviewSize) {
return;
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
mTextureView.setTransform(matrix);
}
my Full code for the camera is here
package com.example.myapplication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import static android.content.ContentValues.TAG;
import static java.lang.Math.abs;
import static java.lang.Math.pow;
public class camera_ocr extends AppCompatActivity {
static int CAMERA_FRONT_BACK = 0;
private MeteringRectangle[] mAFRegions = AutoFocusHelperLib.getZeroWeightRegion();
private MeteringRectangle[] mAERegions = AutoFocusHelperLib.getZeroWeightRegion();
private Rect mCropRegion;
private Boolean cameraReady = false;
private static String versionName = "V7.7.5";
private int flag = 0;
private int[] position;
private View linearLayout;
private static final String TAG = "Camera Error";
private int[] focusMode = {};
private CameraCharacteristics cameraChar;
private Fragment fragment;
private Bitmap output;
private static int totalRotation;
ImageButton camera;
ImageButton flashLight ;
Button btnSnapPhoto;
private static Boolean displayResult = false;
Boolean cameraGranted = false;
public static String licenseKey ;
ValueAnimator animation = null ;
private int[] boxPosition = new int[4];
private float [] cardPosation = new float[8];
private static final int CAMERA_PERMISSION = 123;
private String mCameraId;
private Size mPreviewSize;
private CaptureRequest.Builder mCaptureRequestBuilder;
private HandlerThread mBackgroundHandlerThread;
private Handler mBackgroundHandler;
private static SparseIntArray ORIENTATIONS = new SparseIntArray();
private float screenWidth ;
private float screenHeight ;
CameraCharacteristics characteristics;
private CaptureRequest mPreviewRequest;
private CameraCaptureSession mCaptureSession;
private int mSensorOrientation;
private CaptureRequest.Builder mPreviewRequestBuilder;
private int mState = STATE_PREVIEW;
private ImageReader mImageReader;
private float cameraFocusDistance = 4.5f ;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 0);
ORIENTATIONS.append(Surface.ROTATION_90, 90);
ORIENTATIONS.append(Surface.ROTATION_180, 180);
ORIENTATIONS.append(Surface.ROTATION_270, 270);
}
private static CameraDevice mCameraDevice;
/**
* Camera state: Showing camera preview.
*/
private static final int STATE_PREVIEW = 0;
/**
* Camera state: Waiting for the focus to be locked.
*/
private static final int STATE_WAITING_LOCK = 1;
/**
* Camera state: Waiting for the exposure to be precapture state.
*/
private static final int STATE_WAITING_PRECAPTURE = 2;
/**
* Camera state: Waiting for the exposure state to be something other than precapture.
*/
private static final int STATE_WAITING_NON_PRECAPTURE = 3;
/**
* Camera state: Picture was taken.
*/
private static final int STATE_PICTURE_TAKEN = 4;
/**
* Max preview width that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_WIDTH = 1920;
/**
* Max preview height that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_HEIGHT = 1080;
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
CameraManager manager = null ;
private AutoFitTextureViewLib mTextureView;
#SuppressLint("MissingPermission")
private void openCamera(int width, int height, int camera_front_back) {
setUpCameraOutputs(width, height, camera_front_back);
configureTransform(width, height);
manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
// getCurrentPhoto();
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
static class CompareSizesByArea implements Comparator<Size> {
#Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
// Collect the supported resolutions that are smaller than the preview Surface
List<Size> notBigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
option.getHeight() == option.getWidth() * h / w) {
if (option.getWidth() >= textureViewWidth &&
option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
private void setUpCameraOutputs(int width, int height, int camera_front_back) {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
String cameraId = manager.getCameraIdList()[camera_front_back];
characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
return;
}
// For still image captures, we use the largest available size.
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
ImageFormat.JPEG, /*maxImages*/2);
// Find out if we need to swap dimension to get the preview size relative to sensor
// coordinate.
int displayRotation = getWindowManager().getDefaultDisplay().getRotation();
//noinspection ConstantConditions
mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
boolean swappedDimensions = false;
switch (displayRotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_180:
if (mSensorOrientation == 90 || mSensorOrientation == 270) {
swappedDimensions = true;
}
break;
case Surface.ROTATION_90:
case Surface.ROTATION_270:
if (mSensorOrientation == 0 || mSensorOrientation == 180) {
swappedDimensions = true;
}
break;
default:
Log.e(TAG, "Display rotation is invalid: " + displayRotation);
}
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getSize(displaySize);
int rotatedPreviewWidth = width;
int rotatedPreviewHeight = height;
int maxPreviewWidth = displaySize.x;
int maxPreviewHeight = displaySize.y;
if (swappedDimensions) {
rotatedPreviewWidth = height;
rotatedPreviewHeight = width;
maxPreviewWidth = displaySize.y;
maxPreviewHeight = displaySize.x;
}
if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
maxPreviewWidth = MAX_PREVIEW_WIDTH;
}
if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
maxPreviewHeight = MAX_PREVIEW_HEIGHT;
}
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, largest);
// We fit the aspect ratio of TextureView to the size of preview we picked.
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mTextureView.setAspectRatio(
mPreviewSize.getWidth(), mPreviewSize.getHeight());
} else {
mTextureView.setAspectRatio(
mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
mCropRegion = AutoFocusHelperLib.cropRegionForZoom(characteristics,
CameraConstantsLib.ZOOM_REGION_DEFAULT);
mCameraId = cameraId;
return;
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
}
}
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mTextureView || null == mPreviewSize) {
return;
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(#NonNull CameraDevice cameraDevice) {
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
#Override
public void onDisconnected(#NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}
#Override
public void onError(#NonNull CameraDevice cameraDevice, int error) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
finish();
}
};
private void createCameraPreviewSession() {
try {
final int[] x = new int[1];
final int[] y = new int[1];
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
// GET SCREEN SIZE
MeteringRectangle screenSize[] = mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_REGIONS);
// When the session is ready, we start displaying the preview.
mCaptureSession = cameraCaptureSession;
long loopTime = System.currentTimeMillis();
long time = System.currentTimeMillis();
// GET SCREEN WIDTH
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
screenWidth = screenSize[0].getWidth();
screenHeight = screenSize[0].getHeight();
// CREATE NEW FOCUS POINT
// testing
for(int i = 0; i < 1 ; i++) {
setManualFocusAt(x[0], y[0]);
// GET CURRENT FOCUS POINT
MeteringRectangle currentFocusArea[] = mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_REGIONS);
time = System.currentTimeMillis();
// WAIT FOR FOCUS TO READY
while((System.currentTimeMillis() - time) < 800){
Log.d("focus time:" , Float.toString(System.currentTimeMillis() - time));
Log.e("focus time:" , Float.toString(System.currentTimeMillis() - time));
Log.wtf("focus time:" , Float.toString(System.currentTimeMillis() - time));
};
}
long x = System.currentTimeMillis() - loopTime;
Log.d("focus time:" , Float.toString(x));
cameraReady = true;
}
#Override
public void onConfigureFailed(
#NonNull CameraCaptureSession cameraCaptureSession) {
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Initiate a still image capture.
*/
void setManualFocusAt(int x, int y) {
int mDisplayOrientation = getWindowManager().getDefaultDisplay().getRotation();
float points[] = new float[2];
points[0] = (float) x / mTextureView.getWidth();
points[1] = (float) y / mTextureView.getHeight();
Matrix rotationMatrix = new Matrix();
rotationMatrix.setRotate(mDisplayOrientation, 0.5f, 0.5f);
rotationMatrix.mapPoints(points);
if (mPreviewRequestBuilder != null) {
// mIsManualFocusing = true;
updateManualFocus(points[0], points[1]);
if (mCaptureSession != null) {
try {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
mCaptureSession.capture(mPreviewRequestBuilder.build(), null, mBackgroundHandler);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
null, mBackgroundHandler);
} catch (CameraAccessException | IllegalStateException e) {
Log.e(TAG, "Failed to set manual focus.", e);
}
}
// resumeAutoFocusAfterManualFocus();
}
}
void updateManualFocus(float x, float y) {
#SuppressWarnings("ConstantConditions")
int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
mAFRegions = AutoFocusHelperLib.afRegionsForNormalizedCoord(x, y, mCropRegion, sensorOrientation);
mAERegions = AutoFocusHelperLib.aeRegionsForNormalizedCoord(x, y, mCropRegion, sensorOrientation);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, mAFRegions);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, mAERegions);
// mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
// fixe distance focuse // testing
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_OFF);
mPreviewRequestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, cameraFocusDistance);
}
/**
* Lock the focus as the first step for a still image capture.
*/
private void closeCamera() {
try {
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
}
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height,CAMERA_FRONT_BACK);
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_ocr);
mTextureView = findViewById(R.id.textureView);
}
#Override
protected void onResume(){
super.onResume();
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight(),CAMERA_FRONT_BACK);
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
}
It looks like you got height and width backwards on this line:
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
Try the following and you should get the correct result:
RectF bufferRect = new RectF(0, 0, mPreviewSize.getWidth(), mPreviewSize.getHeight());
I am attempting to get bitmap from my surface view. I've already seen many answers on this forum and on mostly all forums. I am using a custom class of ViewGroup in which i'm using a surface view to preview the camera image and the Graphic Overlay.
This is my CameraSourcePreview.class
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.SurfaceTexture;
import android.support.v7.util.AsyncListUtil;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.common.images.Size;
import com.google.android.gms.vision.CameraSource;
import java.io.IOException;
public class CameraSourcePreview extends ViewGroup {
private static final String TAG = "CameraSourcePreview";
private Context mContext;
private SurfaceView mSurfaceView;
private boolean mStartRequested;
private boolean mSurfaceAvailable;
private CameraSource mCameraSource;
private GraphicOverlay mOverlay;
public CameraSourcePreview(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mStartRequested = false;
mSurfaceAvailable = false;
mSurfaceView = new SurfaceView(context);
mSurfaceView.getHolder().addCallback(new SurfaceCallback());
addView(mSurfaceView);
}
public void start(CameraSource cameraSource) throws IOException {
if (cameraSource == null) {
stop();
}
mCameraSource = cameraSource;
if (mCameraSource != null) {
mStartRequested = true;
startIfReady();
}
}
public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException {
mOverlay = overlay;
start(cameraSource);
}
public void stop() {
if (mCameraSource != null) {
mCameraSource.stop();
}
}
public void release() {
if (mCameraSource != null) {
mCameraSource.release();
mCameraSource = null;
}
}
private void startIfReady() throws IOException {
if (mStartRequested && mSurfaceAvailable) {
mCameraSource.start(mSurfaceView.getHolder());
if (mOverlay != null) {
Size size = mCameraSource.getPreviewSize();
int min = Math.min(size.getWidth(), size.getHeight());
int max = Math.max(size.getWidth(), size.getHeight());
if (isPortraitMode()) {
// Swap width and height sizes when in portrait, since it will be rotated by
// 90 degrees
mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing());
} else {
mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing());
}
mOverlay.clear();
}
mStartRequested = false;
}
}
private class SurfaceCallback implements SurfaceHolder.Callback {
#Override
public void surfaceCreated(SurfaceHolder surface) {
mSurfaceAvailable = true;
try {
startIfReady();
} catch (IOException e) {
Log.e(TAG, "Could not start camera source.", e);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surface) {
mSurfaceAvailable = false;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int width = 320;
int height = 240;
if (mCameraSource != null) {
Size size = mCameraSource.getPreviewSize();
if (size != null) {
width = size.getWidth();
height = size.getHeight();
}
}
// Swap width and height sizes when in portrait, since it will be rotated 90 degrees
if (isPortraitMode()) {
int tmp = width;
width = height;
height = tmp;
}
final int layoutWidth = right - left;
final int layoutHeight = bottom - top;
// Computes height and width for potentially doing fit width.
int childWidth = layoutWidth;
int childHeight = layoutHeight;
//(int)(((float) layoutWidth / (float) width) * height);
/*
// If height is too tall using fit width, does fit height instead.
if (childHeight > layoutHeight) {
childHeight = layoutHeight;
childWidth = (int)(((float) layoutHeight / (float) height) * width);
}*/
for (int i = 0; i < getChildCount(); ++i) {
getChildAt(i).layout(0, 0, childWidth, childHeight);
}
try {
startIfReady();
} catch (IOException e) {
Log.e(TAG, "Could not start camera source.", e);
}
}
private boolean isPortraitMode() {
int orientation = mContext.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return false;
}
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
return true;
}
Log.d(TAG, "isPortraitMode returning false by default");
return false;
}
}
Now when I capture the image from CameraSource it snapshots the image but not the graphic overlay which i'm inflating on the surface view. Neither does the surface view lets me take a bitmap from it. If i try to get screenshot of the root view then it captures only the Graphic Overlay not the surface view image.
My app is working on the logic of a face filter github project(link will be provided upon request)
I'm basically stuck because i want to capture both.
you have to take a particular view screenshot like convert any view into bitmap this way
How to convert Views to bitmaps?
I made the snake and ladder game in android studio. I want to change the height and width of the buttons, and I want to add one more button for exit. Right now I am using only one .png file for all buttons. So, How to change it? When i add the fourth button my application crash and its shows the error.
Check this image
Error
FATAL EXCEPTION: main
Process: com.sixtinbyte.snakeandladder, PID: 25115
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.sixtinbyte.snakeandladder.components.GameButton.onDraw(android.graphics.Canvas)' on a null object reference
at com.sixtinbyte.snakeandladder.HomeView.onDraw(HomeView.java:151)
HomeView. java (This are the java file where i want to change because this is the homescreen of the game)
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.view.View;
import com.sixtinbyte.snakeandladder.components.GameButton;
import com.sixtinbyte.snakeandladder.constant.Sound;
public class HomeView extends View {
public Context ctx;
private Resources res;
private String refText ="Snakes and Ladders";
private GameButton gButton, sButton, hButton,eButton;
private Bitmap logo, play, settings, help, exit;
private int hWidth, hHeight;
private Point touchCoordinate = new Point();
public HomeView(Context ctx) {
super(ctx);
this.ctx = ctx;
res = getResources();
logo = BitmapFactory.decodeResource(res, R.drawable.snakes_n_ladders);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO: Implement this method
super.onSizeChanged(w, h, oldw, oldh);
hWidth = w;
hHeight = h;
preparePlayButton();
prepareSettingsButton();
prepareHelpButton();
// prepareExitButton();
}
private void preparePlayButton() {
String text =" ";
int textSize = 40;
Point po = getBmpMeasureFrom(text, refText, textSize);
play = getBitmap(R.drawable.play, po.x, po.y);
gButton = new GameButton(play, text, textSize);
gButton.setLocation((hWidth - po.x) / 2, (int)(hHeight * 0.6f));
gButton.setTextColor(Color.CYAN);
}
private void prepareSettingsButton() {
String text =" ";
int textSize = 40;
Point po = getBmpMeasureFrom(text, refText, textSize);
settings = getBitmap(R.drawable.play, po.x, po.y);
sButton = new GameButton(settings, text, textSize);
sButton.setLocation((hWidth - po.x) / 2, (int)(hHeight * 0.7f));
sButton.setTextColor(Color.CYAN);
}
//
//
private void prepareHelpButton() {
String text =" ";
int textSize = 40;
Point po = getBmpMeasureFrom(text, refText, textSize);
help = getBitmap(R.drawable.play, po.x, po.y);
hButton = new GameButton(help, text, textSize);
hButton.setLocation((hWidth - po.x) / 2, (int)(hHeight * 0.8f));
hButton.setTextColor(Color.CYAN);
}
// private void prepareExitButton() {
// String text =" ";
// int textSize = 40;
// Point po = getBmpMeasureFrom(text, refText, textSize);
// help = getBitmap(R.drawable.play, po.x, po.y);
// hButton = new GameButton(help, text, textSize);
// hButton.setLocation((hWidth - po.x) / 2, (int)(hHeight * 0.8f));
// hButton.setTextColor(Color.CYAN);
//
// }
private Point getBmpMeasureFrom(String text, String refText, int textSize) {
Point p = new Point();
Paint pa = new Paint();
pa.setTextSize(textSize);
p.y = (int)(pa.getTextSize() * 2.2f);
String tt = refText.length() > text.length() ?refText: text;
p.x = (int) (pa.measureText(tt) * 1.2f);
return p;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO: Implement this method
int evt = event.getAction();
touchCoordinate.x = (int)event.getX();
touchCoordinate.y = (int)event.getY();
switch (evt) {
case MotionEvent.ACTION_DOWN:
MainActivity.playSound(Sound.BUTTON_CLICK_1, false);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
// MainActivity.stopSound();
if (gButton.contains(touchCoordinate)) {
Intent intent = new Intent(ctx, GameActivity.class);
ctx. startActivity(intent);
}
if (sButton.contains(touchCoordinate)) {
Intent intent = new Intent(ctx, Playing.class);
ctx.startActivity(intent);
}
if (hButton.contains(touchCoordinate)) {
Intent intent = new Intent(ctx, Aboutus.class);
ctx. startActivity(intent);
}
// if(eButton.contains(touchCoordinate)){
// Intent intent = new Intent(ctx, Exit.class);
// ctx.startActivity(intent);
// }
break;
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
// TODO: Implement this method
super.onDraw(canvas);
canvas.drawColor(Color.CYAN);
canvas.drawBitmap(logo, (hWidth - logo.getWidth()) / 2, (int)((hHeight - logo.getHeight()) * 0.1), null);
gButton.onDraw(canvas);
sButton.onDraw(canvas);
hButton.onDraw(canvas);
// eButton.onDraw(canvas);
// Paint p= new Paint();
// p.setColor(Color.RED);
// canvas.drawRect(100, 200, 400, 500, p);
invalidate();
}
private Bitmap getBitmap(int drawableRes, int width, int height) {
Drawable d = res.getDrawable(drawableRes);
Canvas c = new Canvas();
Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
c.setBitmap(b);
d.setBounds(0, 0,width, height);
d.draw(c);
return b;
}
}
The prepareExitButton method is assigning hButton, I think it should be assigning eButton. That's why you're getting a NullPointerException there. Also, you're setting the y-coordinate to (int)(hHeight * 0.8f)) which means it will overlap with the help-button.
You may need to alter the GameButton class to get the ability to adjust the button's size. In particular, you'll want to change onDraw to draw the button in the specified size.
I was trying to draw some balloons on screeen using canvas in SurfaceView class, I was successfully able to draw Multiple balloons on canvas and animate them by swapping different images. The problem is When I try touching on Oneballoon I need to remove it from screen .Here I get this exception and I am stuck
Code:
MainActivity:
package com.pradhul.game.touchball;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GameView(this));
/*TODO Hide the bottom navigation bar */
}
}
GameView.java
package com.pradhul.game.touchball;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GameView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener {
private static final int NUM_OF_BALLOONS = 5; //TODO for more than one balloons animations dont work(?)
/*use SurfaceView because we want complete control over the screen.
* unlike extending View class the oDraw() Method will not be called automatically
* from the method onSurfaceCreated() we have to call it Manually and pass a canvas object into it
* */
private final SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Balloon> balloons = new ArrayList<>();
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(this);
createBalloons(NUM_OF_BALLOONS);
this.setOnTouchListener(this);
}
private void createBalloons(int count) {
for(int i=0 ; i< count ;i++){
balloons.add(createBalloon());
}
}
#Override
protected void onDraw(Canvas canvas) {
if(canvas != null) {
canvas.drawColor(Color.WHITE);
for(Balloon balloon : balloons){
try {
gameLoopThread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
balloon.onDraw(canvas);
}
}
}
#SuppressLint("WrongCall")
#Override
public void surfaceCreated(SurfaceHolder holder) {
/*this is called when the view is created*/
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
/*pausing game Thread*/
gameLoopThread.setRunning(false);
while (true){
try {
gameLoopThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Balloon createBalloon(){
return new Balloon(this);
}
#Override
public synchronized boolean onTouch(View v, MotionEvent event) {
Log.d("OnTouch real -", "x: " + event.getX() + ", Y: " + event.getY());
/* for (int i = balloons.size()-1; i >= 0; i--) {
Balloon balloon = balloons.get(i);
Log.d("OnTouch collision -", !balloon.isCollision(event.getX(), event.getY())+"");
if (!balloon.isCollision(event.getX(), event.getY())) {
balloons.remove(0);
break;
}
}*/
Iterator<Balloon> balloonIterator = balloons.iterator();
while(balloonIterator.hasNext()){
Balloon balloon = balloonIterator.next();
balloons.remove(0);
}
return true;
}
}
GameLoopThread.java
package com.pradhul.game.touchball;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
private GameView view;
private boolean running = false;
public GameLoopThread(GameView view){
this.view = view;
}
public void setRunning(boolean run){
running = run;
}
#SuppressLint("WrongCall")
public void run(){
while (running){
Canvas canvas = null;
try{
canvas = view.getHolder().lockCanvas();
synchronized (view.getHolder()){
view.onDraw(canvas);
}
}finally{
if(canvas != null) {
view.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
}
Balloon.java
package com.pradhul.game.touchball;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;
import java.util.Random;
public class Balloon {
private static final int BALLOON_SPEED = 10;
private int y = 0;
private int x = 0;
private int speed = 1;
private GameView gameView;
private Bitmap balloon;
public Bitmap[] normalBalloons;
private int balloonIndex = 0;
private int normalImages[] = {R.drawable.normal_01,R.drawable.normal_02,R.drawable.normal_03,
R.drawable.normal_04,R.drawable.normal_05,R.drawable.normal_06,R.drawable.normal_07,
R.drawable.normal_08,
};
private int crackingImages[] = {R.drawable.crack_01,R.drawable.crack_02,R.drawable.crack_03,
R.drawable.crack_04, R.drawable.crack_05,R.drawable.crack_04,R.drawable.crack_03,
R.drawable.crack_02
};
private boolean reverseSwap = false;
public Balloon(GameView gameView){
this.gameView = gameView;
normalBalloons = new Bitmap[8];
setUpImages();
}
public void onDraw(Canvas canvas){
/*draws the balloon in canvas */
animateBalloon();
update(canvas.getWidth());
canvas.drawBitmap(balloon, x, y, null);
}
public boolean isCollision(float x2, float y2) {
return x2 > x && x2 < x + balloon.getWidth() && y2 > y && y2 < y + balloon.getHeight();
}
private int getRandomX(int maxVal) {
Random rand = new Random();
return rand.nextInt(maxVal);
}
private void animateBalloon() {
/*Animates the balloon by swapping resource image at each call*/
this.balloon = getBalloons();
Log.d("Balloon",balloonIndex % normalBalloons.length + "");
}
private void update(int canvasWidth) {
/*updates the y position for moving the balloon*/
if (y <= 0){
/*so that the balloon starts from bottom
* gameView will return a height only after the View is ready
* getting 0 in constructor of this class*/
y = gameView.getHeight();
/*x is assigned a random between the width od the canvas
* so that the balloons will appear random positions from below*/
x = getRandomX(canvasWidth - balloon.getWidth());
}
if (y > gameView.getHeight() - balloon.getHeight() - speed) {
speed = -BALLOON_SPEED;
}
y = y + speed;
Log.d("Balloon","Positions:"+x+","+y);
}
private Bitmap getBalloons() {
if(balloonIndex == normalBalloons.length-1) {
reverseSwap = true;
}
if(balloonIndex == 0){
reverseSwap = false;
}
balloonIndex = reverseSwap?balloonIndex-1:balloonIndex+1;
return normalBalloons[balloonIndex];
}
private void setUpImages() {
/*setting up resources array*/
for(int count =0; count < normalImages.length; count++){
Bitmap balloon = BitmapFactory.decodeResource(gameView.getResources(), normalImages[count]);
normalBalloons[count] = balloon;
}
}
}
I am confused that why it causes an error like this , Can anybody can take look at it and suggest me a solution, is this the right way to remove ?
please share any suggestion
Thanks
It's the wrong way to remove.
If you iterate over a Collection / List, and want to remove the current element, you must use Iterator.remove() method.
In your case, simply call balloonIterator.remove() instead of balloons.remove(0)
Even simpler, since you want to remove all elements from the list - you should simply call balloons.clear() and remove the loop completely.
This exception is due to concurrent modification of list balloons.
As soon as you touch surface view onDraw() gets invoked with onTouch(), in which you are working on list balloons.
I think you should add touch listener to balloon instead of GameView.
okay ,
I need to wrap all my code inside a synchronized holder inorder this to work
(don't know much about it)
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("OnTouch","x:"+event.getX()+"Y:"+event.getY());
synchronized (getHolder()){
for (int i=0 ;i<balloons.size();i++){
balloons.remove(0);
break;
}
}
return true;
}
I've started the development of a guitar application, and am stuck on a graphical aspect. My screen contains a background image, which you can move with 2 fingers, by sliding up or down. My background image, a bitmap, moves at low fps on an android device. I'm looking to see how to make it run smoother.
Code details:
I have 3 classes: one main activity, a second for graphics, and a third as a splash screen (I have't included the last one as it has nothing to do with my problems).
MainActivity.java
package com.example.androidapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class MainActivity extends Activity implements OnTouchListener{
FretBoard ourView;
float y1, y2, dy;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
ourView = new FretBoard(this);
ourView.setOnTouchListener(this);
setContentView(ourView);
}
//Method to be used in future
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
ourView.pause();
}
//Method to be used in future
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
ourView.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event){
//For debugging
if(event!= null) ourView.fingerCount = event.getPointerCount();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//Save first y position when finger touches screen
y1 = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if(event != null && event.getPointerCount() == 2){
//Save second y position when finger moves
y2 = event.getY();
//Total distance moved
dy = y2 - y1;
if((ourView.bgY + dy)<0) ourView.bgY += dy/2;
//For Debugging
System.out.println("Y1 : " + y1 + " Y2 : " + y2 + " DY : " + dy);
//Redraw the Screen
ourView.invalidate();
//Reset y1
y1 = y2;
}
break;
}
return true;
}
}
FretBoard.java
package com.example.androidapplication;
import java.io.InputStream;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class FretBoard extends SurfaceView implements Runnable{
//Background image variables
Bitmap background;
InputStream is = getResources().openRawResource(R.drawable.fret_board);
//Debugger text variables
Paint mPaint = new Paint();
String string = "Fingers: ";
int fingerCount = 0;
//Position Variables
int width, height;
float bgY = 0, bgX = 0;
//Holder variable
SurfaceHolder ourHolder;
//To be used in future
Thread ourThread = null;
Boolean isRunning = false;
public FretBoard(Context context) {
super(context);
// TODO Auto-generated constructor stub
ourHolder = getHolder();
ourThread = new Thread(this);
ourThread.start();
}
//To be used in the future
public void pause(){
isRunning = false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
ourThread = null;
}
//To be used in the future
public void resume(){
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
//Creates a InputStream to set width, then converts to bitmap to be
//drawn as background on the canvas.
public Bitmap bmScale(InputStream is){
BitmapFactory.Options o = new BitmapFactory.Options();
o.inSampleSize = 2;
Bitmap bit = BitmapFactory.decodeStream(is, null, o);
background = Bitmap.createScaledBitmap(bit, width, 2300, true);
bit.recycle();
return background;
}
//This is the draw method where I need help
#Override
public void run() {
// TODO Auto-generated method stub
while(isRunning){
if(!ourHolder.getSurface().isValid()) continue;
//Lock canvas to draw
Canvas canvas = ourHolder.lockCanvas();
//Do Things Here:
width = getWidth();
height = getHeight();
//Collision detection for the background
if(bgY > 0 ) bgY = 0;
//Draws the background
bmScale(is);
canvas.drawBitmap(background, bgX, bgY, null);
//For debugging, displays the number of fingers on the screen
canvas.drawText(string + fingerCount, 50, 50, mPaint);
//Unlock canvas to show
ourHolder.unlockCanvasAndPost(canvas);
}
}
}
It seems you are loading/reading and scaling the bitmap on each render pass. This is enormously inefficient.
(see call to bmScale(is); in FretBoard.java).
Instead, load (and scale your) bitmap only once (when the view gets laid out) and then draw the already loaded and scaled bitmap.
Also, you seem to use a SurfaceView for your FretBoard. Why? In your code sample, you are not using OpenGL or Video/Camera textures at all.
Scaling in your loop (as mentioned by the other answer here) is definitely your major problem. Once you fix that, things should be much smoother. You will probably find some minor stutters still though because you are creating other objects in your loop, namely strings. Given that you will never have more than 5 fingers on the fretboard at once (OK, maybe theoretically 10 if you're tapping) there are only 5 or 10 possible values for string (which by the way is a confusing name, especially in this context). It'd be better to create these up front as well outside of the loop and just pick the correct reference.