title: Displaying marquee-like scrolling text in Android
body: <p>I'm working on Android app which main purpose is to display CNN breaking news styled bar on the bottom and some pictures. I created two custom views, one for displaying photos and the second one for displaying bar. It displays one picture at a time for specified amount of time and swaps current picture with next one from the queue.</p>
  
  <p>To animate text in bottom bar I used canvas, onDraw() and handler.postDelayed. This solution gives poor result. Text movement is not smooth especially when it comes to swap image.</p>
  
  <p>What should I use instead of canvas? Is there any OpenGL-based lib which could make this task relatively painless? I tried to use AndEngine, but its lack of documentation and problems with threads discouraged me to working with it anymore.</p>
  
  <pre><code>public class Infobar extends View {
  
      private List&lt;Message&gt; messages;
  
      private Handler handler;
  
      private Paint boxPaint;
      private Paint defaultTextPaint;
      private Paint importantTextPaint;
  
      private long offset = 0;
      private long maxOffset = 1000;
      private int textWidth = 1000;
      private int textHeight = 50;
  
      private int measuredWidth;
      private int measuredHeight;
  
      private Runnable animateRunnable = new Runnable() {
          public void run() {
              animateMessage();
          }
      };
  
      long startTime = new Date().getTime();
  
      private int backgroundCol = Color.parseColor("#ffff00");
      private int textColor = Color.parseColor("#000000");
  
      private static final int FRAME_DELAY = 10;
      private static final int FRAME_SHIFT    = 3;
  
      private static final int EMPTY_SPACE = 2;
      private static final String SEPARATOR = "  ✩  ";
  
      private static final int TEXT_SIZE = 35;
  
  
      public Infobar(Context context, AttributeSet attrs) {
          super(context, attrs);
  
          handler = new Handler();
          messages = new ArrayList&lt;Message&gt;();
  
          boxPaint = new Paint();
  
          defaultTextPaint = new Paint();
          defaultTextPaint.setColor(getResources().getColor(R.color.info_bar_default_text_color));
  
          importantTextPaint = new Paint();
          importantTextPaint.setColor(getResources().getColor(R.color.info_bar_important_text_color));
  
          handler.postDelayed(animateRunnable, FRAME_DELAY);
  
      }
  
      public void setMessagesList(List&lt;Message&gt; list) {
          messages = list;
      }
  
      public void setBackgroundColor(String color) {
          backgroundCol = Color.parseColor(color);
      }
  
      public void setTextColor(String color) {
          textColor = Color.parseColor(color);
      }
  
      public List&lt;Message&gt; getMessagesList() {
          return messages;
      }
  
      private String getMessagesString() {
          StringBuilder builder = new StringBuilder();
          for(Message message : messages) {
              builder.append(message.content);
              if(messages.indexOf(message) != (messages.size() - 1)) {
                  builder.append(SEPARATOR);
              }
          }
          return builder.toString();
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);   
          measuredWidth = getMeasuredWidth();
          measuredHeight = getMeasuredHeight();
  
      }
  
      @Override
      protected void onDraw(Canvas canvas) {
  
          drawBackground(canvas, false);
          drawMessage(canvas, getMessagesString());
  
  
          super.onDraw(canvas);
      }
  
      private void drawBackground(Canvas canvas, boolean important) {
  
          boxPaint.setColor(backgroundCol) ;
  
          canvas.drawRect(0, 0, measuredWidth, measuredHeight, boxPaint);
      }
  
      private void drawMessage(Canvas canvas, String message) {
          defaultTextPaint.setTextSize(TEXT_SIZE);
  
          Rect bounds = new Rect();
          defaultTextPaint.getTextBounds(message, 0, message.length(), bounds);
          defaultTextPaint.setColor(textColor);
  
          textWidth = bounds.width();
          textHeight = bounds.height();
  
          int positionX = measuredWidth - (int)offset;
          int positionY = measuredHeight - textHeight/2;      
  
          if(offset  &gt; (measuredWidth + textWidth)) {
              offset = 0;
              positionX = measuredWidth;
          }
  
          canvas.drawText(message, positionX, positionY, defaultTextPaint);
      }
  
      private void animateMessage() {
          offset += FRAME_SHIFT; 
          //offset = Math.round((new Date().getTime() - startTime) * 0.2) % (measuredWidth + textWidth);
          invalidate();
          handler.removeCallbacks(animateRunnable);
          handler.postDelayed(animateRunnable, FRAME_DELAY);
      }
  
  
  
  }
  </code></pre>