目前分類:Android (21)

瀏覽方式: 標題列表 簡短摘要

根據上一章節,這次加入 preview 處理,把 preview 存入 SDcard。

  • preview 處理要先 implements Camera.PreviewCallback
  • 然後再 Camera.setPreviewCallback(this)
  • 這樣每次 camera preview 時都會呼叫 onPreviewFrame()
  • Android 提供 YuvImage class 處理 preview 的 raw data
  • 可以直接使用 YuvImage.compressToJpeg() 存成 JPEG
  • 雖然預覽有轉 90 度,但是 preview raw data 卻沒有,網上找了一個 rotateYUV420Degree90() 幫助轉換角度。
  • preview 轉 90 度後要注意 width height 也要互換喔,長變寬,寬變長,為此我浪費了半天的時間找原因。
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;

public class CameraTestActivity extends Activity implements SurfaceHolder.Callback, Camera.PreviewCallback {

    private SurfaceView mSurfaceview = null;
    private SurfaceHolder mSurfaceHolder = null;
    private Camera mCamera = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout fl = new FrameLayout(this);
        setContentView(fl);

        mSurfaceview = new SurfaceView(this);
        mSurfaceHolder = mSurfaceview.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mSurfaceHolder.addCallback(this);
        fl.addView(mSurfaceview);
    }

    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        try {
            mCamera = Camera.open();
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mSurfaceHolder);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        // set auto focus mode
        Camera.Parameters parameters = mCamera.getParameters();
        List allFocus = parameters.getSupportedFocusModes();
        if(allFocus.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)){
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
        }
        else if(allFocus.contains(Camera.Parameters.FLASH_MODE_AUTO)){
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        mCamera.setParameters(parameters);
        mCamera.setPreviewCallback(this);
        mCamera.startPreview();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
    }
@Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        String sFileName = "IMG_"
                + new SimpleDateFormat("yyyyMMdd_HHmmss")
                    .format(new Date())
                    .toString() + ".jpg";
        File root = Environment.getExternalStorageDirectory();
        String dir = "/Camera/";
        File CameraDir = new File(root, dir);
        if (!CameraDir.exists()) CameraDir.mkdirs();
        File jpgFile = new File(root, dir + sFileName);
        if (!jpgFile.exists()) {
            try {
                Camera.Parameters parameters = camera.getParameters();
                Size size = parameters.getPreviewSize();
                YuvImage image = new YuvImage(
                        rotateYUV420Degree90(data, size.width, size.height),
                        parameters.getPreviewFormat(), size.height, size.width, null);
                FileOutputStream filecon = new FileOutputStream(jpgFile);
                image.compressToJpeg(
                        new Rect(0, 0, image.getWidth(), image.getHeight()),
                        90, filecon);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private byte[] rotateYUV420Degree90(byte[] data, int imageWidth, int imageHeight) {
        byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
        // Rotate the Y luma
        int i = 0;
        for (int x = 0; x < imageWidth; x++) {
            for (int y = imageHeight - 1; y >= 0; y--) {
                yuv[i] = data[y * imageWidth + x];
                i++;
            }
        }
        // Rotate the U and V color components
        i = imageWidth * imageHeight * 3 / 2 - 1;
        for (int x = imageWidth - 1; x > 0; x = x - 2) {
            for (int y = 0; y < imageHeight / 2; y++) {
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
                i--;
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
                i--;
            }
        }
        return yuv;
    }
}

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()


台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()


台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

Camera Focus mode 必須透過 setParameters() 設定,自動對焦則有三種模式,FOCUS_MODE_CONTINUOUS_PICTURE,FOCUS_MODE_CONTINUOUS_VIDEO,FOCUS_MODE_AUTO。

  • FOCUS_MODE_CONTINUOUS_PICTURE (API level 14)

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

在手機開啟 Camera 其實很簡單,就類似撥放影片一樣,把 Camera 擷取的影像輸出到 SurfaceView 就可以了。

AndroidManifest.xml

在 AndroidManifest.xml 一定要加入 android.permission.CAMERA,不然程式一開啟就會閃退,因為 APP 沒有 CAMERA 的權限。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

camera source code resource

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

  • Official ZXing ("Zebra Crossing") : link
  • dm77/barcodescanner: link
  • 透過ZXing讀取1D/2D barcode: link
  • Android ZXing 二维码、条形码扫描介绍: link
  • Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果: link
  • ZXing 开源中国社区: link
  • Getting Started Developing: link
  • zxing源码分析——QR码部分: link

zxing


台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

離線安裝檔下載網址http://forum.xda-developers.com/wiki/BlueStacks_App_Player

官網下載的安裝檔會在安裝過程中下載離線安裝檔到 C:\ProgramData\BlueStacksSetup,而且安裝完也不會刪除掉,浪費便用者空間,所以建議直接下載離線安裝檔即可。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

adt-bundle-windows-x86_64-20131030 用來開發 Android 4.0 以上的專案都沒啥問題,今天我拿書籍的範例光碟來編譯,卻出現 "Unable to Execute Dex: java.nio.BufferOverflowException" 的錯誤訊息。google 半天的結果,原來是內建的 Android SDK Build-tools (19) 有問題,只要換成新版的 19.0.1 或降版成 18.1.1 就 OK 了。

  1. 執行 Android SDK Manager
  2. 勾選 Tool → Android SDK Build-tools (19.0.1)
  3. 勾選 Tool → Android SDK Build-tools (19)
  4. 按下 Install packages... 執行安裝
  5. 按下 Delete packages... 執行卸載

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

Android SDK source code 自 4.0 以後都可以自 Android SDK Manager 直接下載,不用自己安裝 git, repo 等工具去下載。

  1. 打開 Android SDK Manager。
  2. 選擇要下載 Sources for Android SDK 版本(API 19),每一種版本都可以下載。
  3. 按下 Install packages.. 按鈕,執行下載。
  4. 下載資料會放在 adt-bundle-windows\sdk\sources\android-19,大小約 80MB 左右。

android-support-v4.jar 由於放在 Android Private Libraries,無法設定 Java Source Attachment,按 Ctrl+滑鼠左鍵無法直接讀取 Android SDK Sources。google 的結果只要新增一個檔案即可。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

下載頁面: http://developer.android.com/sdk

下載點有分 32bit 及 64bit,下載內容包含 Eclipse + ADT plugin,Android SDK Tools,Android Platform-tools,The latest Android platform,The latest Android system image for the emulator。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

font2
  • font height = descent + ascent
  • font size = font height - leading = descent + ascent - leading

android function

  • Paint.ascent()
    Return the distance above (negative) the baseline (ascent) based on the current typeface and text size.
  • Paint.descent()

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

XML 解析有分二種方式,一是 DOM,一是 SAX。

DOM 使用的 Class

  • javax.xml.parsers.DocumentBuilder
  • javax.xml.parsers.DocumentBuilderFactory
  • org.w3c.dom.Document
  • org.w3c.dom.Element
  • org.w3c.dom.NodeList

DOM 解析步驟

  • DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

setContentView() 是一個跟很多函數都有相關的函數,requestWindowFeature() 要在 setContentView() 前面執行才不會發生錯誤,而 findViewById() 則必須在 setContentView() 後面執行才不會取得 null。不知道為什麼,文件也沒有註明,有空再來研究吧。


台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

BroadcastReceiver 的執行時間最好不要超過 5s,否則會讓 Android 系統判定為 Timeout,並顯示錯誤對話視窗。BroadcastReceiver 被註冊後並不會在背景執行,而是當事件觸發才會執行。所以過多的 BroadcastReceiver 並不會影響系統運作,但是仍會佔用一些記憶體,還是不要太多才好。

implement

public class BroadcastReceiverDemo extends BroadcastReceiver {
    @Override
    public void onReceive(Context arg0, Intent arg1) {
    }
}

register

IntentFilter filter = new IntentFilter("NEW_BROADCAST");
BroadcastReceiverDemo receiver = new BroadcastReceiverDemo();
BroadcastReceiverDemoActivity.this.registerReceiver(receiver, filter);
或是在 AndroidManifest.xml 的 application 標籤內加入下列程式碼

    
        
    

在 AndroidManifest.xml 內註冊必須是一個 public 的非 inner class,也就是單獨的 class file 且宣告成 public。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AbsoluteLayout;
import android.widget.Button;

@SuppressWarnings("deprecation")
public class WhiteBoardActivity extends Activity {
    private AbsoluteLayout layout;
    private Bitmap bmp;
    private Canvas canvas;
    private Paint brush;
    private View canvasView;
    private float oldX=0, oldY=0;
    private boolean paintStart = false;
    
    /** Called when the activity is first created. */
    /* (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // fullscreen
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        this.getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // paint brush
        brush = new Paint();
        brush.setColor(Color.BLACK);

        // bitmap
        bmp = Bitmap.createBitmap(
                this.getWindowManager().getDefaultDisplay().getWidth(),
                this.getWindowManager().getDefaultDisplay().getHeight(),
                Bitmap.Config.ARGB_8888);
        canvas = new Canvas(bmp);
        canvas.drawColor(Color.WHITE);

        // bitmap view
        canvasView = new View(this) {
            @Override
            public void onDraw(Canvas canvas){
                canvas.drawBitmap(bmp, 0, 0, null);
            }
        };
       // clear button
        Button btnClear = new Button(this);
        btnClear.setText("Clear");
        btnClear.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                canvas.drawColor(Color.WHITE);
                canvasView.invalidate();
            }
        });

        // color button
        final Button btnBlack = new Button(this);
        btnBlack.setText("Black");
        btnBlack.setTextColor(Color.BLUE);
        final Button btnBlue = new Button(this);
        btnBlue.setText("Blue");
        btnBlue.setTextColor(Color.BLACK);
        final Button btnRed = new Button(this);
        btnRed.setText("Red");
        btnRed.setTextColor(Color.BLACK);

        btnBlack.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                btnBlack.setTextColor(Color.BLUE);
                btnBlue.setTextColor(Color.BLACK);
                btnRed.setTextColor(Color.BLACK);
                brush.setColor(Color.BLACK);
            }
        });
        btnBlue.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                btnBlack.setTextColor(Color.BLACK);
                btnBlue.setTextColor(Color.BLUE);
                btnRed.setTextColor(Color.BLACK);
                brush.setColor(Color.BLUE);
            }
        });
        btnRed.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                btnBlack.setTextColor(Color.BLACK);
                btnBlue.setTextColor(Color.BLACK);
                btnRed.setTextColor(Color.BLUE);
                brush.setColor(Color.RED);
            }
        });
       // layout create
        layout = new AbsoluteLayout(this);
        layout.addView(canvasView,
                new AbsoluteLayout.LayoutParams(
                        this.getWindowManager().getDefaultDisplay().getWidth(),
                        this.getWindowManager().getDefaultDisplay().getHeight(),
                        0, 0));
        layout.addView(btnClear,
                new AbsoluteLayout.LayoutParams(
                        100, 50, 0, 0));
        layout.addView(btnBlack,
                new AbsoluteLayout.LayoutParams(
                        100, 50, 0, 50));
        layout.addView(btnBlue,
                new AbsoluteLayout.LayoutParams(
                        100, 50, 0, 100));
        layout.addView(btnRed,
                new AbsoluteLayout.LayoutParams(
                        100, 50, 0, 150));
        setContentView(layout);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            paintStart = true;
            oldX = event.getX();
            oldY = event.getY();
            canvas.drawPoint(oldX, oldY, brush);
            canvasView.invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            if (paintStart) {
                float newX = event.getX();
                float newY = event.getY();
                canvas.drawLine(oldX, oldY, newX, newY, brush);
                oldX = newX;
                oldY = newY;
                canvasView.invalidate();
            }
            break;
        case MotionEvent.ACTION_UP:
            paintStart = false;
            break;
        }
        return true;
    }
}

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

很多遊戲的 APPS 都可以全螢幕下操作,去除了 apps title 及 statusbar,下面就介紹如何實現 fullscreen apps。

// 去除 apps title
requestWindowFeature(Window.FEATURE_NO_TITLE);

// full screen
getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);

切記 requestWindowFeature 必須在 setContentView 之前執行,否則會造成程式當掉。

台南小新 發表在 痞客邦 PIXNET 留言(1) 人氣()

使用 SurfaceView + MediaPlayer 有幾項要注意的重點,不然是無法順利播放影片。

  • MediaPlayer.setDisplay(SurfaceHolder) 必須在 surfaceCreated 之後才能執行,不然只會聽到聲音看不到影像。
  • SurfaceHolder.setType() 必須設定成 SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS,否則播放會出錯。
  • SurfaceHolder.setType() 必須在 surfaceCreated 之前設定完成,不然只會聽到聲音看不到影像。。
  • MediaPlayer.setDisplay() 不一定要在 MediaPlayer.start() 前面,隨時都可以變更。
  • MediaPlayer.setDataSource() 可以輸入網址或是檔案路徑。
  • 如果要重覆撥放,需要設定 OnCompletionListener,在 onCompletion 放一個 mediaPlayer.start() 即可。
  • 如果撥完要撥另一個檔案,則需要先下 reset() 進入 IDLE stage,才能再用 setDataSource(),不然會出現 error stage 的錯誤訊息。
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class VideoTestActivity extends Activity {

    MediaPlayer mediaPlayer;
    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        surfaceView = new SurfaceView(this);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        setContentView(surfaceView);

        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource("/mnt/sdcard/video/video2.3gp");
            mediaPlayer.prepare();
        } catch (Exception e) {
        }
        mediaPlayer.start();

        mediaPlayer.setOnCompletionListener(
            new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    try {
                        mp.reset();
                        mp.setDataSource("/mnt/sdcard/video/video1.3gp");
                        mp.prepare();
                    } catch (Exception e) {
                    }
                    // repeat play 只要下 start()
                    mp.start();
                }
            });

        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
                Log.e("video","surfaceChanged");
            }
            @Override
            public void surfaceCreated(SurfaceHolder arg0) {
                Log.e("video","surfaceCreated");
                mediaPlayer.setDisplay(surfaceHolder);
            }
            @Override
            public void surfaceDestroyed(SurfaceHolder arg0) {
                Log.e("video","surfaceDestroyed");
                if (mediaPlayer != null) mediaPlayer.release();
            }
        });
    }
}

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

一般要取得 android 營幕的解析度大小,可以藉由 Activity.getWindowManager().getDefaultDisplay() 先取得 Display 物件,再利用 Display 來取得 height 及 width。但是這個尺吋不是 layout 真正的尺寸,android API 也說了,因為還有一些裝飾(如 status bar),所以 layout 的尺寸會小一點。

那要如何取得 layout 真正的尺寸大小呢?由於 layout 也是繼承於 View,所以可以藉由 getHeigth 及 getWidth 來取得,但是一開始初始化時,所取得的值卻為 0。原因就出在 layout 一開始並沒有計算尺寸大小,而是在後面重新安排元件位置時才會計算。但是在那一個過程才能取得 layout 實際大小呢?

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

打開 sdcard File Explorer

  • 第一要先啟動一台 Android Virtual Device(AVD),這樣才能知道要設定那一台的 sdcard。
  • Window → Open Perspective → DDMS,設定 DDMS 顯示。

    sdcard.jpg
  • 接著 DDMS 就會出現在 eclipse 右上角,點擊 DDMS 就會看到 File Explorer。

台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()

1 2