Android Development 开发笔记

2020/02/05 Android Platform

将遇到的有关 Android 开发的细节放在这里。这块本人是超级入门级菜鸟,因此很多地方可能有错误。


确定屏幕当前所处的旋转方向

通常手机屏幕有两种模式:portrait mode(垂直)和 landscape mode(水平),其中后者又包括顺时针水平屏(即从垂直状态开始顺时针旋转 90 度)和逆时针水平屏(即从垂直状态开始逆时针旋转 90 度)。垂直屏就是默认状态。

private ScreenOrientation getScreenOrientation()
{
    int rot = getWindowManager().getDefaultDisplay().getRotation();
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
    {
        if (rot == Surface.ROTATION_270)
            // Clockwise mode means holding the phone portrait and rotate it clockwise to portrait mode.
            return ScreenOrientation.CLOCKWISE;
        else if (rot == Surface.ROTATION_90)
            // Clockwise mode means holding the phone portrait and rotate it counter-clockwise to portrait mode.
            return ScreenOrientation.COUNTER_CLOCKWISE;
    }
    else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        return ScreenOrientation.PORTRAIT;
    return ScreenOrientation.NONE;
}

将 Bitmap 经过变换处理后渲染到 TextureView 上

我们想要把一个 Bitmap 渲染到 TextureView,但通常 TextureView 的默认尺寸和图片尺寸是不一样的。因此,此时需要首先对图片进行变换处理,然后再渲染。此时一个方便的方法是,先计算一个变换矩阵,它包含了旋转、缩放、平移等全部的变换形式,然后再渲染时使用该矩阵即可。一个例子:

// Compute transformation matrix
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, textureView.getWidth(), textureView.getHeight()); // textureView 的尺寸
RectF bufferRect = new RectF(0, 0, bufferWidth, bufferHeight); // 这是 Bitmap 的尺寸
// 将 Bitmap 缩放到和 textureView 的尺寸相同,缩放形式是对准两者的中心点缩放,直到 fit 为止,即,这样保证了图片的宽高比不变
matrix.setRectToRect(bufferRect, viewRect, Matrix.ScaleToFit.CENTER);
// 再增加一个旋转。注意,postRotate 是左乘旋转矩阵到当前 matrix 上,而 Matrix 成员函数还有一个是 setRotate() 是直接设置矩阵为旋转阵。注意两者区别。
matrix.postRotate(90, centerX, centerY);

// Draw bitmap by transform
Canvas canvas = textureView.lockCanvas();
canvas.drawBitmap(bitmap, matrix, null);
textureView.unlockCanvasAndPost(canvas);

在屏幕上显式一个小的提示

可以用 Toasts,它很方便用来显式一个小的提示到屏幕,例如几个单词“Image Saved”之类的。用法很简单。可参考官方文档:https://developer.android.com/guide/topics/ui/notifiers/toasts

遍历所有的相机模式和参数(以深度相机为例)

一台手机会有多个相机模式,比如 color camera 会有不同分辨率、某些手机有深度相机(TOF depth camera) 等。下面是一个遍历全部相机模式,并查找深度相机的例子。

try
{
    for (String camera : cameraManager.getCameraIdList())
    {
        CameraCharacteristics chars = cameraManager.getCameraCharacteristics(camera);
        final int[] capabilities = chars.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
        // 必须是向后的相机。不过某些型号手机的深度相机是朝前的。
        boolean facingBack = (chars.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_BACK);
        boolean depthCapable = false;
        for (int capability : capabilities)
        {
            // depth output 就是确定深度相机是否存在的方法
            boolean capable = (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
            depthCapable = depthCapable || capable;
        }
        if (depthCapable && facingBack)
        {
            // 获取深度相机的尺寸。SizeF 只有 width 和 height 这两个参数。通常是 320x240 的分辨率,不过这比相机屏幕分辨率小很多。
            SizeF sensorSize = chars.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
            Log.i(TAG, "Sensor size: " + sensorSize);

            // 获取焦距,用于相机内参
            float[] focalLengths = chars.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
            if (focalLengths.length > 0)
            {
                float focalLength = focalLengths[0]; // 实验中,这个焦距是3.26,不过单位是  pixel/cm,使用时应该是用 fx = 326
                // Fov 并不在相机内参中,不过还是计算一下
                double fov = 2 * Math.atan(sensorSize.getWidth() / (2 * focalLength));
                Log.i(TAG, "focalLength: " + focalLength + ", Calculated FoV: " + fov);
            }
            return camera;
        }
    }
}
catch (CameraAccessException e)
{
    Log.e(TAG, "Could not initialize Camera Cache");
    e.printStackTrace();
}

深度图片(depth image)的获取和处理

整个流程主要参考:

Depth Image 的处理

Search

    Table of Contents