• Android自定义相机

    2018-10-30 18:51:43
    相机成为了一个app必不可少的一个重要组成 它可以实现拍照,扫描二维码,视频聊天等 今天博主就来讲一下安卓开发相机 今天就带大家入门 更多资料请参考谷歌安卓开发官网android developers 简介 相机是属于...

    转载请指明出处@https://blog.csdn.net/qq_41912447
    版权归博主所有

    /**
    **本文由自己搜集资料编程 如有错误请指明
    **李紫洋著
    */

    导引

    相机成为了一个app必不可少的一个重要组成 它可以实现拍照,扫描二维码,视频聊天等 今天博主就来讲一下安卓开发相机 今天就带大家入门 更多资料请参考谷歌安卓开发官网android developers

    • 简介
      相机是属于系统资源 本身硬件就自带相机 你可以直接调用他来大大减少你的开发时间 像美颜相机这种app就需要自定义相机 现在大部分都有第三方平台 提供api供你使用 这同时大大减少你的代码量和团队创建的资金 第三方框架的使用大大减少你的工作量 同时他们的体系更加的完善 会减少bug的出现

    • 设备兼容的问题需要大家考虑 像小米 oppo vivo 华为等设备都对硬件系统做了一定的处理 用来优化他们的设备性能性 所以请参考他们这些设备的官网开发资料进行一定的处理

    一般的处理方法是在功能清单注册这些设备的权限 在代码中对这些设备进行一定的处理

    • 同时6.0动态权限也需要大家考虑 让用户有一个高的体验 这的确是初学者的一个重大考验 你需要考虑所有会发生的情况
      1软件安装提示权限
      2
      <1.用户同意继续使用app
      <2.用户拒绝

      》》》1如果有必不可少的权限则退出 像相机app如果不给予相机权限只能强制退出软件了 这时就要提示用户如果拒绝则不再可以使用此软件
      》》》2如果此权限并不正常影响你的app正常启动 则可以选择在使用你的功能时再次询问是否打开权限 这时可以使用intent信使让用户跳转到系统权限打开的类手动打开权限
      》》为了避免多次对话框出现对用户体验造成影响 甚至会卸载app
      用户可能随时关闭权限 对你的app进行管理 有可能你的app是一个相机软件 用户不能理解你使用电话权限的理由 你可以在用户拒绝的时候提示用户此权限的意义 不要在权限申请的时候进行处理 过多的提示会降低用户的体验

    3对手机系统版本做出判断 低版本安卓设备权限处理与6.0版本以上处理方法不同 主要是6.0动态权限加入危险权限和安全权限 安全权限则只需要在功能清单注册即可 一般一组权限只需要注册一个即可 用户同意则会给予你这组权限的所有

    **需要对设备本地数据库具有一定的理解 用来存储一些重要数据永久保存 将在软件卸载的时候消失 同时也要对网络传输数据库有一定的理解 了解json xml传输数据 json有一个好的工具是谷歌的gson 后续会编写相关的博客

    • 6.0动态权限有一张步骤图 这里就不公告了 我用自己的理解把他用通俗的语言写出来了 请不要觉得文字太多 每个点都是需要考虑的

    • 注 所有资源请以谷歌开发者文档官网为标准
      好了废话不多说 现在来看我的一个简单示例代码
      这里示例调用系统相机 和自定义相机两个部分

    调用系统相机

    先来看布局文件来了解一下步骤

    下面来示例这个java类的编码

    package com.liziyang.dall;
    //这里是自己的包名 必不可少的
    
    import android.annotation.SuppressLint;
    import android.app.Activity;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import java.io.File;
    import java.util.EventListener;
    //这里是导入的api库 有些是安卓的 有些是java的 也可以是框架的库 在写本类的时候需要查看导入的库 别导错库
    
    public class MainActivity extends Activity {
    //新建一个类的的初始格式  这个类继承了活动  用来实现UI界面的逻辑实现 
    //extends是继承属性  你可以根据需要继承所需的类 
        //声明控件
        private Button button,button2;
        private ImageView imageView;
        private TextView textView;
        private String fileName="";
        //定义一个 路径的字符串 这里是一个空值 下面将会用到
           //定义一个string类型的fileName方便以后使用  用来作为拍摄的图片名字
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate( savedInstanceState );
            //四大组件活动的生命周期 oncreate
            //UI界面控件的逻辑实现 需要在这里实现  
            setContentView( R.layout.activity_main );
            //设置上下文对象
            //当java类继承活动就需要设置上下文
            button=findViewById( R.id.button );
            button2=findViewById( R.id.button2 );
            imageView=findViewById( R.id.imageView );
            textView=findViewById( R.id.textView );
            //声明对象绑定UI控件ID
            button.setOnClickListener( new View.OnClickListener() {
            //按钮实现点击功能  不要在xml布局文件中使用onclick 可能会出现bug 部分机型不匹配的可能
                public void onClick(View view) {
                    //使用intent启动组件
                    //系统相机的activity位置 mediaStore.ACTION_IMAGE_CAPTURE
                    //这里使用意图 将界面跳转到系统相机界面 这些系统界面的代码简写在官方文档查询 最好把他们下载下来 方便使用的时候查询
               
                     Intent intent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE  );
                     //意图intent 也可以写成下面两行 相信大家都懂
                     //Intent intent=new Intent(); 
                     //ntent.setClass(MainActivity.this,MediaStore.ACTION_IMAGE_CAPTURE .class);
    
                     //定义一个file路径  设置为内存卡路径
                    File dir= Environment.getExternalStorageDirectory();//存在sd卡上
                    //如果没有sd卡或sd卡内存已满则会保存到手机设备中
                    //最好把网络需要的图片缓存下来 像app内置图片最好保存到分配的内置包中 防止给用户带来差的体验 
                    //假如你在自己相册看到你不想要的图片视频等 你就会卸载
                    //命名图片 得到系统毫秒
                    fileName="hey"+System.currentTimeMillis()+".jpg";
                    //初始化路径 把是存在在sd卡上和 保存图片的名字设置进去
                    File file=new File( dir,fileName );
                    //封装路径 把文件路径和名字两个参数封装进去
                    
                    Uri fileuri=Uri.fromFile( file );
                    //设置图片路径 可以为网络等 最好选择一个框架 例如glide和ImageLoader框架对图片进行优化 如果是网络获取文件 例如okHttp或xUtils 这些框架建议大家去了解一下
                    //设置uri 其他需要图片解码 uri途径就是存储设备和图片名
                    //选择拍摄照片存放位置 第二个可以给这个图片一个uri
                    //将intent添加额外数据 额外 读出 uri位置
                    intent.putExtra( MediaStore.EXTRA_OUTPUT,fileuri );
                    //意图添加其他传输数据
                    //需要接受的方法 用类似方法将他取出来
                    //有返回启动activity intent传输 返回码为0 返回码是自己写的
                    startActivityForResult( intent,0);
                    //带传值的启动活动  这个方法常用  
                    //startActivity不太常用 他不可以传值
                }
            } );
            /**启动系统相机的方法 只需要用intent启动系统组件 指明系统组件
             * 自定义存储位置 得到外部存储  定义路径 自定义图片名字 写一个string数组为图片名字 为了防止名字相同 使用毫秒
             * 将自定义名字和路径包装到uri中
             *指明intent需要的功能 回传数据
             *添加intent功能和uri地址
             * 带回传启动activity 指明intent和回传码
             */
            button2.setOnClickListener( new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                //这里就直接使用意图跳转界面 所需的放在另一个类方法实现
                    Intent intent=new Intent( MainActivity.this,Main2Activity.class );
                    startActivity( intent );
    
                }
            } );
        }
    
        //有返回值会回调这个方法
        @Override
        protected void onActivityResult(int requestCode,int resultCode,Intent data) {
            super.onActivityResult( requestCode,resultCode,data );
            //打印log 方便测试
            Log.i( "TEST","onActivityResult" );
            //如果回传码为0和回传码生成
            if(requestCode==0&&resultCode==RESULT_OK) {
                //指明路径
                File dir = Environment.getExternalStorageDirectory();//存在sd卡上
                //命名图片 得到系统毫秒
                File file = new File( dir,fileName );
                Uri fileuri = Uri.fromFile( file );
                //intent启动组件 返回数据的方法 onActivityResult
                //设置图片uri uri图片不用解码
                imageView.setImageURI( fileuri );
                //建议使用URI方法 不要需要解码否则可能会出现bug  要不然使用框架
            }
        }
    }
    
    

    在这里插入图片描述
    布局不是很美观 只是一个demo 有需要可以参考网络资料 对布局优化
    来看一下布局代码

    <?xml version="1.0" encoding="utf-8"?>
    <!--relativeLayout相对布局
    这个布局可以让你更好的精确控制控件位置 最好使用谷歌新推出的布局   他避免了嵌套布局 加快了加载速度-->
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".Main2Activity">
        <!---
        使用surfaceView来当作相机的画面 注意要全屏设置 用来美化布局-->
        <SurfaceView
            android:id="@+id/surfaceView2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentBottom="true" />
            <!--这里嵌套了一个布局 
            android:gravity="center" 把布局里的控件放到布局中心 如 --*-- -->
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:gravity="center"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true">
            <!--图片按钮 用来设置拍摄图片-->
    
            <ImageButton
                android:id="@+id/imageButton2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@android:drawable/btn_radio" />
                <!--图片控件进行预览-->
    
            <ImageView
                android:id="@+id/imageView3"
                android:layout_width="80dp"
                android:layout_height="80dp"
                android:src="@color/colorAccent" />
    
        </LinearLayout>
    
    
    
    
    </RelativeLayout>
    

    来看自定义相机的java具体实现类的代码编写

    package com.liziyang.dall;
    
    
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.hardware.Camera;
    import android.os.Bundle;
    import android.app.Activity;
    import android.os.Environment;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class Main2Activity extends Activity {
        //指明需要使用到相机
        //声明相机 因为需要用到自定义相机
        private Camera camera;
        //使用屏幕surface
        //只要是自定义的相机视频动画等需要用到屏幕view
        private SurfaceView surfaceView;
        //屏幕管理者
        private SurfaceHolder surfaceHolder;
        //屏幕管理的方法名
        private ImageButton imageButton2;
        private ImageView imageView3;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate( savedInstanceState );
            setContentView( R.layout.activity_main2 );
            surfaceView=findViewById( R.id.surfaceView2 );
            surfaceHolder=surfaceView.getHolder();
            imageButton2=findViewById( R.id.imageButton2 );
            imageView3=findViewById( R.id.imageView3 );
            imageButton2.setOnClickListener( new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //照片拍摄图片 照片回调
                    //括号中需要传入几个参数
                    camera.takePicture( null,null,new Camera.PictureCallback() {
                        @Override
                        public void onPictureTaken(byte[] data,Camera camera) {
                            //预览
                            //bitmap位图  array数组 数据  数据的长度
                            //得到数据bitmap  数组 数据为0  数据的长度
                            Bitmap bitmap= BitmapFactory.decodeByteArray( data,0,data.length );
                            //预览视图设置图片位图
                            imageView3.setImageBitmap( bitmap );
                            //这里把拍摄的图片设置到UI界面的图片上 
                            //并没有进行处理 你也可以把他储存到内存卡上 把bitmap进行封装
                            //buffer缓冲 outPut输出 steam流 缓冲输入流初始化
                            BufferedOutputStream bufferedOutputStream=null;
                            //对缓存进行初始化 
                            //可能会出现bug  对可能出现的异常进行抛出
                            try  {
                                String fileName="hey"+System.currentTimeMillis()+".jpg";
                                //设置文件名
                                 bufferedOutputStream = new BufferedOutputStream( new FileOutputStream( new File( Environment.getExternalStorageDirectory(),fileName ) )) ;
                                //保存  100压缩率 按原图保存
                                //compress压缩 format格式 100 缓冲输出流
                                bitmap.compress( Bitmap.CompressFormat.JPEG,100,bufferedOutputStream );
                                //对图片进行压缩  设置输出流为内容
    
                            }catch (FileNotFoundException e){
                                e.printStackTrace();
                            }finally {
                            //最后对所用到的东西进行关闭 以免内存泄漏
                                //使用完毕假如缓冲输出流不是空值 关闭缓存输入流
                                //防止使用后用户打开其他软件会出现bug
                                if (bufferedOutputStream!=null){
                                //判断输出流是否为空  
                                    try {
                                    //对输出流进行关闭
                                        bufferedOutputStream.close();
                                    } catch (IOException e) {
                                    //抛出异常
                                        e.printStackTrace();
                                    }
                                }
    
                            }
                        }
                    } );
                }
            } );
            //缓存持有者 添加回传
            surfaceHolder.addCallback( new SurfaceHolder.Callback(){
            //重写下面的方法
                @Override
                public void surfaceCreated(SurfaceHolder surfaceHolder) {
                    //缓存创建的时候
                    //得到相机 初始化
                    //得到相机对象 
                    camera=getCamera();
                    //如果相机不为空
                    if (camera!=null){
                        try {
                            //preview预览 display显示 缓存持有者
                            //设置缓存路径
                            camera.setPreviewDisplay( surfaceHolder );
                            //缓存开始预览
                            //开始缓存
                            camera.startPreview();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
    
                }
    
                @Override
                public void surfaceChanged(SurfaceHolder surfaceHolder,int i,int i1,int i2) {
    
                }
    
                @Override
                public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
                    //缓存销毁时 release释放
                    //释放相机 以免再次使用相机出现被占用的现象
                    releaseCamera();
    
                }
            } );
            //内容持有者设置种类
            surfaceHolder.setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            //得到相机
            camera=getCamera();
            //如果相机不为空
            if (camera!=null){
                try {
                    //相机 设置preview预览 display显示
                    camera.setPreviewDisplay( surfaceHolder );
                    //相机开始预览  这是相机里的方法  调用
                    //preview预览
                    camera.startPreview();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        @Override
        protected void onDestroy() {
            //release释放
            releaseCamera();
            super.onDestroy();
        }
    
        //得到相机
        private Camera getCamera() {
            //如果相机 为空
            if (camera == null) {
                try {
                    //打开相机
                    camera = Camera.open();
                    //返回相机
                    return camera;
                } catch (Exception ex) {
    
                    return null;
                }
            }
            //不管怎么样  返回相机
            return camera;
        }
        //release释放
        private void releaseCamera(){
            //如果相机不为空 释放相机   初始化
            if (camera!=null){
                camera.release();
                camera=null;
            }
        }
    
    }
    
    

    /***

    • 自定义相机的实现
    • 首先使用surface布局视图来显示自定义的画面
    • 得到画面缓存器 用来实现预览
    • 路径图片名字 压缩的处理
    • 使用surface回传
    • 软件进程关闭的处理 释放相机和画面持有者
      */

    最后别忘了在功能清单注册权限

    • 注:6.0动态权限危险权限 需要特殊的处理
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.liziyang.dall">
    
    
        <!-- 如果不必须使用自定义相机改为false  required必须 -->
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.CAMERA2" />
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
        <uses-feature android:name="android.hardware.camera.any" />
    
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name=".Main2Activity"
                android:screenOrientation="landscape"
                android:label="@string/title_activity_main2">
            </activity>
        </application>
    
    </manifest>
    

    注:自定义相机权限处理方法不同 读取内存卡 使用相机 为危险权限 请注意加以处理 这里不写 用户必须去打开相应的权限 才可以继续使用
    假如进行拍摄录制 需要加入一个音频录制的权限 后续会写6.0动态权限的处理

     <!-- 如果不必须使用自定义相机改为false  required必须 -->
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.CAMERA2" />
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
        <uses-feature android:name="android.hardware.camera.any" />
    
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

    欢迎大家来参考我的博客 如有错误 请评论指出

    同时我简书和博客有些不同步 关注我的技术主题,每天都有优质技术文章推送 简书扫一扫下方二维码即可关注:
    在这里插入图片描述

    展开全文
  • 解决Android 6.0以上的高版本跳转相机闪退问题,以及相机返回图片不显示问题。 AndroidManifest.xml 中添加权限 <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=...

    解决Android 6.0以上的高版本跳转相机闪退问题,以及相机返回图片不显示问题。

    代码复制可用,开发工具为:Android Studio

    在这里插入图片描述
    AndroidManifest.xml 中添加权限

       <!--拍照-->
        <uses-permission android:name="android.permission.CAMERA" />
        <!--读写-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.PERMISSIONS_STORAGE"/>
    
    

    跳转相册和相机

    //跳转相册
        private void toPicture() {
            Intent intent = new Intent(Intent.ACTION_PICK);  //跳转到 ACTION_IMAGE_CAPTURE
            intent.setType("image/*");
            startActivityForResult(intent,100);
            L.e("跳转相册成功");
        }
    
        //跳转相机
        private void toCamera() {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  //跳转到 ACTION_IMAGE_CAPTURE
            //判断内存卡是否可用,可用的话就进行存储
            //putExtra:取值,Uri.fromFile:传一个拍照所得到的文件,fileImg.jpg:文件名
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory(),"fileImg.jpg")));
            startActivityForResult(intent,101); // 101: 相机的返回码参数(随便一个值就行,只要不冲突就好)
            L.e("跳转相机成功");
        }
    

    解决Android 6.0以上的高版本跳转相机闪退问题

    在 BaseActivity 中添加以下代码

    //跳转相机动态权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
                StrictMode.setVmPolicy(builder.build());
            }
    

    写的权限处理

    /**
         * 检查是否有对应权限
         *
         * @param activity 上下文
         * @param permission 要检查的权限
         * @return  结果标识
         */
        public int verifyPermissions(Activity activity,java.lang.String permission) {
            int Permission = ActivityCompat.checkSelfPermission(activity,permission);
            if (Permission == PackageManager.PERMISSION_GRANTED) {
                L.e("已经同意权限");
                return 1;
            }else{
                L.e("没有同意权限");
                return 0;
            }
        }
    
    
    //检查是否已经获得相机的权限
    if(verifyPermissions(MainActivity.this,PERMISSIONS_STORAGE[2]) == 0){
    	L.e("提示是否要授权");
        ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, 3);
    }else{
        //已经有权限
        toCamera();  //打开相机
    }
    

    获取相机返回的图片并显示

    @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            //判断返回码不等于0
            if (requestCode != RESULT_CANCELED){    //RESULT_CANCELED = 0(也可以直接写“if (requestCode != 0 )”)
                //读取返回码
                switch (requestCode){
                    case 100:   //相册返回的数据(相册的返回码)
                        L.e("相册");
                        Uri uri01 = data.getData();
                        try {
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri01));
                            ImageView01.setImageBitmap(bitmap);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
    
                        break;
                    case 101:  //相机返回的数据(相机的返回码)
                        L.e("相机");
                        try {
                            tempFile = new File(Environment.getExternalStorageDirectory(),"fileImg.jpg");  //相机取图片数据文件
                            Uri uri02 = Uri.fromFile(tempFile);   //图片文件
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri02));
                            ImageView01.setImageBitmap(bitmap);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        break;
                }
            }
        }
    

    详细完整代码:

    【BaseActivity .java】

    public class BaseActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            //跳转相机动态权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
                StrictMode.setVmPolicy(builder.build());
            }
        }
    
        /**
         * 检查是否有对应权限
         *
         * @param activity 上下文
         * @param permission 要检查的权限
         * @return  结果标识
         */
        public int verifyPermissions(Activity activity,java.lang.String permission) {
            int Permission = ActivityCompat.checkSelfPermission(activity,permission);
            if (Permission == PackageManager.PERMISSION_GRANTED) {
                L.e("已经同意权限");
                return 1;
            }else{
                L.e("没有同意权限");
                return 0;
            }
        }
    }
    

    【MainActivity .java】

    package com.example.erp_lxkun_jak.testcamera;
    
    import android.Manifest;
    import android.app.Activity;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.support.v4.app.ActivityCompat;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    import java.io.File;
    import java.io.IOException;
    
    
    public class MainActivity extends BaseActivity implements View.OnClickListener {
    
        //需要的权限数组 读/写/相机
        private static String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA };
    
        private Button Button01;
        private Button Button02;
        private ImageView ImageView01;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initView();
        }
    
        private void initView() {
    
            Button01 = findViewById(R.id.Button01);
            Button02 = findViewById(R.id.Button02);
            ImageView01 = findViewById(R.id.ImageView);
    
            Button01.setOnClickListener(this);
            Button02.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.Button01:
                    toPicture();
                    Toast.makeText(MainActivity.this,"Button 01",Toast.LENGTH_SHORT).show();
                    break;
                case R.id.Button02:
                    //检查是否已经获得相机的权限
                    if(verifyPermissions(MainActivity.this,PERMISSIONS_STORAGE[2]) == 0){
                        L.e("提示是否要授权");
                        ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, 3);
                    }else{
                        //已经有权限
                        toCamera();  //打开相机
                    }
                    break;
            }
        }
    
        private File tempFile = null;   //新建一个 File 文件(用于相机拿数据)
    
        //获取 相机 或者 图库 返回的图片
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            //判断返回码不等于0
            if (requestCode != RESULT_CANCELED){    //RESULT_CANCELED = 0(也可以直接写“if (requestCode != 0 )”)
                //读取返回码
                switch (requestCode){
                    case 100:   //相册返回的数据(相册的返回码)
                        L.e("相册");
                        Uri uri01 = data.getData();
                        try {
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri01));
                            ImageView01.setImageBitmap(bitmap);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
    
                        break;
                    case 101:  //相机返回的数据(相机的返回码)
                        L.e("相机");
                        try {
                            tempFile = new File(Environment.getExternalStorageDirectory(),"fileImg.jpg");  //相机取图片数据文件
                            Uri uri02 = Uri.fromFile(tempFile);   //图片文件
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri02));
                            ImageView01.setImageBitmap(bitmap);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        break;
                }
            }
        }
    
        //跳转相册
        private void toPicture() {
            Intent intent = new Intent(Intent.ACTION_PICK);  //跳转到 ACTION_IMAGE_CAPTURE
            intent.setType("image/*");
            startActivityForResult(intent,100);
            L.e("跳转相册成功");
        }
    
        //跳转相机
        private void toCamera() {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  //跳转到 ACTION_IMAGE_CAPTURE
            //判断内存卡是否可用,可用的话就进行存储
            //putExtra:取值,Uri.fromFile:传一个拍照所得到的文件,fileImg.jpg:文件名
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory(),"fileImg.jpg")));
            startActivityForResult(intent,101); // 101: 相机的返回码参数(随便一个值就行,只要不冲突就好)
            L.e("跳转相机成功");
        }
    }
    
    

    【布局文件:activity_main.xml】

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <ImageView
            android:id="@+id/ImageView"
            android:layout_marginTop="50dp"
            android:layout_width="match_parent"
            android:layout_height="300dp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="50dp"
            android:layout_marginRight="20dp"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/Button01"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="从相册获取照片" />
    
            <Button
                android:id="@+id/Button02"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="从相机获取照片" />
        </LinearLayout>
    </LinearLayout>
    

    App下载: https://aifabu.com/IVrU
    .
    源码下载:https://download.csdn.net/download/erp_lxkun_jak/11829418
    【注意:使用开发工具是 Android Studio 哦!】
    .
    .
    感谢你的查阅,希望可以帮到你,祝你学习愉快!
    .

    我是和你一起学习的 易君

    展开全文
  • 在application的onCreate()调用StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); builder.detectFileUriExposure()

    在application的onCreate()调用

    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());
    builder.detectFileUriExposure()

    展开全文
  • Android自定义相机超详细讲解 转载请标明出处: http://blog.csdn.net/vinicolor/article/details/49642861; 由于网上关于Android自定义相机的文章写得不是太详细,Google官方的文档又说得不太容易理解,所以...

    使用surfaceview自定义相机,同时把自己踩过的坑分享给大家,希望大家有所收获。

    #写在前面的话
    前一阵子负责一个自定义相机进行拍照并在另一个页面进行人脸识别的模块,相机部分需求并不怎么复杂,可以切换前后摄像头,可以拍照并把照片返回上一个页面。由于没有怎么接触过自定义相机的部分,而网上的一些资料又不全,踩了不少坑。故在这里总结一下,希望对大家有所帮助,同时把自定义控件系列的最后一个坑填上(surfaceview)。效果图如下:

    这里写图片描述

    #Android中开发相机的两种方式

    Android系统提供了两种使用手机相机资源实现拍摄功能的方法,一种是直接通过Intent调用系统相机组件,这种方法快速方便,适用于直接获得照片的场景,如上传相册,微博、朋友圈发照片等。另一种是使用相机API来定制自定义相机,这种方法适用于需要定制相机界面或者开发特殊相机功能的场景,如需要对照片做裁剪、滤镜处理,添加贴纸,表情,地点标签等。

    ##调用系统自带相机
    关于系统自带相机的调用非常简单,这里我就不过多叙述了,具体可以参考谷歌的Training。我只说容易被大家忽视的几个点:

    • 如果我们的应用使用相机,但相机并不是应用的正常运行所必不可少的组件,可以将权限声明中的android:required设置为”false”。这样的话,Google Play 也会允许没有相机的设备下载该应用。当然我们有必要在使用相机之前通过调用hasSystemFeature(PackageManager.FEATURE_CAMERA)方法来检查设备上是否有相机。如果没有,我们应该禁用和相机相关的功能!

    • 在调用startActivityForResult()方法之前,先调用resolveActivity(),这个方法会返回能处理该Intent的第一个Activity(译注:即检查有没有能处理这个Intent的Activity)。执行这个检查非常重要,因为如果在调用startActivityForResult()时,没有应用能处理你的Intent,应用将会崩溃。所以只要返回结果不为null,使用该Intent就是安全的。

    ##使用Android框架所提供的API来直接控制相机硬件

    使用API来控制相机我们需要用到关键类和接口:

    • 使用Camera对象来控制相机
    • 使用SurfaceView来展现照相机采集的图像
    • 通过surfaceholder来控制surfac的尺寸和格式,修改surface的像素,监视surface的变化等等
    • 通过SurfaceHolder.Callback 接口,监听surface状态变化

    接下来我们分为以下三部分来介绍:关键类以及接口的作用和方法,Camera控制拍照步骤,自定义相机容易踩到的坑以及解决办法。

    ###API说明

    Camera :最主要的类,用于管理和操作camera资源。它提供了完整的相机底层接口,支持相机资源切换,设置预览/拍摄尺寸,设定光圈、曝光、聚焦等相关参数,获取预览/拍摄帧数据等功能,主要方法有以下这些:

    • open():获取camera实例。
    • setPreviewDisplay(SurfaceHolder):绑定绘制预览图像的surface。surface是指向屏幕窗口原始图像缓冲区(raw buffer)的一个句柄,通过它可以获得这块屏幕上对应的canvas,进而完成在屏幕上绘制View的工作。通过surfaceHolder可以将Camera和surface连接起来,当camera和surface连接后,camera获得的预览帧数据就可以通过surface显示在屏幕上了。
    • setPrameters设置相机参数,包括前后摄像头,闪光灯模式、聚焦模式、预览和拍照尺寸等。
    • startPreview():开始预览,将camera底层硬件传来的预览帧数据显示在绑定的surface上。
    • stopPreview():停止预览,关闭camra底层的帧数据传递以及surface上的绘制。
    • release():释放Camera实例
    • takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg):这个是实现相机拍照的主要方法,包含了三个回调参数。shutter是快门按下时的回调,raw是获取拍照原始数据的回调,jpeg是获取经过压缩成jpg格式的图像数据的回调。

    SurfaceView :用于绘制相机预览图像的类,提供给用户实时的预览图像。普通的view以及派生类都是共享同一个surface的,所有的绘制都必须在UI线程中进行。而surfaceview是一种比较特殊的view,它并不与其他普通view共享surface,而是在内部持有了一个独立的surface,surfaceview负责管理这个surface的格式、尺寸以及显示位置。由于UI线程还要同时处理其他交互逻辑,因此对view的更新速度和帧率无法保证,而surfaceview由于持有一个独立的surface,因而可以在独立的线程中进行绘制,因此可以提供更高的帧率。自定义相机的预览图像由于对更新速度和帧率要求比较高,所以比较适合用surfaceview来显示。

    SurfaceHolder :surfaceholder是控制surface的一个抽象接口,它能够控制surface的尺寸和格式,修改surface的像素,监视surface的变化等等,surfaceholder的典型应用就是用于surfaceview中。surfaceview通过getHolder()方法获得surfaceholder 实例,通过后者管理监听surface 的状态。

    SurfaceHolder.Callback 接口 :负责监听surface状态变化的接口,有三个方法:

    • surfaceCreated(SurfaceHolder holder):在surface创建后立即被调用。在开发自定义相机时,可以通过重载这个函数调用camera.open()、camera.setPreviewDisplay(),来实现获取相机资源、连接camera和surface等操作。
    • surfaceChanged(SurfaceHolder holder, int format, int width, int height):在surface发生format或size变化时调用。在开发自定义相机时,可以通过重载这个函数调用camera.startPreview来开启相机预览,使得camera预览帧数据可以传递给surface,从而实时显示相机预览图像。
    • surfaceDestroyed(SurfaceHolder holder):在surface销毁之前被调用。在开发自定义相机时,可以通过重载这个函数调用camera.stopPreview(),camera.release()来实现停止相机预览及释放相机资源等操作。

    ###Camera控制拍照的过程

    1. 调用Camera的open()方法打开相机。
    2. 调用Camera的getParameters()获取拍照参数,该方法返回一个Cmera.Parameters对象。
    3. 调用Camera.Parameters对象对照相的参数进行设置。
    4. 调用Camera的setParameters(),并将Camera.Parameters对象作为参数传入,这样就可以对拍照进行参数控制,Android2.3.3以后不用设置。
    5. 调用Camerade的startPreview()的方法开始预览取景,在之前需要调用Camera的setPreviewDisplay(SurfaceHolder holder)设置使用哪个SurfaceView来显示取得的图片。
    6. 调用Camera的takePicture()方法进行拍照。
    7. 程序结束时,要调用Camera的stopPreview()方法停止预览,并且通过Camera.release()来释放资源。

    具体实现代码戳后面链接

    ###踩坑与填坑

    ####预览方向

    先看下官方文档的说明

    Most camera applications lock the display into landscape mode because that is the natural orientation of the camera sensor. This setting does not prevent you from taking portrait-mode photos, because the orientation of the device is recorded in the EXIF header. The setCameraDisplayOrientation() method lets you change how the preview is displayed without affecting how the image is recorded. However, in Android prior to API level 14, you must stop your preview before changing the orientation and then restart it.

    大多数相机程序会锁定预览为横屏状态,因为该方向是相机传感器的自然方向。当然这一设定并不会阻止我们去拍竖屏的照片,因为设备的方向信息会被记录在EXIF头中。setCameraDisplayOrientation()方法可以让你在不影响照片拍摄过程的情况下,改变预览的方向。然而,对于Android API Level 14及以下版本的系统,在改变方向之前,我们必须先停止预览,然后再去重启它。

    ####SurfaceView预览图像拉伸变形,拍摄照片尺寸不对

    说明这个问题之前,同样先说一下几个跟相机有关的尺寸。

    • SurfaceView尺寸 :即自定义相机应用中用于显示相机预览图像的View的尺寸,当它铺满全屏时就是屏幕的大小。这里surfaceview显示的预览图像暂且称作手机预览图像。

    • Previewsize :相机硬件提供的预览帧数据尺寸。预览帧数据传递给SurfaceView,实现预览图像的显示。这里预览帧数据对应的预览图像暂且称作相机预览图像。

    • Picturesize :相机硬件提供的拍摄帧数据尺寸。拍摄帧数据可以生成位图文件,最终保存成.jpg或者.png等格式的图片。这里拍摄帧数据对应的图像称作相机拍摄图像。图4说明了以上几种图像及照片之间的关系。手机预览图像是直接提供给用户看的图像,它由相机预览图像生成,拍摄照片的数据则来自于相机拍摄图像。

    原因是没有正确设置比例 parameter.setPictureSize(width,height),这个比例不是你决定的,要先通过camera.getParameters().getSupportedPictureSizes()获得手机支持的尺寸。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    /**
         * 设置照片格式
         */
        private void setParameter() {
            Camera.Parameters parameters = camera.getParameters(); // 获取各项参数
            parameters.setPictureFormat(PixelFormat.JPEG); // 设置图片格式
            parameters.setJpegQuality(100); // 设置照片质量
            //获得相机支持的照片尺寸,选择合适的尺寸
            List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
            int maxSize = Math.max(display.getWidth(), display.getHeight());
            int length = sizes.size();
            if (maxSize > 0) {
                for (int i = 0; i < length; i++) {
                    if (maxSize <= Math.max(sizes.get(i).width, sizes.get(i).height)) {
                        parameters.setPictureSize(sizes.get(i).width, sizes.get(i).height);
                        break;
                    }
                }
            }
            List<Camera.Size> ShowSizes = parameters.getSupportedPreviewSizes();
            int showLength = ShowSizes.size();
            if (maxSize > 0) {
                for (int i = 0; i < showLength; i++) {
                    if (maxSize <= Math.max(ShowSizes.get(i).width, ShowSizes.get(i).height)) {
                        parameters.setPreviewSize(ShowSizes.get(i).width, ShowSizes.get(i).height);
                        break;
                    }
                }
            }
            camera.setParameters(parameters);
        }
    

    ####前置摄像头的镜像效果

    • Android 相机硬件有个特殊设定,就是对于前置摄像头,在展示预览视图时采用类似镜面的效果,显示的是摄像头成像的镜像。而拍摄出的照片则仍采用摄像头成像。看到这里,大家可能会有些怀疑,不妨现在就试试自己 Android 手机上的前置摄像头,对比下预览图像和拍摄出照片的区别。这是由于底层相机在传递前置摄像头预览数据时做了水平翻转变换,即将x方向镜像翻转180度。这个变化对之前竖屏预览的方向也会造成影响,本来对于后置摄像头旋转90度即可使预览视图正确,而对前置摄像头,如果也旋转90度的话,看到的预览图像则是上下颠倒的(因为x方向翻转了180度),因此必须再旋转180度,才能显示正确。
      解决方案,在保存图片的时候根据选择的摄像头做对应的翻转。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    //将照片改为竖直方向
                        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        Matrix matrix = new Matrix();
                        switch (cameraPosition) {
                            case 0://前
                                matrix.preRotate(270);
                                break;
                            case 1:
                                matrix.preRotate(90);
                                break;
                        }
                        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    
    • 同时在开发的过程中发现了一个有趣的东西,我们用前置摄像头拍出来的照片其实是左右翻转的。但我用小米自带的相机测试发现,当摄像头中有人脸出现的时候,相机会做左右翻转的操作,以给用户更好的体验。

    源码戳这里。

    
    展开全文
  • 如何开发Android美颜相机前言原理一、色彩矩阵基本原理*什么是色彩矩阵**改变颜色*二、相机的基本原理*openGL ES**制作一个camera*CameraDemo 前言 如今美颜相机可以说是每个女生甚至是男生手机上的必备软件,可以...

    前言

    如今美颜相机可以说是每个女生甚至是男生手机上的必备软件,可以轻而易举的拍出令人满意的“照骗”,本人的女盆友(非本专业)一直好奇美颜相机是如何开发出来的,这片文章我们主要来实现对相片的色调调整的基本方法,而要制作成一个功能完备的美颜相机还远不止这一项内容哦。

    原理

    一、色彩矩阵基本原理

    Android在处理图片时,最常使用到的数据结构是位图(Bitmap),它包含了一张图片所有的数据。整个图片都是由点阵和颜色值组成的,所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像素。而颜色值——ARGB,分别对应着透明度、红、绿、蓝这四个通道分量,他们共同决定了每个像素点显示的颜色
    在这里插入图片描述

    什么是色彩矩阵

    在Android中,系统使用一个颜色矩阵-ColorMatrix来处理图像的色彩效果。对于图像的每个像素点,都有一个颜色分量矩阵用来保存颜色的RGBA值,Android中的颜色矩阵是一个 4x5 的数字矩阵,它用来对图片的色彩进行处理。
    在这里插入图片描述
    在Android系统中,如果想要改变一张图像的色彩显示效果,可以使用矩阵的乘法运算来修改颜色分量矩阵的值。上面矩阵A就是一个 4x5 的颜色矩阵。在Android中,它会以一维数组的形式来存储[a,b,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],而C则是一个颜色矩阵分量。在处理图像时,使用矩阵乘法运算AC来处理颜色分量矩阵。
    在这里插入图片描述

    R1 = aR + bG + cB + dA + e;
    G1 = fR + gG + hB + iA + j;
    B1 = kR + lG + mB + nA + o;
    A1 = pR + qG + rB + sA + t;
    

    从等式中可以看出abcde决定红色、fghi决定绿色、klmn决定蓝色、pqrs决定透明色
    而ejot就决定了偏移量。

    改变颜色

    通过上面对色彩矩阵的简单了解,我们可以想到改变图片的颜色方法来给相片进行色彩渲染,有以下两种方法可以实现:

    1. 改变偏移量
      即保持矩阵其他列的值不变,只改变最后一列的值来是图片的rgb值发生变化
    2. 改变rgba值分量的系数
      使r、g、b、a分量的系数改变,即使原矩阵乘以对角矩阵使得每个值发生不同的改变来进行色调的改变。

    二、相机的基本原理

    openGL ES

    在制作Camera前还需要向大家介绍一下openGL ES本人也是第一次接触这个技术,所以有一些地方并是很理解,个人感觉这项技术对于新手不是很容易接受。
    并且在安卓中,OpenGL ES的开发有基本着固定的代码形式,为实现不同的功能,只需将shader编写好,按步就班的在代码中调用就可以了,但是OpenGL ES提供的API比较抽象,写起来不容易记忆,而且重复代码也比较多。

    制作一个camera

    1.新建一个Android project
    这是很基本的操作想必大家也都清楚,如果有初学者,新建Android project这是基本的新建方法。
    2.集成camera框架到项目中
    在project/build.grdle文件中添加依赖:

    allprojects {
    		repositories {
    			...
    			maven { url 'https://jitpack.io' }
    		}
    	}
    
    

    在app/build.grdle文件中添加依赖:

    dependencies {
    	        implementation 'com.github.smzhldr:AGLFramework:v1.0'
    	}
    
    

    调用美颜相机:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".CameraActivity">
    
        <com.aglframework.smzh.camera.CameraPreview
            android:id="@+id/camera_preview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </RelativeLayout>
    
    

    在Activity中加入以下代码:

      public class CameraActivity extends Activity {
    
        private AGLView aglView;
        private AGLCamera1 aglCamera1;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_camea);
            aglView = findViewById(R.id.camera_preview);
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (aglCamera1 == null) {
                 aglCamera1 = new AGLCamera1(aglView, 1080, 1920);
                }
                aglCamera1.open();
            }
        }
        
        @Override
        protected void onPause() {
            super.onPause();
            if (aglCamera1 != null) {
                aglCamera1.close();
            }
        }
    }
    
    

    CameraDemo

    将 4x5 矩阵转换成一维数组,然后再将这一维数组设置到ColorMatrix类里去:

    //将矩阵设置到图像
        private void setImageMatrix() {
            Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
            ColorMatrix colorMatrix = new ColorMatrix();
            colorMatrix.set(mColorMatrix);//将一维数组设置到ColorMatrix
    
            Canvas canvas = new Canvas(bmp);
            Paint paint = new Paint();
            paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
            canvas.drawBitmap(bitmap, 0, 0, paint);
            iv_photo.setImageBitmap(bmp);
        }
    
    

    xml布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.deeson.mycolormatrix.MainActivity"
        android:orientation="vertical">
    
        <ImageView
            android:id="@+id/iv_photo"
            android:layout_width="300dp"
            android:layout_height="0dp"
            android:layout_weight="3"
            android:layout_gravity="center_horizontal"/>
    
        <GridLayout
            android:id="@+id/matrix_layout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="4"
            android:columnCount="5"
            android:rowCount="4">
    
        </GridLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/btn_change"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="change"/>
            <Button
                android:id="@+id/btn_reset"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="reset"/>
        </LinearLayout>
    
    </LinearLayout>
    
    

    通过 View 的 post() 方法,在视图创建完毕后获得其宽高值:

    matrixLayout.post(new Runnable() {
        @Override
        public void run() {
            mEtWidth = matrixLayout.getWidth() / 5;
            mEtHeight = matrixLayout.getHeight() / 4;
            addEts();
            initMatrix();
        }
    
    });
    
    

    MainActivity代码:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        Bitmap bitmap;
        ImageView iv_photo;
        GridLayout matrixLayout;
        //每个edittext的宽高
        int mEtWidth;
        int mEtHeight;
        //保存20个edittext
        EditText[] mEts = new EditText[20];
    
        //一维数组保存20个矩阵值
        float[] mColorMatrix = new float[20];
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iv_model);
            iv_photo = (ImageView) findViewById(R.id.iv_photo);
            matrixLayout = (GridLayout) findViewById(R.id.matrix_layout);
            Button btn_change = (Button) findViewById(R.id.btn_change);
            Button btn_reset = (Button) findViewById(R.id.btn_reset);
            btn_change.setOnClickListener(this);
            btn_reset.setOnClickListener(this);
            iv_photo.setImageBitmap(bitmap);
    
            //我们无法在onCreate()方法中获得视图的宽高值,所以通过View的post()方法,在视图创建完毕后获得其宽高值
            matrixLayout.post(new Runnable() {
                @Override
                public void run() {
                    mEtWidth = matrixLayout.getWidth() / 5;
                    mEtHeight = matrixLayout.getHeight() / 4;
                    addEts();
                    initMatrix();
                }
    
            });
        }
    
        //动态添加edittext
        private void addEts() {
            for (int i = 0; i < 20; i++) {
                EditText et = new EditText(this);
                et.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
                mEts[i] = et;
                matrixLayout.addView(et, mEtWidth, mEtHeight);
            }
        }
    
        //初始化颜色矩阵
        private void initMatrix() {
            for (int i = 0; i < 20; i++) {
                if (i % 6 == 0) {
                    mEts[i].setText(String.valueOf(1));
                } else {
                    mEts[i].setText(String.valueOf(0));
                }
            }
        }
    
    
        //获取矩阵值
        private void getMatrix() {
            for (int i = 0; i < 20; i++) {
                String matrix = mEts[i].getText().toString();
                boolean isNone = null == matrix || "".equals(matrix);
                mColorMatrix[i] = isNone ? 0.0f : Float.valueOf(matrix);
                if (isNone) {
                    mEts[i].setText("0");
                }
            }
        }
    
        //将矩阵设置到图像
        private void setImageMatrix() {
            Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
            ColorMatrix colorMatrix = new ColorMatrix();
            colorMatrix.set(mColorMatrix);//将一维数组设置到ColorMatrix
    
            Canvas canvas = new Canvas(bmp);
            Paint paint = new Paint();
            paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
            canvas.drawBitmap(bitmap, 0, 0, paint);
            iv_photo.setImageBitmap(bmp);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_change:
                    //作用矩阵效果
                    break;
                case R.id.btn_reset:
                    //重置矩阵效果
                    initMatrix();
                    break;
            }
            //作用矩阵效果
            getMatrix();
            setImageMatrix();
        }
    }
    
    

    Camera代码详见
    效果图

    通过以上方法便可以初步完成一个可以初步调整色调的美颜相机,还有许多功能本人会在以后的学习中逐步完善的!

    展开全文
  • Android相机的使用

    2017-06-03 15:04:20
    android相机的编程,主要介绍如何使用Camera1和Camera2接口进行Camera的编程
  • 在解决项目中相机某些机型无法自动对焦的问题时,在网上找到了一些资料,写下解决问题过程,以备查看。 Android相机实时自动对焦的完美实现 Android图像滤镜框架GPUImage从配置到应用 GPUImage for Android ...
  • 通常情况下,调用android系统相机基本上可以满足拍照的需求,而自定义相机的需求一般来自于开发自己的相机应用,今天我们来简单聊聊android自定义相机的实现,限于篇幅,我们上篇只讨论android.hardware.Camera,下...
  • Android相机屏幕适配

    2018-07-13 15:34:33
    本文默认你已经会的Android相机开发,但是苦恼于相机屏幕适配 如果不会相机开发,可以参考以下作者的文章: Tong ZHAN Android相机开发(一):最简单的相机 Android相机开发(二): 给相机加上偏好设置 Android...
  • Android中如何后台开启照相机,就是点击拍照之后不显示预览界面,直接后台自动开启照相机进行拍照,并自动将照片保存!
  • 从5.0开始(API Level 21),可以完全控制Android设备相机的新api Camera2(android.hardware.Camera2)被引入了进来。在以前的Camera api(android.hardware.Camera)中,对相机的手动控制需要更改系统才能实现,而且...
  • 相机 Android核心框架包括各种摄像机和照相机功能,在应用程序中允许你拍照和录像。本文讨论了一个快速,简单的拍照和摄影的方法,并概述为用户创建自定义相机的高级体验方法。 注意事项 在Android...
  • 相机 Android框架架包含了各种相机相机功能的支持,使你可以在你的应用中捕获图像和视频.本文档讨论一个简单快速的获取图像和视频的方法,并概述一个创建自定义用户相机体验的高级方法.想一想 在使你的应用能...
  • 该文章 主要使用 自定义 surfaceview 及 camera 知识点,来实现一个自定义的拍照 、切换闪光灯 和 前后...在记录自定义camera相机前,先简单提一下调用系统相机和获取图片返回值的方式。 Intent intent = new I...
  • 项目运行两个礼拜了,相机也在7.0以下运行的完美,突然早上同事拿他的7.0手机给我说 这是一个大bug.我一看调用相机直接崩溃。报的错误如下图: 接着我以为是我的文件路径错误,找了老半天没发现问题,仔细想想不...
  • ...最近由于廖子尧忙于自己公司的事情和 OkGo (一款专注于让网络请求更简单的网络框架) ... ,故让LZ 接替维护 ImagePicker(一款支持单... Android N (API 24) 以上的相机适配方案分享给大家。  Android No
  • 针对部分高版本机型打开系统相机的适配代码 /** * 类名称:PhotographForSignActivity * 类功能:打开系统相机拍照 * 类作者:Qw * 类日期: **/ public class PhotographForSignActivity extends Activity { ...
  • 现在想写个android第一视角用来操作四轴飞行器,不知道怎么能得到相机的图像, appstore上有ismartdv之类的,但我想自己定制一个,因为自己想做出不同的效果来。请问各位有谁玩过这个的吗,具体操作指令是什么样的?
  • Android中,相信都在自己的程序中调用过系统相机拍照或者选择图片,但直接调用系统相机有时候并不能满足我们的需求,或者说我们如何去自定义一个相机,那么,我们可以通过Camera和SurfaceView来实现自己的相机应用...
  • 解决 Android N 上 安装Apk时报错:android.os.FileUriExposedException: file:///storage/emulated/0/Download/appName-2.3.0.apk exposed beyond app through Intent.getData() 解决方法1、在AndroidManifest.xml...
1 2 3 4 5 ... 20
收藏数 46,630
精华内容 18,652