2012年6月24日 星期日

選擇相簿裡的照片後,傳到另一個畫面

        在欣爺目前希望做的功能中,其中一項是要從手機相簿中選擇一張照片,利用 setAction(Intent.ACTION_GET_CONTENT); 去做,然後在後來的 startActivityForResult 中再利用 getDataString() 取得圖片資料...

但是,傳到另一個Activity執行一些圖片處裡的動作,因此這部分要如何做呢?



跨 Activity,當然必須要使用到 Intent.putExtras() & get.Extras 這個方法了
傳送一些數值、或者字串的話,就是把它塞入 Bundle 裡面再塞進 Intent.putExtras....
但是要傳送圖片?! 該如何做?

通常圖片顯示,是使用 Bitmap 方法,讓圖片在 ImageView上面呈現
但是如果要跨 Activity 將圖片傳到另一個畫面的 ImageView上面呈現!?
該怎麼做呢?這讓我傷腦筋了一陣子....
也Google了一陣子,嘗試了許多網路上蒐集的方法,但皆不盡理想
(PS: 其實應該是欣爺 Java 底子太差,還部會融會貫通的緣故吧...)

後來總算想到了 URI 方法,將圖片丟到 URI ,轉成字串再傳送就好,豁然開朗啊...
程式碼如下:
首先是發送端:

  1. Uri uri = Uri.parse(data.getDataString());//將擷取的圖片資料塞入URI
  2. Intent intent = new Intent(this, AnotherActivity.class); //指定目的端的Activity
  3. intent.putExtras("URI", uri.toString());//將URI轉成String塞入intent
  4. startActivity(intent);
接收端的部分:

  1. String photoUri = getIntent.getStringExtra("URI"); //接收傳來的Sting, 就是圖片啦..這樣就OK了
  2. ImageView imageView = imageView.setImageURI(Uri.parse(photoUri)); //指定image view來源

2012年6月23日 星期六

發生這種事就真的需要一個擁抱...



是的,越是到達終點,越是不能大意....
不然那感覺真的很糟!!

打球就像人生,記得笑一個...



去年清邁,在 Green Valley Golf Club 從下午三點開球,打到晚上.....
舟車勞頓手軟腳軟狀況下就像這個樣子.....不是沖天炮就是抓青蛙...

不過這就是高爾夫啊....笑一笑就好~~
人生也不過如此,偶爾也是常有出槌的時候,笑一個!!

2012年6月22日 星期五

打造具備自動對焦、閃光燈、儲存照片的 Android 程式


        經過無盡的搜尋資料,參考國內外的程式高手們分享的程式碼,我總算融會貫通後,成功地寫出來完整功能的相機程式了....

可能有人會質疑:利用 Intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE) ;
直接呼叫內建的相機功能就好啦,各種功能樣樣齊全呢!



但是之所以要自己寫相機程式必定有其特殊需求,例如想設計類似大頭貼的攝影程式,就是在相機預覽畫面中先疊上一張圖,像這樣:

         是的,那只能要自己去 Layout 、自己寫camera方法去呼叫相機了,先從Layout開始說明:要如何將圖檔放在相機預覽畫面上呢?答案就是通常我們要放相機預覽畫面,必須要使用 SurfaceView,所以只要再SurfaceView的Background屬性中指定圖檔就可以了。

        另外,Android的拍照、錄影,都是預設橫式的,但是我需要的是直式的拍照啊?這部分我也嘗試了很久,例如使用 camera.setDisplayOrientation(90); 嘗試將顯示畫面選轉90度,但是實際手機預覽畫面卻明顯失真、比例都扁掉了...所以最終,我就將直式的Layout更改成橫式的就好囉,對程式而言是標準的橫式,但是人拿起手機拍照就像是直式拍照囉:

另外一個大缺點,就是儲存後的照片也是橫式的,所以想要轉成直式的照片,就需要利用程式在照片存檔之前先選轉乘90度再儲存,如此一來,實際手機畫面看到的就是直式的照片,所以拍照的部分,除了是要指定拍照後為JPEG檔案,品質參數以及拍完照後的儲存方式,使用獨立的副程式處理:

  1. private final class TakePictureCallback implements PictureCallback {
  2. @Override
  3. public void onPictureTaken(byte[] data, Camera camera) {
  4. try {
  5. Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,data.length);
  6.          Matrix matrix = new Matrix();   //
  7.          matrix.setRotate(90);   //
  8.          bitmap = Bitmap.createBitmap(bitmap, 0, 0,   //將相片旋轉90度後回存
  9.          bitmap.getWidth(),  //
  10.          bitmap.getHeight(),matrix, true);   //         
  11. String Path = createFilePath(); //載入儲存路徑
  12.                                                     //createFilePath() 為處理儲存檔案、儲存路徑的副程式
  13. FileOutputStream outStream = new FileOutputStream(Path);
  14. bitmap.compress(CompressFormat.JPEG, 100, outStream);
  15. outStream.close();
  16. camera.stopPreview();
  17. camera.startPreview();//開始預覽
  18. } catch (Exception e) {
  19. Log.e(TAG, e.toString());
  20. }
  21. }
  22. }
以下是處理檔案的副程式:

  1. private String createFilePath() {
  2. File dir = Environment.getExternalStorageDirectory();
  3. File appDir = new File(dir, "GSC/downline"); //指定資料夾
  4. if(!appDir.exists()) appDir.mkdir();
  5. String name = System.currentTimeMillis() + ".jpg"; //檔案以系統時間來命名
  6. return new File(appDir, name).getAbsolutePath();
  7. }


        自動對焦功能,我希望在拍照前可以進行自動對焦,因此,我將自動對焦部分包成一個副程式,副程式當中才去呼叫拍照的程式,因此拍照前都會先自動對焦了,因此我在處理"拍照按鈕"的部分,呼叫的是autoFoucs這支副程式,而不是TalkPictureCallback,在autoFoucs裡面才呼叫TalkPictureCallback:

  1. Camera.AutoFocusCallback autoFocus = new Camera.AutoFocusCallback() {
  2. @Override
  3. public void onAutoFocus(boolean success, Camera camera) 
  4. {// TODO Auto-generated method stub
  5. if(success){
  6. camera.takePicture(null, null, new TakePictureCallback()); 
  7.                                                                        //TakePictureCallback()是另外處理拍照的副程式
  8. }else{
  9. Toast.makeText(camera_downline.this, R.string.focusError, Toast.LENGTH_LONG).show();
  10. }
  11. }
  12. };

      當然,相機最主要的程式碼,包含一剛開始在開始構圖預覽、轉換、結束以及相關重要的參數如下:

  1. private SurfaceHolder.Callback surfaceholdercallback = new SurfaceHolder.Callback(){
  2. @Override
  3. public void surfaceChanged(SurfaceHolder holder, int format, int width,
  4. int height) {
  5. }
  6. @Override
  7. public void surfaceCreated(SurfaceHolder holder) {
  8. try{
  9. if(camera==null){
  10. camera = Camera.open();
  11. }else{
  12. Toast.makeText(camera_downline.this, "相機正在使用中", 1).show();
  13. }
  14. WindowManager wm = (WindowManager)  getSystemService(Context.WINDOW_SERVICE);
  15. Display display = wm.getDefaultDisplay();//設定這變數為預設的螢幕大小
  16. parameters = camera.getParameters();
  17. parameters.setPreviewSize(display.getWidth(), display.getHeight());
  18.                                  //設置預覽照片的大小
  19. parameters.setPreviewFrameRate(3);//每秒三張
  20. parameters.setPictureFormat(PixelFormat.JPEG);//照片的輸出格式
  21. parameters.set("jpeg-quality", 85);//照片質量
  22. parameters.setPictureSize(display.getWidth(), display.getHeight());
  23.                                 //設置照片的Size, 這邊也可以指定Pixel: Ex: 768, 1024
  24. camera.setParameters(parameters);
  25. camera.setPreviewDisplay(surfaceview.getHolder());
  26.                                 //透過Surface View顯示預覽畫面
  27. camera.startPreview();//開始預覽
  28. preview = true;
  29. }catch(IOException e){
  30. e.printStackTrace();
  31. }
  32. }
  33. @Override
  34. public void surfaceDestroyed(SurfaceHolder holder) {
  35. // TODO Auto-generated method stub
  36. if (camera != null) {
  37. if (preview)
  38. camera.stopPreview();
  39. camera.release();
  40. }
  41. }
  42. };


閃光燈的部分,放在按鈕裡面處裡就可以了,可以使用ToggleButton來去開啟或者關閉,因為我的ToggleButton是用自己設計的底圖,因此希望按下去的時候可以更換另外一個底圖,以下是 flash按鈕的程式碼:



  1.     flash.setOnClickListener(new OnClickListener(){
  2.          //定義返回按鍵後動作
  3.      public void onClick(View arg0){
  4.      if (flash.isChecked()){     
  5.                                flash.setBackgroundDrawable(
  6.                                        getResources().getDrawable(R.drawable.record_flesh_on));
  7.                                        //點選後更換底圖    
  8.      parameters.setFlashMode(Parameters.FLASH_MODE_ON);//開啟閃光燈
  9.      camera.setParameters(parameters);
  10.      Toast.makeText(camera_downline.this, R.string.fleshOn, 1).show();    
  11.      }else { 
  12.                                 flash.setBackgroundDrawable(
  13.                                    getResources().getDrawable(R.drawable.record_flesh_off));
  14.                                     //點選後更換底圖   
  15.      parameters.setFlashMode(Parameters.FLASH_MODE_OFF); 關閉閃光燈
  16.      camera.setParameters(parameters);
  17.      Toast.makeText(camera_downline.this, R.string.fleshOff, 1).show();    
  18.      }
  19.      }
  20.      });



整體的程式碼大概就是如此了,另外,通常拍照的時候,希望是全螢幕、沒有軟體抬頭、或者保持螢幕開啟,可以在onCreate程式開頭加上以下這段程式碼:


  1. public void onCreate(Bundle savedInstanceState){
  2. super.onCreate(savedInstanceState); 
  3. Window window = getWindow();
  4. requestWindowFeature(Window.FEATURE_NO_TITLE);//取消軟體抬頭
  5. window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  6. WindowManager.LayoutParams.FLAG_FULLSCREEN);//全螢幕
  7. window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  8.                                                                                                            //螢幕保持開啟


以上,如果有甚麼更好方式,歡迎與我討論...

2012年6月1日 星期五

ToggleButton 的進階用法

是這樣的,我希望做一個錄影按鈕的效果:
1. 按下去之後,按鈕呈現紅燈亮的畫面,並開始進行錄影的動作
2. 再按一次,按鈕呈現紅燈熄滅的畫面,並停止錄影的動作







因此,ToggleButton 是最理想的選擇,
首先,在 Layout 部分:


  1. <ToggleButton android:id="@+id/按鈕的ID"
  2.         android:layout_width="30px"
  3.         android:layout_height="30px"
  4.         android:textOn="" android:textOff="" android:layout_alignParentLeft="true"
  5.         android:layout_marginLeft="5px"
  6.         android:layout_marginTop="5px" android:background="@drawable/locate_me"/>


接下來是Java部分:
當然之前要先作定義按鈕的動作,然後在onClick事件中描述...

  1. 定義按鈕的ID.setOnClickListener(new OnClickListener() {
  2.         public void onClick(View v) {
  3.             if (定義按鈕的ID.isChecked()) {          
  4. 定義按鈕的ID.setBackgroundDrawable(getResources().getDrawable(R.drawable.底圖檔名一));           
  5. } else {
  6.                 定義按鈕的ID.setBackgroundDrawable(getResources().getDrawable(R.drawable.底圖檔名二));
  7.             }
  8.         }
  9. });