title: Android Animated Bar Chart (invalidate())
body: <p>I am very novice in android drawing and animation on android. I have a bar chart and I want to animate the bars (bottom-up drawing). I know that invalidate() within onDraw() is a good manner to do it but until now I fails to do it...</p>
  
  <p>import android.graphics.Path;
  import android.graphics.Region;</p>
  
  <p>public class Bar {</p>
  
  <pre><code>private int color;
  private String name;
  private float value;
  private Path path;
  private Region region;
  
  public int getColor() {
      return color;
  }
  
  public void setColor(int color) {
      this.color = color;
  }
  
  public String getName() {
      return name;
  }
  
  public void setName(String name) {
      this.name = name;
  }
  
  public float getValue() {
      return value;
  }
  
  public void setValue(float value) {
      this.value = value;
  }
  
  public Path getPath() {
      return path;
  }
  
  public void setPath(Path path) {
      this.path = path;
  }
  
  public Region getRegion() {
      return region;
  }
  
  public void setRegion(Region region) {
      this.region = region;
  }
  </code></pre>
  
  <p>}</p>
  
  <pre><code>import java.text.DecimalFormat;
  import java.util.ArrayList;
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Bitmap.Config;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.Rect;
  import android.graphics.drawable.NinePatchDrawable;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.view.View;
  
  public class BarGraph extends View {
  
      private ArrayList&lt;Bar&gt; bars = new ArrayList&lt;Bar&gt;();
      private Paint p = new Paint();
      private Path path = new Path();
      private Rect r;
      private boolean showBarText = true;
      private int indexSelected = -1;
      private OnBarClickedListener listener;
      private Bitmap fullImage;
      private boolean shouldUpdate = false;
      private String unit = "";
      private DecimalFormat df = new DecimalFormat("#.#");
      private Boolean append = false;
      private Rect r2 = new Rect();
      private Rect r3 = new Rect();
  
  
  
      private boolean shouldCacheToBitmap;
  
      public BarGraph(Context context) {
          super(context);
      }
  
      public BarGraph(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public void setShowBarText(boolean show) {
          showBarText = show;
      }
  
      public void setBars(ArrayList&lt;Bar&gt; bars) {
          this.bars = bars;
          postInvalidate();
      }
  
      public void setUnit(String unit) {
          this.unit = unit;
      }
  
      public String getUnit() {
          return this.unit;
      }
  
      public void appendUnit(Boolean doAppend) {
          this.append = doAppend;
      }
  
      public Boolean isAppended() {
          return this.append;
      }
  
      public ArrayList&lt;Bar&gt; getBars() {
          return this.bars;
      }
  
      public void drawToCanvas(Canvas canvas) {
          canvas.drawColor(Color.TRANSPARENT);
          NinePatchDrawable popup = (NinePatchDrawable) this.getResources()
                  .getDrawable(R.drawable.popup_black);
  
          float maxValue = 0;
          float padding = 7;
          int selectPadding = 4;
          float bottomPadding = 40;
  
          float usableHeight;
          if (showBarText) {
              this.p.setTextSize(40);
              this.p.getTextBounds("$", 0, 1, r3);
              usableHeight = getHeight() - bottomPadding
                      - Math.abs(r3.top - r3.bottom) - 26;
          } else {
              usableHeight = getHeight() - bottomPadding;
          }
  
          p.setColor(Color.BLACK);
          p.setStrokeWidth(2);
          p.setAlpha(50);
          p.setAntiAlias(true);
  
          canvas.drawLine(0, getHeight() - bottomPadding + 10, getWidth(),
                  getHeight() - bottomPadding + 10, p);
  
          float barWidth = (getWidth() - (padding * 2) * getBars().size())
                  / getBars().size();
  
          for (Bar p : getBars()) {
              maxValue = Math.max(maxValue, p.getValue());
          }
  
          r = new Rect();
  
          // path.reset();
  
          int count = 0;
          for (Bar p : getBars()) {
              r.set((int) ((padding * 2) * count + padding + barWidth * count),
                      (int) (getHeight() - bottomPadding - (usableHeight * (p
                              .getValue() / maxValue))), (int) ((padding * 2)
                              * count + padding + barWidth * (count + 1)),
                      (int) (getHeight() - bottomPadding));
  
              // path.addRect(new RectF(r.left - selectPadding, r.top -
              // selectPadding, r.right + selectPadding, r.bottom +
              // selectPadding), Path.Direction.CW);
              // p.setPath(path);
              // p.setRegion(new Region(r.left - selectPadding, r.top -
              // selectPadding, r.right + selectPadding, r.bottom +
              // selectPadding));
  
              this.p.setColor(p.getColor());
              this.p.setAlpha(255);
              canvas.drawRect(r, this.p);
              this.p.setStyle(Paint.Style.STROKE);
              this.p.setStrokeWidth(2);
              this.p.setColor(Util.getStrokeColor(p.getColor()));
              canvas.drawRect(r, this.p);
              this.p.setStyle(Paint.Style.FILL);
              this.p.setStrokeWidth(0);
              this.p.setColor(p.getColor());
              this.p.setTextSize(Util.dipToPixels(getContext(), 11));
              canvas.drawText(p.getName(),
                      (int) (((r.left + r.right) / 2) - (this.p.measureText(p
                              .getName()) / 2)), getHeight() - 5, this.p);
              if (showBarText) {
                  this.p.setTextSize(16);
                  this.p.setColor(Color.WHITE);
                  this.p.getTextBounds(unit + df.format(p.getValue()), 0, 1, r2);
                  if (popup != null)
                      popup.setBounds(
                              (int) (((r.left + r.right) / 2) - (this.p
                                      .measureText(unit + df.format(p.getValue())) / 2)) - 14,
                              r.top + (r2.top - r2.bottom) - 26,
                              (int) (((r.left + r.right) / 2) + (this.p
                                      .measureText(unit + df.format(p.getValue())) / 2)) + 14,
                              r.top);
                  popup.draw(canvas);
                  if (isAppended())
                      canvas.drawText(
                              df.format(p.getValue()) + unit,
                              (int) (((r.left + r.right) / 2) - (this.p
                                      .measureText(unit + df.format(p.getValue())) / 2)),
                              r.top - 20, this.p);
                  else
                      canvas.drawText(
                              unit + df.format(p.getValue()),
                              (int) (((r.left + r.right) / 2) - (this.p
                                      .measureText(unit + df.format(p.getValue())) / 2)),
                              r.top - 20, this.p);
              }
              count++;
          }
  
      }
  
      @Override
      public void onDraw(Canvas ca) {
          if (!shouldCacheToBitmap) {
              drawToCanvas(ca);
              return;
          }
          if (fullImage == null || shouldUpdate) {
              fullImage = Bitmap.createBitmap(getWidth(), getHeight(),
                      Config.ARGB_8888);
              Canvas canvas = new Canvas(fullImage);
              drawToCanvas(canvas);
  
              shouldUpdate = false;
  
          }
  
          ca.drawBitmap(fullImage, 0, 0, null);
          invalidate();
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          return true;
      }
  
      public void setOnBarClickedListener(OnBarClickedListener listener) {
          this.listener = listener;
      }
  
      public boolean isShouldCacheToBitmap() {
          return shouldCacheToBitmap;
      }
  
      public void setShouldCacheToBitmap(boolean shouldCacheToBitmap) {
          this.shouldCacheToBitmap = shouldCacheToBitmap;
      }
  
      public interface OnBarClickedListener {
          abstract void onClick(int index);
      }
  }
  </code></pre>