Why invalidate is not working? - android

I am trying to click a button and add a resistor. So what I need is to invalidate the view when the button is clicked. But the invalidate that is inside MyView inside the method update() is not working. I have been trying to search for this problem but I have found nothing similar to what I am trying to do, or maybe this is not the way to do it.
DefaultActivity.java
public class DefaulActivity extends Activity {
MyView myView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myView = new MyView(this, null);
final Button bAddResistor = (Button) findViewById(R.id.bAdd);
bAddResistor.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
myView.update();
Log.d("ButtonADD", "Button Add has been clicked");
}
});
}
}
MyView.java
public class MyView extends SurfaceView implements SurfaceHolder.Callback{
Resistor myResistor;
private ArrayList<Resistor> mElements = new ArrayList<Resistor>();
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
synchronized (mElements) {
for (Resistor element : mElements) {
element.doDraw(canvas);
}
}
}
public void update() {
mElements.add(new Resistor(getContext(), (int) 10, (int) 10));
invalidate(); //Does not work!
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas();
onDraw(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
Resistor.java
public class Resistor extends View{
private Path mSymbol;
private Paint mPaint;
private int mX;
private int mY;
//...Override Constructors...
public Resistor(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Resistor(Context context, int x, int y){
super(context);
mX = x;
mY = y;
init();
}
private void init() {
mSymbol = new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(2);
mPaint.setColor(-7829368);
mPaint.setStyle(Paint.Style.STROKE);
//...Your code here to set up the path,
//...allocate objects here, never in the drawing code.
mSymbol.moveTo(0.0F, 0.0F);
mSymbol.lineTo(0.0F, 50.0F);
mSymbol.lineTo(16.666666F, 58.333332F);
mSymbol.lineTo(-16.666666F, 75.0F);
mSymbol.lineTo(16.666666F, 91.666664F);
mSymbol.lineTo(-16.666666F, 108.33333F);
mSymbol.lineTo(16.666666F, 124.99999F);
mSymbol.lineTo(-16.666666F, 141.66666F);
mSymbol.lineTo(0.0F, 150.0F);
mSymbol.lineTo(0.0F, 200.0F);
mSymbol.offset(mX, mY);
}
public void doDraw(Canvas canvas) {
canvas.drawPath(mSymbol, mPaint);
}

You should use a ViewGroup instead of a SurfaceView. Add the Resistor-Views as children to MyView, measure and layout them appropriately and the ViewGroup will automatically take care of drawing them.
SurfaceView requires you to do your drawing in a seperate thread. You have to start that thread in surfaceCreated and keep redrawing the view as needed. For simple views like the one you posted it is perfectly acceptable to just do your drawing in the UI thread. No SurfaceView required.

I haven't used Surfaceviews before but if you in the API you can see, that there is no function called "invalidate()" for the SurfaceView class.
Maybe you can manage to re-use this line somehow:
holder.unlockCanvasAndPost(c);

Related

Android canvas - setWillNotDraw(false) not working

The following code it's an activity class with a custom view class. This code is the only working example of canvas for me.
I just need to recall in loop the onDraw function (like animationframe in javascript canvas2d).
I can always use timers to make a call but maybe there is some low level nice way for that. I'm a newbie in Android development.
Question update: I use class name instead of instance name. No need for static for sure.
Current status : crash on activate activity.
public class CanvasNativeSurface extends AppCompatActivity {
private GoogleApiClient client;
private myview myView;
private Handler mHandler;
class myview extends View
{
private void init() {
}
public myview(Context context) {
super(context);
init();
}
int x=80;
int y=180;
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
x=80;
y = y + 1;
int radius=40;
Paint paint=new Paint();
// Use Color.parseColor to define HTML colors
paint.setColor(Color.parseColor("#CD5C5C"));
canvas.drawCircle(x,y, radius, paint);
}
public void move()
{
y= y+30;// 30 is value for testing
// change it to whatever you want.
invalidate();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewGroup view1;
view1 = (ViewGroup) findViewById( R.id.activity_canvas_native_surface );
/// setContentView(R.layout.activity_canvas_native_surface );
setContentView(new myview(this));
mHandler = new Handler();
mHandler.postDelayed(mRunnable, 1000);
}
private Runnable mRunnable = new Runnable() {
#Override
public void run() {
myView.move();
mHandler.postDelayed(this, 1000);
}
};
y = y + 1; Must push object to the bottom. Circle has no movement.
Catch in log:
Attempt to invoke virtual method 'void....CanvasNativeSurface$myview.move()' on a null object reference
Also Android Studio on mouseover it says: myView never assingned
MyView.jav // separate file
public class MyView extends View
{
private int x=80,y=180,radius=40;
private Paint paint;
public MyView(Context context) {
super(context);
init();
}
public void init() {
paint=new Paint();
paint.setColor(Color.parseColor("#CD5C5C"));
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawCircle(x,y, radius, paint);
}
public void move()
{
y= y+30;// 30 is value for testing
// change it to whatever you want.
invalidate();
}
}
And in your activity you can do something like
private MyView myView;
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView = new MyView(this);
setContentView(myView);
mHandler = new Handler();
mHandler.postDelayed(mRunnable, 1000);
}
private Runnable mRunnable = new Runnable() {
#Override
public void run() {
myView.move();
mHandler.postDelayed(this, 1000);
}
};
Finally don't forget to
mHandler.removeCallbacksAndMessages(mRunnable);
When you want to stop.
As i said earlier you don't need setWillNotDraw(false); and your constructor is called only once.
You probably need a timer mechanism to move the circle every second or something. I hope you got the idea and you can make changes accordingly.
If you are looking for animation
public class MyView extends View
{
private int x=80,y=180,radius=40;
private Paint paint;
public MyView(Context context) {
super(context);
init();
}
public void init() {
paint=new Paint();
paint.setColor(Color.parseColor("#CD5C5C"));
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawCircle(x,y, radius, paint);
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setStateOnObject(){
// 900 is the end value
ObjectAnimator animateBottom = ObjectAnimator.ofFloat(this,"y",y,900);
animateBottom.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
postInvalidate();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(animateBottom);
animatorSet.setDuration(5000).start();
}
}
And instead of handler you would do
myView.setStateOnObject();

Android: strange green pixels on Bitmap with Alpha 200

I just get green Pixels at the border of my drawn bitmap if I set the alpha of the paint to 200.
The problem does not appear if I set the alpha to 100 or 255.
How can I fix this?
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private DrawThread drawThread;
private boolean surfaceCreated;
Paint paint = new Paint();
private Bitmap bitmap;
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.circleyellow);
paint.setAlpha(200);
}
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bitmap, 200, 200, paint);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
setSurfaceCreated(true);
createDrawThread();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
setSurfaceCreated(false);
}
public void setSurfaceCreated(boolean surfaceCreated) {
this.surfaceCreated = surfaceCreated;
}
public boolean getSurfaceCreated() {
return surfaceCreated;
}
public void createDrawThread(){
if (drawThread != null) {
drawThread.destroy();
}
drawThread = new DrawThread(getHolder(), this);
drawThread.setRunning(true);
drawThread.start();
}
public DrawThread getDrawThread(){
return drawThread;
}
I think I have solved the problem. A friend has told me this solution:
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
bitmap = BitmapFactory.decodeResource(
getResources(),
R.drawable.circleyellow
);
paint.setAlpha(200);
}

Cannot call custom draw method from another class in Android

its my first time to ask a question here. I have this problem with my android app.
I would like to create different draw methods inside my custom view class. There are three buttons corresponding to 3 different shapes. When a button is pressed it will draw its shape. But the app crashes when I try to call the custom draw from the MainActivity for me to test it.
MainActivity
import com.example.shapes.view.ShapesView;
public class MainActivity extends Activity {
ShapesView shapesview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
shapesview = (ShapesView) findViewById(R.id.ShapesViewID);
shapesview.DrawRectangle();
}
ShapesView
public class ShapesView extends View{
Canvas canvas;
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
this.canvas = canvas;
}
public void DrawRectangle() {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
My XML layout file
<com.example.shapes.view.ShapesView
android:id="#+id/ShapesViewID"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />`
Please do help! Thank you very much!
If I understand your question you need a custom view (a button) which will draw itself differently based on some event. If that is the case then you need to respect the android's guidelines about the view drawing take a look here.
Now one possible solution for your case is to set some kind of flag about the state of your view and then use that flag when you are ready to draw. For example you can do this:
public class ShapesView extends View{
public int state = 0;
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (state == 1) {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
and then whenever you need to draw your view from an activity you can use the following:
myview.state = 1;
myview.invalidate();
In your code what you are doing is that you call a views function during the onCreate of your activity which in turn tries to use a null canvas because the onDraw method of your view has not be called during that time. Furthermore as others have pointed out you must not use a canvas object outside the onDraw method.
Hope this helps...
I changed your code with following.
public class ShapesView extends View {
Paint mypaint;
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
mypaint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
When you want to draw, call shapesview.invalidate();
Look at method's name its onDraw() and not getCanvas(). The documentation also doesn't makes any claims about the Canvas provided.
After onDraw() is done, that canvas may be disposed, its bitmap/buffer may be re-cycled, who knows.
So, it is not safe to use the Canvas outside of this method. Draw what you want, but only inside onDraw() method.
If you want to trigger the View to re-draw, at some other time, call invalidate().
Example:
View class to render any shape:
public class ShapeView extends View {
private Paint mPaint;
private ShapeRenderer mRenderer;
public ShapeView(Context context) {
super(context);
init();
}
public ShapeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ShapeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
mPaint = new Paint();
}
public void setPaintColor(int color){
mPaint.setColor(color);
}
public void setPaintStrokeWidth(float width){
mPaint.setStrokeWidth(width);
}
public void setRenderer(ShapeRenderer renderer) {
mRenderer = renderer;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mRenderer != null){
mRenderer.drawShape(canvas,mPaint);
}
}
public static interface ShapeRenderer{
public void drawShape(Canvas canvas, Paint paint);
}
}
A Class that draws a rectangle:
public class RectRenderer implements ShapeView.ShapeRenderer {
#Override
public void drawShape(Canvas canvas, Paint paint) {
canvas.drawRect(0,0,100,100,paint);
}
}
Draw a shape at runtime:
myShapeView.setPaintColor(Color.GREEN);
myShapeView.setPaintStroke(5f);
myShapeView.setRenderer(new RectRenderer());
myShapeView.invalidate();
I think you must redesign your component, so it can draw itself in onDraw method. If you need some class that must contain Canvas, you can try to do something like this.
class Drawer {
private Canvas canvas;
public Drawer(Canvas canvas) {
this.canvas = canvas;
}
public void DrawRectangle() {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}
On yours custom view you can do something like this.
public class ShapesView extends View{
public ShapesView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Drawer drawer = new Drawer(canvas);
drawer.DrawRectangle();
}
public void DrawRectangle() {
Paint mypaint = new Paint();
mypaint.setColor(Color.BLUE);
canvas.drawRect(30, 30, 200, 200, mypaint);
}
}

SurfaceView did not draw border

I have an android application showing coverflow animation.I want to use view extending surfaceview inside coverflow.But this code did not show anything.
public class CoverFlowView extends SurfaceView implements Callback {
public CoverFlowView(Context context) {
super(context);
}
public CoverFlowView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CoverFlowView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
MyThread myThread = new MyThread(holder);
myThread.setFlag(true);
myThread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint borderPaint = new Paint();
borderPaint.setARGB(255, 255, 128, 0);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(4);
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, borderPaint);
}
class MyThread extends Thread {
boolean flag;
SurfaceHolder myHolder;
public MyThread(SurfaceHolder holder) {
myHolder = holder;
}
public void setFlag(boolean myFlag) {
flag = myFlag;
}
public void run() {
Canvas canvas = null;
while (flag) {
try {
canvas = myHolder.lockCanvas();
synchronized (myHolder) {
invalidate();
}
} finally {
if (canvas != null) {
myHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
What is the problem here?I am new with Surfaceview.
Thanks in advance.
Rewrite your code like this , then it will work fine, i have checked it
public class CoverFlowView extends SurfaceView implements SurfaceHolder.Callback {
then change your constructor to
public CoverFlowView(Context context) {
super(context);
getHolder().addCallback(this);
}

SurfaceView drawing failure

I am trying an example having SurfaceView. I have inherited my class from SurfaceView and using as follows:
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
Context context;
MySurfaceViewThread mThread;
SurfaceHolder holder;
Paint paint;
int x = 20, y = 20, r = 10;
public void init() {
holder = getHolder();
holder.addCallback(this);
mThread = new MySurfaceViewThread(getHolder(), this);
paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setStrokeCap(Cap.ROUND);
paint.setStrokeWidth(1);
paint.setColor(Color.rgb(255, 255, 255));
}
public MySurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
this.context = context;
init();
}
public MySurfaceView(Context context, AttributeSet attr) {
super(context,attr);
this.context = context;
init();
}
public MySurfaceView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
this.context = context;
init();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
mThread.isRunning = false;
while (true) {
try {
mThread.join();
break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// canvas.drawColor(0, Mode.CLEAR);
x += 2;
y += 2;
r += 3;
canvas.drawColor(Color.rgb(x%255, y%255, (x+y)%255));
canvas.drawCircle(x, y, r, paint);
canvas.drawText("x:"+x, 100, 100, paint);
Log.d("onDraw","onDraw()"+"x:"+x + ",y:"+y+",r:"+r);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.d("surfaceCreated", "surfaceCreated()");
mThread.isRunning = true;
mThread.start();
}
}
I am updating my Canvas from a Thread which is:
public class MySurfaceViewThread extends Thread {
SurfaceHolder holder;
MySurfaceView surfaceView;
boolean isRunning = false;
public MySurfaceViewThread(SurfaceHolder holder, MySurfaceView surfaceView) {
Log.d("thread","thread constructor");
this.holder = holder;
this.surfaceView = surfaceView;
}
#Override
public void run() {
Log.d("run","run()");
while(isRunning) {
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
synchronized(holder) {
surfaceView.onDraw(canvas);
}
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
holder.unlockCanvasAndPost(canvas);
Log.d("canvas status", "canvas unlocaked...");
}
}
}
}
When application starts and its onDraw() is called, it draws circle and text as well but on further calls to onDraw() it draws nothing. Means nothing changed on screen after first update. Any idea where I am getting wrong? I am new to android and slow learner as well.
I got the answer. Its very strange. I don't know why this worked.
I commented the following line from my activity and it runs.
surfaceView.setBackgroundColor(Color.rgb(0, 255, 0));
the activity code is:
public class SurfaceViewTutorialActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MySurfaceView surfaceView = new MySurfaceView(this);
// surfaceView.setBackgroundColor(Color.rgb(0, 255, 0));
surfaceView.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher));
setContentView(surfaceView);
}
}
If any one knows about this, kindly guide me to the right direction.

Categories

Resources