custom image view android - java

My custom view looks as below
package com.mypackage;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.widget.ImageView;
public class CustomDrawableView extends ImageView {
// private ShapeDrawable mDrawable;
public int imageid = 0;
ShapeDrawable bgDrawable = null;
List<ShapeDrawable> ls = new ArrayList<ShapeDrawable>();
final int COUNT_SUMMERY = 3;
final int VERTICAL_OFFSET = 200;
final int HORIZENTAL_OFFSET = 20;
final int HORIZENTAL_GAP = 85;
final int VERTICAL_Y_POINT = 15;
final int VERTICAL_MAX_HEIGHT = 195;
final int HORIZENTAL_WIDTH = 60;
final int percentage[] = {25, 40, 35};
public CustomDrawableView(Context context, int id) {
super(context);
imageid = id;
switch(id) {
case R.drawable.summarychart:
for(int i = 0; i < COUNT_SUMMERY; i++) {
int x = HORIZENTAL_OFFSET + (HORIZENTAL_GAP * i);
int width = HORIZENTAL_WIDTH;
int height = VERTICAL_MAX_HEIGHT * percentage[i] / 100;
int y = VERTICAL_OFFSET + VERTICAL_Y_POINT + VERTICAL_MAX_HEIGHT - height;
ShapeDrawable objDrawable = new ShapeDrawable(new RectShape());
int color = 0;
switch(i) {
case 0:
color = Color.RED;
break;
case 1:
color = Color.GREEN;
break;
case 2:
color = Color.YELLOW;
break;
}
objDrawable.getPaint().setColor(color);
objDrawable.setBounds(x, y, x + width, y + height);
ls.add(objDrawable);
}
break;
default:
break;
}
int x = 0;
int y = 0;
int width = 320;
int height = 480;
bgDrawable = new ShapeDrawable(new RectShape());
bgDrawable.getPaint().setColor(0xffffffff);
bgDrawable.setBounds(x, y, x + width, y + height);
}
protected void onDraw(Canvas canvas) {
/* // Draw the white background
// bgDrawable.draw(canvas);
// Draw the bitmaps
Bitmap bmp = BitmapFactory.decodeResource(getResources(), imageid);
canvas.drawBitmap(bmp, 0, VERTICAL_OFFSET, null);
*/ // Draw bars
super.onDraw(canvas);
for(int i = 0; i < COUNT_SUMMERY; i++) {
ShapeDrawable objDrawable = ls.get(i);
objDrawable.draw(canvas);
}
}
}
And in the layout I have:
<com.mypackage.CustomDrawableView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="#drawable/mychart" />
However I see the message:
NoSuchMethodException:com.mypackage.CustomDrawableView.(Android.context.Context, Android.util.AttributeSet)
and if I try to run it the application crashes. If I uncomment the drawable way, then it works, but other layouts are not displayed.
Any help is appreciated.

You can fix it by implementing your missing constructor. When you try to instantiate this from XML it is invoked.
public CustomDrawableView(Context context, AttributeSet attrs) {
super(context, attrs);
}

All you need to do is look at the exception you are seeing.
NoSuchMethodException:com.mypackage.CustomDrawableView.(Android.context.Context,
Android.util.AttributeSet)
It means that there is no method with the signature com.mypackage.CustomDrawableView.(Android.context.Context, Android.util.AttributeSet) because you haven't overridden it in your class.

Related

How can achieve this custom view wave animation?

I'm trying to achieve this custom wave animation with circle in the middle of the wave.
Below is my custom view. It runs in a different direction and the draw has a line in the middle of the wave that results in a bad UX.
I try to follow some related tutorials but I cannot get the same animation.
If there is any library o code sample to follow it could help me a lot.
Thanks.
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import com.guille.stressmeterapp.R;
import org.jetbrains.annotations.Nullable;
public class WaveCustomView extends View {
private int mWidth = 0;
private int mHeight = 0;
private Path path;
private Paint paint;
private int waveHeight = 300;
private int waveWidth = 600;
private int originalY = 750;
private Region region;
private int dx = 0;
private Bitmap mBitmap;
private int animationDuration = 3000;
public WaveCustomView(Context context) {
super(context, null);
initUi();
}
public WaveCustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs, 0);
initUi();
}
public WaveCustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initUi();
}
private void initUi() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#000000"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(15);
path = new Path();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.circle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
int desired = (int) (getPaddingLeft() + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
int desired = (int) (getPaddingTop() + getPaddingBottom());
height = desired;
}
mWidth = width;
mHeight = height;
waveWidth = mWidth / 2;
setMeasuredDimension(width, height);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
setDrawData();
Rect bounds = region.getBounds();
if (bounds.top < originalY) {
canvas.drawBitmap(mBitmap, bounds.right - (mBitmap.getWidth() >> 1), bounds.top - (mBitmap.getHeight() >> 1), paint);
} else {
canvas.drawBitmap(mBitmap, bounds.right - (mBitmap.getWidth() >> 1), bounds.bottom - (mBitmap.getHeight() >> 1), paint);
}
}
private void setDrawData() {
path.reset();
int halfWaveWidth = waveWidth / 2;
path.moveTo(-waveWidth + dx, originalY);
for (int i = -waveWidth; i < mWidth + waveWidth; i = i + waveWidth) {
path.rQuadTo(halfWaveWidth >> 1, -waveHeight, halfWaveWidth, 0);
path.rQuadTo(halfWaveWidth >> 1, waveHeight, halfWaveWidth, 0);
}
region = new Region();
Region clip = new Region((int) (mWidth / 2 - 0.1), 0, mWidth / 2, mHeight * 2);
region.setPath(path, clip);
path.close();
}
public void startAnimate() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float factor = (float) valueAnimator.getAnimatedValue();
dx = (int) ((waveWidth) * factor);
invalidate();
}
});
animator.setDuration(animationDuration);
animator.start();
}
Your code looks OK. Just remove this line from setDrawData method.
path.close();
This line closes path. It means it connect path begginnig with path end. That's why you see line in the middle of the wave.
Here is result without middle line:
If you want to change the direction of animation just change sign of dx variable. Change this:
dx = (int) ((waveWidth) * factor);
To this:
dx = - (int) (waveWidth * factor);
Or instead of this:
path.moveTo(-waveWidth + dx, originalY);
Do this:
path.moveTo(-waveWidth - dx, originalY);
Final result:

How to change the height and width of button and add buttons through drawable canvas in android studio?

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.

Rect.intersects not working

I understand that this is technically a repeat question, but all of the similar questions include code I do not understand, so I decided to ask the question using code I understand.
I am attempting to make a flappy bird style game to try android programing and I cannot get Rect.intersects to change the player's score when the player collides with an object (cloud)
Thanks in advance!!
View class:
package com.gregsapps.fallingbird;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
public class GameView extends View{
private Bird bird;
private boolean runOnce = false;
private Context context;
private Paint red;
ArrayList<Cloud> clouds = new ArrayList<Cloud>();
private int cloudDelay;
private Collision collision;
//private Paint textPaint;
public GameView(Context context) {
super(context);
this.context = context;
this.setDrawingCacheEnabled(true);
red = new Paint();
red.setColor(Color.RED);
red.setTextSize(100f);
collision = new Collision();
//textPaint.setColor(Color.BLACK);
//textPaint.setTextAlign(Align.RIGHT);
// TODO add setup code
}
protected void onDraw(Canvas canvas){
update(canvas);
System.out.println(bird.score);
//TODO add drawing code
this.buildDrawingCache();
//bird.canvasImage = this.getDrawingCache(true);
canvas.drawColor(Color.rgb(10, 255, 255));
canvas.drawRect(bird.birdRect, red);
canvas.drawBitmap(bird.image, bird.x, bird.y, null);
for(int i = 0; i < clouds.size(); i++){
System.out.println("Drawing cloud");
Cloud cloud = (Cloud) clouds.get(i);
cloud.move(5);
System.out.println(cloud.leftX + "/t" + cloud.rightX);
canvas.drawRect(cloud.rightCloud, red);
canvas.drawRect(cloud.leftCloud, red);
canvas.drawBitmap(cloud.image, cloud.leftX, cloud.leftY, null);
canvas.drawBitmap(cloud.image, cloud.rightX, cloud.rightY, null);
if(cloud.leftY <= -144)clouds.remove(cloud);
if(bird.y > cloud.leftY + bird.height) bird.score++;
if(Rect.intersects(bird.birdRect, cloud.leftCloud)){
bird.score = 0;
}
else if(Rect.intersects(bird.birdRect, cloud.rightCloud)){
bird.score = 0;
}
}
canvas.drawLine(canvas.getWidth()/2 - 1, 0, canvas.getWidth()/2 - 1, canvas.getHeight(), red);
cloudDelay --;
if(cloudDelay <= 0){
System.out.println("new cloud");
Cloud cloud = new Cloud(com.gregsapps.fallingbird.R.drawable.cloud, context);
System.out.println("added");
clouds.add(cloud);
cloudDelay = 175;
}
canvas.drawText(Integer.toString(bird.score/12), 50, 100, red);
invalidate();
}
private void update(Canvas canvas){
//TODO add code to update stuff
if(runOnce == false){
bird = new Bird(canvas, com.gregsapps.fallingbird.R.drawable.bird, context);
runOnce = true;
StaticVarHandler.canvasHeight = canvas.getHeight();
StaticVarHandler.canvasWidth = canvas.getWidth();
}
bird.move();
}
}
Cloud class:
package com.gregsapps.fallingbird;
import java.util.Random;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
public class Cloud {
public int leftX;
public int leftY;
public int rightX;
public int rightY;
private Random random;
public Bitmap image;
private Context context;
public Rect leftCloud;
public Rect rightCloud;
public int height;
public int width;
Cloud(int image, Context context){
this.context = context;
this.image = BitmapFactory.decodeResource(this.context.getResources(), R.drawable.cloud);
random = new Random();
leftX = random.nextInt(StaticVarHandler.canvasWidth-(StaticVarHandler.birdWidth*2));
rightX = leftX + StaticVarHandler.birdWidth*2;
rightY = leftY = StaticVarHandler.canvasHeight+this.image.getHeight();
leftX -= this.image.getWidth();
leftCloud = new Rect(leftX, leftY, this.image.getWidth(), this.image.getHeight());
rightCloud = new Rect(rightX, rightY, this.image.getWidth(), this.image.getHeight());
}
public void move(int scrollSpeed){
leftCloud.offset(0, -scrollSpeed);
rightCloud.offset(0, -scrollSpeed);
leftY-=scrollSpeed;
rightY-=scrollSpeed;
}
}
Bird class:
package com.gregsapps.fallingbird;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
public class Bird {
public int x;
public int y;
private int speed;
public Bitmap image;
Context context;
private int gravity;
public int width;
public int height;
private int canvasWidth;
private int canvasHeight;
public Bitmap canvasImage;
public boolean touch;
public int touchX;
public int touchY;
private int gravDelay = 0;
private int jump = 0;
public int score;
public Rect birdRect;
Bird(Canvas canvas, int image, Context context){
this.context = context;
this.image = BitmapFactory.decodeResource(this.context.getResources(), image);
x = canvas.getWidth()/2 - this.image.getWidth()/2;
y = 10;
speed = this.image.getWidth()/10;
//setup gravity, speed, width and height attributes
speed = canvas.getWidth()/25;
gravity = speed/10;
StaticVarHandler.birdWidth = width = this.image.getWidth();
height = this.image.getHeight();
canvasWidth = canvas.getWidth();
canvasHeight = canvas.getHeight();
System.out.println(canvasWidth);
System.out.println(canvas.getWidth());
birdRect = new Rect(x, y, this.image.getWidth(), this.image.getHeight());
}
public void move(){
gravDelay --;
jump --;
if(StaticVarHandler.touch) jump = 3;
if(jump >= 0){
if(StaticVarHandler.touchX < canvasWidth/2){
x -= speed/3;
}
if(StaticVarHandler.touchX > canvasWidth/2){
x += speed/3;
}
StaticVarHandler.touch = false;
if(jump == 0) gravDelay = 7;
}
else if(gravDelay <= 0){
System.out.println("GRAVITY");
if(x+width/2 < canvasWidth/2){
x += gravity;
//code to move bird via gravity
}
if(x+width/2 > canvasWidth/2){
x -= gravity;
//same as above but other side
}
gravDelay = 1;
}
if(x <= 0){
score = 0;
x = 0;
}
else if(x+width >= canvasWidth){
score = 0;
x = canvasWidth - width;
}
birdRect.offsetTo(x-1, y-1);
}
private void collisionCheck(){
if(longEquation()){
}
}
}
I think I found your problem.
birdRect = new Rect(x, y, this.image.getWidth(), this.image.getHeight());
and
leftCloud = new Rect(leftX, leftY, this.image.getWidth(), this.image.getHeight());
rightCloud = new Rect(rightX, rightY, this.image.getWidth(), this.image.getHeight());
Should not use getWidth() or getHeight() but should use x + width and y + height.
public Rect (int left, int top, int right, int bottom)
Added in API level 1 Create a new rectangle with the specified
coordinates. Note: no range checking is performed, so the caller must
ensure that left <= right and top <= bottom.
Parameters
left The X coordinate of the left side of the rectangle
top The Y coordinate of the top of the rectangle
right The X
coordinate of the right side of the rectangle
bottom The Y coordinate
of the bottom of the rectangle
This is from the documentation.

ClassCastException when calling constructor for custom Button class

I`m playing with some simple Android app code but there is problem related to all layouts in code.
This is error when I open layout in eclipse
The following classes could not be instantiated:
- com.android2.calculator3.view.ColorButton (Open Class, Show Error Log)
See the Error Log (Window > Show View) for more details.
Tip: Use View.isInEditMode() in your custom views to skip code when shown in Eclipse
java.lang.ClassCastException: com.android.layoutlib.bridge.android.BridgeContext cannot be cast to com.android2.calculator3.Calculator
at com.android2.calculator3.view.ColorButton.<init>(ColorButton.java:39)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0( at sun.reflect.NativeConstructorAccessorImpl.newInstance( at sun.reflect.DelegatingConstructorAccessorImpl.newInstance( at java.lang.reflect.Constructor.newInstance( at com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.instantiateClass(ProjectCallback.java:442)
at com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback.loadView(ProjectCallback.java:194)
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:207)
at android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:132)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:806)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:64)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:782)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:809)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:64)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:782)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:809)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:64)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:782)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:809)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:64)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:782)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:809)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:64)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:782)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:385)
And this is my code in Colorbutton.java
package com.android2.calculator3.view;
import java.util.regex.Pattern;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;
import com.android2.calculator3.Calculator;
import com.android2.calculator3.EventListener;
import calculator.app.R;
/**
* Button with click-animation effect.
*/
class ColorButton extends Button {
int CLICK_FEEDBACK_COLOR;
static final int CLICK_FEEDBACK_INTERVAL = 10;
static final int CLICK_FEEDBACK_DURATION = 350;
float mTextX;
float mTextY;
long mAnimStart;
EventListener mListener;
Paint mFeedbackPaint;
Paint mHintPaint = new Paint();
Rect bounds = new Rect();
float mTextSize = 0f;
public ColorButton(Context context, AttributeSet attrs) {
super(context, attrs);
Calculator calc = (Calculator) context;
init(calc);
mListener = calc.mListener;
setOnClickListener(mListener);
setOnLongClickListener(mListener);
}
private void init(Calculator calc) {
Resources res = getResources();
CLICK_FEEDBACK_COLOR = res.getColor(R.color.magic_flame);
mFeedbackPaint = new Paint();
mFeedbackPaint.setStyle(Style.STROKE);
mFeedbackPaint.setStrokeWidth(2);
getPaint().setColor(res.getColor(R.color.button_text));
mHintPaint.setColor(res.getColor(R.color.button_hint_text));
mAnimStart = -1;
}
private void layoutText() {
Paint paint = getPaint();
if(mTextSize != 0f) paint.setTextSize(mTextSize);
float textWidth = paint.measureText(getText().toString());
float width = getWidth() - getPaddingLeft() - getPaddingRight();
float textSize = getTextSize();
if(textWidth > width) {
paint.setTextSize(textSize * width / textWidth);
mTextX = getPaddingLeft();
mTextSize = textSize;
}
else {
mTextX = (getWidth() - textWidth) / 2;
}
mTextY = (getHeight() - paint.ascent() - paint.descent()) / 2;
if(mHintPaint != null) mHintPaint.setTextSize(paint.getTextSize() * 0.8f);
}
#Override
protected void onTextChanged(CharSequence text, int start, int before, int after) {
layoutText();
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(changed) layoutText();
}
private void drawMagicFlame(int duration, Canvas canvas) {
int alpha = 255 - 255 * duration / CLICK_FEEDBACK_DURATION;
int color = CLICK_FEEDBACK_COLOR | (alpha << 24);
mFeedbackPaint.setColor(color);
canvas.drawRect(1, 1, getWidth() - 1, getHeight() - 1, mFeedbackPaint);
}
#Override
public void onDraw(Canvas canvas) {
if(mAnimStart != -1) {
int animDuration = (int) (System.currentTimeMillis() - mAnimStart);
if(animDuration >= CLICK_FEEDBACK_DURATION) {
mAnimStart = -1;
}
else {
drawMagicFlame(animDuration, canvas);
postInvalidateDelayed(CLICK_FEEDBACK_INTERVAL);
}
}
else if(isPressed()) {
drawMagicFlame(0, canvas);
}
CharSequence hint = getHint();
if(hint != null) {
String[] exponents = hint.toString().split(Pattern.quote("^"));
int offsetX = getContext().getResources().getDimensionPixelSize(R.dimen.button_hint_offset_x);
int offsetY = (int) ((mTextY + getContext().getResources().getDimensionPixelSize(R.dimen.button_hint_offset_y) - getTextHeight(mHintPaint,
hint.toString())) / 2)
- getPaddingTop();
float textWidth = mHintPaint.measureText(hint.toString());
float width = getWidth() - getPaddingLeft() - getPaddingRight() - mTextX - offsetX;
float textSize = mHintPaint.getTextSize();
if(textWidth > width) {
mHintPaint.setTextSize(textSize * width / textWidth);
}
for(String str : exponents) {
if(str == exponents[0]) {
canvas.drawText(str, 0, str.length(), mTextX + offsetX, mTextY - offsetY, mHintPaint);
offsetY += getContext().getResources().getDimensionPixelSize(R.dimen.button_hint_exponent_jump);
offsetX += mHintPaint.measureText(str);
}
else {
canvas.drawText(str, 0, str.length(), mTextX + offsetX, mTextY - offsetY, mHintPaint);
offsetY += getContext().getResources().getDimensionPixelSize(R.dimen.button_hint_exponent_jump);
offsetX += mHintPaint.measureText(str);
}
}
}
CharSequence text = getText();
canvas.drawText(text, 0, text.length(), mTextX, mTextY, getPaint());
}
private int getTextHeight(Paint paint, String text) {
mHintPaint.getTextBounds(text, 0, text.length(), bounds);
int height = bounds.height();
String[] exponents = text.split(Pattern.quote("^"));
for(int i = 1; i < exponents.length; i++) {
height += getContext().getResources().getDimensionPixelSize(R.dimen.button_hint_exponent_jump);
}
return height;
}
public void animateClickFeedback() {
mAnimStart = System.currentTimeMillis();
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
if(isPressed()) {
animateClickFeedback();
}
else {
invalidate();
}
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_CANCEL:
mAnimStart = -1;
invalidate();
break;
}
return result;
}
}
I can not figure out whats going wrong here?
Your error log does most of the work for you:
java.lang.ClassCastException: com.android.layoutlib.bridge.android.BridgeContext cannot be cast to com.android2.calculator3.Calculator
at com.android2.calculator3.view.ColorButton.<init>(ColorButton.java:39)
Essentially, you are attempting to cast BridgeContext to Calculator, which I assume refers to this line in your constructor:
public ColorButton(Context context, AttributeSet attrs) {
super(context, attrs);
Calculator calc = (Calculator) context; //This Line
init(calc);
mListener = calc.mListener;
setOnClickListener(mListener);
setOnLongClickListener(mListener);
}
For this to work, your context argument needs to inherit from Calculator. A simple test would be:
if (context instanceof Calculator) {
Calculator calc = (Calculator) context;
} else {
Log.e("Log Tag", context.toString() + " must inherit from Calculator class");
}
or, with a try/catch block:
try {
Calculator calc = (Calculator) context;
} catch (ClassCastException e) {
Log.e("Log Tag", context.toString() + " must inherit from Calculator class");
e.printStackTrace();
}
Edit:
A possible fix for your situation could be the following amendments to your constructor:
public ColorButton(Context context, AttributeSet attrs, Caculator calculator) {
super(context, attrs);
Calculator calc = calculator;
init(calc);
mListener = calc.mListener;
setOnClickListener(mListener);
setOnLongClickListener(mListener);
}
Of course, this is because I know nothing of your custom Calculator class (i.e., whether it is even a sublcass of Context). This method will bypass the context casting completely, so you can pass whatever you like for your first argument for as long as it inherits from the Context class (most commonly an Activity).

Android is trying to use recycled image

I'm getting
Canvas: trying to use a recycled bitmap
android.graphics.Bitmap#4057a3a8
everytime i'm trying to show one image.
Image
When i delete bmp.recycle() everything goes well but i dont use this image in my code so i dont understand where the problem is.
package com.example.photobooth;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
public class EditorActivity extends Activity implements OnClickListener{
String path = null;
private int screen_height;
private int screen_width;
private Bitmap setUpImage(Bitmap image) {
int min_side = Math.min(screen_height, screen_width);
float scale_factor = (float) (((float) min_side / image.getWidth()) * 1.5);
float[] scalef = { scale_factor, scale_factor };
Bitmap scaled_image = ImageUtilities.scaleImage(image, scalef);
return scaled_image;
}
private void setUp() {
Bundle b = getIntent().getExtras();
if (b != null) {
path = b.getString("path");
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
this.screen_height = metrics.heightPixels;
this.screen_width = metrics.widthPixels;
int min_measure = Math.min(screen_width, screen_height);
// Make ImageView square
ImageView img = (ImageView) findViewById(R.id.photo_holder);
android.view.ViewGroup.LayoutParams lp = img.getLayoutParams();
lp.height = min_measure;
img.setLayoutParams(lp);
Bitmap bmp = BitmapFactory.decodeFile(path);
final Bitmap ready_image = setUpImage(bmp);
bmp.recycle();
ImageView iv = (ImageView) findViewById(R.id.photo_holder);
iv.setImageBitmap(ready_image);
// set up touch event for imageview(photo_holder)
img.setOnTouchListener(new OnTouchListener() {
float touch_x, touch_y, scrolled_x = 0.0f, scrolled_y = 0.0f;
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_x = event.getX();
touch_y = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float cur_x = event.getX();
float cur_y = event.getY();
float scroll_x = -cur_x + touch_x;
float scroll_y = -cur_y + touch_y;
scrolled_x += scroll_x;
scrolled_y += scroll_y;
if (scrolled_x > (ready_image.getWidth() - screen_width)/2
|| scrolled_x < -(ready_image.getWidth() - screen_width)/2){
scrolled_x -= scroll_x;
scroll_x = 0;
}
if (scrolled_y > (ready_image.getHeight() - screen_width)/2
|| scrolled_y < -(ready_image.getHeight() - screen_width)/2){
scrolled_y -= scroll_y;
scroll_y = 0;
}
v.scrollBy((int) (scroll_x),
(int) (scroll_y));
touch_x = cur_x;
touch_y = cur_y;
break;
}
return true;
}
});
//Set up buttons
Button btn = (Button)findViewById(R.id.save);
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
ImageView img = (ImageView)findViewById(R.id.photo_holder);
int scroll_x = img.getScrollX();
int scroll_y = img.getScrollY();
int left = (ready_image.getWidth() - screen_width)/2
+ scroll_x;
int top = (ready_image.getHeight() - screen_width)/2
+ scroll_y;
int right = left + screen_width;
int bottom = top + screen_width;
Rect r = new Rect(left, top, right, bottom);
Bitmap croped_image = ImageUtilities.cropImage(ready_image,
r,
screen_width,
screen_width);
String path_to_folder = Environment.getExternalStorageDirectory()
.getAbsolutePath();
String pic_path = path_to_folder + File.separator + MainActivity.app_name;
File f = new File(pic_path);
File picture = null;
try {
picture = File.createTempFile("photo_", ".jpg", f);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
FileOutputStream fos = new FileOutputStream(picture);
croped_image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (requestWindowFeature(Window.FEATURE_NO_TITLE))
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_editor);
setUp();
}
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
bmp is recycled in setUp() method.
ImageUtility is
package com.example.photobooth;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
public class ImageUtilities {
public static Bitmap getRoundedCornerBitmap(Context context, Bitmap input,
int pixels, int w, int h, boolean squareTL, boolean squareTR,
boolean squareBL, boolean squareBR, boolean border) {
Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final float densityMultiplier = context.getResources()
.getDisplayMetrics().density;
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, w, h);
final RectF rectF = new RectF(rect);
// make sure that our rounded corner is scaled appropriately
final float roundPx = pixels * densityMultiplier;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
// draw rectangles over the corners we want to be square
if (squareTL) {
canvas.drawRect(0, 0, w / 2, h / 2, paint);
}
if (squareTR) {
canvas.drawRect(w / 2, 0, w, h / 2, paint);
}
if (squareBL) {
canvas.drawRect(0, h / 2, w / 2, h, paint);
}
if (squareBR) {
canvas.drawRect(w / 2, h / 2, w, h, paint);
}
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(input, 0, 0, paint);
if (border) {
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(3);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
}
return output;
}
public static Bitmap cropImage(Bitmap origina_bmp, Rect rec, int w, int h) {
Bitmap target_bitmap = Bitmap.createBitmap(w, h,
Bitmap.Config.ARGB_8888);
target_bitmap.setDensity(origina_bmp.getDensity());
Canvas canvas = new Canvas(target_bitmap);
canvas.drawBitmap(origina_bmp, new Rect(rec.left, rec.top, rec.right,
rec.bottom), new Rect(0, 0, w, h), null);
return target_bitmap;
}
public static Bitmap makeSquareImage(Bitmap original_image, int size){
int min_side = Math.min(original_image.getWidth(),
original_image.getHeight());
int side_size = ImageUtilities.get2del(min_side);
int crop_to;
Bitmap croped_image = null;
if (min_side == original_image.getWidth()){
crop_to = (original_image.getHeight() - side_size) / 2;
croped_image = ImageUtilities.cropImage(original_image, new Rect(
0, crop_to, original_image.getWidth(),
original_image.getHeight() - crop_to), size, size);
}else{
crop_to = (original_image.getWidth() - side_size) / 2;
croped_image = ImageUtilities.cropImage(original_image, new Rect(
crop_to, 0, original_image.getWidth() - crop_to,
original_image.getHeight()), size, size);
}
return croped_image;
}
public static int get2del(int num) {
while (num % 2 != 0)
num++;
return num;
}
public static Bitmap scaleImage(Bitmap originalBMP, float[] scaleFactor) {
Matrix scaleMat = new Matrix();
scaleMat.postScale(scaleFactor[0], scaleFactor[1]);
Bitmap scaledImage = Bitmap.createBitmap(originalBMP, 0, 0,
originalBMP.getWidth(), originalBMP.getHeight(), scaleMat,
false);
return scaledImage;
}
}
so it doesn't.
If i write bmp = null instead of bmp.recycle() everything is ok but i wonder why in the second chance application is crashed.
What is ImageUtilities? Maybe scaleImage may reuse the same image.
Does your program work correctly if you do:
bmp = null;
instead of
bmp.recycle();
?
The official documentation of recycle says:
"This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap. "
So using "bmp = null" should be better than "bmp.recycle()".

Categories