Mobile Info

Controlling Hardware Acceleration of individual View by calling setLayerType() method

http://latest-mobileinfo.blogspot.com/2014/05/controlling-hardware-acceleration-of.html

Beginning in Android 3.0 (API level 11), you have more control on how and when to use layers with the View.setLayerType() method. This API takes two parameters: the type of layer you want to use and an optional Paint object that describes how the layer should be composited... ... ... A view can use one of three layer types:
  • LAYER_TYPE_NONE: The view is rendered normally and is not backed by an off-screen buffer. This is the default behavior.
  • LAYER_TYPE_HARDWARE: The view is rendered in hardware into a hardware texture if the application is hardware accelerated. If the application is not hardware accelerated, this layer type behaves the same as LAYER_TYPE_SOFTWARE.
  • LAYER_TYPE_SOFTWARE: The view is rendered in software into a bitmap.

You can disable hardware acceleration for an individual view at runtime with the following code:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

Note: You currently cannot enable hardware acceleration at the view level. View layers have other functions besides disabling hardware acceleration.

reference: http://developer.android.com/guide/topics/graphics/hardware-accel.html#controlling

To varify if a View is Hardware Accelerated, we can call myView.isHardwareAccelerated() and/or Canvas.isHardwareAccelerated().
  • View.isHardwareAccelerated() returns true if the View is attached to a hardware accelerated window.
  • Canvas.isHardwareAccelerated() returns true if the Canvas is hardware accelerated
If you must do this check in your drawing code, use Canvas.isHardwareAccelerated() instead of View.isHardwareAccelerated() when possible.

reference: http://developer.android.com/guide/topics/graphics/hardware-accel.html#determining

Also have to be noticed is when hardware accelerated, some operation is unsupported, depends on API levels. That's why we have to disable Hardware Acceleration.

reference: http://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported


In this example, we call setLayerType() method programatically, to set various layer type, by user selection. And show isHardwareAccelerated() of both myBiew and canvas. Also I count the processing time of our drawing in nano-second for reference, such that we can know how the hardware acceleration affect the performance.


MainActivity.java
package com.example.androiddrawpath;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.RadioButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends Activity {

 SeekBar radiusBar;
 MyView myView;
 
 SeekBar ptBar;
 TextView textPt;
 final static int MIN_PT = 3;
 
 RadioButton optLayerTypeNone, optLayerTypeSoftware, optLayerTypeHardware;
 TextView textLayerInfo;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  radiusBar = (SeekBar) findViewById(R.id.radiusbar);
  myView = (MyView) findViewById(R.id.myview);
  float defaultRatio = (float) (radiusBar.getProgress())
    / (float) (radiusBar.getMax());
  myView.setShapeRadiusRatio(defaultRatio);

  radiusBar.setOnSeekBarChangeListener(radiusBarOnSeekBarChangeListener);
  
  textPt = (TextView)findViewById(R.id.pttext);
  ptBar = (SeekBar)findViewById(R.id.ptbar);
  ptBar.setOnSeekBarChangeListener(ptBarOnSeekBarChangeListener);
  
  optLayerTypeNone = (RadioButton)findViewById(R.id.typeNone);
  optLayerTypeSoftware = (RadioButton)findViewById(R.id.typeSoftware);
  optLayerTypeHardware = (RadioButton)findViewById(R.id.typeHardware);
  textLayerInfo = (TextView)findViewById(R.id.typeinfo);
  
  myView.passElements(textLayerInfo);
  myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

  optLayerTypeNone.setOnCheckedChangeListener(optLayerTypeOnCheckedChangeListener);
  optLayerTypeSoftware.setOnCheckedChangeListener(optLayerTypeOnCheckedChangeListener);
  optLayerTypeHardware.setOnCheckedChangeListener(optLayerTypeOnCheckedChangeListener);
 };
 
 OnCheckedChangeListener optLayerTypeOnCheckedChangeListener = 
  new OnCheckedChangeListener(){

   @Override
   public void onCheckedChanged(CompoundButton buttonView,
     boolean isChecked) {
    if(optLayerTypeNone.isChecked()){
     myView.setLayerType(View.LAYER_TYPE_NONE, null);
    }else if(optLayerTypeSoftware.isChecked()){
     myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }else{
     myView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    }
    
    myView.invalidate();
   }};

 OnSeekBarChangeListener radiusBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener() {

  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
   float ratio = (float) (radiusBar.getProgress())
     / (float) (radiusBar.getMax());
   myView.setShapeRadiusRatio(ratio);
   myView.invalidate();
  }

  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {}

  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {}

 };
 
 OnSeekBarChangeListener ptBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener() {

  @Override
  public void onProgressChanged(SeekBar seekBar, int progress,
    boolean fromUser) {
   int pt = progress + MIN_PT;
   textPt.setText("number of point in polygon: " + String.valueOf(pt));
   myView.setNumberOfPoint(pt);
   myView.invalidate();
  }

  @Override
  public void onStartTrackingTouch(SeekBar seekBar) {}

  @Override
  public void onStopTrackingTouch(SeekBar seekBar) {}

 };

}

MyView.java
package com.example.androiddrawpath;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

public class MyView extends View {
 
 MyShape myShape;
 float ratioRadius;
 int numberOfPoint = 3; //default
 
 //corresponding to UI element
 TextView textLayerInfo;

 public MyView(Context context) {
  super(context);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  myShape = new MyShape();
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  
  long starting = System.nanoTime();
  
  int w = getWidth();
  int h = getHeight();
  
  if((w==0) || (h==0)){
   return;
  }
  
  float x = (float)w/2.0f;
  float y = (float)h/2.0f;
  float radius;
  if(w > h){
   radius = h * ratioRadius;
  }else{
   radius = w * ratioRadius;
  }
  
  myShape.setPolygon(x, y, radius, numberOfPoint);
  canvas.drawPath(myShape.getPath(), myShape.getPaint());
  
  long end = System.nanoTime();
  
  String info = "myView.isHardwareAccelerated() = " + isHardwareAccelerated() + "\n"
    + "canvas.isHardwareAccelerated() = " + canvas.isHardwareAccelerated() + "\n"
    + "processing time (reference only) : " + String.valueOf(end - starting) + " (ns)";
  textLayerInfo.setText(info);
  
 }
 
 public void setShapeRadiusRatio(float ratio){
  ratioRadius = ratio;
 }
 
 public void setNumberOfPoint(int pt){
  numberOfPoint = pt;
 }
 
 public void passElements(TextView textLayerInfo){
  this.textLayerInfo = textLayerInfo;
 }

}

MyShape.java
package com.example.androiddrawpath;

import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;

public class MyShape {

 private Paint paint;
 private Path path;

 public MyShape() {
  paint = new Paint();
  paint.setColor(Color.BLUE);
  paint.setStrokeWidth(3);
  paint.setStyle(Paint.Style.STROKE);
  
  path = new Path();
 }

 public void setCircle(float x, float y, float radius, Path.Direction dir){
  path.reset();
  path.addCircle(x, y, radius, dir);
 }
 
 public void setPolygon(float x, float y, float radius, int numOfPt){
  
  double section = 2.0 * Math.PI/numOfPt;
  
  path.reset();
  path.moveTo(
   (float)(x + radius * Math.cos(0)), 
   (float)(y + radius * Math.sin(0)));
  
  for(int i=1; i<numOfPt; i++){
   path.lineTo(
    (float)(x + radius * Math.cos(section * i)), 
    (float)(y + radius * Math.sin(section * i)));
  }
  
  path.close();
  
 }
 
 public Path getPath(){
  return path;
 }
 
 public Paint getPaint(){
  return paint;
 }
 
}

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.androiddrawpath.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="radius(%)"/>
    <SeekBar 
        android:id="@+id/radiusbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="50" />
    <TextView 
        android:id="@+id/pttext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="number of point in polygon: 3"/>
    <SeekBar 
        android:id="@+id/ptbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="0" />
    
    <RadioGroup
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
     <RadioButton android:id="@+id/typeNone"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content"
         android:text="LAYER_TYPE_NONE"/>
     <RadioButton android:id="@+id/typeSoftware"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content"
         android:text="LAYER_TYPE_SOFTWARE"/>
     <RadioButton android:id="@+id/typeHardware"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="wrap_content"
         android:text="LAYER_TYPE_HARDWARE"/>
    </RadioGroup>
    
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     <TextView 
         android:id="@+id/typeinfo"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true" />
     <com.example.androiddrawpath.MyView 
         android:id="@+id/myview"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
    </RelativeLayout>

</LinearLayout>

In order to call setLayerType(int layerType, Paint paint), View.isHardwareAccelerated() and Canvas.isHardwareAccelerated (), android:minSdkVersion in AndroidManifest.xml have to be set ="11" in .

download filesDownload the files.


More examples of Draw Path on canvas of custom View.

Copyright © 2014 Mobile Info Design by SHUKAKU4RT - All Rights Reserved