Android简明开发教程十九:线程 Bezier曲线

jerry Android 2015年08月24日 收藏

Android中使用线程Thread的方法和Java SE相同。和大多数OS系统一样,Android中也有称为UI Thread的主线程。UI Thread 主要用来给相应的Widget分发消息,包括绘制(Drawing)事件。UI Thread 也是用来处理用户交互事件的线程。比如:如果你按下屏幕上某个按钮,UI 线程则将Touch 事件通知对应的控件(Widgets),Widget 则将其状态设置成“按下”,并把“重绘”(Invalidate)事件发到Event Queue中去。 UI线程从Event Queue中读取事件后通知Widgets重画自身。 

如果你的应用设计不好的话, UI线程的这种单线程模式就会导致非常差的用户响应性能。特别是你将一些费时的操作如网络访问或数据库访问也放在UI线程中,这些操作会造成用户界面无反应,最糟糕的是,如果UI线程阻塞超过几秒(5秒),著名的ANR对话框就会出现: 

 

所以在设计应用时,需要把一些费时的任务使用单独的工作线程来运行避免阻塞UI线程,但是如果在工作线程中想更新UI线程的话,不能直接在工作线程中更新UI,这是因为UI线程不是“Thread Safe”。因此所有UI相关的操作一般必须在UI Thread中进行。 

Android OS提供了多种方法可以用在非UI线程访问UI线程。 

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)
  • Handler

Bezier 示例动态显示Bezier曲线,使用了Activity.runOnUiThread 来更新屏幕,完整代码如下: 

  1. public class Bezier extends Graphics2DActivity
  2. implements OnClickListener,Runnable{ 
  3.  
  4.  /**
  5.      * The animation thread.
  6.      */
  7.     private Thread thread;
  8.     private volatile boolean stopThread=false;
  9.     private boolean stopOrNot=false;
  10.     boolean drawn;
  11.     /**
  12.      * The random number generator.
  13.      */
  14.     static java.util.Random random = new java.util.Random();
  15.     /**
  16.      * The animated path
  17.      */
  18.     Path path = new Path();
  19.     /**
  20.      * Red brush used to fill the path.
  21.      */
  22.     SolidBrush brush = new SolidBrush(Color.RED);
  23.     private static final int NUMPTS = 6;
  24.     private int animpts[] = new int[NUMPTS * 2];
  25.     private int deltas[] = new int[NUMPTS * 2];
  26.     long startt, endt;
  27.    
  28.  private Button btnOptions;
  29.  @Override
  30.  protected void drawImage() {
  31.    drawDemo(100, 100);
  32.   
  33.  }
  34.  
  35.  public void onCreate(Bundle savedInstanceState) {
  36.   super.onCreate(savedInstanceState);
  37.   setContentView(R.layout.beziers);
  38.   graphic2dView
  39.      = (GuidebeeGraphics2DView) findViewById(R.id.graphics2dview);
  40.   btnOptions = (Button) findViewById(R.id.btnStopStart);
  41.   btnOptions.setOnClickListener(this);
  42.   reset(100,100);
  43.   if (thread == null) {
  44.             thread = new Thread(this);
  45.             thread.start();
  46.         }
  47.   
  48.  } 
  49.  
  50.  @Override
  51.  public void onClick(View view) {
  52.   
  53.   if(!stopOrNot){
  54.    btnOptions.setText("Start");
  55.       stopThread=true;
  56.   }
  57.   else{
  58.    stopThread=false;
  59.    btnOptions.setText("Stop");
  60.    if (thread == null) {
  61.              thread = new Thread(this);
  62.              thread.start();
  63.          }
  64.   }
  65.   stopOrNot=!stopOrNot;
  66.   
  67.  } 
  68.     /**
  69.      * Generates new points for the path.
  70.      */
  71.     private void animate(int[] pts, int[] deltas,
  72.       int i, int limit) {
  73.         int newpt = pts[i] + deltas[i];
  74.         if (newpt <= 0) {
  75.             newpt = -newpt;
  76.             deltas[i] = (random.nextInt() & 0x00000003)
  77.             + 2;
  78.         } else if (newpt >= limit) {
  79.             newpt = 2 * limit - newpt;
  80.             deltas[i] = -((random.nextInt() & 0x00000003)
  81.               + 2);
  82.         }
  83.         pts[i] = newpt;
  84.     } 
  85.  
  86.     /**
  87.      * Resets the animation data.
  88.      */
  89.     private void reset(int w, int h) {
  90.         for (int i = 0; i < animpts.length; i += 2) {
  91.             animpts[i + 0]
  92.                     = (random.nextInt() & 0x00000003)
  93.                     * w / 2;
  94.             animpts[i + 1]
  95.                     = (random.nextInt() & 0x00000003)
  96.                     * h / 2;
  97.             deltas[i + 0]
  98.                    = (random.nextInt() & 0x00000003)
  99.                    * 6 + 4;
  100.             deltas[i + 1]
  101.                    = (random.nextInt() & 0x00000003)
  102.                    * 6 + 4;
  103.             if (animpts[i + 0] > w / 2) {
  104.                 deltas[i + 0] = -deltas[i + 0];
  105.             }
  106.             if (animpts[i + 1] > h / 2) {
  107.                 deltas[i + 1] = -deltas[i + 1];
  108.             }
  109.         }
  110.     } 
  111.  
  112.     final Runnable updateCanvas = new Runnable() {
  113.   public void run() {
  114.    int offsetX = (graphic2dView.getWidth() -
  115.      SharedGraphics2DInstance.CANVAS_WIDTH) / 2;
  116.    int offsetY = (graphic2dView.getHeight()
  117.      - SharedGraphics2DInstance.CANVAS_HEIGHT) / 2;
  118.    graphic2dView.invalidate(offsetX,offsetY,
  119.      offsetX+100,offsetY+100);
  120.   }
  121.  };
  122.     /**
  123.      * Sets the points of the path and draws and fills the path.
  124.      */
  125.     private void drawDemo(int w, int h) {
  126.         for (int i = 0; i < animpts.length; i += 2) {
  127.             animate(animpts, deltas, i + 0, w);
  128.             animate(animpts, deltas, i + 1, h);
  129.         }
  130.         //Generates the new pata data.
  131.         path.reset();
  132.         int[] ctrlpts = animpts;
  133.         int len = ctrlpts.length;
  134.         int prevx = ctrlpts[len - 2];
  135.         int prevy = ctrlpts[len - 1];
  136.         int curx = ctrlpts[0];
  137.         int cury = ctrlpts[1];
  138.         int midx = (curx + prevx) / 2;
  139.         int midy = (cury + prevy) / 2;
  140.         path.moveTo(midx, midy);
  141.         for (int i = 2; i <= ctrlpts.length; i += 2) {
  142.             int x1 = (curx + midx) / 2;
  143.             int y1 = (cury + midy) / 2;
  144.             prevx = curx;
  145.             prevy = cury;
  146.             if (i < ctrlpts.length) {
  147.                 curx = ctrlpts[i + 0];
  148.                 cury = ctrlpts[i + 1];
  149.             } else {
  150.                 curx = ctrlpts[0];
  151.                 cury = ctrlpts[1];
  152.             }
  153.             midx = (curx + prevx) / 2;
  154.             midy = (cury + prevy) / 2;
  155.             int x2 = (prevx + midx) / 2;
  156.             int y2 = (prevy + midy) / 2;
  157.             path.curveTo(x1, y1, x2, y2, midx, midy);
  158.         }
  159.         path.closePath();
  160.         // clear the clipRect area before production 
  161.  
  162.         graphics2D.clear(Color.WHITE);
  163.         graphics2D.fill(brush, path); 
  164.  
  165.         this.runOnUiThread(updateCanvas); 
  166.     }
  167.    
  168.     
  169.  
  170.     public void run() {
  171.         Thread me = Thread.currentThread(); 
  172.  
  173.         if (!drawn) {
  174.             synchronized (this) {
  175.                 graphics2D.clear(Color.WHITE);
  176.                 graphics2D.fill(brush, path);
  177.                 graphic2dView.refreshCanvas();
  178.                 drawn = true;
  179.             }
  180.         }
  181.         while (thread == me && !stopThread) {
  182.             drawDemo(100,100);
  183.         }
  184.         thread = null;
  185.     }
  186. }

 

除了上述的方法外,Android还提供了AsyncTask类以简化工作线程与UI线程之间的通信。这里不详述。此外,上面Bezier曲线动画在屏幕上显示时有闪烁的现象,这是动态显示图像的一个常见问题,后面将专门讨论。