Force redraw of custom view (android) - android

I just have read similar questions but all answer don't work for me.
I have this custom view:
public class CompassTargetView extends View {
Context mContext;
Bitmap mBmp;
Matrix matrix;
int w, h, bw, bh;
int px = -1, py = -1;
public CompassTargetView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mContext = context;
Resources res = getResources();
mBmp = BitmapFactory.decodeResource(res, R.drawable.ttriangle);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(px == -1 && py == -1){
px=w/2-bw/2;
py=h/2-bh/2;
}
if (matrix != null) {
canvas.drawBitmap(mBmp, matrix, null);
matrix = null;
} else {
canvas.drawBitmap(mBmp, px, py, null);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//registriamo le dimensioni della view
w = MeasureSpec.getSize(widthMeasureSpec);
h = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(w, h);
}
public void setTrinagleIcon(){
Resources res = getResources();
mBmp = BitmapFactory.decodeResource(res, R.drawable.ttriangle);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
invalidate();
}
public void setCircleIcon(){
Resources res = getResources();
mBmp = BitmapFactory.decodeResource(res, R.drawable.tcircle);
bw = mBmp.getWidth();
bh = mBmp.getHeight();
invalidate();
}
public void setXY(Integer nx, Integer ny){
px = nx - bw/2;
py = ny - bh/2;
invalidate();
}
public void translateIcon(Integer nx, Integer ny){
matrix = new Matrix();
matrix.reset();
matrix.postTranslate(nx, ny);
invalidate();
}
public void rotateIcon(Integer nx, Integer ny, Float ang){
matrix = new Matrix();
matrix.reset();
matrix.postRotate(ang, nx, ny);
invalidate();
}
}
it show a small icon then I want use setXY, translateIcon and rotateIcon to "animate" the icon. However the onDraw method is call only on view create after this if I call setXY, translateIcon and rotateIcon onDraw is not call.
I just use invalidate(); and I have try setWillNotDraw(false); but this don't work.
Any idea?

Displaying a Toast in the onDraw is a very bad idea since it may cause a call to onDraw which will display the Toast and it will call onDraw.... infinite loop.
So replace the Toast by log (and keep the log only during debugging since onDraw is call very very often) and see what append.
The problem is that you are not updating the member matrix. You are creating a new one !
public void translateIcon(Integer nx, Integer ny){
Matrix matrix = new Matrix(); // <-- WRONG
matrix = new Matrix(); // <-- CORRECT
...
invalidate();
}

Related

Get Touch Coordinates Relative To A Custom ImageView

I implemented custom ImageView. Because my image is large I'm using ScrollView and HorizontalScrollView to scrolling image. I want to draw circle in onTouch event, but its not work in all cases. When I scroll image and fire touch event the x,y coordinates apply to current visible sector of image ( in top right corner its e.g. 0x0, but when I scroll somwhere and in visible top corner it will be 0x0 too), but circle is drawing according to image size.
Another problem is that app could be used on various screen size.
I know that it xamarin c# but in native Android it should be the same. Anyone knows how to draw/get coordinates properly?
public class DrawViewInside : ImageView
{
public static Bitmap bitmapInside;
private Paint paint = new Paint();
private Point point = new Point();
private Canvas mCanvas;
private static Bitmap mutableBitmap;
private Context mContext;
public static Bitmap b;
public void SetCarId(int id)
{
carId = id;
}
public DrawViewInside(Context context, IAttributeSet attrs) : base(context, attrs)
{
mContext = context;
setDefault(drawable);
}
public void SetBitmap(int drawableId)
{
setDefault(drawableId);
Invalidate();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.GetSize(widthMeasureSpec);
int height = width;
var metrics = Resources.DisplayMetrics;
if (b != null)
{
SetMeasuredDimension(b.Width, b.Height);
}
}
public async void setDefault(int drawableId)
{
BitmapFactory.Options options = await GetBitmapOptionsOfImage(drawableId);
paint.Color = Color.Red;
paint.StrokeWidth = 15;
paint.SetStyle(Paint.Style.Stroke);
var metrics = Resources.DisplayMetrics;
var widthInDp = ConvertPixelsToDp(metrics.WidthPixels);
var heightInDp = ConvertPixelsToDp(metrics.HeightPixels);
b = BitmapFactory.DecodeResource(Resources, drawableId);esources, drawableId);
mutableBitmap = b.Copy(Bitmap.Config.Argb8888, true);
mCanvas = new Canvas(mutableBitmap);
mCanvas.Save();
}
protected override void OnDraw(Canvas canvas)
{
DrawCircle(canvas);
}
private void DrawCircle(Canvas canvas)
{
if (mCanvas == null)
{
setDefault(drawable);
}
else
{
mCanvas.Restore();
mCanvas.DrawCircle(point.x, point.y, 10, paint);
mCanvas.Save();
canvas.DrawBitmap(mutableBitmap, 0, 0, paint);
bitmapInside = mutableBitmap;
}
}
public override bool OnTouchEvent(MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
break;
case MotionEventActions.Up:
Activity act = (Activity)mContext;
float viewX = e.RawX - this.Left;
float viewY = e.RawY - this.Top;
point.x = viewX;// e.RawX;
point.y = viewY;// e.RawY;
break;
}
Invalidate();
return true;
}
}
public class Point
{
public float x, y;
}
I found solution. Just replace
float viewX = e.RawX - this.Left;
float viewY = e.RawY - this.Top;
point.x = viewX;// e.RawX;
point.y = viewY;// e.RawY;
with
float viewX = e.RawX;// - this.Left;
float viewY = e.RawY;// - this.Top;
int[] viewCoords = new int[2];
this.GetLocationOnScreen(viewCoords);
point.x = viewX - viewCoords[0];// e.RawX;
point.y = viewY - viewCoords[1];// e.RawY;

How to draw an audio waveform on Android

I have a custom view that I want to use to display the amplitude of audio coming in through the microphone in a line graph.
Getting the amplitude and all that I have no problem with, and drawing the lines is not really a problem either.
What I want to do is show the amplitude starting at the far right edge, moving left. So with each new sample I want to translate the bitmap to the left, then draw a line from the last point to the new point. I'm not sure what the easiest way to achieve this is. I originally was able to do it by drawing Paths and just adding a new point to the path with each sample, the problem was that after like a minute the path was too big to be drawn. So I thought about it and wanted to switch to using a cached bitmap, translate that on each iteration, and draw from the last point to the new point. However this is tricky to do as (after experimentation). When I translate the bitmap it doesn't move the far left pixels off the bitmap, it just moves the entire bitmap in the canvas and I have no way to write pixels to the right side.
Below is a description of what I'm trying to do:
Given this:
I want to translate that to the left:
Then draw a line to a new point the space space on the right
Of course, step 2 and 3 should happen at essentially the same time.
How can I achieve this? I'm open to new ideas altogether, like perhaps saving all the points for up to 1 screen worth and drawing them out on each onDraw call. I'd prefer to just save them in a bitmap and do some kind of translation/clipping etc to achieve the same thing with perhaps less overhead.
private static final int MAX_AMPLITUDE = 32767;
float lx, ly;
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.Black);
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
height = h;
width = w;
ly = height;
lx = width;
amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
}
#Override
public void onDraw(Canvas canvas) {
mAmplitude = (float)(MAX_AMPLITUDE * Math.random());
float dx = width - delta;
float dy = height - (mAmplitude / amplitudeDivisor);
mCanvas.drawLine(lx, ly, dx, dy, mPaint);
mCanvas.translate(-delta, 0);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
lx = dx;
ly = dy;
delta+=10;
postInvalidateDelayed(200);
}
The above is just a sample, I'm just using a random value for the amplitude to simplify for now. I've tried a bunch of things with no luck. Any help would be greatly appreciated.
I ended up getting this working by saving the points to an array. I draw a white line before the recording starts. Note that I use an EvictingQueue from the Guava library as a circular buffer of points to render on a line. To use this, once a recording starts call start() and when it ends call stop. From your activity you will need to send MediaRecorder getMaxAmplitude() values to the updateAmplitude() method of this class, and do so at an interval of say 50 ms. The view also supports rotation.
public class AmplitudeWaveFormView extends View {
private static final String TAG = AmplitudeWaveFormView.class.getSimpleName();
private static final int MAX_AMPLITUDE = 32767;
private static final int SAMPLES_PER_SCREEN = 100;
private float mAmplitude = 0;
private Paint mRecordingPaint, mNotRecordingPaint;
private int height = -1;
private int width = -1;
private boolean mIsStarted;
private float[] lastPoints;
private int oldWidth = -1, oldHeight = -1;
private int mCurrentSample;
private float amplitudeDivisor = 1;
private float lx,ly, deltaX;
private EvictingQueue<Float> mPointQueue;
private int recordColor;
private int notRecordingColor;
public AmplitudeWaveFormView(Context context) {
super(context);
init();
}
public AmplitudeWaveFormView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AmplitudeWaveFormView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void start() {
mIsStarted = true;
}
public void stop() {
mIsStarted = false;
}
public void updateAmplitude(float amplitude) {
mAmplitude = amplitude;
postInvalidate();
}
private void init() {
recordColor = getResources().getColor(R.color.mint);
notRecordingColor = getResources().getColor(R.color.alpine);
mRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRecordingPaint.setStyle(Paint.Style.STROKE);
mRecordingPaint.setStrokeWidth(5);
mRecordingPaint.setColor(recordColor);
mNotRecordingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mNotRecordingPaint.setStyle(Paint.Style.STROKE);
mNotRecordingPaint.setStrokeWidth(5);
mNotRecordingPaint.setColor(notRecordingColor);
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
height = h;
width = w;
ly = height;
lx = width;
deltaX = (float)width / (float)SAMPLES_PER_SCREEN;
amplitudeDivisor = ((float) MAX_AMPLITUDE / (float) height);
mPointQueue = EvictingQueue.create(SAMPLES_PER_SCREEN * 4);
if (lastPoints != null && lastPoints.length > 0) {
float xScale = (float) width/oldWidth;
float yScale = (float) height/oldHeight;
Matrix matrix = new Matrix();
matrix.setScale(xScale, yScale);
matrix.mapPoints(lastPoints);
mPointQueue.addAll(Floats.asList(lastPoints));
ly = lastPoints[lastPoints.length-1];
lx= lastPoints[lastPoints.length -2];
lastPoints = null;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mCurrentSample = bundle.getInt("sample");
lastPoints = bundle.getFloatArray("lines");
oldWidth = bundle.getInt("oldWidth");
oldHeight = bundle.getInt("oldHeight");
state = ((Bundle) state).getParcelable("parent");
}
super.onRestoreInstanceState(state);
}
#Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putFloatArray("lines", Floats.toArray(mPointQueue));
bundle.putInt("sample", mCurrentSample);
bundle.putParcelable("parent", super.onSaveInstanceState());
bundle.putInt("oldWidth", width);
bundle.putInt("oldHeight", height);
return bundle;
}
#Override
public void onDraw(Canvas canvas) {
if (mIsStarted) {
float x = lx + deltaX;
float y = height - (mAmplitude / amplitudeDivisor);
mPointQueue.add(lx);
mPointQueue.add(ly);
mPointQueue.add(x);
mPointQueue.add(y);
lastPoints = Floats.toArray(mPointQueue);
lx = x;
ly = y;
}
if (lastPoints != null && lastPoints.length > 0) {
int len = mPointQueue.size() / 4 >= SAMPLES_PER_SCREEN ? SAMPLES_PER_SCREEN * 4 : mPointQueue.size();
float translateX = width - lastPoints[lastPoints.length - 2];
canvas.translate(translateX, 0);
canvas.drawLines(lastPoints, 0, len, mRecordingPaint);
}
if (mCurrentSample <= SAMPLES_PER_SCREEN) {
drawNotRecordingLine(canvas);
}
mCurrentSample++;
}
private void drawNotRecordingLine(Canvas canvas) {
canvas.drawLine(0,height, width, height, mNotRecordingPaint);
}
}

change color when int is changed in other class

I have IndicationBar class that should draw rectangle with logo inside and on int value case change rectangle color. I am new to java and android so im learning everyday.
At this moment the value of int is changing in other class and i call it BluetoothChat.statusSviesos. its public static int. Do i need to create interface Listener or how can i run private void Sviesos() every time int is changed? It runs only once in start.
public class IndicationBar extends View {
private static final String TAG = IndicationBar.class.getSimpleName();
// drawing tools
private RectF Rect;
private Paint rectPaint;
private Paint rimCirclePaint;
private RectF faceRect;
private Bitmap faceTexture;
private Paint facePaint;
private Paint rimShadowPaint;
private Paint titlePaint;
private Path titlePath;
private Paint logoPaint;
private Bitmap logo;
private Matrix logoMatrix;
private float logoScale;
private Paint backgroundPaint;
// end drawing tools
private Bitmap background; // holds the cached static part
public IndicationBar(Context context) {
super(context);
init();
}
public IndicationBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public IndicationBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
Parcelable superState = bundle.getParcelable("superState");
super.onRestoreInstanceState(superState);
}
private void init() {
initDrawingTools();
}
// private String getTitle() {
// return "DANCER";
// }
private void Sviesos(){
//Rect = new RectF(0.1f, 0.1f, 0.9f, 0.9f);
//rectPaint = new Paint();
//valueSviesos = BluetoothChat.statusSviesos;
switch(BluetoothChat.statusSviesos){
case 0 : rectPaint.setColor(Color.parseColor("#1BA1E2"));
break;
case 1 : rectPaint.setColor(Color.parseColor("#A05000"));
break;
case 2 : rectPaint.setColor(Color.parseColor("#E671B8"));
break;
case 3 : rectPaint.setColor(Color.parseColor("#F09609"));
break;
case 4 : rectPaint.setColor(Color.parseColor("#1BA1E2"));
break;
}
//rectPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
}
private void initDrawingTools() {
Rect = new RectF(0.1f, 0.1f, 0.9f, 0.9f);
rectPaint = new Paint();
rectPaint.setColor(Color.parseColor("#8CBF26"));
rectPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
Sviesos();
rimCirclePaint = new Paint();
rimCirclePaint.setAntiAlias(true);
rimCirclePaint.setStyle(Paint.Style.STROKE);
rimCirclePaint.setColor(Color.argb(0x4f, 0x33, 0x36, 0x33));
rimCirclePaint.setStrokeWidth(0.005f);
float rimSize = 0.02f;
faceRect = new RectF();
faceRect.set(Rect.left + rimSize, Rect.top + rimSize,
Rect.right - rimSize, Rect.bottom - rimSize);
faceTexture = BitmapFactory.decodeResource(getContext().getResources(),
R.drawable.plastic);
BitmapShader paperShader = new BitmapShader(faceTexture,
Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
Matrix paperMatrix = new Matrix();
facePaint = new Paint();
facePaint.setFilterBitmap(true);
paperMatrix.setScale(1.0f / faceTexture.getWidth(),
1.0f / faceTexture.getHeight());
paperShader.setLocalMatrix(paperMatrix);
facePaint.setStyle(Paint.Style.FILL);
facePaint.setShader(paperShader);
rimShadowPaint = new Paint();
rimShadowPaint.setShader(new RadialGradient(0.5f, 0.5f, faceRect.width() / 2.0f,
new int[] { 0x00000000, 0x00000500, 0x50000500 },
new float[] { 0.96f, 0.96f, 0.99f },
Shader.TileMode.MIRROR));
rimShadowPaint.setStyle(Paint.Style.FILL);
titlePaint = new Paint();
titlePaint.setColor(Color.parseColor("#1BA1E2"));
titlePaint.setAntiAlias(true);
titlePaint.setTypeface(Typeface.DEFAULT_BOLD);
titlePaint.setTextAlign(Paint.Align.CENTER);
titlePaint.setTextSize(0.09f);
titlePaint.setTextScaleX(0.9f);
titlePath = new Path();
titlePath.addArc(new RectF(0.24f, 0.24f, 0.76f, 0.76f), -180.0f, -180.0f);
logoPaint = new Paint();
logoPaint.setFilterBitmap(true);
logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sviesos);
logoMatrix = new Matrix();
logoScale = (1.0f / logo.getWidth()) * 0.3f;;
logoMatrix.setScale(logoScale, logoScale);
backgroundPaint = new Paint();
backgroundPaint.setFilterBitmap(true);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "Width spec: " + MeasureSpec.toString(widthMeasureSpec));
Log.d(TAG, "Height spec: " + MeasureSpec.toString(heightMeasureSpec));
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size) {
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
return size;
} else { // (mode == MeasureSpec.UNSPECIFIED)
return getPreferredSize();
}
}
// in case there is no size specified
private int getPreferredSize() {
return 300;
}
private void drawRect(Canvas canvas) {
// first, draw the metallic body
canvas.drawRect(Rect, rectPaint);
// now the outer rim circle
//canvas.drawOval(rimRect, rimCirclePaint);
}
// private void drawFace(Canvas canvas) {
// canvas.drawOval(faceRect, facePaint);
// // draw the inner rim circle
// canvas.drawOval(faceRect, rimCirclePaint);
// // draw the rim shadow inside the face
// canvas.drawOval(faceRect, rimShadowPaint);
// }
private void drawBackground(Canvas canvas) {
if (background == null) {
Log.w(TAG, "Background not created");
} else {
canvas.drawBitmap(background, 0, 0, backgroundPaint);
}
}
#Override
protected void onDraw(Canvas canvas) {
drawBackground(canvas);
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawLogo(canvas);
canvas.restore();
}
private void drawLogo(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(0.5f - logo.getWidth() * logoScale / 2.0f,
0.5f - logo.getHeight() * logoScale / 2.0f);
canvas.drawBitmap(logo, logoMatrix, logoPaint);
canvas.restore();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.d(TAG, "Size changed to " + w + "x" + h);
regenerateBackground();
}
private void regenerateBackground() {
// free the old bitmap
Sviesos();
if (background != null) {
background.recycle();
}
background = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas backgroundCanvas = new Canvas(background);
float scale = (float) getWidth();
backgroundCanvas.scale(scale, scale);
drawRect(backgroundCanvas);
}
}
Use the Observer pattern for this. Change the BluetoothChat
public class BluetoothChat {
private static int statusSviesos;
//the list of observers
private static List<Observer> observerList;
//adds an observer to observe statusSciesos
public static void addStatusSviesosObserver(Observer observer) {
observerList.add(observer);
}
//getter function returns the value
public static int getStatusSviesos(){
return statusSviesos;
}
//setter function sets the value and notifies observer
public static void setStatusSviesos(int statusSviesos){
BluetoothChat.statusSviesos=statusSviesos;
for(Observer current: observerList){
current.notifyChange(statusSviesos);
}
}
public static interface Observer {
public void notifyChange(int newStatus);
}
...
Then create an Observer in the IndicationBar and add it to the BluetoothChat:
Observer observer = new Observer() {
#Override
public void notifyChange(int newStatus) {
Sviesos();
}
};
BluetoothChat.addStatusSviesosObserver(observer);
This is a raw implementation of the Observer pattern. There should be a removeObserver-method too and it is not thread-safe. There is a default implementations ready in Java (Observer-class and Observable-class) and there are alternative implementations like PropertyChangeObserver. But for your current use case this should do the job. Also read Wikipedia and Vogellas tutorial
General tips:
Does BluetoothChat's attribute really has to be static? I recommend you to use static as less as possible. Better create an Object once and pass it around instead. You can search for "global state" and why to avoid it for further information.
You could create an array of Colors (or Color-strings like "#F09609") and access it with the int you get to get rid of the switch-statement.
Try to apply code style conventions everywhere, method names and variables should always start with a small letter (except constants which are ALL_BIG) and should be descriptive.

Android: How to rotate my Bitmap using accelerometer?

i want to rotate my bitmap img which is a car steering using one axis of accelerometer. I have no idea how i can use it with matrix or calling the sensorchanged method in other class. My app is in landscape mode and i have two classes one for sensor and one for bitmap. I am completely lost.
public class CustomDrawableView extends View {
AnimationSteeringActivity sens = new AnimationSteeringActivity();
public CustomDrawableView(Context context) {
// TODO Auto-generated constructor stub
super(context);
}
#Override
public void onDraw(Canvas canvas) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.steering);
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int bitmapWidth = bmp.getWidth();
int bitmapHeight = bmp.getHeight();
int centreX = (canvasWidth - bitmapWidth) / 2;
int centreY = (canvasWidth - bitmapHeight) / 2;
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bmp, centreX, centreY, null);
and
public class AnimationSteeringActivity extends Activity implements
SensorEventListener {
/** Called when the activity is first created. */
/** Called when the activity is first created. */
CustomDrawableView mCustomDrawableView = null;
ShapeDrawable mDrawable = new ShapeDrawable();
public static int x;
public static int y;
private SensorManager sensorManager = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get a reference to a SensorManager
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mCustomDrawableView = new CustomDrawableView(this);
setContentView(mCustomDrawableView);
// setContentView(R.layout.main);
}
// This method will update the UI on new sensor events
public void onSensorChanged(SensorEvent sensorEvent) {
{
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// the values you were calculating originally here were over
// 10000!
x = (int) Math.pow(sensorEvent.values[1], 2);
y = (int) Math.pow(sensorEvent.values[2], 2);
}
if (sensorEvent.sensor.getType() == Sensor.TYPE_ORIENTATION) {
}
}
}
Look for doing actually rotation at this link
rotating a bitmap
so in the onSensorChanged method there you will rotate with the wanted x or y value depending on what you want
Bitmap myBitmap = BitmapFactory.decodeStream(downloadImageFromWeb());
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android1);
Display d = getWindowManager().getDefaultDisplay();
int x = d.getWidth();
int y = d.getHeight();
ImageView img1 = (ImageView)findViewById(R.id.img1);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(myBitmap, y, x, true);
Matrix matrix = new Matrix();
matrix.postRotate(-90);
Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap , 0, 0, scaledBitmap .getWidth(), scaledBitmap .getHeight(), matrix, true);
img1.setImageBitmap(rotatedBitmap);

Android custom view Bitmap memory leak

I've got a custom view in which I need to draw two bitmaps, one is a background, representing the image of a map and one is a pin which will be drawn on top/left position in canvas.
The both images are drawn onDraw and remain the same during the live of the activity that contains the view. After a while I get a
OutOfMemoryError: bitmap size exceeds
VM budget
This means that I have a leak and the bitmaps don't get garbage collected. I asked this question before, but now the situation changed a little. I made a init method where I set the bitmaps I want to use but this still isn't a good approach, the error appears later, but still there.
Here is the code
public class MyMapView extends View {
private int xPos = 0;
private int yPos = 0;
private int space = 0;
private Bitmap resizedBitmap;
private Bitmap position;
private Bitmap mapBitmap;
public void setMapBitmap(Bitmap value) {
this.mapBitmap = value;
}
public MyMapView(Context context) {
super(context);
}
public MyMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(final Context context) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
resizedBitmap = Bitmap.createScaledBitmap(mapBitmap, height, height, true);
position = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.position);
space = (width - resizedBitmap.getWidth()) / 2;
}
public void destroy()
{
resizedBitmap.recycle();
resizedBitmap=null;
position.recycle();
position=null;
mapBitmap.recycle();
mapBitmap=null;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
if (mapBitmap != null) {
canvas.drawBitmap(resizedBitmap, space, 0, null);
}
if (xPos != 0 && yPos != 0) {
canvas.translate(xPos + space - position.getWidth() / 2, yPos - position.getHeight() / 2);
canvas.drawBitmap(position, new Matrix(), new Paint());
}
}
public void updatePosition(int xpos, int ypos)
{
xPos = xpos;
yPos = ypos;
invalidate();
}
}
I call the setMapBitmap() and init() on onCreate of the activity that contains the view. In there I know what bitmap will be used as map. onPause of the activity I call the destroy() of the view. I still get errors.
I've read this this but I don't know how to adapt it on my case
I AM OPEN TO ANY OTHER SOLUTION. Please note that I need to resize the map bitmap (based on the height of the layout that contains it in mai activity) and still be able to draw the position on correct place.
You obviously have a memory leak. Read how to avoid memory leaks and how to find memory leaks.
Here is a your code refactored to use WeakReference:
public class MyMapView extends View {
private int xPos = 0;
private int yPos = 0;
private int space = 0;
private WeakReference<Bitmap> resizedBitmap;
private WeakReference<Bitmap> position;
private WeakReference<Bitmap> mapBitmap;
public void setMapBitmap(Bitmap value) {
this.mapBitmap = new WeakReference<Bitmap>(value);
}
public MyMapView(Context context) {
super(context);
}
public MyMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(Bitmap mapBitmap) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
resizedBitmap = new WeakReference<Bitmap>(Bitmap.createScaledBitmap(
mapBitmap, width, height, true));
position = new WeakReference(BitmapFactory.decodeResource(
getContext().getResources(), R.drawable.position));
space = (width - resizedBitmap.get().getWidth()) / 2;
}
// public void destroy() {
// resizedBitmap.recycle();
// resizedBitmap = null;
// position.recycle();
// position = null;
// mapBitmap.recycle();
// mapBitmap = null;
// }
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
protected void onDraw(Canvas canvas) {
if (mapBitmap != null) {
canvas.drawBitmap(resizedBitmap.get(), space, 0, null);
}
if (xPos != 0 && yPos != 0) {
canvas.translate(xPos + space - position.get().getWidth() / 2,
yPos - position.get().getHeight() / 2);
canvas.drawBitmap(position.get(), new Matrix(), null);
}
}
public void updatePosition(int xpos, int ypos) {
xPos = xpos;
yPos = ypos;
invalidate();
}
}
Something that marked me in your code is that you don't recycle all your bitmaps apparently. Here are a couple of code snippets that could help:
bmp = getBitmapFromRessourceID(R.drawable.menu);
ratio = (float)this.height/(float)bmp.getScaledHeight(canvas);
temp = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth()*ratio), (int) (bmp.getHeight()*ratio-10), false);
bmp.recycle();
bmp = temp;
and:
Bitmap temp = BitmapFactory.decodeResource(context.getResources(), resource, opts);
Bitmap bmp = Bitmap.createBitmap(temp, 0, 0, temp.getWidth(), temp.getHeight(), flip, true);
temp.recycle();

Categories

Resources