LOFTER for ipad —— 让兴趣,更有趣

点击下载 关闭
关于Android手机拍照预览、剪裁界面出现照片九十度旋转的问题
q1q2q3q4q5q6ln 2020-04-20

关于Android手机拍照预览、剪裁界面出现照片九十度旋转的问题

原创Devter 最后发布于2018-09-10 11:00:38 阅读数 751  收藏

展开

案场还原:

最近做的项目,测试机小米6X及本人的努比亚Z11测试拍照环节均正常,但在领导的三星手机及Oppo FindX上就出现了奇葩现象,拍照完预览照片、剪裁照片出现了九十度的旋转,如果这时候你用模拟器,比如Genymotion也能发现此问题,预览及剪裁出现旋转。


原因排查:

通过搜索大量墙里墙内资料,原因大概总结为以下几点,自我理解,若有不对,还望指正:


Android原生系统设定的拍照界面是Landscape也就是横屏模式,因此即使你调用拍照的Activity是竖屏模式,拍照画面也是竖屏模式,但到了预览和剪裁就默认切换成了横屏的画面。

可能是某些手机厂商深度定制系统的结果,比如三星反其道而行,系统设定拍照旋转,简直是反人类,反社会!

有网友也反应可能与Activity启动模式有关,建议设置当前启动模式为Standard,但我不是这个原因,因为默认模式就是Standard。

对于以上原因,可是尝试在配置文件中设定:

        <activity

            android:name=".activity.ProfileActivity"

            android:configChanges="keyboardHidden|orientation|screenSize"

            android:exported="true"

            android:launchMode="standard"

            android:screenOrientation="portrait" />

1

2

3

4

5

6

But,这些对我来说都不起作用

4 .代码问题


解决:

强制将旋转后的照片转回来

最重要的也就是从ExifInterface这个多媒体相关类中读取照片被旋转的角度,然后使用Matrix强制转回来。代码如下


 /**

     * 获取原始图片的角度(***解决三星手机拍照后图片是横着的问题***)

     * 读取照片exif信息中的旋转角度

     *

     * @param path 照片路径

     * @return角度

     */

    private int getPictureDegree(String path) {


        int degree = 0;

        /**

         * 方法一

         */

//        boolean hasRotation = false;

//        String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};

//        Cursor cursor = context.getContentResolver().query(cameraUri, orientationColumn, null, null, null);

//        Log.e(TAG, "cursor: " + cursor +  "  "  );

//        if (cursor.getCount() != 1) {

//            cursor.close();

//            return -1;

//        }

//        if (cursor.moveToFirst()){

//            degree = cursor.getInt(cursor.getColumnIndex(orientationColumn[0]));

//            hasRotation = true;

//        }

//        cursor.close();

//        cursor = null;

//        Log.e(TAG, "getPictureDegree: " + degree );


        /**

         * 方法二

         */

        try {

            ExifInterface exifInterface = new ExifInterface(path);

            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

            Log.e(TAG, "原图被旋转角度: ========== " + orientation );

            switch (orientation) {

                case ExifInterface.ORIENTATION_ROTATE_90:

                    degree = 90;

                    break;

                case ExifInterface.ORIENTATION_ROTATE_180:

                    degree = 180;

                    break;

                case ExifInterface.ORIENTATION_ROTATE_270:

                    degree = 270;

                    break;

            }

        } catch (IOException e) {

            e.printStackTrace();

        }


        /**

         * 方法三

         */

//        int degree = 0;

//        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();

//        android.hardware.Camera.getCameraInfo(0, info);

//        degree = info.orientation;

//        Log.e(TAG, "CameraInfo: " + info.orientation );


        return degree;

    }

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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

然后转回来:


        Matrix matrix = new Matrix();

        int degree = getPictureDegree(cameraImagePath);

        matrix.postRotate(degree); /*翻转90度*/

1

2

3

这里需要说明一下获取照片被旋转角度getPictureDegree这个方法,有三种获取角度的方式


使用数据库Cursor读取信息context.getContentResolver().query(cameraUri, orientationColumn, null, null, null)但我读不到String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};相关的信息,报空指针异常,因此放弃。

直接读取硬件摄像头CameraInfo相关信息再进行旋转,这个方法也有硬伤,在正常的测试机上预览剪裁本来不会旋转,结果你用你用摄像机竖屏拍照,得到的info.orientation是九十度,然后进行旋转,本来正常的拍照也被颠倒了,因此同样放弃该方式。

使用ExifInterface读取,关于该类的详细说明请百度。国内多数开发者遇见此坑都推荐运用此方式读取旋转角度,但我遇见一个更奇葩的问题,通过 int orientation=exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)读取到的角度永远为0!!!!!!!!

此问题搞得我焦头烂额,欲哭无泪。经过一番Google,Stack Overflow上的开发者建议切换为第一种Cursor读取信息,但我刚才也讲了,方法报空指针,有待商榷。

现在所有的问题都集中在一点,明明旋转了的照片读取到的旋转角度却是0度!!

好吧,一天两天过去,终于找到了一种思路


还别说,之前我还真的是在读取角度信息之前做了图片压缩处理,然后才进行旋转操作


    /**

     * 旋转

     * @return

     */

    private Bitmap toturn() {

        BitmapFactory.Options options = new BitmapFactory.Options();

        options.inJustDecodeBounds = true;

        // 简单的压缩

            options.inSampleSize = 4;           //把原图按1/4的比例压缩

            options.inJustDecodeBounds = false; // 压缩完后便可以将inJustDecodeBounds设置为false

            // 把流转化为Bitmap图片

            Bitmap bitmap = BitmapFactory.decodeFile(cameraImagePath, options);

            Matrix matrix = new Matrix();

            int degree = getPictureDegree(cameraImagePath);

            matrix.postRotate(degree); /*翻转90度*/

            int width = bitmap.getWidth();

            int height = bitmap.getHeight();

            Bitmap returnBm  = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

            if (returnBm == null) {

                returnBm = bitmap;

            }

            if (bitmap != returnBm) {

                bitmap.recycle();

            }

             Log.e(TAG, "returnBm: " + returnBm );

            return returnBm;

    }

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

好吧,皇天不负有心人,先旋转再压缩之后,运行,终于解决了,在这也要感谢链接的博主。

有时候我们找bug,就跟买房一样,大海捞针,也是看运气。


附上我所有的拍照相关代码


    private Uri cameraUri;

    // 裁剪后图片的宽(X)和高(Y),480 X 480的正方形。

    private static int output_X = 480;

    private static int output_Y = 480;

    private Uri imageUriCrop;   //剪裁后的图片uri


    /**

     * 拍照

     */

    public void fromCamera(){

        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

            File dir = new File(ConstantManager.cameraPath);//创建文件夹

            if (!dir.exists()) dir.mkdirs();

            Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

            cameraFile = new File(dir, String.valueOf(System.currentTimeMillis()) + ".jpg");//指定照片路径

            if (!cameraFile.exists()) {

                try {

                    cameraFile.createNewFile();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

            cameraImagePath = cameraFile.getPath();


            if (Build.VERSION.SDK_INT >= 24) {

                cameraUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", cameraFile);

            } else {

                cameraUri = Uri.fromFile(cameraFile);

            }

//            Log.e(TAG, "cameraUri: " + cameraUri );

            openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);

            openCameraIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);

            ((Activity) context).startActivityForResult(openCameraIntent, 0x008);

        } else {

            view.showMsg("没有储存卡");

        }

    }



    /**

     * 图片剪裁

     *

     * @param system

     * @param in

     */

    public void crop(boolean system, Intent in,@Nullable  File cameraFile) {


        Intent intent = new Intent("com.android.camera.action.CROP");

        //设置数据源

        Uri uri;

        if (system) {       //从系统相册选择

            if (in != null && in.getData() != null) {

                uri = createSingleCropUri("crop_image.jpg");   //剪裁后的图库图片名

                intent.setDataAndType(in.getData(), "image/*");

            } else {

                return;

            }

        } else {        //从相机拍照选择

            if (Build.VERSION.SDK_INT >= 24) {

                //添加这一句表示对目标应用临时授权该Uri所代表的文件

                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

                /**

                 * Android 7.0要求设置数据源使用ContentUri,输出使用正常的Uri

                 */

                Uri contentUri = getImageContentUri(context,cameraFile);

                intent.setDataAndType(contentUri,"image/*");        //设置剪裁后的图片数据源

                uri = createSingleCropUri(cameraFile.getName());    //剪裁后的照片名


            } else {

                intent.setDataAndType(Uri.fromFile(cameraFile), "image/*");    //设置剪裁后的图片数据源

                uri = createSingleCropUri(cameraFile.getName());    //剪裁后的照片名

            }

        }

        Log.e(TAG, "crop: " + uri );

        intent.putExtra("crop", "true");// 设置裁剪

        intent.putExtra("aspectX", 1);// aspectX , aspectY :宽高的比例,为浮点数则不会固定裁剪框比例

        intent.putExtra("aspectY", 1);

        intent.putExtra("outputX", output_X);// 定义输出图片大小,不定义则按裁剪框大小定义,但裁剪耗时会加长

        intent.putExtra("outputY", output_Y);

        intent.putExtra("return-data", true);

        intent.putExtra("scale", true);//保留比例

        intent.putExtra("return-data", false);//是否返回data

        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);//输出路径,剪裁后的图片

        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//编码格式

        intent.putExtra("noFaceDetection", true);

        ((Activity) context).startActivityForResult(intent, 0x009);

    }


    private Uri createSingleCropUri(String fileName) {

        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

            File dir = new File(ConstantManager.cameraPath);//创建文件夹

            if (!dir.exists()) dir.mkdirs();

            File cropFile = new File(dir, fileName);//指定裁剪路径,多图情况下裁剪路径不能唯一,否则文件会被覆盖

            imageUriCrop = Uri.fromFile(cropFile);

        }

        return imageUriCrop;

    }



    /**

     * 安卓7.0裁剪根据文件路径获取uri

     */

    public static Uri getImageContentUri(Context context, File imageFile) {

        String filePath = imageFile.getAbsolutePath();

        Cursor cursor = context.getContentResolver().query(

                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,

                new String[]{MediaStore.Images.Media._ID},

                MediaStore.Images.Media.DATA + "=? ",

                new String[]{filePath}, null);


        if (cursor != null && cursor.moveToFirst()) {

            int id = cursor.getInt(cursor

                    .getColumnIndex(MediaStore.MediaColumns._ID));

            Uri baseUri = Uri.parse("content://media/external/images/media");

            return Uri.withAppendedPath(baseUri, "" + id);

        } else {

            if (imageFile.exists()) {

                ContentValues values = new ContentValues();

                values.put(MediaStore.Images.Media.DATA, filePath);

                return context.getContentResolver().insert(

                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            } else {

                return null;

            }

        }

    }


    /**

     * 获取原始图片的角度(***解决三星手机拍照后图片是横着的问题***)

     * 读取照片exif信息中的旋转角度

     *

     * @param path 照片路径

     * @return角度

     */

    private int getPictureDegree(String path) {


        int degree = 0;

        /**

         * 方法一

         */

//        boolean hasRotation = false;

//        String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};

//        Cursor cursor = context.getContentResolver().query(cameraUri, orientationColumn, null, null, null);

//        Log.e(TAG, "cursor: " + cursor +  "  "  );

//        if (cursor.getCount() != 1) {

//            cursor.close();

//            return -1;

//        }

//        if (cursor.moveToFirst()){

//            degree = cursor.getInt(cursor.getColumnIndex(orientationColumn[0]));

//            hasRotation = true;

//        }

//        cursor.close();

//        cursor = null;

//        Log.e(TAG, "getPictureDegree: " + degree );


        /**

         * 方法二

         */

        try {

            ExifInterface exifInterface = new ExifInterface(path);

            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

            Log.e(TAG, "原图被旋转角度: ========== " + orientation );

            switch (orientation) {

                case ExifInterface.ORIENTATION_ROTATE_90:

                    degree = 90;

                    break;

                case ExifInterface.ORIENTATION_ROTATE_180:

                    degree = 180;

                    break;

                case ExifInterface.ORIENTATION_ROTATE_270:

                    degree = 270;

                    break;

            }

        } catch (IOException e) {

            e.printStackTrace();

        }


        /**

         * 方法三

         */

//        int degree = 0;

//        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();

//        android.hardware.Camera.getCameraInfo(0, info);

//        degree = info.orientation;

//        Log.e(TAG, "CameraInfo: " + info.orientation );


        return degree;

    }


    /**

     * 旋转

     * @return

     */

    private Bitmap toturn() {

        Matrix matrix = new Matrix();

        int degree = getPictureDegree(cameraImagePath);

        matrix.postRotate(degree); /*翻转90度*/


        BitmapFactory.Options options = new BitmapFactory.Options();

        options.inJustDecodeBounds = true;

        // 简单的压缩

        options.inSampleSize = 4;           //把原图按1/4的比例压缩

        options.inJustDecodeBounds = false; // 压缩完后便可以将inJustDecodeBounds设置为false

        // 把流转化为Bitmap图片

        Bitmap bitmap = BitmapFactory.decodeFile(cameraImagePath, options);


        int width = bitmap.getWidth();

        int height = bitmap.getHeight();

        Bitmap returnBm  = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

//        if (returnBm == null) {

//            returnBm = bitmap;

//        }

//        if (bitmap != returnBm) {

//            bitmap.recycle();

//        }

//           Log.e(TAG, "returnBm: " + returnBm );

        return returnBm;

    }


    /**

     * 保存旋转后的Bitmap图片在SD卡中

     * 如果没有SD卡则存在手机中

     * @return 保存成功时返回图片的路径,失败时返回null

     */

    public String savePhotoToSD() {

        Bitmap mbitmap = toturn();

        FileOutputStream outStream = null;

        String fileName = String.valueOf(System.currentTimeMillis()) + ".jpg";

        File file = new File(ConstantManager.cameraPath );

        String filePath = file + "/" +fileName;

        // 判断文件是否已经存在,不存在则创建

        if ( !file.exists() ) {

            file.mkdirs();

        }

        try {

            outStream = new FileOutputStream(filePath);

            // 把数据写入文件,100表示不压缩

            mbitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);

            Log.e(TAG, "旋转处理后的图片: " + filePath );

            return filePath;

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        } finally {

            try {

                if (outStream != null) {

                    outStream.close();

                }

                if (mbitmap != null) {

                    mbitmap.recycle();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

Activity中回调处理:


    @Override

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        Log.e(TAG, "resultCode: " + resultCode);

        if (resultCode == this.RESULT_OK) {

            Log.e(TAG, "requestcode: " + requestCode);

            switch (requestCode) {

                case 0x007:

                    presenter.crop(true, data, null);

                    break;

                case 0x008:

                    File imageFile = new File(presenter.savePhotoToSD());

                    presenter.crop(false, data, imageFile);

                    break;

                case 0x009:

                    presenter.uploadImage(userId, entity);

                    break;

                default:

                    break;

            }

        }

    }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22


————————————————

版权声明:本文为CSDN博主「Devter」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/u013214588/java/article/details/82586691


推荐文章
评论(0)
联系我们|招贤纳士|移动客户端|风格模板|官方博客|侵权投诉 Reporting Infringements|未成年人有害信息举报 0571-89852053|涉企举报专区
网易公司版权所有 ©1997-2024  浙公网安备 33010802010186号 浙ICP备16011220号-11 增值电信业务经营许可证:浙B2-20160599
网络文化经营许可证: 浙网文[2022]1208-054号 自营经营者信息 工业和信息化部备案管理系统网站 12318全国文化市场举报网站
网信算备330108093980202220015号 网信算备330108093980204230011号
分享到
转载我的主页