精华内容
下载资源
问答
  • 拍照时,需添加时间、地理位置的水印 然后看一下功能预览: 相机预览 生成水印图片 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? 示例:pandas 是基于NumPy 的一种工具,...


    前言

    先说一下我这里的基本需求:

    1. 限制图片拍照比例 4:3
    2. 拍照时,需添加时间、地理位置的水印

    然后看一下功能预览:

    相机预览 生成水印图片
    在这里插入图片描述 在这里插入图片描述

    一、相机预览

    1. 引入库

    使用camera库

    dependencies:
      camera: ^0.7.0+2
    

    2. 自定拍照状态

    enum TakeStatus {
      /// 准备中
      preparing,
      /// 拍摄中
      taking,
      /// 待确认
      confirm,
      /// 已完成
      done
    }
    

    3. 预览布局

      Widget _buildCameraArea() {
        Widget area;
        if (_takeStatus == TakeStatus.confirm && _curFile != null) {
          // 待确认状态下,显示图片(按照宽度填充,高度超出部分隐藏)
          area = Image.file(File(_curFile.path), fit: BoxFit.fitWidth,);
        } else if (_cameraController != null && _cameraController.value.isInitialized) {
          // 相机预览
          final double screenWidth = MediaQuery.of(context).size.width;
          // 超出部分裁剪
          area = ClipRect(
            child: OverflowBox(
              alignment: Alignment.center,
              child: FittedBox(
                fit: BoxFit.fitWidth,
                child: Container(
                  width: screenWidth,
                  height: screenWidth * _cameraController.value.aspectRatio,
                  child: CameraPreview(_cameraController),
                )
              )
            ),
          );
        } else {
          // 加载时,显示空白
          area = Container(color: Colors.black,);
        }
    
        return Center(
          // 指定需要截图的区域
          child: RepaintBoundary(
            key: _cameraKey,
            child: Stack(
              children: [
                AspectRatio(
                  aspectRatio: widget.aspectRatio ?? 4 / 3,
                  child: area,
                ),
                Positioned(
                    left: 10,
                    right: 120,
                    bottom: 10,
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(_time ?? '', style: TextStyle(color: Colors.white, fontSize: 13),),
                        Text(_address ?? '', style: TextStyle(color: Colors.white, fontSize: 13),),
                      ],
                    )
                ),
              ],
            ),
          ),
        );
      }
    

    注意:CameraPreview显示在指定区域会被拉伸变形。故,需要CameraPreview正常比例显示,然后在指定区域内超出隐藏。

    二、核心功能实现

    1. 拍照

      /// 拍照
      void _takePicture() async {
        if (_cameraController == null || _cameraController.value.isTakingPicture) return;
        _timer?.cancel();
    
        XFile file = await _cameraController.takePicture();
        setState(() {
          _curFile = file;
          _takeStatus = TakeStatus.confirm;
        });
      }
    

    注意:CameraController.takePicture() 拍出的照片,是按照默认相机比例的图片。在回显的时候,按照图片宽度铺满,高度隐藏,即可得到预览相机的图片。

    2. 确认时,区域截图

      /// 确认。返回图片数据
      void _confirm() async {
        if (_isCapturing) return;
        _isCapturing = true;
        try {
          // 获取指定区域
          RenderRepaintBoundary boundary = _cameraKey.currentContext.findRenderObject();
          // 转成图片
          ui.Image image = await boundary.toImage(pixelRatio: widget.pixelRatio ?? 2.0);
          ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
          Uint8List imgBytes = byteData.buffer.asUint8List();
          // 保存图片文件在本地
          String basePath = await findSavePath(WatermarkPhoto.SAVE_DIR);
          File file = File('$basePath/${DateTime.now().millisecondsSinceEpoch}.jpg');
          file.writeAsBytesSync(imgBytes);
          // 页面返回图片文件
          Navigator.of(context).pop(file);
        } catch (e) {
          print(e);
        }
        _isCapturing = false;
      }
    

    注意:通过RepaintBoundary包裹需要截图的指定区域,并指定key。通过key获取到该区域的信息,然后实现截图。

    3. 打开水印拍照

    Future<File> takeWatermarkPhoto(BuildContext context, {
      double aspectRatio,
      double pixelRatio,
    }) async  {
      return await Navigator.of(context).push(PageRouteBuilder(
        opaque:false,
        pageBuilder: (BuildContext context, Animation<double> animation,Animation<double> secondaryAnimation) {
          return WatermarkPhoto(aspectRatio: aspectRatio, pixelRatio: pixelRatio);
        },
        transitionsBuilder: (
          BuildContext context,
          Animation<double> animation,
          Animation<double> secondaryAnimation,
          Widget child,
        ) => FadeTransition(
          opacity: animation,
          child: child,
        ),
      ));
    }
    

    设置默认的打开页面交互方式,只需关注水印拍照后返回的结果。

    4. 定位获取

    本例子中,定位功能是使用高德地图Android定位SDK实现的。你也可以直接使用别人封装好的Flutter定位插件。

    三、调用示例

    final File pickedFile = await takeWatermarkPhoto(context);
    if (pickedFile != null) {
      // 可通过Image.file()来显示图片
    }
    

    四、完整代码

    import 'dart:async';
    import 'dart:io';
    import 'dart:typed_data';
    import 'dart:ui' as ui;
    
    import 'package:camera/camera.dart';
    import 'package:date_format/date_format.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:stmy_mobile/plugin/amap/amap_location.dart';
    import 'package:stmy_mobile/plugin/amap/amap_location_option.dart';
    import 'package:stmy_mobile/utils/permission_util.dart';
    
    class WatermarkPhoto extends StatefulWidget {
      static const String SAVE_DIR = 'tempImage';
    
      final double aspectRatio;
      final double pixelRatio;
    
      WatermarkPhoto({this.aspectRatio, this.pixelRatio});
    
      @override
      _WatermarkPhotoState createState() => _WatermarkPhotoState();
    }
    
    class _WatermarkPhotoState extends State<WatermarkPhoto> with WidgetsBindingObserver {
      final GlobalKey _cameraKey = GlobalKey();
      CameraController _cameraController;
      String _time;
      String _address;
      TakeStatus _takeStatus = TakeStatus.preparing;
      XFile _curFile;
      Timer _timer;
      bool _isCapturing = false;
      
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addObserver(this);
    
        AMapLocation.init(AMapLocationOption());
        _time = formatDate(DateTime.now(), [yyyy, '-', mm, '-' , dd, ' ', HH, ':', nn, ':', ss]);
        _address = '未知位置';
        _initCamera();
      }
    
      void _initCamera() async {
        try {
          _timer?.cancel();
          _timer = Timer.periodic(Duration(seconds: 1), (timer) {
            if (mounted) {
              setState(() {
                _time = formatDate(DateTime.now(), [yyyy, '-', mm, '-' , dd, ' ', HH, ':', nn, ':', ss]);
              });
            }
          });
          setState(() {
            _takeStatus = TakeStatus.preparing;
          });
          List cameras = await availableCameras();
          _cameraController = CameraController(cameras.first, ResolutionPreset.high,
            enableAudio: false,
            imageFormatGroup: ImageFormatGroup.jpeg,
          );
          _cameraController.addListener(() {
            if (mounted) setState(() {});
          });
          await _cameraController.initialize();
          if (mounted) {
            setState(() {
              _takeStatus = TakeStatus.taking;
            });
          }
          if (await checkLocationPermission()) {
            LocationInfo info = await AMapLocation.getLocation(true);
            if (info.isSuccess()) {
              String address = info.formattedAddress;
              if ((address == null || address.isEmpty) && (info.province != null)) {
                address = info.province + info.city + info.district;
              }
              setState(() {
                _address = address;
              });
            }
          }
        } on CameraException catch (e) {
          print(e);
        }
      }
    
      @override
      void didChangeAppLifecycleState(AppLifecycleState state) {
        if (_cameraController == null || !_cameraController.value.isInitialized) {
          return;
        }
        if (state == AppLifecycleState.inactive) {
          _cameraController?.dispose();
        } else if (state == AppLifecycleState.resumed) {
          if (_cameraController != null) {
            _initCamera();
          }
        }
      }
    
      @override
      void dispose() {
        WidgetsBinding.instance.removeObserver(this);
        _cameraController?.dispose();
        AMapLocation.destroy();
        _timer?.cancel();
        super.dispose();
      }
      
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.black,
          body: Stack(
            children: [
              _buildCameraArea(),
              _buildTopBar(),
              _buildAction(),
            ],
          ),
        );
      }
      
      Widget _buildCameraArea() {
        Widget area;
        if (_takeStatus == TakeStatus.confirm && _curFile != null) {
          area = Image.file(File(_curFile.path), fit: BoxFit.fitWidth,);
        } else if (_cameraController != null && _cameraController.value.isInitialized) {
          final double screenWidth = MediaQuery.of(context).size.width;
          area = ClipRect(
            child: OverflowBox(
              alignment: Alignment.center,
              child: FittedBox(
                fit: BoxFit.fitWidth,
                child: Container(
                  width: screenWidth,
                  height: screenWidth * _cameraController.value.aspectRatio,
                  child: CameraPreview(_cameraController),
                )
              )
            ),
          );
        } else {
          area = Container(color: Colors.black,);
        }
    
        return Center(
          child: RepaintBoundary(
            key: _cameraKey,
            child: Stack(
              children: [
                AspectRatio(
                  aspectRatio: widget.aspectRatio ?? 4 / 3,
                  child: area,
                ),
                Positioned(
                    left: 10,
                    right: 120,
                    bottom: 10,
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(_time ?? '', style: TextStyle(color: Colors.white, fontSize: 13),),
                        Text(_address ?? '', style: TextStyle(color: Colors.white, fontSize: 13),),
                      ],
                    )
                ),
              ],
            ),
          ),
        );
      }
    
      Widget _buildTopBar() {
        String flashIcon = 'assets/icon-flash-auto.png';
        if (_cameraController != null && _cameraController.value.isInitialized) {
          switch (_cameraController.value.flashMode) {
            case FlashMode.auto:
              flashIcon = 'assets/icon-flash-auto.png';
              break;
            case FlashMode.off:
              flashIcon = 'assets/icon-flash-off.png';
              break;
            case FlashMode.always:
            case FlashMode.torch:
              flashIcon = 'assets/icon-flash-on.png';
              break;
          }
        }
    
        if (_takeStatus == TakeStatus.confirm) {
          return Container();
        }
    
        return Positioned(
          top: MediaQuery.of(context).padding.top + 10,
          left: 10,
          right: 10,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              IconButton(
                color: Colors.white,
                icon: Icon(Icons.arrow_back, size: 32,),
                onPressed: () => Navigator.of(context).pop()
              ),
              IconButton(
                color: Colors.white,
                icon: Image.asset(flashIcon, width: 32, height: 32,),
                onPressed: _toggleFlash
              )
            ],
          )
        );
      }
    
      Widget _buildAction() {
        Widget child;
        if (_takeStatus == TakeStatus.confirm) {
          child = Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              OutlineButton(
                shape: CircleBorder(),
                color: Colors.black.withOpacity(0.5),
                padding: EdgeInsets.all(10),
                borderSide: BorderSide(color: Colors.grey),
                child: Image.asset('assets/icon-close.png', width: 24, height: 24,),
                onPressed: _cancel
              ),
              OutlineButton(
                shape: CircleBorder(),
                color: Colors.black.withOpacity(0.5),
                padding: EdgeInsets.all(10),
                borderSide: BorderSide(color: Colors.grey),
                child: Image.asset('assets/icon-confirm.png', width: 24, height: 24,),
                onPressed: _confirm
              )
            ],
          );
        } else {
          child = OutlineButton(
            shape: CircleBorder(),
            color: Colors.black.withOpacity(0.5),
            padding: EdgeInsets.all(8),
            borderSide: BorderSide(color: Colors.grey),
            child: Icon(Icons.camera, color: Colors.white, size: 48,),
            onPressed: _takePicture
          );
        }
    
        return Positioned(
          bottom: 50,
          left: 50,
          right: 50,
          child: child
        );
      }
    
      /// 切换闪光灯
      void _toggleFlash() {
        if (_cameraController == null) return;
    
        switch (_cameraController.value.flashMode) {
          case FlashMode.auto:
            _cameraController.setFlashMode(FlashMode.always);
            break;
          case FlashMode.off:
            _cameraController.setFlashMode(FlashMode.auto);
            break;
          case FlashMode.always:
          case FlashMode.torch:
            _cameraController.setFlashMode(FlashMode.off);
            break;
        }
      }
    
      /// 拍照
      void _takePicture() async {
        if (_cameraController == null || _cameraController.value.isTakingPicture) return;
        _timer?.cancel();
    
        XFile file = await _cameraController.takePicture();
        setState(() {
          _curFile = file;
          _takeStatus = TakeStatus.confirm;
        });
      }
    
      /// 取消。重新拍照
      void _cancel() {
        setState(() {
          _takeStatus = TakeStatus.preparing;
        });
        _cameraController?.dispose();
        _initCamera();
      }
    
      /// 确认。返回图片数据
      void _confirm() async {
        if (_isCapturing) return;
        _isCapturing = true;
        try {
          RenderRepaintBoundary boundary = _cameraKey.currentContext.findRenderObject();
          ui.Image image = await boundary.toImage(pixelRatio: widget.pixelRatio ?? 2.0);
          ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
          Uint8List imgBytes = byteData.buffer.asUint8List();
          String basePath = await findSavePath(WatermarkPhoto.SAVE_DIR);
          File file = File('$basePath/${DateTime.now().millisecondsSinceEpoch}.jpg');
          file.writeAsBytesSync(imgBytes);
          Navigator.of(context).pop(file);
        } catch (e) {
          print(e);
        }
        _isCapturing = false;
      }
    }
    
    enum TakeStatus {
      /// 准备中
      preparing,
      /// 拍摄中
      taking,
      /// 待确认
      confirm,
      /// 已完成
      done
    }
    
    Future<File> takeWatermarkPhoto(BuildContext context, {
      double aspectRatio,
      double pixelRatio,
    }) async  {
      return await Navigator.of(context).push(PageRouteBuilder(
        opaque:false,
        pageBuilder: (BuildContext context, Animation<double> animation,Animation<double> secondaryAnimation) {
          return WatermarkPhoto(aspectRatio: aspectRatio, pixelRatio: pixelRatio);
        },
        transitionsBuilder: (
          BuildContext context,
          Animation<double> animation,
          Animation<double> secondaryAnimation,
          Widget child,
        ) => FadeTransition(
          opacity: animation,
          child: child,
        ),
      ));
    }
    
    
    /// 获取文件存储路径
    Future<String> findSavePath([ String basePath ]) async {
      final directory = Platform.isAndroid
          ? await getExternalStorageDirectory()
          : await getApplicationDocumentsDirectory();
      if (basePath == null) {
        return directory.path;
      }
      String saveDir = path.join(directory.path, basePath);
      Directory root = Directory(saveDir);
      if (!root.existsSync()) {
        await root.create();
      }
      return saveDir;
    }
    
    展开全文
  • android自定义相机添加自定义水印

    千次阅读 2014-06-19 16:11:43
    最近做了一个自定义的相机,拍照后自动添加自定义

    最近做了一个自定义的相机,拍照后自动添加自定义水印图片,并且在预览窗口的右下角显示出来。下面总结一下自定义相机的步骤

    一、继承SurfaceHolder.Callback回调接口,实现重载的三个方法,初始化摄像头和预览框:

    	@Override
    	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    		initCamera();
    	}	
    	@Override
    	public void surfaceCreated(SurfaceHolder paramSurfaceHolder) {
    		  try{
    		      this.mCamera = Camera.open();
    		      //this.mCamera.setDisplayOrientation(90);
    		      
    		      //在顺时针方向的基础上获取旋转角度
    		      int degrees = getDisplayOritation(getDispalyRotation(), getDefaultCameraId());
    		      mCamera.setDisplayOrientation(degrees);//设置相机预览的旋转角度
    		      
    		      this.mCamera.setPreviewDisplay(paramSurfaceHolder);//设置预览所要用的surfaces对象
    		      return;
    		    }catch (IOException localIOException){
    		      this.mCamera.release();
    		      this.mCamera = null;
    		    } 
    	}
    	@Override
    	public void surfaceDestroyed(SurfaceHolder arg0) {
    	    stopCamera();
    	    if (this.mCamera != null)
    	    {
    	      this.mCamera.release();
    	      this.mCamera = null;
    	    }
    	}

    二、创建Camera.AutoFocusCallback对象,实现调用mCamera.autoFocus(this.mAutoFocusCallback)这个方法时就能够自动拍照:

    	private Camera.AutoFocusCallback mAutoFocusCallback = new Camera.AutoFocusCallback(){
    	    public void onAutoFocus(boolean paramBoolean, Camera paramCamera){
    	    	MainActivity.this.mCamera.takePicture(MainActivity.this.shutterCallback, 
    	    			MainActivity.this.rawCallback, MainActivity.this.jpegCallback);
    	    }
    	};

    三、创建onAutoFocus方法中所需的三个对象,获取图片数据:

    	private Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback(){
    	    public void onShutter(){}
    	};
    	private Camera.PictureCallback rawCallback = new Camera.PictureCallback(){
    	    public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){}
    	};
    	private Camera.PictureCallback jpegCallback = new Camera.PictureCallback(){
    	    public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){
    	    	File pictureFile = MainActivity.this.getOutputMediaFile();	        	    	
    	    	savePicture(pictureFile, paramArrayOfByte);	              	              
    	        initCamera();       	        
    	    }
    	};

    四、在PictureCallback对象中的重载方法中获取图像数据,在这里就能够对图像进行一系列操作,如:压缩,加水印等等,然后再初始化摄像头。

    设置拍照按钮,在onclick方法中调用autoFocus方法:

    	private void takePicture(){
    	    if (this.mCamera != null){
    	    	this.mCamera.autoFocus(this.mAutoFocusCallback);
    	    }
    	}

    这样一个自定义的相机就完成了,能够实现水印,压缩等功能。项目代码链接:自定义水印相机

     

    展开全文
  • android之利用surfaceView实现自定义水印相机

    万次阅读 热门讨论 2016-07-24 09:47:20
    android之利用surfaceView实现自定义水印相机 知识点 1、自定义相机+预览相机 2、截屏拍照加水印 3、关于不使用intent来传输图片 俗话说,有图有真相。很多人都是喜欢直接看图,不像我,比较喜欢文字多点,经常看...

    android之利用surfaceView实现自定义水印相机

    知识点

    1、自定义相机+预览相机

    2、截屏拍照加水印

    3、关于不使用intent来传输图片

    4、关于大家说要demo的,因为这里是项目里头的,不方便弄出来,大家可以直接复制代码过去,就差不多的了。


    俗话说,有图有真相。很多人都是喜欢直接看图,不像我,比较喜欢文字多点,经常看看散文什么的陶冶一下情操。

    好了,说到这里,就引出我们今天要做的这个功能,那就是水印相机。水印相机说白了,就是在拍照的图片上面加上自己想要的各种信息,包括文字,图片或者其它你想要的信息。

    在这里,我自己定义了一个类WaterCameraActivity,是自定义的相机的,然后还有一个类ViewPhoto,是用来查看你拍照后的图片的,有使用图片和取消/重新拍照功能。

    3、关于不使用intent来传输图片

    因为intent最大的传输数据为1m,一张图片随便都有3,4m,再加之读取到内存中,就可能变成2倍3倍大了,很容易造成oom。所以我们还是利用本地储存来进行,只要传输一个路径就OK了,这样做的问题就是,老是要读取本地图片,性能不是很好呢。关于这个,如果各位有好的建议可以提出来一起探讨。


    下面我们直接上代码,毕竟还是代码说事比较清楚,代码里面都注释好了,各位可以认真去看。

    WaterCameraActivity的布局:布局很简单,就是一个SurfaceView+需要加入的水印信息,如下图


    布局

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <!--这是用来预览相机的空间,大家可以去好好了解一下,这里我就不详细讲了-->
            <SurfaceView
                android:id="@+id/sfv_camera"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
            <!--想要的信息放在surfaceview之上就OK了,下面是我想要加入的信息-->
            <RelativeLayout
                android:id="@+id/rl_setting"
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:background="@null"
                android:gravity="center_vertical">
    
                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="5dp"
                    android:orientation="vertical">
    
                    <TextView
                        android:id="@+id/tv_time"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:shadowColor="#000"
                        android:shadowDx="3"
                        android:shadowDy="3"
                        android:shadowRadius="2"
                        android:text="08:27"
                        android:textColor="#fff"
                        android:textSize="25sp" />
    
                    <TextView
                        android:id="@+id/tv_date"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:shadowColor="#000"
                        android:shadowDx="3"
                        android:shadowDy="3"
                        android:shadowRadius="2"
                        android:text="2016/07/24"
                        android:textColor="#fff"
                        android:textSize="15sp" />
                </LinearLayout>
    
                <ImageButton
                    android:id="@+id/imgvBtn_switchFlash"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_marginRight="20dp"
                    android:background="@null"
                    android:paddingLeft="10dp"
                    android:paddingRight="10dp" />
    
                <ImageButton
                    android:id="@+id/imgvBtn_switchCamera"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_marginRight="20dp"
                    android:background="@null"
                    android:paddingLeft="10dp"
                    android:paddingRight="10dp"
                    android:visibility="gone" />
            </RelativeLayout>
    
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="75dp"
                android:layout_marginRight="3dp"
                android:gravity="right"
                android:orientation="vertical">
    
                <TextView
                    android:id="@+id/tv_operation"
                    style="@style/textView_water"
                    android:drawableLeft="@drawable/operation"
                    android:text="点击操作" />
    
                <TextView
                    android:id="@+id/tv_username"
                    style="@style/textView_water"
                    android:drawableLeft="@drawable/user_icon"
                    android:text="钓鱼的小木匠" />
    
                <TextView
                    android:id="@+id/tv_address"
                    style="@style/textView_water"
                    android:drawableLeft="@drawable/address"
                    android:text="广东省惠州市惠城区鹅岭南路6号TCL工业大厦" />
            </LinearLayout>
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="75dp"
                android:layout_alignParentBottom="true"
                android:layout_gravity="center_vertical">
    
                <!--android:background="@drawable/take_photo_selector"
                定义一个根据点击状态改变的xml文件-->
                <ImageButton
                    android:id="@+id/btn_takePic"
                    android:layout_width="64dp"
                    android:layout_height="64dp"
                    android:layout_centerInParent="true"
                    android:background="@drawable/take_photo_selector" />
    
                <Button
                    android:id="@+id/imgvBtn_back"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_marginLeft="20dp"
                    android:background="@null"
                    android:text="取 消"
                    android:textColor="#0f0"
                    android:textSize="20sp" />
            </RelativeLayout>
        </RelativeLayout>
    
    </RelativeLayout>
    

    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类的布局,布局入下图所示



    <span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <RelativeLayout
            android:id="@+id/rl_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="0dp"
            android:padding="0dp">
    
            <ImageView
                android:id="@+id/imgv_photo"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#000000" />
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="12dp"
                android:layout_marginTop="65dp"
                android:orientation="vertical">
    
                <TextView
                    android:id="@+id/tv_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:shadowColor="#000"
                    android:shadowDx="3"
                    android:shadowDy="3"
                    android:shadowRadius="2"
                    android:text="08:27"
                    android:textColor="#fff"
                    android:textSize="25sp" />
    
                <TextView
                    android:id="@+id/tv_date"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/tv_time"
                    android:shadowColor="#000"
                    android:shadowDx="3"
                    android:shadowDy="3"
                    android:shadowRadius="2"
                    android:text="2016/07/24"
                    android:textColor="#fff"
                    android:textSize="15sp" />
    
            </RelativeLayout>
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginBottom="70dp"
                android:layout_marginRight="1dp"
                android:gravity="right"
                android:orientation="vertical">
    
                <TextView
                    android:id="@+id/tv_operation"
                    style="@style/textView_water"
                    android:drawableLeft="@drawable/operation"
                    android:text="点击操作" />
    
                <TextView
                    android:id="@+id/tv_userName"
                    style="@style/textView_water"
                    android:drawableLeft="@drawable/user_icon"
                    android:text="钓鱼的小木匠" />
    
                <TextView
                    android:id="@+id/tv_address"
                    style="@style/textView_water"
                    android:drawableLeft="@drawable/address"
                    android:text="广东省惠州市惠城区鹅岭南路6号TCL工业大厦" />
            </LinearLayout>
        </RelativeLayout>
    
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="10dp"
            android:background="#000"
            android:gravity="center_vertical">
    
            <TextView
                android:id="@+id/tv_cancel"
                style="@style/textView_common"
                android:layout_marginLeft="20dp"
                android:text="重 拍" />
    
            <TextView
                android:id="@+id/tv_ok"
                style="@style/textView_common"
                android:layout_alignParentRight="true"
                android:layout_marginRight="20dp"
                android:text="使用图片" />
    
            <CheckBox
                android:id="@+id/cb_savePic"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:checked="true"
                android:text="保存"
                android:textColor="#4fe19f" />
        </RelativeLayout>
    </RelativeLayout>
    </span>

    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>

    使用

    <span style="font-size:12px;">//例如:启动水印相机的代码
    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);
    </span>
    回调

    <span style="font-size:12px;">//拍照回调方法
    @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
    		//以下是需要拍照的图片参数
                            final String imgPath = data.getStringExtra(StaticParam.PIC_PATH);//图片路径
                            Bitmap bitmap = BitmapFactory.decodeFile(imgPath);//获取到图片了
    }</span>


    上面这两个类,直接复制来就可以用了,可以根据你的需求进行修改,不难,很简单。关于前后摄像头切换的,因为时间紧任务重,就没有时间去做了。

    如有任何问题,请及时与我联系,谢谢。





    展开全文
  • 这几天在做一个拍照水印的功能,开发的过程中要求可以横向拍摄,但是水印也要跟随着屏幕的方向,因为水印显示的是当前的经纬度,总不能说我把手机倒着,让文字也倒着显示吧,所以就要求写一个能跟着屏幕旋转的水印...

    这几天在做一个拍照加水印的功能,开发的过程中要求可以横向拍摄,但是水印也要跟随着屏幕的方向,因为水印显示的是当前的经纬度,总不能说我把手机倒着,让文字也倒着显示吧,所以就要求写一个能跟着屏幕旋转的水印控件。

    自定义控件的代码如下:

    public class WaterMarkView extends View {
    
        private Context mContext;
        private String mTime;
        private String mLocation;
        private Paint mPaint;
        private int mDegree;
        private float mPaintTextSize = 16;
    
        public WaterMarkView(Context context) {
            super(context);
            setWillNotDraw(false);
        }
    
        public WaterMarkView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setWillNotDraw(false);
            this.mContext = context;
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(mContext.getResources().getColor(R.color.mis_pink));
            mPaint.setTextSize(DisplayUtil.dip2px(mContext, mPaintTextSize));
            mPaint.setFakeBoldText(true);
            mPaint.setShadowLayer(1.6f, 1.5f, 1.3f, Color.BLACK);
        }
    
        public void setTvTime(String time) {
            this.mTime = time;
        }
    
        public void setTvLocation(String location) {
            this.mLocation = location;
        }
    
        public void setDegree(int degree) {
            this.mDegree = degree;
        }
    
        public void setTextSize(int size) {
            this.mPaintTextSize = size;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int width = ScreenUtils.getScreenWidth(mContext);
            int height = ScreenUtils.getScreenHeight(mContext);
            Log.i("sherry", "width=" + width + "height=" + height);
            switch (mDegree) {
                case 0:
                    canvas.rotate(0);
                    canvas.drawText(mTime, 60, height - mPaintTextSize / 16 * 120, mPaint);
                    canvas.drawText(mLocation, 60, height - mPaintTextSize / 16 * 60, mPaint);
                    break;
                case -90:
                    canvas.rotate(-90);
                    canvas.translate(-height, 0);
                    canvas.drawText(mTime, 120, width - mPaintTextSize / 16 * 120, mPaint);
                    canvas.drawText(mLocation, 120, width - mPaintTextSize / 16 * 60, mPaint);
                    break;
                case 90:
                    canvas.rotate(90);
                    canvas.translate(0, -width);
                    canvas.drawText(mTime, 120, width - mPaintTextSize / 16 * 120, mPaint);
                    canvas.drawText(mLocation, 120, width - mPaintTextSize / 16 * 60, mPaint);
                    break;
                case 180:
                    canvas.rotate(180, width / 2, height / 2);
                    canvas.drawText(mTime, 60, height - mPaintTextSize / 16 * 120, mPaint);
                    canvas.drawText(mLocation, 60, height - mPaintTextSize / 16 * 60, mPaint);
                    break;
            }
        }
    }
    

    项目下载地址:https://download.csdn.net/download/shan286/10417458

    展开全文
  • 相机-设置-打开照片水印-添加自定义(添加黄色表情)-拍照-观察照片 问题分析: 水印实现流程为:APP传输水印yuv到hal层,在hal层完成叠加动作 dump出水印yuv图与拍照生成的yuv图观察: 水印yuv图用nv12解析时显示...
  • android实现自定义相机以及图片的水印

    千次阅读 热门讨论 2016-06-26 16:34:53
    最近在做图片的水印功能,也就研究了一下android的相机。在android中有两种方式可以进行拍照,一种是调用系统的拍照功能调用方式比较简单而且不需要权限。 调用方式: //照相机中获取照片 public void ...
  • android 自定义比例裁剪图片(拍照 相册)方式

    千次阅读 热门讨论 2016-12-16 16:29:30
    先说下我这个有啥好处 主要是能够自定义设置裁剪图片的比例.比如 :今天产品 给你说裁剪 成16:9 的图片. 你做好了 OK 明天 产品又和你说 裁剪成10:7 的图片 ,你是不是要吐血, 我这里只要设置一行代码就可以改变裁剪的...
  • 这是一款使用 AVFoundation 自定义的相机,与系统原生相机有一样的外观但比系统的相机更好、更符合实际的开发需要、可以自己修改 UI、实现拍照、取消、闪光灯控制、前后摄像头控制、聚焦、放大缩小、拍照后预览、重...
  • 业务场景描述 :特殊照片防止被别人利用,需要添加水印,最近...2.自定义要添加水印的文本内容 3.再把文本内容绘制到Bitmap上就可以 4.下面是我自己封装的一个添加水印的工具类: public class CommonUtil { ...
  • 自定义相机

    2014-06-19 16:11:40
    能够实现拍照,添加水印,预览窗口自适应屏幕
  • Android自定义相机相册

    2021-05-14 11:03:29
    Android自定义相机相册写成了库 通过需要自行修改 内置前后置拍照水印闪光聚焦等功能
  • 更惨的是获取旋转角度什么的始终是0,没办法弃疗使用自定义相机,顺便可以去掉系统相机的水印。 简单归纳为以下一点: 横竖屏拍摄后,竖屏状态下查看图片始终是正的 2.选材 自己做个相机在我的项目中没有必要,...
  • EasyPhotos QQ交流群: 若无法下载demo,可以在QQ交流群的群文件下载,文件名为demo-release,下载最新的即可。 无选中状态(默认UI色调) 选中状态( ) 其他功能(各功能可自选) 专辑列表(默认UI色调) ...
  • 第二种是拍照后,把文字等等ui要求的东西通过bitmap代码以水印的方式加上去。 截屏的话ui可以随意布局,都能得到想要的照片;但Android方面截屏参差不齐,有的还要root;我还没有细看,所以暂时用第二种方式实现。 ...
  • Android 水印相机开发

    千次阅读 2019-03-08 16:57:46
    水印相机是自定义相机的一种,实现方法有很多,我看了很多别人的做的很漂亮,我做的就很普通了,不过总算是实现了拍照水印的功能。 我这边用到了SurfaceView,有人没用这个也做出来水印相机,个人觉得还是...
  • 可牛拍照 v1.0.2.1001

    2019-03-28 11:09:31
    • 新增设置,可自定义水印、关闭拍照声音 • 修复与部分虚拟摄像头共存崩溃的BUG • 去掉使用拍照时创建的闪图文件夹 • 开始菜单中增加拍照卸载快捷方式 • 修复了部分系统出现的皮肤包加载失败BUG
  • 自定义最大选择量及最大预览量.zip,方便易用的相册多选框架,支持预览/相册内拍照及录视频、拖拽/滑动选择,3DTouch预览,编辑裁剪图片/视频,导出视频(可添加水印,粒子特效,视频转码);支持多语言国际化(中文简/...
  • Android水印相机和过渡动画的实现

    千次阅读 2018-05-09 12:11:10
     好了社会我杰哥人狠话不多,接下来直接开始:小目标有个分享页,分享页面有很多图片供用户分享,用户也可以选择自定义拍照或者选择照片上传分享.公司的UI大(mei)神(zi)不想要原生的相机拍照,于是乎就有了以下的想法: ...
  • Luban_iOS:维基-源码

    2021-05-16 22:49:44
    现在可以添加自定义文字 水印 了 | Add custom text as an watermark to your image 对应的调用方法:[UIImage lubanCompressImage:image withMask:maskName] Luban-iOS 借鉴 压缩库,生成的 iOS 版 压缩代码 压缩...
  •  功能介绍: 1、自定义照相机UI; 2、多张水印循环加载; 3、闪光灯设置; 4、前后摄像头切换; 5、拍照后静态图片显示,保存或取消拍摄的图。 测试准备:由于作者没有做屏幕大小适配,所以目前代码只能在...
  • 批量水印:由用户自定义水印图片和合成方式,加入到选择图片及文件夹下所有图片的指定位置; 批量更名:由用户自定义更名规则,将指定文件进行统一更名; 邮件发送:发送前可转换图像大小,更易于邮件的发送和接收;...

空空如也

空空如也

1 2 3
收藏数 43
精华内容 17
关键字:

拍照自定义水印