Android自定义相机,带定位功能

1.公司项目要求,拍照时要显示自己的位置;排出的照片也要在照片上能看到位置;

大致两种思路:自定义拍照页面后,点击拍照的时候,不拍照而是截屏;第二种是拍照后,把文字等等ui要求的东西通过bitmap代码以水印的方式加上去。

截屏的话ui可以随意布局,都能得到想要的照片;但Android方面截屏参差不齐,有的还要root;我还没有细看,所以暂时用第二种方式实现。

2.先看大概的样子

 

这是打开相机和拍完照的样子。 

3.我这里使用的是camera1;对camera2来说,各个手机厂家支持情况不太一样,项目赶时间就没用。

3.1调用相机的代码网上很多这里就不说了;文末有我的完整demo;

3.2定位使用的是高德地图,官方demo代码拷贝过来就好。

3.3说一下下面的位置信息布局跟随屏幕转换的代码

private void  initOrientate(){
        if(mOrientationEventListener == null){
            mOrientationEventListener = new OrientationEventListener(getActivity()) {
                @Override
                public void onOrientationChanged(int orientation) {
                    // i的范围是0-359
                    // 屏幕左边在顶部的时候 i = 90;
                    // 屏幕顶部在底部的时候 i = 180;
                    // 屏幕右边在底部的时候 i = 270;
                    // 正常的情况默认i = 0;
                    if(45 <= orientation && orientation < 135){
                        takePhotoOrientation = 180;
                    } else if(135 <= orientation && orientation < 225){
                        takePhotoOrientation = 270;
                    } else if(225 <= orientation && orientation < 315){
                        takePhotoOrientation = 0;
                    } else {
                        takePhotoOrientation = 90;
                    }

                    // 只检测是否有四个角度的改变
                    if (orientation < 60 && orientation > 30) { // 动画0度与接口360度相反,增加下限抵消0度影响
                        orientation = 360;
                    } else if (orientation > 70 && orientation < 110) { // 动画90度与接口270度相反
                        orientation = 270;
                    } else if (orientation > 160 && orientation < 200) { // 180度
                        orientation = 180;
                    } else if (orientation > 240 && orientation < 300) {
                        orientation = 90;
                    } else if (orientation > 320 && orientation < 340) {// 减少上限减少360度的影响
                        orientation = 0;
                    } else {
                        return;
                    }
                    if (oldOrientation != orientation) {
                        ObjectAnimator rotation = ObjectAnimator
                                .ofFloat(ll_photo_message, "Rotation", oldOrientation,
                                        orientation).setDuration(0);
                        int photoMessageHeight = ll_photo_message.getHeight();
                        System.out.println("ll_photo_message--h---->"+photoMessageHeight+"--w---->"+screenWidth);
                        System.out.println("orientation------>"+orientation);
                        if (orientation == 270) {
                            ll_photo_message.setPivotX(screenWidth/2);
                            ll_photo_message.setPivotY(-(screenWidth/2-photoMessageHeight));
                        }else if(orientation == 90){
                            ll_photo_message.setPivotX(screenWidth/2);
                            ll_photo_message.setPivotY(-(screenWidth/2-photoMessageHeight));
                        }else if(orientation == 180){
                            ll_photo_message.setPivotX(screenWidth/2);
                            ll_photo_message.setPivotY(-(screenWidth/2-photoMessageHeight));
                        }else {
                            ll_photo_message.setPivotX(0);
                            ll_photo_message.setPivotY(0);
                        }
                        rotation.start();
                        if (orientation == 270 || orientation == 0){
                            ObjectAnimator translationY = ObjectAnimator.ofFloat(ll_photo_message, "translationY",
                                    -(cameraPreview.getHeight() - screenWidth), 0);
                            translationY.setDuration(0);
                            translationY.start();
                        } else if(orientation == 90 || orientation == 180){
                            ObjectAnimator translationY = ObjectAnimator.ofFloat(ll_photo_message, "translationY",
                                    0,-(cameraPreview.getHeight() - screenWidth));
                            translationY.setDuration(0);
                            translationY.start();
                        }
                        ll_photo_message.clearAnimation();
                        oldOrientation = orientation;
                    }
                }
            };

        }
        mOrientationEventListener.enable();

    }

首先,最上面是通过手机方向确定相机的方向,保证拍的照片是正向的;关键在下面给view做属性动画;因为相机的框是长方形的,只做旋转是不能达到效果的;所以当做不同位置旋转时,就要再加上平移的操作了;这样就能保证文字提示永远在屏幕的左下角了。

可以卡到手机在四个方向旋转时,文字会始终在屏幕的左下角显示。至于里面具体的宽度高度,就是相机显示的ui宽高和屏幕的宽高来做旋转和平移。

ObjectAnimator rotation = ObjectAnimator
                                .ofFloat(ll_photo_message, "Rotation", oldOrientation,
                                        orientation).setDuration(0);
rotation.start();
以上是旋转的代码,我们都知道;

那么怎么设置旋转的圆心呢?

ll_photo_message.setPivotX(screenWidth/2);
ll_photo_message.setPivotY(-(screenWidth/2-photoMessageHeight));

setPivotX和setPivotY就是设置的旋转的中心点了;但这个数是  要旋转的控件的左上角那个点的相对位置。找好旋转中心点后,算出中心点距离旋转控件的左上角的距离,向上,向左是负值,向下向右是正值。

3.4拍完照片后,要把水印加上去

package com.example.android.camera2basic.camera1;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.RectF;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.text.Layout;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;

import com.example.android.camera2basic.R;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import androidx.core.content.FileProvider;

public class BitmapManager {
    private Context context;

    public BitmapManager(Context context) {
        this.context = context;
    }

    public  File createFile(String fileName, String dirName) {
        String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
                .getAbsoluteFile() + File.separator + dirName;
        File mIVMSFolder = new File(path);
        if (!mIVMSFolder.exists()) {
            mIVMSFolder.mkdirs();
        }
        return new File(mIVMSFolder.getAbsolutePath(), fileName);
    }
    public Uri getUriFromFile(Context context, File file){
        if (Build.VERSION.SDK_INT >= 24) {
            return FileProvider.getUriForFile(context,context.getPackageName()+".provider", file);
        } else {
            return Uri.fromFile(file);
        }
    }
    public void getPhoto(File mFile, byte[] imageData, int mCameraId, int takePhotoOrientation,String position, String project) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(mFile);
            fos.write(imageData);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                    // 获得图片
                    Bitmap mBitmap = BitmapFactory.decodeFile(mFile.getPath());
                    //添加时间水印
                    Bitmap newBitmap = AddTimeWatermark(mBitmap,mCameraId, takePhotoOrientation, position, project);
                    if (onBitmapCompleteListener != null)
                        onBitmapCompleteListener.OnBitmapComplete(newBitmap);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public  Bitmap AddTimeWatermark(Bitmap mBitmap, int mCameraId, int takePhotoOrientation,String position, String project) {
        Matrix matrix = new Matrix();
        matrix.postRotate(Float.valueOf(takePhotoOrientation));
        if(mCameraId == 1){
            if(takePhotoOrientation == 90){
                matrix.postRotate(180f);
            }
        }
        //获取原始图片与水印图片的宽与高
        Bitmap mNewBitmap = Bitmap.createBitmap(mBitmap, 0, 0,
                mBitmap.getWidth(), mBitmap.getHeight(), matrix, true);
        //新增 如果是前置 需要镜面翻转处理
        if(mCameraId == 1){
            Matrix matrix1 = new Matrix();
            matrix1.postScale(-1f,1f);
            mNewBitmap = Bitmap.createBitmap(mNewBitmap, 0, 0,
                    mNewBitmap.getWidth(), mNewBitmap.getHeight(), matrix1, true);
        }

        Bitmap mNewBitmap2 = Bitmap.createBitmap(mNewBitmap.getWidth(), mNewBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas mCanvas = new Canvas(mNewBitmap2);
        //向位图中开始画入MBitmap原始图片
        mCanvas.drawBitmap(mNewBitmap,0,0,null);
        //添加文字
        Paint mPaint = new Paint();
        mPaint.setColor(Color.WHITE);
        mPaint.setTextSize(SystemUtil.dp2px(context,40));


        //根据路径得到Typeface
//        Typeface typeface=Typeface.createFromAsset(context.getAssets(), "fonts/xs.ttf");
//        mPaint.setTypeface(typeface);
//        mCanvas.rotate(-45, (mNewBitmap2.getWidth() * 1) / 2, (mNewBitmap2.getHeight() * 1) / 2);

        boolean isShuPing = takePhotoOrientation == 0 || takePhotoOrientation == 90;
        //矩形背景
        Paint bgRect=new Paint();
        bgRect.setStyle(Paint.Style.FILL);
        bgRect.setColor(context.getResources().getColor(R.color.gray));
        RectF rectF=new RectF(SystemUtil.dp2px(context,10),
                mNewBitmap.getHeight()-SystemUtil.dp2px(context,isShuPing?120:90),
                mNewBitmap.getWidth()-SystemUtil.dp2px(context,50),
                mNewBitmap.getHeight());
        mCanvas.drawRect(rectF, bgRect);
        //画时间
        drawPosition(mNewBitmap, mCanvas,
                new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()),
                R.drawable.line,
                SystemUtil.dp2px(context,20),
                mNewBitmap.getHeight()-SystemUtil.dp2px(context,isShuPing?120:90),
                0);
        //画位置
        drawPosition(mNewBitmap, mCanvas,
                position,
                R.drawable.location,
                SystemUtil.dp2px(context,20),
                mNewBitmap.getHeight()-SystemUtil.dp2px(context,isShuPing?90:60),
                1);

        //画项目名称
        drawPosition(mNewBitmap, mCanvas,
                project,
                R.drawable.dot,
                SystemUtil.dp2px(context,20),
                mNewBitmap.getHeight()-SystemUtil.dp2px(context,isShuPing?50:30),
                2);


        mCanvas.save();
        mCanvas.restore();
        return mNewBitmap2;
    }

    public  void drawPosition(Bitmap mNewBitmap, Canvas mCanvas, String mFormat, int drawable,
                              int x, int y, int type) {
        if (TextUtils.isEmpty(mFormat)) return;

        Bitmap resource = BitmapFactory.decodeResource(context.getResources(), drawable);
        int width = resource.getWidth();
        int height = resource.getHeight();
        // 设置想要的大小
        int newWidth = 0;
        int newHeight = 0;
        int disX = 0;
        int disY = 0;
        switch (type){
            case 0:
                newWidth = SystemUtil.dp2px(context,2);
                newHeight = SystemUtil.dp2px(context,12);
                disX = SystemUtil.dp2px(context,4);
                disY = SystemUtil.dp2px(context,5);
                break;
            case 1:
                newWidth = SystemUtil.dp2px(context,10);
                newHeight = SystemUtil.dp2px(context,10);
                disX = SystemUtil.dp2px(context,0);
                disY = SystemUtil.dp2px(context,4);
                break;
            case 2:
                newWidth = SystemUtil.dp2px(context,3);
                newHeight = SystemUtil.dp2px(context,3);
                disX = SystemUtil.dp2px(context,3.5f);
                disY = SystemUtil.dp2px(context,5);
                break;
        }
        // 计算缩放比例
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 取得想要缩放的matrix参数
        Matrix matrix2 = new Matrix();
        matrix2.postScale(scaleWidth, scaleHeight);
        // 得到新的图片
        resource = Bitmap.createBitmap(resource, 0, 0, width, height, matrix2, true);
        mCanvas.drawBitmap(resource, x+disX, y+disY, null);

        TextPaint textPaint = new TextPaint();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(type == 0 ?
                SystemUtil.dp2px(context,16):SystemUtil.dp2px(context,12));
        StaticLayout staticLayout = new StaticLayout(mFormat, textPaint,
                mNewBitmap.getWidth()-SystemUtil.dp2px(context,100),
                    Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
        mCanvas.save();
        mCanvas.translate(2*x, y);
        staticLayout.draw(mCanvas);
        mCanvas.restore();

//        for (int i = 0; i < Layout.getLineCount(); i++) {
//            Rect rect = new Rect();
//            Paint bgRect=new Paint();
//            bgRect.setStyle(Paint.Style.FILL);
//            bgRect.setColor(Color.YELLOW);
//            Layout.getLineBounds(i, rect);
//            mCanvas.drawRect(rect, bgRect);
//        }

//        Rect rect = new Rect();
//        float v = textPaint.measureText(mFormat, 0, mFormat.length() - 1);
        Paint bgRect=new Paint();
//        textPaint.setStyle(Paint.Style.FILL);
//        textPaint.setColor(Color.parseColor("#66FFFF00"));
//        RectF rectF=new RectF(3*x, y, x+v, y+(isTime?SystemUtil.dp2px(context,40):SystemUtil.dp2px(context,30))+SystemUtil.dp2px(context,10));
//        mCanvas.drawRect(rectF, textPaint);
    }
    public void saveBitmapFile(File mFile, Bitmap bitmap){
        if (null == bitmap) return;
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(mFile));
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            bos.flush();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public interface OnBitmapCompleteListener{
        void OnBitmapComplete(Bitmap bitmap);
    }
    public OnBitmapCompleteListener onBitmapCompleteListener;
    public void setOnBitmapCompleteListener(OnBitmapCompleteListener onBitmapCompleteListener) {
        this.onBitmapCompleteListener = onBitmapCompleteListener;
    }
}

这个类中就是把拍好的照片转换成bitmap;然后通过代码画文字,画图片到bitmap上;这样就实现了水印。

里面就是一些canvas的drowText,drowBitmap的方法;

TextPaint textPaint = new TextPaint();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(type == 0 ?
                SystemUtil.dp2px(context,16):SystemUtil.dp2px(context,12));
        StaticLayout staticLayout = new StaticLayout(mFormat, textPaint,
                mNewBitmap.getWidth()-SystemUtil.dp2px(context,100),
                    Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
        mCanvas.save();
        mCanvas.translate(2*x, y);
        staticLayout.draw(mCanvas);
        mCanvas.restore();

上面这段代码可以画多行的文字;其实就是StaticLayout的作用,view画图中都有详细介绍。

3.5选择位置

这部分内容就是高德地图,先定位到当前的位置,如果不太准确就点击右侧的搜索按钮,搜索周边的poi;然后列表形式展示出来。

package com.example.android.camera2basic.camera1;

import android.content.Context;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationClientOption.AMapLocationMode;
import com.amap.api.location.AMapLocationClientOption.AMapLocationProtocol;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.location.AMapLocationQualityReport;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.core.PoiItem;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.amap.api.services.geocoder.GeocodeSearch.OnGeocodeSearchListener;
import com.amap.api.services.geocoder.RegeocodeQuery;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.amap.api.services.poisearch.PoiResult;
import com.amap.api.services.poisearch.PoiSearch;
import com.amap.api.services.poisearch.PoiSearch.OnPoiSearchListener;
import com.amap.api.services.poisearch.PoiSearch.SearchBound;

import java.text.SimpleDateFormat;
import java.util.Date;

public class LocationManager {
    private AMapLocationClient locationClient = null;
    private AMapLocationClientOption locationOption = null;
    private GeocodeSearch geocoderSearch;
    private PoiSearch.Query query;
    private PoiSearch poiSearch;
    public Context context;
    public OnPoiSearchListener onPoiSearchListener;
    public AMapLocationListener aMapLocationListener;

    public void setOnPoiSearchListener(OnPoiSearchListener onPoiSearchListener) {
        this.onPoiSearchListener = onPoiSearchListener;
    }
    public void setaMapLocationListener(AMapLocationListener aMapLocationListener) {
        this.aMapLocationListener = aMapLocationListener;
    }

    public LocationManager(Context context) {
        this.context = context;
    }

    public void initLocation(){
        //初始化client
        locationClient = new AMapLocationClient(context);
        locationOption = getDefaultOption();
        //设置定位参数
        locationClient.setLocationOption(locationOption);
        // 设置定位监听
        locationClient.setLocationListener(aMapLocationListener);

        //poi查询
        //keyWord表示搜索字符串,
        //第二个参数表示POI搜索类型,二者选填其一,选用POI搜索类型时建议填写类型代码,码表可以参考下方(而非文字)
        //cityCode表示POI搜索区域,可以是城市编码也可以是城市名称,也可以传空字符串,空字符串代表全国在全国范围内进行搜索
        query = new PoiSearch.Query("汽车服务|汽车销售|\n" +
                "        汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|\n" +
                "        住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|\n" +
                "        金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施", "", "");
        query.setPageSize(10);// 设置每页最多返回多少条poiitem
        poiSearch = new PoiSearch(context, query);
        poiSearch.setOnPoiSearchListener(onPoiSearchListener);
    }

    public void setBound(LatLonPoint latLonPoint, int distance) {
        //设置周边搜索的中心点以及半径
        if (poiSearch != null) {
            poiSearch.setBound(new SearchBound(latLonPoint, distance));
        }
    }

    public void query(int currentPage) {
        query.setPageNum(currentPage);//设置查询页码
        poiSearch.searchPOIAsyn();
    }
    private AMapLocationClientOption getDefaultOption(){
        AMapLocationClientOption mOption = new AMapLocationClientOption();
        mOption.setLocationMode(AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
        mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭
        mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setInterval(2000);//可选,设置定位间隔。默认为2秒
        mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是true
        mOption.setOnceLocation(false);//可选,设置是否单次定位。默认是false
        mOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
        AMapLocationClientOption.setLocationProtocol(AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
        mOption.setSensorEnable(false);//可选,设置是否使用传感器。默认是false
        mOption.setWifiScan(true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差
        mOption.setLocationCacheEnable(true); //可选,设置是否使用缓存定位,默认为true
        return mOption;
    }
    public String getGPSStatusString(int statusCode){
        String str = "";
        switch (statusCode){
            case AMapLocationQualityReport.GPS_STATUS_OK:
                str = "GPS状态正常";
                break;
            case AMapLocationQualityReport.GPS_STATUS_NOGPSPROVIDER:
                str = "手机中没有GPS Provider,无法进行GPS定位";
                break;
            case AMapLocationQualityReport.GPS_STATUS_OFF:
                str = "GPS关闭,建议开启GPS,提高定位质量";
                break;
            case AMapLocationQualityReport.GPS_STATUS_MODE_SAVING:
                str = "选择的定位模式中不包含GPS定位,建议选择包含GPS定位的模式,提高定位质量";
                break;
            case AMapLocationQualityReport.GPS_STATUS_NOGPSPERMISSION:
                str = "没有GPS定位权限,建议开启gps定位权限";
                break;
        }
        return str;
    }
    public void startLocation(){
        System.out.println("========开始定位");
        // 设置定位参数
        locationClient.setLocationOption(locationOption);
        // 启动定位
        locationClient.startLocation();
    }
    public void destroyLocation(){
        if (null != locationClient) {
            /**
             * 如果AMapLocationClient是在当前Activity实例化的,
             * 在Activity的onDestroy中一定要执行AMapLocationClient的onDestroy
             */
            locationClient.onDestroy();
            locationClient = null;
            locationOption = null;
        }
    }

    //逆坐标查询
    private void niQuery(LatLonPoint latLonPoint) {
        geocoderSearch = new GeocodeSearch(context);
        geocoderSearch.setOnGeocodeSearchListener(new OnGeocodeSearchListener() {
            @Override
            public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
                System.out.println("你坐标:"+regeocodeResult.getRegeocodeAddress().getCity()+"\n"
                        +regeocodeResult.getRegeocodeAddress().getDistrict()+"\n"
                        +regeocodeResult.getRegeocodeAddress().getFormatAddress()+"\n"
                        +regeocodeResult.getRegeocodeAddress().getBusinessAreas()+"\n"
                        +regeocodeResult.getRegeocodeAddress().getPois()+"\n"
                );
            }

            @Override
            public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {

            }
        });
        //查询 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系
        RegeocodeQuery query = new RegeocodeQuery(latLonPoint, 200,GeocodeSearch.GPS);
        geocoderSearch.getFromLocationAsyn(query);
    }
}

这个工具类可以直接拿去用;这里搜索的poi是把20多种类别全都传进去了;当然你也可以根据需要传入自己想要的。

4.完整的代码已经放在了GitHub上;地址:https://github.com/yueshaolong/MyCamera

如果没看明白,可以把demo拉下来跑一跑。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在Android Studio中实现录像添加时间水印的功能可以通过以下步骤来完成: 1.首先,为了实现录像功能,你需要在你的项目中添加相应的权限,包括相机和录音权限。在AndroidManifest.xml文件中添加以下代码: ```xml <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> ``` 2.接下来,在你的布局文件中添加一个SurfaceView,用于预览和展示相机视频。这可以通过在XML布局文件中添加以下代码实现: ```xml <SurfaceView android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 3.在Java代码中,你需要创建一个Camera类的实例,并设置相机参数,以及开始预览。在你的活动类(Activity)中添加以下代码: ```java private SurfaceView surfaceView; private Camera camera; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surfaceView); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // 打开相机 camera = Camera.open(); camera.setDisplayOrientation(90); // 设置相机预览界面 try { camera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } } ... }); } ``` 4.接下来,你需要在开始录制视频之前,创建一个MediaRecorder的实例,并设置相应的参数。在活动类的代码中,添加以下代码: ```java private MediaRecorder mediaRecorder; private void startRecording() { // 释放相机资源 camera.unlock(); // 创建MediaRecorder实例 mediaRecorder = new MediaRecorder(); mediaRecorder.setCamera(camera); // 设置视频源和输出格式 mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置输出文件路径 String fileName = "video_" + System.currentTimeMillis() + ".mp4"; String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileName; mediaRecorder.setOutputFile(filePath); // 设置视频编码器 mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); // 设置视频预览界面 mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface()); // 添加时间水印 mediaRecorder.setOrientationHint(90); // 设置视频方向 mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { @Override public void onInfo(MediaRecorder mediaRecorder, int what, int extra) { if (what == MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN) { // 在视频上绘制文本水印 Canvas canvas = surfaceView.getHolder().getSurface().lockCanvas(null); Paint paint = new Paint(); paint.setTextSize(30); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = sdf.format(new Date()); canvas.drawText(time, 10, 10, paint); surfaceView.getHolder().getSurface().unlockCanvasAndPost(canvas); } } }); // 准备、开始录制 try { mediaRecorder.prepare(); mediaRecorder.start(); } catch (IOException e) { e.printStackTrace(); } } ``` 5.最后,在需要开始录制的地方调用startRecording()方法即可开始录像。在活动类中添加相应的触发方法,并调用startRecording(): ```java public void startButtonClicked(View view) { startRecording(); } ``` 通过上述步骤即可在Android Studio中实现录像添加时间水印的功能。需要注意的是,上述代码中只是一个简化的示例,你可能还需要进行适当的调整和优化来满足你的具体需求。 ### 回答2: 在Android Studio中,要向录像添加时间水印,可以按照以下步骤操作: 1. 准备水印素材:首先,需要准备一张包含所需时间信息的水印图片,可以使用图片编辑软件或在线工具制作。确保水印图片的格式和大小适合你的需求。 2. 创建布局文件:在res/layout文件夹下,创建一个新的布局文件,用于展示录像内容和水印。在该布局文件中,使用VideoView控件展示录像内容,以及一个ImageView控件展示水印图片。可以使用ConstraintLayout等布局管理器来定位和调整控件的位置。 3. 设置水印图片:在Java代码中,找到布局文件对应的Activity或Fragment类。在该类的onCreate方法中,使用findViewById方法获取到ImageView和VideoView控件的引用。然后,使用setImageResource方法将水印图片设置给ImageView控件。 4. 播放录像:继续在Java代码中,使用setVideoPath方法将录像的文件路径设置给VideoView控件。最后,调用start方法开始播放录像。 5. 调整水印位置和大小:如果需要对水印进行定位和缩放,可以在Java代码中使用LayoutParams类来设置ImageView控件的布局参数。通过设置布局参数的属性,如marginLeft、marginTop等可以调整水印的位置并使其适应屏幕大小。 6. 导出水印的录像:如果希望导出水印的录像,可以使用MediaRecorder类来录制屏幕内容,同时将水印图片叠加到视频帧上。具体实现过程较为复杂,需要涉及到视频编码和处理。 需要注意的是,以上步骤只能在播放录像时展示水印,如果需要导出水印的录像,还需额外处理。此外,根据具体需求,还可以自定义水印样式,如字体颜色、大小、背景等。 ### 回答3: 要在Android Studio中实现录像添加时间水印,可以按照以下步骤进行操作: 1. 在项目的build.gradle文件中,确保已经添加了相应的依赖项。比如,可以在dependencies中添加以下依赖项: dependencies { implementation 'com.google.android.exoplayer:exoplayer:2.X.X' } 2. 在布局文件中,可以添加一个SurfaceView来预览和播放录像。 <SurfaceView android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> 3. 在Activity中,可以初始化ExoPlayer来录制视频和添加水印。 // 初始化ExoPlayer DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "App")); MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory) .createMediaSource(Uri.parse(videoPath)); // 创建一个水印图片 Drawable watermarkDrawable = getResources().getDrawable(R.drawable.watermark); Bitmap bitmap = ((BitmapDrawable) watermarkDrawable).getBitmap(); Bitmap watermarkedBitmap = drawWatermark(bitmap, "时间水印"); // 将水印图片转化为Drawable Drawable watermarkedDrawable = new BitmapDrawable(getResources(), watermarkedBitmap); // 创建一个水印覆盖层 Overlay overlay = new Overlay(watermarkedDrawable, Gravity.END | Gravity.BOTTOM); // 创建一个处理器,并将水印覆盖层添加到处理器中 VideoProcessor videoProcessor = new VideoProcessor(videoSource); videoProcessor.addOverlay(overlay); // 为SurfaceView设置播放器 SimpleExoPlayer player = new SimpleExoPlayer.Builder(this).build(); player.setVideoSurfaceView(surfaceView); player.prepare(videoProcessor.getMediaSource()); player.setPlayWhenReady(true); 4. 创建一个drawWatermark方法来将水印绘制到视频上。 private Bitmap drawWatermark(Bitmap bitmap, String watermarkText) { Bitmap watermarkedBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); Canvas canvas = new Canvas(watermarkedBitmap); // 将原始视频帧绘制到画布上 canvas.drawBitmap(bitmap, 0, 0, null); // 绘制水印文字 Paint paint = new Paint(); paint.setColor(Color.WHITE); paint.setTextSize(50); canvas.drawText(watermarkText, 10, 50, paint); return watermarkedBitmap; } 以上就是在Android Studio中实现录像添加时间水印的步骤。通过创建ExoPlayer和处理器,加上水印覆盖层,然后在SurfaceView中播放录像,即可实现这一功能
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值