วันอาทิตย์ที่ 5 มกราคม พ.ศ. 2557

Android Graphics : SurfaceView



Android Graphics : SurfaceView
SurfaceView class is a View that handles a Surface, another class of the Android API.

What is a Surface? 
It’s an abstraction of a raw buffer that is used by the screen compositor for rendering that specific View. The screen compositor is the mastermind behind all rendering on Android, and is ultimately responsible for pushing all pixels to the GPU. The Surface can be hardware accelerated in some cases. We don’t care that much about that fact, though. All we need to know is that it is a more
direct way to render things to the screen.

Our goal is it to perform our rendering in a separate thread so that we do not hog the UI thread, which is busy with other things. The SurfaceView class provides us with a way to render to it from a thread other than the UI thread.

SurfaceHolder and Locking
In order to render to a SurfaceView from a different thread than the UI thread, we need to acquire an instance of the SurfaceHolder class, like this:

SurfaceHolder holder = surfaceView.getHolder();

The SurfaceHolder is a wrapper around the Surface, and does some bookkeeping for us. It provides us with two methods:

Canvas SurfaceHolder.lockCanvas();
SurfaceHolder.unlockAndPost(Canvas canvas);

The first method locks the Surface for rendering and returns a nice Canvas instance we can use. The second method unlocks the Surface again and makes sure that what we’ve drawn via the Canvas gets displayed on the screen. We will use these two methods in our rendering thread to acquire the Canvas, render with it, and finally make the image we just rendered visible on the screen. The Canvas we have to pass to the SurfaceHolder.unlockAndPost() method must be the one we received from the SurfaceHolder.lockCanvas() method.

The Surface is not immediately created when the SurfaceView is instantiated. Instead it is created asynchronously. The surface will be destroyed each time the activity is paused and recreated when the activity is resumed again.

SurfaceViewTest.java sourcecode


package android.example.surfaceview;

import android.os.Bundle;
import android.app.Activity;
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.Paint.Style;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class SurfaceViewTest extends Activity {

      FastRenderView renderView;
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_surface_view);
       
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
            renderView = new FastRenderView(this);
            setContentView(renderView);
    }
   
    protected void onResume() {
      super.onResume();
      renderView.resume();
    }
   
    protected void onPause() {
      super.onPause();
      renderView.pause();
    }
   
   
    class FastRenderView extends SurfaceView implements Runnable {
     
      Thread renderThread = null;
      SurfaceHolder holder;
      Paint paint;
      Bitmap icon;
     
      volatile boolean running = false;
     
      public FastRenderView(Context context) {       
            super(context);        
            holder = getHolder();
            paint = new Paint();
           
            // Read from Drawable              
                  icon = BitmapFactory.decodeResource(getResources(),
                              R.drawable.ic_launcher);
      }
   
   
      public void resume() {
            running = true;
            renderThread = new Thread(this);
            renderThread.start();
      }
   
      public void pause() {
            running = false;
     
            while(true) {
     
                  try {
                        renderThread.join();
                  } catch (InterruptedException e) {
                  // retry
                  }
            }               
      }


      public void run() {
           
            while(running) {                   
                  if(!holder.getSurface().isValid())                   
                        continue;
                        Canvas canvas = holder.lockCanvas();
                        canvas.drawRGB(255, 0, 0);
                       
                        // Draw Line
                  paint.setColor(Color.WHITE);
                  canvas.drawLine(0, 0, canvas.getWidth()-1, canvas.getHeight()-1, paint);                    
                       
                        // Draw Rectangle
                        paint.setStyle(Style.FILL);
                  paint.setColor(0x77ffffff);               // White                 
                  canvas.drawRect(100, 100, 400, 400, paint);
                 
                  // Draw Circle
                  paint.setStyle(Style.STROKE);
                  paint.setColor(0xff00ff00);               // Green
                  canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, 80, paint);
                 
                  canvas.drawBitmap(icon, 140, 500, null);
                        holder.unlockCanvasAndPost(canvas);
            }                      
      }
   
    }
  
}




Android Graphics SurfaceView Example Code

วันศุกร์ที่ 3 มกราคม พ.ศ. 2557

Android Graphics : Drawing Text


Android Graphics : Drawing Text

Loading Fonts
The Android API provides us with a class called Typeface that encapsulates a TrueType font. It provides a simple static method to load such a font file from the assets/

directory:
Typeface font = Typeface.createFromAsset(context.getAssets(), "font.ttf");

Interestingly enough, this method does not throw any kind of Exception if the font file can’t be loaded. Instead a RuntimeException is thrown. Why no explicit exception is thrown for this method is a bit of a mystery to me.

Drawing Text with a Font
Once we have our font, we set it as the Typeface of a Paint instance:
paint.setTypeFace(font);

Via the Paint instance, we also specify the size we want to render the font at:
paint.setTextSize(30);

The documentation of this method is again a little sparse. It doesn’t tell whether the text size is given in points or pixels. We just assume the latter.

Finally, we can draw text with this font via the following Canvas method:
canvas.drawText("This is a test!", 100, 100, paint);

MainActivity.java sourcecode


package android.graphics.drawText;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {

      @Override
      protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
           
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
            setContentView(new RenderView(this));
      }

      class RenderView extends View {
            Paint paint;
            //Typeface font;
            Bitmap icon;
            Rect bounds = new Rect();

            public RenderView(Context context) {
                  super(context);
                  paint = new Paint();
            }

            protected void onDraw(Canvas canvas) {
                             
                  paint.setColor(Color.BLUE);
                  paint.setTypeface(Typeface.SERIF);
                  paint.setTextSize(60);
                  paint.setTextAlign(Paint.Align.CENTER);              
                  canvas.drawText("Drawing Text Test", canvas.getWidth() / 2, 100,paint);
                  paint.setTextSize(42);
                  canvas.drawText("Paint.Align.CENTER", canvas.getWidth() / 2, 150,paint);
                 
                  String text = "Paint.Align.RIGHT.";
                  paint.setColor(Color.MAGENTA);
                  paint.setTextSize(42);
                  paint.setTextAlign(Paint.Align.RIGHT);               
                  paint.getTextBounds(text, 0, text.length(), bounds);
                  canvas.drawText(text, canvas.getWidth()/2, 250,paint);
                 
                  String text2 = "Paint.Align.LEFT.";
                  paint.setColor(Color.RED);
                  paint.setTextSize(42);
                  paint.setTextAlign(Paint.Align.LEFT);
                  paint.getTextBounds(text2, 0, text2.length(), bounds);
                  canvas.drawText(text2, canvas.getWidth() - bounds.width(), 350,paint);
                  invalidate();                
            }
      }
}

วันพฤหัสบดีที่ 2 มกราคม พ.ศ. 2557

Android Graphics : Drawing Bitmaps


Android Graphics : Drawing Bitmaps

Using Bitmaps
While making a game with basic shapes such as lines or circles is a possibility, it’s not exactly sexy. We want an awesome artist to create sprites and backgrounds and all that jazz for us, which we can then load from PNG or JPEG files. Doing this on Android is extremely easy.

Loading and Examining Bitmaps
The Bitmap class will become our best friend. We load a bitmap from a file by using the BitmapFactory singleton. As we store our images in the form of assets, let’s see how we can load an image from the assets/ directory:

InputStream inputStream = assetManager.open("android.png");
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

The Bitmap class itself has a couple of methods that are of interest to us. First we want
to get to know its width and height in pixels:

int width = bitmap.getWidth();
int height = bitmap.getHeight();

Drawing Bitmaps
Once we have loaded our bitmaps, we can draw them via the Canvas. The easiest method to do this looks as follows:

Canvas.drawBitmap(Bitmap bitmap, float topLeftX, float topLeftY, Paint paint);

The first argument should be obvious. The arguments topLeftX and topLeftY specify the coordinates on the screen where the top-left corner of the bitmap will be placed. The last argument can be null. We could specify some very advanced drawing parameters with the Paint, but we don’t really need those.

There’s another method that will come in handy, as well:

Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint);

This method is super-awesome. It allows us to specify a portion of the Bitmap to draw via the second parameter. The Rect class holds the top-left and bottom-right corner coordinates of a rectangle. When we specify a portion of the Bitmap via the src, we do it in the Bitmap’s coordinate system. If we specify null, the complete Bitmap will be used.

Enum Values
Bitmap.Config ALPHA_8 Each pixel is stored as a single translucency (alpha) channel. 
Bitmap.Config ARGB_4444 This field was deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead.  
Bitmap.Config ARGB_8888 Each pixel is stored on 4 bytes. 
Bitmap.Config RGB_565 Each pixel is stored on 2 bytes and only the RGB channels are encoded: red is stored with 5 bits of precision (32 possible values), green is stored with 6 bits of precision (64 possible values) and blue is stored with 5 bits of precision. 
Original on developer.android.com
http://developer.android.com/reference/android/graphics/Bitmap.Config.html

DrawingBitmapsActivity.java sourcecode

package android.example.drawingbitmap;

import java.io.IOException;
import java.io.InputStream;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class DrawingBitmapActivity extends Activity {
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(new RenderView(this));      
    }

    class RenderView extends View {
      Bitmap cow_565;
      Bitmap android_8888;
      Bitmap icon;
      Rect dst = new Rect();
   
      public RenderView(Context context) {
            super(context);
     
            try {
                  // Read from res/assets/***.png
                  AssetManager assetManager = context.getAssets();
                 
                  InputStream inputStream = assetManager.open("cow.png");
                  cow_565 = BitmapFactory.decodeStream(inputStream);
                  inputStream.close();
                             
                  inputStream = assetManager.open("android_logo.png");
                  BitmapFactory.Options options = new BitmapFactory.Options();
                  options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                  android_8888 = BitmapFactory.decodeStream(inputStream, null, options);              
                  inputStream.close();
                 
                  // Read from Drawable
                  BitmapFactory.Options opt = new BitmapFactory.Options();
                  options.inJustDecodeBounds = true;
                  icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, opt);
                 
                 
            } catch (IOException e) {
                  // silently ignored, bad coder monkey, baaad!
            } finally {
                  // we should really close our input streams here.
            }
      }
           
      protected void onDraw(Canvas canvas) {
            dst.set(50, 50, 350, 350);
            canvas.drawBitmap(cow_565, null, dst, null);         
            canvas.drawBitmap(android_8888, 50, 400, null);
            canvas.drawBitmap(icon, 140, 750, null);
            invalidate();
      }
   
    } // RenderView
}




Download Android Graphics Drawing Bitmaps Example code