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?
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());
hello I am working on a android launcher and I want it to where there is a circle shape behind every app icon I am referencing the shape via the java code NOT XML! So I used this code as my shape:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<stroke android:color="#ccc" android:width="2dp" />
<solid android:color="#ffffff"/>
<size android:width="100dp" android:height="100dp"/>
</shape>
</item>
</selector>
and here's my class that makes the app irons for the launcher:
package appname.widget;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.widget.ImageView;
import appname.R;
import appname.util.AppManager;
import appname.util.DragAction;
import appname.util.GoodDragShadowBuilder;
import appname.util.LauncherSettings;
import appname.util.Tool;
import static android.R.attr.button;
import static android.R.attr.width;
import static com.bennyv5.materialpreffragment.R.attr.height;
/**
* Created by BennyKok on 10/23/2016.
*/
public class AppItemView extends View implements Drawable.Callback{
public Drawable getIcon() {
return icon;
}
public void setIcon(Drawable icon, boolean isStatic) {
this.icon = icon;
this.icon.setCallback(this);
}
#Override
public void refreshDrawableState() {
invalidateDrawable(icon);
super.refreshDrawableState();
}
#Override
public void invalidateDrawable(Drawable drawable) {
invalidate();
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public float getIconSize() {
return iconSize;
}
public void setIconSize(float iconSize) {
this.iconSize = iconSize;
}
private float iconSize;
private Drawable icon;
private String label;
public boolean isShortcut;
public Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Rect mTextBound = new Rect();
private boolean noLabel,vibrateWhenLongPress;
private float labelHeight;
public AppItemView(Context context) {
super(context);
init();
}
public AppItemView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
setWillNotDraw(false);
labelHeight = Tool.convertDpToPixel(14,getContext());
textPaint.setTextSize(sp2px(getContext(),14));
textPaint.setColor(Color.DKGRAY);
}
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (label != null && !noLabel){
textPaint.getTextBounds(label,0,label.length(),mTextBound);
}
//The height should be the same as they have the same text size.
float mHeight = iconSize + (noLabel? 0 : labelHeight);
float heightPadding = (getHeight() - mHeight)/2f;
if (label != null && !noLabel) {
float x = (getWidth()-mTextBound.width())/2f;
if (x < 0)
x = 0;
canvas.drawText(label,x, getHeight() - heightPadding, textPaint);
}
if (icon != null){
canvas.save();
canvas.translate((getWidth()-iconSize)/2,heightPadding);
icon.setBounds(0,0,(int)iconSize,(int)iconSize);
icon.draw(canvas);
canvas.restore();
}
}
public static class Builder{
AppItemView view;
public Builder(Context context){
view = new AppItemView(context);
float iconSize = Tool.convertDpToPixel(LauncherSettings.getInstance(view.getContext()).generalSettings.iconSize, view.getContext());
view.setIconSize(iconSize);
}
public AppItemView getView(){
return view;
}
public Builder setAppItem(AppManager.App app){
view.setIcon(app.icon,true);
view.setLabel(app.appName);
view.setBackgroundResource(R.drawable.iconbackbg);
view.setScaleX(0.75f); // <- resized by scaling
view.setScaleY(0.75f);
return this;
}
public Builder withOnClickLaunchApp(final AppManager.App app){
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Tool.createScaleInScaleOutAnim(view, new Runnable() {
#Override
public void run() {
Tool.startApp(view.getContext(), app);
}
});
}
});
return this;
}
public Builder withOnLongClickDrag(final AppManager.App app,final DragAction.Action action,#Nullable final OnLongClickListener eventAction){
withOnLongClickDrag(Desktop.Item.newAppItem(app),action,eventAction);
view.setScaleX(0.75f); // <- resized by scaling
view.setScaleY(0.75f);
return this;
}
public Builder withOnLongClickDrag(final Desktop.Item item, final DragAction.Action action, #Nullable final OnLongClickListener eventAction){
view.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (view.vibrateWhenLongPress)
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Intent i = new Intent();
i.putExtra("mDragData", item);
ClipData data = ClipData.newIntent("mDragIntent", i);
v.startDrag(data, new GoodDragShadowBuilder(v), new DragAction(action), 0);
if (eventAction != null)
eventAction.onLongClick(v);
return true;
}
});
return this;
}
public Builder withOnTouchGetPosition(){
view.setOnTouchListener(Tool.getItemOnTouchListener());
return this;
}
public Builder setTextColor(#ColorInt int color){
view.textPaint.setColor(color);
return this;
}
public Builder setNoLabel(){
view.noLabel = true;
return this;
}
public Builder vibrateWhenLongPress(){
view.vibrateWhenLongPress = true;
return this;
}
public Builder setShortcutItem(final Intent intent){
view.isShortcut = true;
view.setScaleX(0.75f); // <- resized by scaling
view.setScaleY(0.75f);
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Tool.createScaleInScaleOutAnim(view, new Runnable() {
#Override
public void run() {
view.getContext().startActivity(intent);
}
});
}
});
view.setIcon(Tool.getIconFromID(view.getContext(),intent.getStringExtra("shortCutIconID")),true);
view.setLabel(intent.getStringExtra("shortCutName"));
return this;
}
}
}
EDIT I Tried suggestion but the circle does not show up at all heres the Updated On draw method:
#Override
protected void onDraw(Canvas canvas) {
//Load your oval shape
Drawable background = getResources().getDrawable(R.drawable.iconbackbg, getContext().getTheme());
//create a LayerDrawable that combines the oval shape with the icon
Drawable[] layers = {background, icon};
LayerDrawable layerDrawable = new LayerDrawable(layers);
//set the gravity of both drawables to center
layerDrawable.setLayerGravity(0, Gravity.CENTER);
layerDrawable.setLayerGravity(1, Gravity.CENTER);
super.onDraw(canvas);
if (label != null && !noLabel){
textPaint.getTextBounds(label,0,label.length(),mTextBound);
}
//The height should be the same as they have the same text size.
float mHeight = iconSize + (noLabel? 0 : labelHeight);
float heightPadding = (getHeight() - mHeight)/2f;
if (label != null && !noLabel) {
float x = (getWidth()-mTextBound.width())/2f;
if (x < 0)
x = 0;
canvas.drawText(label,x, getHeight() - heightPadding, textPaint);
}
if (layerDrawable != null){
canvas.save();
canvas.translate((getWidth()-iconSize)/2,heightPadding);
layerDrawable.setLayerWidth(1, (int) iconSize);
layerDrawable.setLayerHeight(1, (int) iconSize);
layerDrawable.draw(canvas);
canvas.restore();
}
if (icon != null){
canvas.save();
canvas.translate((getWidth()-iconSize)/2,heightPadding);
icon.setBounds(0,0,(int)iconSize,(int)iconSize);
icon.draw(canvas);
canvas.restore();
}
}
the circle shape is showing up its just resized very funky heres a screenshot of what it looks link on a nexus 7 tablet:
any help would be amazing!
Thanks in advance :)
I guess R.drawable.iconbackbg is the reference for the ShapeDrawable?
If so, setting it as the background of your View makes it draw with the View's bounds. So if your View's width and height are not equal, it will draw an oval as your background instead of a circle (hence the name of the shape "oval").
If you want to draw it as a circle, you need to override the drawables bounds in some way. That's not possible when using platform drawables and setting them as the background of a view, as the view will override the drawables bounds during draw().
A possible solution would be to wrap the app's icon together with your oval ShapeDrawable in a LayerDrawable (A LayerDrawable is basically a list of drawables that get drawn on top of each other):
private void init(){
//... do your initilizations
//Load your oval shape
background = getResources().getDrawable(R.drawable.iconbackbg, getContext().getTheme());
}
public void setIcon(Drawable icon){
//... set the icon, be sure to remove callbacks to the previously set icon!
//create a LayerDrawable that combines the oval shape with the icon
LayerDrawable layerDrawable = new LayerDrawable(background, icon);
//set the gravity of both drawables to center
layerDrawable.setLayerGravity(0, Gravity.CENTER);
layerDrawable.setLayerGravity(1, Gravity.CENTER);
}
and then in your onDraw() do:
if (layerDrawable != null){
canvas.save();
canvas.translate((getWidth()-iconSize)/2,heightPadding);
layerDrawable.setLayerWidth(1, (int) iconSize);
layerDrawable.setLayerHeight(1, (int) iconSize);
layerDrawable.draw(canvas);
canvas.restore();
}
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 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
}
}
}
I have two images moving across the screen, one is a ball and one is a man.
What I want to happen is when the user touches the image of the man, the ball drops.
My problem is I cannot seem to add an onclick/ontouch event and get it to work.
I'm not implementing it properly, can anyone help please?
I have included the 3 classes below. Greg is the man and the ball is named ball :)
TestAnimationActivity.java
package com.test.firstAnimation;
import android.app.Activity;
import android.os.Bundle;
public class TestAnimationActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyAnimationView(this));
}
}
Sprite.java
package com.test.firstAnimation;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class Sprite extends View implements OnClickListener{
private static int gregCoordX = 410; // the x coordinate at the canvas for greg
private Bitmap img; // the image of Greg
private Bitmap img2; // the image of pointer
private static int gregCoordY = 125; // the y coordinate at the canvas for greg
private static int pointCoordX = 10;
private static int pointCoordY = 10;
private static int count = 1;
private static int ballSpeed = 25;
private static boolean goingRight = false;
private static boolean goingLeft = true;
private static boolean pointerGoingRight = false;
private static boolean pointerGoingLeft = true;
public Sprite(Context context, int drawable) {
super(context);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
img = BitmapFactory.decodeResource(context.getResources(), drawable);
img2 = (BitmapFactory.decodeResource(context.getResources(), drawable));
count++;
}
public static int getCount() {
return count;
}
void setX(int newValue) {
gregCoordX = newValue;
}
public static int getX() {
return gregCoordX;
}
public static int getY() {
return gregCoordY;
}
public static int getBX() {
return pointCoordX;
}
public static int getBY() {
return pointCoordY;
}
public Bitmap getBitmap() {
return img;
}
public Bitmap getImg2() {
return img2;
}
public static void dropBall()
{
pointCoordY++;
}
public static void moveBall(int x) {
// check the borders
//if more than ten go right
//if ten go left
//if more than 250 go left
if (x <= 10 && pointerGoingLeft)
{
pointCoordX = pointCoordX + ballSpeed;
pointerGoingRight = true;
pointerGoingLeft = false;
}
else if (x >= 410 && pointerGoingRight)
{
pointCoordX = pointCoordX - ballSpeed;
pointerGoingLeft = true;
pointerGoingRight = false;
}
else if (pointerGoingRight)
pointCoordX = pointCoordX + ballSpeed;
else
pointCoordX = pointCoordX - ballSpeed;
if(MyAnimationView.ballDropping == true)
{
while (pointCoordY<gregCoordY)
dropBall();
}
}
public static void moveGreg(int x) {
if (x <= 10 && goingLeft)
{
gregCoordX = gregCoordX + count;
goingRight = true;
goingLeft = false;
}
else if (x >= 410 && goingRight)
{
gregCoordX = gregCoordX - count;
goingLeft = true;
goingRight = false;
}
else if (goingRight)
gregCoordX = gregCoordX + count;
else
gregCoordX = gregCoordX - count;
}
#Override
public void onClick(View arg0) {
dropBall();
}
}
MyAnimationView.java
package com.test.firstAnimation;
import android.content.Context;
import android.graphics.Canvas;
import android.view.View;
public class MyAnimationView extends View{
private Sprite greg;
private Sprite ball;
private int xCoOr;
private int ballXCoOr;
public static boolean ballDropping;
public MyAnimationView(Context context) {
super(context);
ballDropping = false;
greg = new Sprite(context,R.drawable.greg);
ball = new Sprite(context, R.drawable.ball);
OnClickListener gregClicked = new OnClickListener() {
public void onClick(View v) {
ballDropping = true;
}
};
greg.setOnClickListener(gregClicked);
}
#Override protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFFFFFFF); //white background
ballXCoOr = Sprite.getBX();
xCoOr = Sprite.getX();
Sprite.moveGreg(xCoOr); //move ball left or right depending
Sprite.moveBall(ballXCoOr);
if(ballDropping == true)
{
Sprite.dropBall();
}
canvas.drawBitmap(greg.getBitmap(), xCoOr, Sprite.getY(), null);
canvas.drawBitmap(ball.getBitmap(), ballXCoOr, Sprite.getBY(), null);
invalidate();
}
}
Thanks in advance, I've been stuck for days!
Ben
float touched_x, touched_y;
boolean touched = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
touchCounter++;
touched_x = event.getX();
touched_y = event.getY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
touched = true;
break;
case MotionEvent.ACTION_MOVE:
touched = true;
break;
case MotionEvent.ACTION_UP:
touched = false;
break;
case MotionEvent.ACTION_CANCEL:
touched = false;
break;
case MotionEvent.ACTION_OUTSIDE:
touched = false;
break;
default:
}
return true; // processed
}
Then;
if (touched) {
//control here
}
touched_x, touched_y are coordinates of the point that is clicked on the screen. You can compare Greg's coordinates and these coordinates. If same, then do what you wanna do.
public class CustomView extends View implements OnClickListener {
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context) {
super(context);
init();
}
private void init(){
setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Do whatever you need to
}
}