Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android之利用surfaceView實現自定義水印相機

android之利用surfaceView實現自定義水印相機

編輯:關於Android編程

android之利用surfaceView實現自定義水印相機

知識點

1、自定義相機+預覽相機

2、截屏拍照加水印

3、關於不使用intent來傳輸圖片

 

俗話說,有圖有真相。很多人都是喜歡直接看圖,不像我,比較喜歡文字多點,經常看看散文什麼的陶冶一下情操。

好了,說到這裡,就引出我們今天要做的這個功能,那就是水印相機。水印相機說白了,就是在拍照的圖片上面加上自己想要的各種信息,包括文字,圖片或者其它你想要的信息。

在這裡,我自己定義了一個類WaterCameraActivity,是自定義的相機的,然後還有一個類ViewPhoto,是用來查看你拍照後的圖片的,有使用圖片和取消/重新拍照功能。

3、關於不使用intent來傳輸圖片

因為intent最大的傳輸數據為1m,一張圖片隨便都有3,4m,再加之讀取到內存中,就可能變成2倍3倍大了,很容易造成oom。所以我們還是利用本地儲存來進行,只要傳輸一個路徑就OK了,這樣做的問題就是,老是要讀取本地圖片,性能不是很好呢。關於這個,如果各位有好的建議可以提出來一起探討。

 

下面我們直接上代碼,畢竟還是代碼說事比較清楚,代碼裡面都注釋好了,各位可以認真去看。

WaterCameraActivity的布局:布局很簡單,就是一個SurfaceView+需要加入的水印信息,如下圖

布局




    

        
        

        
        

            

                

                
            

            

            
        


        

            

            

            
        

        

            
            

WaterCameraActivity類

/**
 * 啟動自定義水印相機
 *
 * Created by tanksu on 16/6/28.
 */
public class WaterCameraActivity extends BaseActivity implements SurfaceHolder.Callback {

    private Context mContext;
    private SurfaceView mSurfaceView;
    private ImageButton imgvBtn_takePic, imgvBtn_switchFlash, imgvBtn_switchCamera;
    private Button btn_back;
    private TextView tv_time, tv_username, tv_address, tv_date, tv_operation;
    private SurfaceHolder mSurfaceHolder;
    private Camera mCamera;
    private String curDate = "", curTime = "", curAddress = "", userName = "", userOperation = "";
    private final int REQUEST_CODE = 1001;
    private Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
    private int mCameraId;
    private long currentTimeMillis = 0;
    private Intent waterIntent;

    /**
     * 這是點擊surfaceview聚焦所調用的方法
     */
    private Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback(){
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
            //success = true,聚焦成功,否則聚焦失敗
            //在這裡我們可以在點擊相機後是否聚焦成功,然後做我們的一些操作,這裡我就省略了,大家自行根據需要添加
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.watercamera_layout);
        initViews();
        initData();
        initListener();
    }

    /**
     * 初始化控件
     */
    private void initViews() {
        mSurfaceView = (SurfaceView) findViewById(R.id.sfv_camera);
        imgvBtn_takePic = (ImageButton) findViewById(R.id.btn_takePic);
        tv_time = (TextView) findViewById(R.id.tv_time);
        tv_username = (TextView) findViewById(R.id.tv_username);
        tv_address = (TextView) findViewById(R.id.tv_address);
        tv_date = (TextView) findViewById(R.id.tv_date);
        tv_operation = (TextView) findViewById(R.id.tv_operation);
        imgvBtn_switchFlash = (ImageButton) findViewById(R.id.imgvBtn_switchFlash);
        imgvBtn_switchFlash.setImageResource(R.drawable.camera_setting_flash_off_normal);
        imgvBtn_switchCamera = (ImageButton) findViewById(R.id.imgvBtn_switchCamera);
        imgvBtn_switchCamera.setImageResource(R.drawable.changing_camera_normal);
        btn_back = (Button) findViewById(R.id.imgvBtn_back);
        mContext = this;
    }

    /**
     * 初始化數據
     */
    private void initData() {
        mSurfaceView.setFocusable(true);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mSurfaceHolder.setKeepScreenOn(true);
        mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
        mSurfaceHolder.addCallback(this);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd"); //獲取當前時間,作為圖片的命名,再轉換為常用時間格式
        currentTimeMillis = System.currentTimeMillis();
        curDate = formatter.format(currentTimeMillis);
        tv_date.setText(curDate);
        SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.getDefault()); //獲取24小時制的時間
        curTime = format.format(currentTimeMillis);
        tv_time.setText(curTime);
        Intent intent = getIntent(); //我寫的這個類,是要用startActivityForResult來啟動的,傳入的參數可以根據自己需求來定,我這裡傳過來的信息有
        //地址CUR_ADDRESS,用戶名USER_NAME,用戶操作USER_OPERATION,然後把信息設置到空間裡面去,同時還要保存intent。
        //而時間和日期,則是在本類中自己獲取,同樣設置入控件裡面去
        if (intent != null) {
            waterIntent = intent;
            curAddress = intent.getStringExtra(StaticParam.CUR_ADDRESS);
            userName = intent.getStringExtra(StaticParam.USER_NAME);
            userOperation = intent.getStringExtra(StaticParam.USER_OPERATION);
            tv_operation.setText(userOperation);
            tv_address.setText(curAddress);
            tv_username.setText(userName);
        }else {
            toast("intent equals null,please try again!");
        }
        mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
    }

    /**
     * 初始化監聽器
     */
    private void initListener() {
        //這個方法是點擊拍照的方法
        imgvBtn_takePic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCamera.takePicture(null, null, new PicCallBacKImpl(WaterCameraActivity.this));
            }
        });
        //設置閃光燈的模式,有禁止,自動和打開閃光燈三種模式
        imgvBtn_switchFlash.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CameraUtil.setFlashMode(mCamera, imgvBtn_switchFlash);
            }
        });
        //這個是切換前後攝像頭的操作,因為時間關系沒有做
        imgvBtn_switchCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            }
        });
        //取消按鈕,finish本頁面
        btn_back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                WaterCameraActivity.this.finish();
            }
        });
        mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mCamera.autoFocus(autoFocusCallback); //設置相機為自動對焦模式,就不用認為去點擊了
                return false;
            }
        });
    }


    /**
     * 我們在此周期方法裡面打開攝像頭
     */
    @Override
    protected void onStart() {
        if (this.checkCameraHardware(this) && (mCamera == null)) {
            openCamera();//打開後置攝像頭
        }
        super.onStart();
    }

    /**
     * 拍照回調類
     */
    class PicCallBacKImpl implements Camera.PictureCallback {
        private Activity mActivity;

        public PicCallBacKImpl(Activity activity) {
            this.mActivity = activity;
        }

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            bitmap = ImageUtil.matrixImageView(bitmap, 90);
            String path = ImageUtil.saveBitmap(null, String.valueOf(currentTimeMillis), bitmap);
            if (path != null && path.length() > 0) {
                waterIntent.setClass(mActivity, ViewPhoto.class);
                waterIntent.putExtra(StaticParam.PIC_PATH, path);
                waterIntent.putExtra(StaticParam.CUR_DATE, curDate);
                waterIntent.putExtra(StaticParam.CUR_TIME, curTime);
                waterIntent.putExtra(StaticParam.CUR_TIME_MILLIS, currentTimeMillis);
                mActivity.startActivityForResult(waterIntent, REQUEST_CODE);
            } else {
                toast("can't save the picture");
                camera.stopPreview();
                camera.release();
                camera = null;
            }

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(RESULT_OK == resultCode){
            switch (requestCode){
                case REQUEST_CODE: //處理返回結果
                    setResult(RESULT_OK, data); //將結果直接給設置為,啟動水印相機的返回結果
                    break;
                default:
                    break;
            }
            WaterCameraActivity.this.finish();//結束本頁面,就會將結果返回到調用本頁的那個activity了
        }
        super.onActivityResult(requestCode, resultCode, data);
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mCamera = Camera.open(mCameraId);
            Camera.getCameraInfo(mCameraId, cameraInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        startPreview(mCamera, mSurfaceHolder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 檢查設備是否有攝像頭
     *
     * @param context context
     * @return boolean
     */
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
            return true;
        }
        return false;
    }

    /**
     * 打開後置攝像頭
     */
    private void openCamera() {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraId, cameraInfo);
        this.cameraInfo = cameraInfo;
        if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) { //後置攝像頭 CAMERA_FACING_FRONT
            mCamera = Camera.open();
            mCamera.startPreview();//開始預覽相機
        }
    }

    /**
     * 開始預覽相機
     *
     * @param camera        camera
     * @param surfaceHolder surfaceHolder
     */
    private void startPreview(Camera camera, SurfaceHolder surfaceHolder) {
        camera.setDisplayOrientation(CameraUtil.getPreviewDegree(WaterCameraActivity.this));
        try {
            camera.setPreviewDisplay(surfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }
        camera.startPreview();//調用此方法,然後真正的預覽相機
    }

    /**
     * 停止相機預覽
     */
    private void stopPreview() {
        if (mCamera != null) {
            mCamera.release();
            mCamera.release();
            mCamera = null;
        }
    }

    @Override
    protected void onDestroy() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        super.onDestroy();
    }

}

下面是察看圖片的類

 

ViewPhoto類的布局,布局入下圖所示

\


 




    

        

        

            

            

        

        

            

            

            
        
    


    

        

        

        
    


 

ViewPhoto類代碼

<span style="font-size:12px;">/**
 * 查看拍照的類
 * <p/>
 * Created by tanksu on 16/6/29.
 */
public class ViewPhoto extends BaseActivity {
    private ImageView imgv_photo;
    private TextView tv_cancel, tv_ok;
    private int width, height;
    private RelativeLayout rl_layout;
    private String picPath = "", curDate = "", curTime = "", curAddress = "", userName = "", userOperation = "";
    private TextView tv_time, tv_date, tv_userName, tv_address, tv_operation;
    private CheckBox cb_savePic;
    private long currentTimeMillis;
    private String signal;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉標題欄
        setContentView(R.layout.viewphoto_layout);
        initViews();
        initData();
        initListener();
    }

    /**
     * 初始化控件
     */
    private void initViews() {
        imgv_photo = (ImageView) findViewById(R.id.imgv_photo);
        tv_cancel = (TextView) findViewById(R.id.tv_cancel);
        tv_ok = (TextView) findViewById(R.id.tv_ok);
        rl_layout = (RelativeLayout) findViewById(R.id.rl_layout);
        tv_time = (TextView) findViewById(R.id.tv_time);
        tv_date = (TextView) findViewById(R.id.tv_date);
        tv_userName = (TextView) findViewById(R.id.tv_userName);
        tv_address = (TextView) findViewById(R.id.tv_address);
        cb_savePic = (CheckBox) findViewById(R.id.cb_savePic);
        tv_operation = (TextView) findViewById(R.id.tv_operation);
    }

    /**
     * 初始化數據
     */
    private void initData() {
        WindowManager windowManager = getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        width = display.getWidth();
        height = display.getHeight();
        Intent intent = getIntent();
        if (intent != null) {
            //這裡的目標是,將所有傳過來的的信息都去取出來,設置到每個相應的空間裡面去
            //有人會問我為什麼要這樣做,其實我在拍照的時候,還沒有真正的拿到一張具有水印的照片
            //我這裡采用的是截屏的方式,所以呢,就要重新吧信息展現出來
            //其實還有很多的方法可以做水印相機,例如用位圖來“畫”信息等,但是有簡單的方法,為什麼不用呢,非要去弄一些很復雜的方法?!
            picPath = intent.getStringExtra(StaticParam.PIC_PATH);
            curDate = intent.getStringExtra(StaticParam.CUR_DATE);
            curTime = intent.getStringExtra(StaticParam.CUR_TIME);
            userName = intent.getStringExtra(StaticParam.USER_NAME);
            curAddress = intent.getStringExtra(StaticParam.CUR_ADDRESS);
            userOperation = intent.getStringExtra(StaticParam.USER_OPERATION);
            signal = intent.getStringExtra(StaticParam.TS_HUB_OP_SIGNAL);
            currentTimeMillis = intent.getLongExtra(StaticParam.CUR_TIME_MILLIS, System.currentTimeMillis());
            tv_time.setText(curTime);
            tv_date.setText(curDate);
            tv_userName.setText(userName);
            tv_address.setText(curAddress);
            tv_operation.setText(userOperation);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.outWidth = width;
            options.outHeight = height;
            Bitmap bitmap = ImageUtil.getPressedBitmap(picPath, width, height);//方法在下面,根據路徑,獲取第一步拍照存本地的圖片
            /**
             *
             *
             * 根據圖片路徑,得到壓縮過的位圖
             *
             * @param path
             * @param width
             * @param height
             * @return returnBitmap
            public static Bitmap getPressedBitmap(String path, int width, int height) {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                Bitmap bitmap = BitmapFactory.decodeFile(path, options);
                options.inSampleSize = getBitmapSampleSize(options, width, height);//getBitmapSampleSize(options, width, height)
                options.inJustDecodeBounds = false;
                Bitmap returnBitmap = BitmapFactory.decodeFile(path, options);
                return returnBitmap;
            }
             * 根據要去的寬高,壓縮圖片
             *
             * @param options   options
             * @param reqWidth  reqWidth
             * @param reqHeight reqHeight
             * @return inSimpleSize
            public static int getBitmapSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
                int imgWidth = options.outWidth;
                int imgHeight = options.outHeight;
                int inSimpleSize = 1;
                if (imgWidth > imgHeight || imgWidth < imgHeight) {
                    final int heightRatio = imgWidth / reqWidth;
                    final int widthRatio = imgHeight / reqHeight;
                    inSimpleSize = widthRatio < heightRatio ? widthRatio : heightRatio;
                }
                return inSimpleSize;
            }
             */
            imgv_photo.setImageBitmap(bitmap);
        } else {
            toast("intent equals null,please try again!");
        }
    }

    /**
     * 初始化監聽器
     */
    private void initListener() {
        //點擊使用圖片按鈕,就可以在啟動水印相機的onactivityresult回調裡面,獲取到圖片的路徑,然後獲取圖片即可使用了
        tv_ok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Bitmap bitmap = getScreenPhoto(rl_layout);
                ImageUtil.saveBitmap(picPath, String.valueOf(currentTimeMillis), bitmap);//根據路徑保存圖片
                /**
                 * 根據路徑和名字保存圖片
                 *
                 * @param path    path
                 * @param imgName imgName
                 * @param bitmap  bitmap
                 * @return createPath
                public static String saveBitmap(String path, String imgName, Bitmap bitmap) {
                    String savePath = null;
                    if (path == null) { //if path is null
                        File fileSDCardDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
                        String imgPath = fileSDCardDir.getAbsolutePath() + "/s/waterCamera/";
                        File fileDir = new File(imgPath);
                        if (!fileDir.exists()) {
                            fileDir.mkdirs();
                        }
                        String photoName = imgName + ".JPG";
                        imgPath = imgPath + photoName;
                        File fileIphoto = new File(imgPath);
                        if (!fileIphoto.exists()) {
                            try {
                                fileIphoto.createNewFile();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        savePath = fileIphoto.getPath();
                        saveBitmap(bitmap, fileIphoto);
                        return savePath;
                    } else { //if path isn't null, override the photo
                        File oldFile = new File(path);
                        if (oldFile.exists()) {
                            oldFile.delete();
                        }
                        File newFile = new File(path);
                        if (!newFile.exists()) {
                            try {
                                newFile.createNewFile();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        saveBitmap(bitmap, newFile);
                        savePath = newFile.getPath();
                        return savePath;
                    }
                }
                 */
                Intent intent = new Intent(ViewPhoto.this, TsDrActivity.class);
                intent.putExtra(StaticParam.PIC_PATH, picPath);//這裡最主要的,就是將儲存在本地的圖片的路徑作為結果返回
                intent.putExtra(StaticParam.IS_SAVE_PIC, cb_savePic.isChecked());//這裡就是是否用戶要保存這張圖片的選項
                intent.putExtra(StaticParam.TS_HUB_OP_SIGNAL, signal);
                setResult(RESULT_OK, intent); //如果是OK,就設置為OK結果
                ViewPhoto.this.finish();
            }
        });
        tv_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ImageUtil.deleteImageFromSDCard(picPath);//重新拍照,就將本地的圖片給刪除掉,然後重新拍照
                    }
                }).start();
                ViewPhoto.this.finish();
            }
        });
    }

    /**
     * 截屏,這裡就是截屏的地方了,我這裡是截屏RelativeLayout,
     * 只要你將需要的信息放到這個RelativeLayout裡面去就可以截取下來了
     *
     * @param waterPhoto waterPhoto
     * @return Bitmap
     */
    public Bitmap getScreenPhoto(RelativeLayout waterPhoto) {
        View view = waterPhoto;
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        int width = view.getWidth();
        int height = view.getHeight();
        Bitmap bitmap1 = Bitmap.createBitmap(bitmap, 0, 0, width, height);
        view.destroyDrawingCache();
        bitmap = null;
        return bitmap1;
    }
</span>
使用

 

//例如:啟動水印相機的代碼
Intent intent = new Intent(TsHubActivity.this, WaterCameraActivity.class);
                        intent.putExtra(StaticParam.CUR_ADDRESS, curAddress);
                        intent.putExtra(StaticParam.USER_NAME, stationName);
                        Button btn_recv = (Button) findViewById(R.id.batchRecv);
                        intent.putExtra(StaticParam.USER_OPERATION, btn_recv.getText().toString());
                        startActivityForResult(intent, JustOneOrder);
回調

 

 

//拍照回調方法
@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
		//以下是需要拍照的圖片參數
                        final String imgPath = data.getStringExtra(StaticParam.PIC_PATH);//圖片路徑
                        Bitmap bitmap = BitmapFactory.decodeFile(imgPath);//獲取到圖片了
}


 

上面這兩個類,直接復制來就可以用了,可以根據你的需求進行修改,不難,很簡單。關於前後攝像頭切換的,因為時間緊任務重,就沒有時間去做了。

如有任何問題,請及時與我聯系,謝謝。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved