I'm trying to show an animated gif. By the way I'm doing it with the class Movie. But the Android Developer page dont grant info about the methods.
How can I make the gif to be resized to fit the layout?
Thanks in advance
I've been trying to do the same thing (to display animated GIF) using this method.
It works only if you specify uses-sdk android:minSdkVersion="3"
For scaling ...
package com.example.GIFShow;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
class MYGIFView extends View {
private static final boolean twigDebug = true;
private Movie mMovie;
private long mMovieStart;
MYGIFView(Context aContext, Movie aMovie) {
super(aContext);
if (aMovie == null)
return;
mMovie = aMovie;
mMovieStart = android.os.SystemClock.uptimeMillis();
}
protected void onDraw(Canvas aCanvas) {
super.onDraw(aCanvas);
if (mMovie == null || mMovie.duration() == 0)
return;
int relTime = (int)((android.os.SystemClock.uptimeMillis() - mMovieStart)
% mMovie.duration());
mMovie.setTime(relTime);
Bitmap movieBitmap = Bitmap.createBitmap(mMovie.width(), mMovie.height(),
Bitmap.Config.ARGB_8888);
Canvas movieCanvas = new Canvas(movieBitmap);
mMovie.draw(movieCanvas, 0, 0);
Rect src = new Rect(0, 0, mMovie.width(), mMovie.height());
Rect dst = new Rect(0, 0, this.getWidth(), this.getHeight());
aCanvas.drawBitmap(movieBitmap, src, dst, null);
this.invalidate();
}
}
Now you can get and pass a Movie object to the class in one of two ways ...
Get input GIF Movie object from project drawable folder
Movie movie = Movie.decodeStream
(context.getResources().openRawResource(R.drawable.somefile));
Or get input GIF Movie object from external storage
(/mnt/sdcard ... /mnt/extSdCard ... etc)
Movie movie = null;
try {
FileInputStream stream =
new FileInputStream(Environment.getExternalStorageDirectory() +
"/somefolder/somefile.gif");
try {
byte[] byteStream = streamToBytes(stream);
movie = Movie.decodeByteArray(byteStream, 0, byteStream.length);
}
finally {
stream.close();
}
}
catch (IOException e) { }
Now set the moving GIF image / Movie object into your activity view:
View view = new MYGIFView(this, movie);
setContentView(view);
If you get the GIF image / Movie object from external storage (second example) you'll need the supporting routine:
private byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0)
os.write(buffer, 0, len);
}
catch (java.io.IOException e) { }
return os.toByteArray();
}
You answer is good. It helped me alot.I have changed one thing for performance .Moved the bitmap creation to outside of ondraw
import java.io.InputStream;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
public class GifView extends View {
private Movie mMovie;
InputStream mStream;
long mMoviestart;
private Context context;
public GifView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public GifView(Context context) {
super(context);
this.context = context;
init();
}
Bitmap movieBitmap = null;
private void init() {
InputStream object = this.getResources().openRawResource(
R.raw.postloadinganimation);
mMovie = Movie.decodeStream(object);// context.getResources().getAssets().open("PostLoadingAnimation.gif"));
movieBitmap = Bitmap.createBitmap(mMovie.width(),
mMovie.height(), Bitmap.Config.ARGB_8888);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
final long now = SystemClock.uptimeMillis();
if (mMoviestart == 0) {
mMoviestart = now;
}
final int relTime = (int) ((now - mMoviestart) % mMovie.duration());
mMovie.setTime(relTime);
// mMovie.draw(canvas, 0, 0);
Canvas movieCanvas = new Canvas(movieBitmap);
mMovie.draw(movieCanvas, 0, 0);
Rect src = new Rect(0, 0, mMovie.width(), mMovie.height());
Rect dst = new Rect(0, 0, this.getWidth(), this.getHeight());
canvas.drawBitmap(movieBitmap, src, dst, null);
this.invalidate();
}
}
I know, this is very old post.
But you can try this ...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mMovie != null) {
int movieWidth = mMovie.width();
int movieHeight = mMovie.height();
/** Calculate horizontal scaling*/
float scaleH = 1f;
int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);
if (measureModeWidth != MeasureSpec.UNSPECIFIED) {
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
if (movieWidth > maximumWidth) {
scaleH = (float) movieWidth / (float) maximumWidth;
}else{
scaleH = (float) maximumWidth / (float) movieWidth;
}
}
/** calculate vertical scaling*/
float scaleW = 1f;
int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);
if (measureModeHeight != MeasureSpec.UNSPECIFIED) {
int maximumHeight = MeasureSpec.getSize(heightMeasureSpec);
if (movieHeight > maximumHeight) {
scaleW = (float) movieHeight / (float) maximumHeight;
}else{
scaleW = (float) maximumHeight / (float) movieHeight;
}
}
/** calculate overall scale*/
mScale = Math.max(scaleH, scaleW);
mMeasuredMovieWidth = (int) (movieWidth * mScale);
mMeasuredMovieHeight = (int) (movieHeight * mScale);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
} else {
/*No movie set, just set minimum available size.*/
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
private void drawMovieFrame(Canvas canvas) {
mMovie.setTime(mCurrentAnimationTime);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScale, mScale);
mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
mMovie.draw(canvas, mLeft / mScale, mTop / mScale);
canvas.restore();
}
Related
I'm using the below Java class (Found on the web) as a countdown timer circle animation, The issue is that on some devices like Huawei Mate 10 Pro & Huawei G8 the animation finishes sooner than it is supposed to while on the other couple of devices that I've tested its working perfectly.
For example on Mate 10 pro, when I call the class to start the timer with '10' as the parameter (circle animation supposed to be completed in 10 seconds but it goes faster and finishes in exactly half the time-5 seconds!-) while in another device with same Android version (Android 10) it's working perfectly (Completes in 10 seconds).
So I figured the android version is not the problem here, Any idea what may be the issue ??
package com.maxwellforest.blogtimer;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import com.borsam.bleexample.R;
import java.util.concurrent.TimeUnit;
/**
* Countdown timer view.
*
* #author Mike Gouline
*/
public class TimerView extends View {
private static final int ARC_START_ANGLE = 270; // 12 o'clock
private static final float THICKNESS_SCALE = 0.03f;
private Bitmap mBitmap;
private Canvas mCanvas;
private RectF mCircleOuterBounds;
private RectF mCircleInnerBounds;
private Paint mCirclePaint;
private Paint mEraserPaint;
private float mCircleSweepAngle;
private ValueAnimator mTimerAnimator;
public TimerView(Context context) {
this(context, null);
}
public TimerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int circleColor = R.color.colorPrimary;
if (attrs != null) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TimerView);
if (ta != null) {
circleColor = ta.getColor(R.styleable.TimerView_circleColor, circleColor);
ta.recycle();
}
}
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(circleColor);
mEraserPaint = new Paint();
mEraserPaint.setAntiAlias(true);
mEraserPaint.setColor(Color.TRANSPARENT);
mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
#SuppressWarnings("SuspiciousNameCombination")
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec); // Trick to make the view square
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(Color.TRANSPARENT);
mCanvas = new Canvas(mBitmap);
}
super.onSizeChanged(w, h, oldw, oldh);
updateBounds();
}
#Override
protected void onDraw(Canvas canvas) {
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
if (mCircleSweepAngle > 0f) {
mCanvas.drawArc(mCircleOuterBounds, ARC_START_ANGLE, mCircleSweepAngle, true, mCirclePaint);
mCanvas.drawOval(mCircleInnerBounds, mEraserPaint);
}
canvas.drawBitmap(mBitmap, 0, 0, null);
}
public void start(int secs) {
stop();
mTimerAnimator = ValueAnimator.ofFloat(0f, 1f);
mTimerAnimator.setDuration(TimeUnit.SECONDS.toMillis(secs));//(secs*1000);
mTimerAnimator.setInterpolator(new LinearInterpolator());
mTimerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
drawProgress((float) animation.getAnimatedValue());
}
});
mTimerAnimator.start();
}
public void stop() {
if (mTimerAnimator != null && mTimerAnimator.isRunning()) {
mTimerAnimator.cancel();
mTimerAnimator = null;
drawProgress(0f);
}
}
private void drawProgress(float progress) {
mCircleSweepAngle = 360 * progress;
invalidate();
}
private void updateBounds() {
final float thickness = getWidth() * THICKNESS_SCALE;
mCircleOuterBounds = new RectF(0, 0, getWidth(), getHeight());
mCircleInnerBounds = new RectF(
mCircleOuterBounds.left + thickness,
mCircleOuterBounds.top + thickness,
mCircleOuterBounds.right - thickness,
mCircleOuterBounds.bottom - thickness);
invalidate();
}
}
There is an option in Developer options "Animator duration scale", by default this value is 1x if they are different for devices then you may experience slower or faster animations based on the value set.
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:
i have sample code for draw line. i need line draw point by point for run time. But now i got line after execution only showing activity. But, i need to start activity and show the line point by point i have done this concept in java sample code also attached check it.
Sample code is here
public class ImgDraw extends ActionBarActivity {
ImageView drawingImageView;
Handler mHandlerAnimation = null;
Runnable mRunnableAnimation = null;
Canvas canvas;
int startx = 0, starty = 0, endx = 0, endy = 0;
Paint paint;
Bitmap bitmap;
int width, height;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_imgdraw);
drawingImageView = (ImageView) findViewById(R.id.DrawingImageView);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
width = metrics.widthPixels;
height = metrics.heightPixels;
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
drawingImageView.setImageBitmap(bitmap);
// Line
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(3);
startx = 50;
starty = 90;
endx = 550;
endy = 90;
// canvas.drawLine(startx, starty, startx+1, endy, paint);
ImgAnimation();
}
private void ImgAnimation() {
mHandlerAnimation = new Handler();
mRunnableAnimation = new Runnable() {
public void run() {
if (endx > startx) {
canvas.drawLine(startx, starty, startx + 1, endy, paint);
startx = startx + 1;
getWindow().getDecorView().findViewById(android.R.id.content).invalidate();
} else {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
drawingImageView.setImageBitmap(bitmap);
startx = 50;
}
mHandlerAnimation.postDelayed(this, 5);
}
};
mHandlerAnimation.postDelayed(mRunnableAnimation, 5);
}}
Needed output format here
I worked on java project code here
public class AndroidTest extends JFrame {
static JPanel jp;
static Graphics g2d;
static int x=10;
public static void main(String[] args) {
AndroidTest a = new AndroidTest();
a.setSize(500,500);
a.setLayout(null);
a.setLocationRelativeTo(null);
a.setDefaultCloseOperation(EXIT_ON_CLOSE);
a.setVisible(true);
jp = new JPanel();
jp.setSize(450,100);
jp.setLocation(10,100);
jp.setBackground(Color.black);
jp.setVisible(true);
a.add(jp);
g2d = jp.getGraphics();
while(x<=450){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int k = 0; k < 10; k++) {
g2d.setColor(Color.black);
g2d.drawLine(x+k, 0 , x + k,99);
}
g2d.setColor(Color.white);
g2d.drawLine(x,50,x+1,50);
x++;
if (x == 446) {
x=10;
}
}
}}
run this code in java then i need same output in android activity
Try this
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 LineView extends View {
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
int x=0,j=0;
public LineView(Context context) {
super(context);
init();
}
public LineView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init()
{
paint.setColor(Color.BLACK);
paint1.setColor(Color.WHITE);
}
#Override
protected synchronized void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawLine(0,50,x+1,50,paint1);
if (x > getWidth()-5) {
canvas.drawRect(j, 0, j + 20, 50, paint);
j+=4;
if(j > getWidth()-5)
j=0;
}
else
{
x+=4;
}
invalidate();
}
}
the xml is
<com.example.myapplication.LineView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#000"/>
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).
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()".