2015年12月2日 星期三

觸碰與手勢判斷

1. 判斷手勢滑動

public boolean onTouchEvent(MotionEvent event) {


        float X = event.getX(); // 觸控的 X 軸位置
        float Y = event.getY(); // 觸控的 Y 軸位置

        switch (event.getAction()) { // 判斷觸控的動作
        case MotionEvent.ACTION_DOWN: // 按下
            downX = event.getX();
            downY = event.getY();
            return true;

        case MotionEvent.ACTION_MOVE: // 拖曳
            return true;

        case MotionEvent.ACTION_UP: // 放開
            Log.d("onTouchEvent-ACTION_UP","UP");
            upX = event.getX();
            upY = event.getY();
            float x=Math.abs(upX-downX);
            float y=Math.abs(upY-downY);
            double z=Math.sqrt(x*x+y*y);
            int jiaodu=Math.round((float)(Math.asin(y/z)/Math.PI*180));//角度
             
            if (upY < downY && jiaodu>45) {//上
                Log.d("onTouchEvent-ACTION_UP","角度:"+jiaodu+", 動作:上");
            }else if(upY > downY && jiaodu>45) {//下
                Log.d("onTouchEvent-ACTION_UP","角度:"+jiaodu+", 動作:下");
            }else if(upX < downX && jiaodu< =45) {//左
                Log.d("onTouchEvent-ACTION_UP","角度:"+jiaodu+", 動作:左");
                // 原方向不是向右時,方向轉右
                if (mDirection != EAST) {
                    mNextDirection = WEST;
                }
            }else if(upX > downX && jiaodu< =45) {//右
                Log.d("onTouchEvent-ACTION_UP","角度:"+jiaodu+", 動作:右");
                // 原方向不是向左時,方向轉右
                if (mDirection ! = WEST) {
                    mNextDirection = EAST;
                }
            }
            return true;

            case MotionEvent.ACTION_POINTER_DOWN:  // 两只手指按下
              return true;
            case MotionEvent.ACTION_POINTER_UP: //一只手指离开屏幕,但还有一只手指在上面会触此事件
              return true;
        }
        return super.onTouchEvent(event);
}


2. 多点触控和缩放:配合 Bitmap、Matrix、TouchEvent 判斷,來完成圖片的縮放、旋轉、移動。


    /**计算两点之间的距离像素**/
    private float distance(MotionEvent e) {
                                                                                                                
        float eX = e.getX(1) - e.getX(0);  //后面的点坐标 - 前面点的坐标
        float eY = e.getY(1) - e.getY(0);
        return FloatMath.sqrt(eX * eX + eY * eY);
    }
                                                                                                            
    /**计算两点之间的中心点**/
    private PointF getMidPoint(MotionEvent event) {
                                                                                                                
        float x = (event.getX(1) - event.getX(0)) / 2;
        float y = (event.getY(1) - event.getY(0)) / 2;
        return new PointF(x,y);
    }

      
    //获得旋转角  
    public float getAngle(MotionEvent Event)  
    {  
        double DeltalX=Event.getX(0)-Event.getX(1);  
        double DeltalY=Event.getY(0)-Event.getY(1);  
        return (float)Math.atan2(DeltalX, DeltalY);  
    }  


Android中图片的多点触控和缩放 (imageView):http://smallwoniu.blog.51cto.com/3911954/1252191
Android开发学习之ImageView手势拖拽、缩放、旋转 (imageView):http://blog.csdn.net/qinyuanpei/article/details/17751481

模擬拖曳小圖示的 Android 程式 (surfaceView):http://dazi2012.blogspot.tw/2012/05/android.html?m=1

3. 應用於 activity 中的架構:
範例為新增自定義的 view,並在裡面繪製圓。透過觸碰改變圓的大小。

import ...
public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new TouchView(this));           
    }

    //自建的類別,繼承View,當成活動之畫面呈現
    private class TouchView extends View{

         public TouchView(Context context) {
              super(context);
         }
         @Override
         protected void onDraw(Canvas canvas) {
              super.onDraw(canvas);
         }
         //螢幕觸控事件處理,這邊是簡易方法只能偵測layout,無法針對特定元件進行監聽
         @Override
         public boolean onTouchEvent(MotionEvent event) {

                    switch (Event.getAction() & MotionEvent.ACTION_MASK){
                    case MotionEvent.ACTION_DOWN :
                            
                            break;
                    case MotionEvent.ACTION_POINTER_DOWN:

                            break;
                    case MotionEvent.ACTION_MOVE :
                            invalidate();//自動呼叫onDraw方法
                            break;
                    case MotionEvent.ACTION_POINTER_UP:
            
                            break;
                    case MotionEvent.ACTION_UP:

                            break;
                    default :
                            break;
                    }
                    return true;
         }
    }
}




參考資料:
  • Android:多點觸控之應用以及取得螢幕解析度 :http://chihweiwu.blogspot.tw/2012/08/android_30.html

常見環境問題

在Eclipse或Android Studio撰寫Android程式時常常會遇到以下幾個問題,這些也是StackOverflow上一直有人不斷提出的:
  1. 「R.java」是Android自動產生的資源檔(通常會在/gen目錄下),但怎麼在我一個動作後她就整個不見了呢?
    • 她沒有不見,是/res/layout(或其他的/res/xxx)下的檔案有錯誤讓她編不出來,所以只要先解決layout(或其他的/res/xxx)的錯誤後->點擊Project名稱->在「Project」功能表上再按一下「Clean」,R.java就恢復了喔!
  2. 加入一個library後出現了「Android Eclipse Jar mismatch! Fix your dependencies」錯誤,天啊!該怎麼辦?
    • 這種通常發生在專案有使用到library,而那個library剛好也有用到「android-support-v4.jar」,但問題就是library專案原本專案的「android-support-v4.jar」版本不同,造成的錯誤(Eclipse會自動做sha1來比對是否一致),但不一定是Android的support library,也有可能是其他衝突的jar包。這問題很好解決,如果是support library的話,在library專案原本專案的libs目錄下都找到「android-support-v4.jar」,按右鍵Delete掉,之後一併在library專案原本專案上按右鍵->Android Tools->Add Support Library,將最新的support library加入讓兩邊的include的library一致,就解決這個問題了。
  3. 錯誤的Include library專案方法
    • Java開發者可能會搞混把專案要用到的library以Java Build Path的方式include進來,請記得若是你要include的是一個library專案(如:actionbarsherlock或Facebook SDK等),應該要在想include library的專案上按右鍵->Properties->Android->在Library的區塊「Add」->把你要include的library專案加進去,這樣才是正確的方法。



Reference Website :
  • 幾個開發Android應用程式常遇到的錯誤與解法
  • http://www.dotblogs.com.tw/cheng/archive/2014/07/18/r-java-android-jar-mismatch-include-library.aspx

常用功能開發

-----------------------------------
1. 手機資料
-----------------------------------
 - 取SIM卡資料
透過getSystemService(TELEPHONY_SERVICE)取得 TelephonyManager物件,在透過其方法取得SIM卡資料(先生成Adapter,在從陣列撈)
取SIM卡狀態:getSimState() 回傳1-無sim卡,2-需PIN解鎖,3-需PUK解鎖,4-需Network PIN解鎖,5-已就緒,0-狀態未知
取SIM卡卡號:getSimSerialNumber()
取SIM卡供應商代碼:getSimOperator()
取SIM卡供應商名稱:getSimOperatorName()
取SIM卡國別:getSimCountryIso()

 - 取得電信及手機相關資訊
TelephonyManager及android.provider.Settings.System(一樣先丟陣列或物件,再取)
ex:telMgr = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
取得手機號碼:getLine1Number()
取得電信網路國別:getNetworkCountryIso()
取得電信公司名稱:getPhoneType()
取得網路類型:getNetworkType()
取得手機IMEI:getDeviceId()
取得IMEI SV:getDeviceSoftwareVersion()
取得IMSI:getSubscriberId()

 - 來電處理
onCallStateChanged

 - 開啟WiFI、GFS
getSystemService、WiFiManager.isWiFiEnabled()、LocationManager.isProviderEnabled()

 - 錄音
MediaRecorder、getExternalStorageState()、File.CreateTempFile、.setAudioSorece
setAudioSamplingRate()、setAudioEncodingBitRate()、setOutputFormat()、setAudioEncoder()

 - 控制音量
AudioManager、getStreamVolume(AudioiManager.STREAM_RING)、getRingerMode()
AudioManager.ADJUST_LOWER、AudioManager.ADJUST_RAISE、
AudioManager.RINGER_MODE_NORMAL、AudioManager.RINGER_MODE_SILENT、AudioManager.RINGER_MODE_VIBRATE


-----------------------------------
2. 基本
-----------------------------------
 - 切換Activity
Intent

 - 滑動的相片
Gallery

 - 樣式主題
Style

 - 取Drawable寬高
Bitmap、BitmapFactory.decodeResource()、getHeight()、getWidth();

 - 取得螢幕寬高
DisplayMetrics、getWindowManager().getDefaultDisplay().getMetrics()、widthPixels、heightPixels

 - TextView - Linkify
透過Linkify依不同類型的文字(電話、郵件、連結)而呼叫不同的相對應程式開啟。

 - 浮動訊息 
Toast
可透過LinearLayout放入圖片丟給Toast,toast.setView(layout)

 - 狀態列
NotificationManager、Notification、PendingIntent
可設定icon、tickerText(文字)、defaults(提醒效果)
提醒效果有:DEFAULT_SOUND(聲音)、DEFAULT_LIGHTS(螢幕發亮)、DEFAULT_VIBRATE(振動)、DEFAULT_ALL(三種都動作)

 - 桌面小工具(Widget)
AppWidgetProvider、BroadcastReceiver去listen、onUpdate()

 - 圖片透明度
Paint、setAlpha


-----------------------------------
3. 功能性
-----------------------------------
 - app載入前播放影片
Uri.parse()、VideoVeiw、setOnCompletionListener


 - 手勢滑動頁面效果
ViewFlipper,簡單來說就是透過觸控壓螢幕的位置X、Y滑動後始末座標去判斷位移,最後在呼叫動畫並切換到下個頁面。
onTouchEvent()、MotionEvent.ACTION_DOWN、MotionEvent.ACTION_UP、event.getX()、event.getX()、setInAnimation()、setOutAnimation()
另一方法:ViewGroup、Scroller、VelocityTracker


 - 多點觸控Multi-touch
onTouchEvent、MotionEvent.getAction()、MotionEvent.ACTION_DOWN(UP)、MotionEvent.ACTION_POINTER_N(1234..)_DOWN(UP)
getPointerCount()、findPointerIndex()、getX()、getY()


 - 換手機背景圖
setWallpaper()
取目前桌布:getWallpaper


 - 變更螢幕方向
setRequestedOrientation()、getRequestedOrientation、LANDSCAPE、PORTRAIT、onSensorChanged、mSensorListener、
透過方向感應onSensorChanged偵測變化在進行變更螢幕方向,另變化後使用onConfigurationChanged進行改變後進行的事件。


 - 定時啟動(鬧鐘、日曆提醒)
getSystemService(ALARM_SERVICE)、TimePickerDialog、AlarmManger、set、setRepeating、BroadcastReceiver


 - 開機執行
onReceive() 繼承BroadcastReceiver


 - 離開跳出訊息確認
AlertDialog.Builder() .setPositiveButton()  . setNegativeButton()
離開按鈕使用setOnClickListener去呼叫AleretDialog.Builder,並設定屬性若點選關閉或返回


 - 隱藏式側選單
SlidingDrawer.setOnDrawerOpenListener() 開啟、SldingDrawer.setOnDrawerCloseListener() 關閉、GridView


 - 解析RSS
SAXParser、SAXParserFactory、SAXParserFactory.newInstance()、newSAXParser()、getXMLReader()、MyHandler()
getParsedData()、getRssTitle()



參考資料:

字串檢查與判別

1. 判斷字串中的特殊符號

String str = "ghf^645";
String regEx = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);


參考資料:



2. 判斷字串長度與字元位置

String a="abcd";.  int. b=a.length();
String string = "java";
System.out.println(string.charAt(1)); // a


參考資料:




3. 判斷國內電話號碼

 - 市內(長途)區域號碼開頭對照:http://zhun.tw/comm/pstn-number.html
 --- 02~09開頭、共9~10碼 (包含區碼)
 - 手機號碼(未加國際碼): 09開頭、共10碼
 - 判斷長度和開頭2碼




4. Email格式判斷

private Pattern pattern; 
private Matcher matcher;
String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
matcher = pattern.matcher(str); 
matcher.matches(); // check result boolean


參考資料:

如何匯出 apk 安裝檔

下面介紹如何透過生成 .keystore 與 導出 .apk (其實就是 jar 檔) 後, 再安裝到 Android 的手持裝置.
由以下步驟來完成安裝檔的產生與裝置安裝:

0. Android Studio > Build > Generate Signed APK

1. 生成 .keystore :
    -  此組 Password 為 Master Password,當完成 Key Store 的建立後,下次進入時直接輸入 Master Password 即可:
 1.
 2.
2. 建立 Key Store 下的 Key alias 鑰匙別名(簽名):一個 Store 可以擁有多個 alias 別名,即每個別名用於不同 app project 上。
 1.
 2.

3. 導出 signed .apk 檔 : Next 至此步驟,並選擇 Build Type 產生 apk 的版本屬於 debug 版還是 release 版。
 - debug 版:用於自行安裝測試於裝置上。
 - release 版:用於上傳至 Google Play Developer Console 中,進行 app 上架用的發布安裝檔。
 - 當跑完後會自動跳出 apk 所在的資料夾視窗:檔名: app-debug.apk / app-release.apk

Google Play Developer Console:https://play.google.com/apps/publish/

4. 安裝 .apk 到手持裝置 :將該 .apk 透過 SD 卡或是 usb 傳輸到 Android 手持裝置上,並使用資料夾瀏覽尋找 apk 檔和點擊安裝。
1. 關閉USB儲存裝置,按設定>應用程式>未知來源打勾 
2. 利用ASTRO檔案總管讀取放在SD那中的APK檔,選擇Open App Manager 並點擊 "Install" 
 1.
2.

5. 若要上架至 Android Store 時,需要注意:
android:versionCode="x"         // 在上架後,versionCode 的設定不能與之前所擁有的號碼相同,否則上傳 apk 時,會顯示 versionCode 有重複並上無法上傳。
android:versionName="1.0.0"  // 此為 play store 上所顯示的版本名稱,不影響上架設定。




參考資料:
  • [Android 精華文章] 生成 .keystore 並導出 .apk 檔案後安裝到 Android 平台:http://puremonkey2010.blogspot.tw/2011/08/android-keystore-apk-android.html

取得螢幕寬高


雖然Android可設定成隨著手機螢幕的大小來調整縮放比例,但是手機開發者在設計的時候還是必須清楚得知手機銀幕的邊界,
避免在縮放的時候造成版面(layout)上的詭異變型,根據各家手機廠商螢幕尺寸的不同,必須對其螢幕解析度有所概念。

ex:藉著幾行程式碼來取得手機螢幕的解析度,當中的關鍵為DisplayMetrics類別應用。
DisplayMetrics metrics = new DisplayMetrics();  
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int dw = metrics.widthPixels;
int dh = metrics.heightPixels;

取Drawable寬高.Bitmap、BitmapFactory.decodeResource()、getHeight()、getWidth();


參考資料:

this、super

this通常指”當前類別”super通常指”父類別”


參考資料:

 - EX1:

 this 用法:
在你的方法中的某个變數名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便需要明确使用this关键字来指明你要使用某个成员,使用方法是“this.成员名”,而不带this的那个便是前對象變數
輸出結果
DemoSuper:
Name=kevin Age=22
 public class DemoThis// 紅字為同一個類別的變數
        private String name// 共用暫存變數
        private int age;
        DemoThis(String nameint age){
             setName(name); //你可以加上this来调用方法,像这                                样:this.setName(name);但这并不是必须的
        setAge(age);
        this.print(); // 可以用 print() 取代
        }
        public void setName(String name){
            this.name=name;//此处必须指明你要引用成员变量
        }
        public void setAge(int age){
            this.age=age;
        }
        public void print(){
            System.out.println("Name="+name+" Age="+age);//在此行中并                        不需要用this,因为没有会导致混淆的东西
        }
        public static void main(String[] args){
            DemoThis dt=new DemoThis("Kevin","22");
        }
}


 - EX2:

 this , super用法:
輸出結果:
A Person.
chinese.
A person name is:kevin
his name is:kevin
A person name is:kevin
his name is:kevin
his age is:22

 父類別:Person
            class Person{
 
         public static void prt(String s){              System.out.println(s);          }
          Person(){               prt("A Person.");          }
          Person(String name){                prt("A person name is:"+name);          }
}
 類別
public class Chinese extends Person{
 
 Chinese(){    super(); //调用父类构造函数(1    prt(“A chinese.”); // 繼承Person使用prt()
  }
  Chinese(String name){
    super(name);//调用父类具有相同形参的构造函数(2    prt(“his name is:”+name);  }
  Chinese(String name,int age){    this(name);//调用当前具有相同形参的构造函数(3    prt("his age is:"+age);  }

public static void main(String[] args){  Chinese cn=new Chinese();  cn=new Chinese("kevin");  cn=new Chinese("kevin",22);  }
}