Search code examples
androidandroid-custom-view

Why invalidate is not working?


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);
  }

Solution

  • 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.