2015年11月30日 星期一

Resource ID


在 Android 中,只要你將一個圖檔 (例如 flag.png) 放入到 res/drawable 目錄中,Android Studio 就會自動幫你為這圖檔產生個 ID 名和值 (int) (ID 名和檔案名是相同的,在此例就是 R.drawable.flag),並定義在 R.java 中

一般使用:圖檔 (例如 flag.png) 放入到 res/drawable 目錄中,並透過 R.drawable.flag 取得id
取得單一圖檔的 Drawable 物件: flag.png
Drawable dw = getResources().getDrawable(R.drawable.flag);
取得多圖擋的Drawable物件: flag1~10.png
var Drawable[] dw = new Drawable[10];
for (int i = 1; i <= 10; i++) {
  int id = getResources().getIdentifier(
                    "flag" + i,
                    "drawable", getPackageName());
  dw[i-1] = getResources().getDrawable(id);
}


參考資料:


SurfaceView

SurfaceView 介面最適合開發顯示速率較快的應用程式。
一張靜態的圖是看不出 SurfaceView 的能力的, 如果要讓圖 "動"起來, 則還需要配合執行緒的運用, 
透過執行緒不斷去改變圖片的座標並反覆貼上圖片, 就可以達到動態的效果。

Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系:
1. Surface:
    屏幕显示内容合成器(screen compositor)所管理的原生缓冲器。
    用来画图形(graphics)或图像(image)的地方。
    常画图是在一个Canvas对象上面进行。
2. SurfaceView:
    通过SurfaceView就可以看到Surface的部分或者全部的内容
    
     
3. SurfaceHolder:
    一个接口,其作用就像一个关于Surface的监听器。
    提供访问和控制SurfaceView背后的Surface 相关的方法
    通过三个回调方法,让我们可以感知到Surface的创建 surfaceCreated、销毁 surfaceDestroyed 或者改变 surfaceChanged 

參考資料:



在 canvas 畫布上貼圖的三個步驟:
1. 鎖住畫布 : canvas = holder.lockCanvas(); 
2. 在畫布上貼圖 : canvas.drawBitmap(bp,x,y,null);
3. 解鎖並po出畫布 : holder.unlockCanvasAndPost(canvas);

SurfaceHolder.Callback 相關聯的三個程序 :
public void surfaceCreated(...) -- surface 建立時
public void surfaceChanged(...) -- surface 改變時
public void surfaceDestroyed(...) -- surface 結束時


參考資料:



用 SurfaceView 與 View 繪圖的差異性:
1. SurfaceView :是在一個新起的單獨執行緒中可以重新繪製畫面
     - 主動更新。比如一個人在一直跑動。這就需要一個單獨的 Thread 不停的重繪人的狀態,避免阻塞主 UI Thread。 (如: 拉霸台 App, 太陽系 App)
2. View :必須在 UI 的主執行緒中更新畫面。
     - 被動更新畫面的。比如棋類,這種用 View 就好了。
     - 因為畫面的更新是賴於 onTouch 來更新,可以直接使用 invalidate。因為這種情況下,這一次 Touch 和下一次的 Touch 需要的時間比較長些,不會產生影響。           (如: 繪圖 App)

在 UI 的主執行緒中更新畫面,可能會引發問題,比如你更新畫面的時間過長,那麼你的主 UI 執行緒會被你正在畫的函數阻塞,那麼將無法回應按鍵,觸屏等消息。
SurfaceView 具有雙緩衝機制。因此,開發遊戲時儘量使用 SurfaceView 而不要使用 View,這樣的話效率較高,而且 SurfaceView 的功能也更加完善。


參考資料:



SurfaceView 刷新特定區域:
內部矩形:比如surfaceview上绘制旋转图片的时候,只刷新一个特定的矩形区域就可以了。
在 "鎖住畫布" 步驟中,加入要刷新的矩形區域:
canvas = holder.lockCanvas(new Rect(0, 0, 130, 130)); //获取画布 像这种带rect参数的就是脏矩形的刷新。


參考資料:


2015年11月29日 星期日

Program, Process, Thread

----------------------------------------------------------------------
1. Program 與 Process 觀念:
----------------------------------------------------------------------
Program:
一群程式碼的集合,用以解決特定的問題。以物件導向的觀念來類比,相當於Class。

Process:
由Program所產生的執行個體,一個Program可以同時執行多次,產生多個Process。以物件導向的觀念來類比,相當於Object。每一個Process又由以下兩個東西組成
    1. 一個Memory Space。相當於Object的variable,不同Process的Memory Space也不同,彼此看不到對方的Memory Space。
    2. 一個以上的Thread。Thread代表從某個起始點開始(例如main),到目前為止所有函數的呼叫路徑,以及這些呼叫路徑上所用到的區域變數。當然程式的執行狀態,除了紀錄在主記憶體外,CPU內部的暫存器(如Program Counter, Stack Pointer, Program Status Word等)也需要一起紀錄。所以Thread又由下面兩項組成
            a. Stack:紀錄函數呼叫路徑,以及這些函數所用到的區域變數
            b. 目前CPU的狀態


參考資料:







----------------------------------------------------------------------
2. Thread的使用
----------------------------------------------------------------------
- Thread 線程:
指運行中的程序的調度單位。一個線程指的是進程(Processes)中一個單一順序的控制流。它是系統獨立調度和分派的基本單位。同一進程中的多個線程將共享該進程中的全部系統資源,比如文件描述符和信號處理等等。 一個進程可以有很多線程,每個線程并行執行不同的任務。.所以意思就是當主程式在進行時,同時又需要去執行別的任務,就可以另起一個Thread(線程),將要執行的內容在背後運作,等到執行完成後,再把結果秀出來

 - Android Thread:
    1. Android於Thread有一個重要的限制「Android為了考量安全性和執行效能,不允許副執行緒直接更改主畫面的資料」,也就是說在我們另起的Thread都沒辦法做出如:將畫面變色與改變文字內容等畫面上的行為,而解決方法就是利用"Handler"來做溝通的橋樑。
    2. MainThread(UI Thread)和Worker Thread:
        a. 所有与UI有关的代码写在MainThread当中。
        b. 主线程不能阻塞,主线程一般不进行耗时较长的操作,比如联网下载,读取大文件等。

 - Android Activity 間传递数据的方法:
Intent对象的基本概念:
    1. Intent是Android应用程序组件之一
    2. Intent对象在Android系统中表示一种意图
    3. Intent当中最重要的内容是action与data
使用Intent在Activity之间传递数据的方法:
    1. 在Activity之间可以通过Intent对象传递数据
    2. 使用putExtra()系列方法向Intent对象当中存储数据
    3. 使用getXXXExtra()系列方法从Intent对象当中取出数据

 - 整個流程是:.
1. 主執行緒開始執行。.
2. 需要Thread(副執行緒)運行比較久的程序,開始執行Thread。.
3. Thread於背景執行(通常附帶時間控管,如Thread.sleep(1000);).
4. Thread完成,傳送訊息告知Handler。.
5. 執行Handler內的邏輯程式,改變畫面內容。


 - 如何產生Thread
1. Class Thread有兩個Constructor: 
    (1) Thread()
    (2) Thread(Runnable)
2. Runnable是一個interface,定義於java.lang內,其宣告為
    public interface Runnable {
      public void run(); 
    }    
3. 使用Thread()產生的Thread,其進入點為Thread裡的run();
4. 使用Thread(Runnable)產生的Thread,其進入點為Runnable物件裡的run()。
5. 當run()結束時,這個Thread也就結束了 。
6. 啟動 thread 時,使用 start() 方法:Thread.start();
7. 清除 thread 時,使thread 為 null: Thread = null;

 (1)Thread thread = new Thread(){
      @Override
        public void run() {

      }
 
 (2)
Thread 
thread = new Thread();
Runnable1 r1  = new Runnable1(“test”);
thread(r1).start();
     
class Runnable1 implements Runnable {
      /* 附加執行的副程式 */
      private String name;

      public Runable1 (String name) {
          this.name = name;
      }
      /* Runnable 主體 */
      public void run(){
   }
};
Thread的優先權
優先權高的Thread其佔有CPU的機會比較高,但優先權低的也都會有機會執行到
Thread.setPriority(int)設定Thread的優先權,數字越大優先權越高。
public static final int MAX_PRIORITY 10 
public static final int MIN_PRIORITY 1 
public static final int NORM_PRIORITY 5
其他有關Thread執行的方法有:
yield():先讓給別的Thread執行
sleep(int time):休息time mini second(1/1000)
join():呼叫ThreadA.join()的執行緒會等到ThreadA結束後,才能繼續執行
Critical Section(關鍵時刻)的保護措施
如果設計者沒有提供保護機制的話,Thread取得和失去CPU控制權的時機是由作業系統來決定
Java的解決辦法是,JVM會在每個物件上擺一把鎖(lock),然後程式設計者可以宣告執行某一段程式,必須拿到某物件的鎖才行,這個鎖同時間最多只有一個執行緒可以擁有它
Ex: 1. 使用string 或 int 等變數 flag 及 if else 判斷式來進行保護。
     2. 
使用 try catch 排除例外狀況
 - Handler、Looper和MessageQueue的基本原理?
Handler把消息对象添加到MessageQueue中,Looper负责从MessageQueue的头部不断取出消息对象,取出一个消息对象之后交给Handler处理。
Message msg = handler.obtainMessage();//获取Message对象
msg.what = 2;//给Message属性赋值,msg.obj = s; // 6. 把s赋值给obj
handler.sendMessage(msg);//把消息对象放到消息队列当中去
//1. Looper将会从消息队列当中将消息对象取出,
//2. Looper将会找到与消息对象对应的Handler对象(每一个消息对象都有一个对应的Handler对象)。
//3. Looper将会调用消息对象的handleMessage(Message msg)方法,用于处理该消息对象

EX:
public class MainActivity enxtends Activity {
private Textview textview;
private Button button;
private Handler handler; //3. 定义一个变量
@Override
protected void conCreate(Bundle savedInstanceState) {
super.onCreate(savedINstanceState);
setContentView(R.layout.activity_main);

textView = (TextView)findViewById(R.id.textViewId);
button = (Button)findViewById(R.id.buttonId);

handler = new Myhandler(); //4.使用Myhandler类生成对象

button.setOnClickListener(new ButtonListener());//绑定监听器
}

class MyHandler extends Handler { 
//1.首先定义一个handler的实现类,继承Handler
@Override
public void handleMessage(Message msg) {
//2. 然后实现handleMessage方法

System.out.println("handleMessage--->" + Thread.currentThread().getName()); // 测试说明:打印当前线程的名字

String s= (String)msg.obj; // 8. 在主线程中将数值取出
textView.setText(s); //在主线程中操作UI

}
}

class ButtonListener implements OnClickListener {

@Override
public void onClick(View v) {
Thread t = new NetWorkThread();
t.start();
}
}

class NetworkThread extends Thread {
//本线程为Worker Thread
//@Override
public void run(){

System.out.println("network--->" + Thread.currentThread().getName()); // 测试说明:打印当前线程的名字

//模拟访问网络,所以当线程运行时,首先休眠2秒
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//变量s的值,模拟从网络中获取的数据
String s = "网络中的数据";
//textView.setText(s);//这样的操作是错误的,安卓系统中UI操作原则上只能在主线程中进行

Message msg = handler.obtainMessage(); // 5.生成Message对象
//sendMessage方法无论在主线程中发送还是在Worker Thread中发送都可以,这里在WorkerThread当中发送,然后在主线程将消息取出。
msg.obj = s; // 6. 把s赋值给obj
handler.sendMessage(msg); // 7. 调用sendMessage方法发送消息

}
}

}


參考資料:






----------------------------------------------------------------------
3. 安全中止一個Thread
----------------------------------------------------------------------
class MyThread implements Runnable{
      public void run() {
       // TODO Auto-generated method stub
       //  定义自己的变量
      while(!Thread.currentThread().isInterrupted()){
                try { 
                       doSomeThingNeed();//需要更新的操作   
                                                         //休眠控制最大帧率为每秒3绘制30次
                       Thread.sleep(30);
                } catch (Exception e) {
                       // TODO: handle exception
                       Log.v(tag2, "DrawSurfaceView:绘制失败...");
                       Thread.currentThread().interrupt();
                } 
       }
}


參考資料:







----------------------------------------------------------------------
4. Thread傳Message到Handler
----------------------------------------------------------------------
Thread中並沒有辦法改變畫面上的任何UI,所以會使用Handler來完成UI更新的目的
在 Android 中,Activity 可透過 Handler 傳遞Message 去切換不同的 UI 介面 (setContentView( ViewSurfaceViewR.Layout.xml_file )
但在計算中並不是都會正確的完成,所以在Handler中也必須判斷Thread的執行狀況來做出不同的反應,那麼就必須依靠Message來完成兩者間的資訊傳遞
1. 建立一個Thread,並傳遞指定相關資訊到Message中,利用handler.sendMessage傳遞出去。
Thread thread = new Thread(){  @Override  public void run() {  Message message;
  String obj = "OK";  message   handler.obtainMessage(1,obj);  handler.sendMessage(message);
  }
thread.start();
thread = null;
2. handler中依照傳遞的訊息做出相對應的反應
private Handler DOFindAttributehandler = new Handler(){    @Override    public void handleMessage(Message msg) {          super.handleMessage(msg);
          String MsgString = (String)msg.obj;
          if (MsgString.equals("OK"))          {
          ......
          } 
     }
};
P.S Override 為覆寫繼承之父類別中的方法,若覆寫完後仍須使用原方法時,即用super (指向繼承之父類別)


參考資料:


 - 範例:500毫秒,TextView 向上計數一次 : http://givemepass.blogspot.tw/2011/11/threadhandler.html
在 main thread 執行時,會產生 ANR (Application is Not Responding)的對話框,並且終止程式
text = (TextView)findViewById(R.id.textView);
int i =0;
while(true){ // 無窮迴圈造成 ANR
    i++;
    text.setText(Integer.toString(i));
}

         new Thread(new Runnable(){
                @Override        
                public void run() {
                // TODO Auto-generated method stub          
                while(true){ // 保護機制
                    try{
                        Message msg = new Message();
                        msg.what = 1;
                        mHandler.sendMessage(msg);
                        Thread.sleep(500); // 休息500ms
                    }
                   catch(Exception e){
                        e.printStackTrace();
                    }        
                }
              }           
           }).start();
             text = (TextView)findViewById(R.id.textView);
             mHandler = new Handler(){
             int i = 0;
             @Override
             public void handleMessage(Message msg) {
                    switch(msg.what){
                    case 1:
                            i++;
                            text.setText(Integer.toString(i));
                    break;
                    }
                    super.handleMessage(msg);
                } 
            };


參考資料:



Matrix, Rect

-----------------------------------
1. Matrix 使用方法:
-----------------------------------

* 重點注意事項:
1. Matrix中带有pre, post的函数需要考虑先后顺序不然的话你会发现可能坐标位置不是你想要的。
Matrix的基本操作包括:+、*。Matrix的乘法不滿足交換律,也就是說A*B ≠B*A。
Preconcats matrix相當於右乘矩陣,Postconcats matrix相當於左乘矩陣


參考資料:


2. 有一种更简便的方法,叫系统帮我们计算:
setConcat(Matrix a, Matrix b)
ex:
Matrix mRotateMatrix = new Matrix();    //控制旋转  
Matrix mScaleMatrix = new Matrix(); //控制缩放  
Matrix mSkewMatrix = new Matrix();  //控制倾斜  
Matrix mPosMatrix = new Matrix();       //控制坐标  
Matrix mMatrix = new Matrix();      //合并  
      
mMatrix.setConcat(mRotateMatrix, mScaleMatrix);  
mMatrix.setConcat(mMatrix, mSkewMatrix);  
mMatrix.setConcat(mMatrix, mPosMatrix);  
      
canvas.drawBitmap(mBitmap, mMatrix, mPaint);  

3. Public Constructors:
Matrix()、Matrix(Matrix src)

4. 將 Matrix 結果放置 Rect 矩形中:

首先得到图片的宽为width,高为height,你使用的matrix为m:
RectF rect = new RectF(0, 0, width, height);
m.mapRect(rect);
这样rect.left,rect.right,rect.top,rect.bottom分别就就是当前屏幕离你的图片的边界的距离。
而这样你要的点(0,0)其实就是界面的左上角(rect.left,rect.top)

 [Android界面] 请教高手Matrix中的mapRect方法如何使用:http://www.eoeandroid.com/thread-113930-1-1.html

5. 顏色矩陣 ColorMatrix - 用於調整圖片色調、慮色等顏色風格調整:

顏色矩陣M是一個5*4的矩陣,其一維數組m:

 
 
透過矩陣調整顏色的色偏:



EX:將變化量 array 設定至 ColorMatrix中,並用 canvas 配合 Paint 來完成 ColorFilter 效果。  
 Paint mPaint = new Paint();                                                                    //新建畫筆對象
 canvas.drawBitmap(mBitmap, 0, 0, mPaint);                                         //描畫(原始圖片)    
        
 ColorMatrix mColorMatrix = new ColorMatrix();                                     //新建顏色矩陣對象    
 mColorMatrix.set(array);                                                                         //設置顏色矩陣的值
 mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));        //設置畫筆顏色過滤器    
 canvas.drawBitmap(mBitmap, 0, 0, mPaint);                                         //描畫(處理後的圖片)


參考資料:

6. 旋转:
 - 围绕点px, py 旋转 degrees度, 
 - 如果没设置坐标,默认以0,0点旋转.
setRotate(float degrees)、setRotate(float degrees, float px, float py)

7. 缩放,翻转:

 - 以点px,py为原点缩放 >=0   1为正常大小  
 - 如果是负数,图形就会翻转
 - 如果没设置原点坐标,默认以0,0点缩放(如果发现图片不见了,检查一下是不是翻转出了屏幕)
setScale(float sx, float sy)、setScale(float sx, float sy, float px, float py)

8. 倾斜:
 - 以点px,py为原点倾斜
 - 如果没有设置原点,则以0,0点为原点.
setSkew(float kx, float ky, float px, float py)、setSkew(float kx, float ky)

9. 坐标:
 - 图片移动到某一个位置
setTranslate(float dx, float dy)

10. 重製:
 - Matrix 會壘加,需要reset來算新的座標
matrix.reset();  

EX:
 - 想要旋转45度,然后平移到100,100的位置需要
Method 1) 
Matrix matrix = new Matrix();  
matrix.postRotate(45);  
matrix.postTranslate(100, 100);  
Method 2)
Matrix matrix = new Matrix();  
matrix.setTranslate(100, 100);  
matrix.preRotate(45);  


參考資料:

EX: 
 - 旋轉、縮放和傾斜都可以圍繞一個中心點進行




參考資料: