Android Camera旋转角度

2023-05-16

首先理解一下 info.orientation 官方解释

官方定义:orientation 表示相机图像的方向。它的值是相机图像顺时针旋转到设备自然方向一致时的角度。例如假设设备是竖屏的。后置相机传感器是横屏安装的。当你面向屏幕时,如果后置相机传感器顶边的和设备自然方向的右边是平行的,则后置相机的 orientation 是 90。如果前置相机传感器顶边和设备自然方向的右边是平行的,则前置相机的 orientation 是 270。

画了一个简单的草图,我用两部手机进行测试,一部是华为荣耀 6plus 、另一个是定做的  T6A,测试的时候把手机固定为了竖屏应用

另一款 Android 机 T6A ,摄像头位置比较特别,我获取的 info.orientation = 0; 那意味着我不用旋转,捕获的数据,都是与屏幕方向一致的

特别说明:

对于后置相机,只需要旋转后置相机的 orientation 即 90即可和屏幕方向保持一致; 

对于前置相机的预览方向,相机预览的图像是相机采集到的图像的镜像,由于系统对前置相机采集到的图像做了镜像,因此需要旋转 270-180,也是 90 度才可与屏幕方向保持一致。
 

一、适配目标

根据相机旋转角度以及屏幕显示旋转角度选择相机预览数据显示到View上的预览数据显示旋转角度,使眼睛直接看到的真实画面和手机屏幕中显示的画面效果相同。

  • 相机旋转角度:相机成像相对于手机的旋转角度,若设备已经安装上了相机,那么该相机相对于设备的旋转角度是固定的。

    • Camera API获取方式
     Camera.CameraInfo info = new Camera.CameraInfo();
     Camera.getCameraInfo(cameraId, info);
     Log.i(TAG, "orientation: " + info.orientation);
    
    • Camera2 API获取方式
    CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
    sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    Log.i(TAG, "orientation: " + sensorOrientation);
    
  • 屏幕显示旋转角度: Activity#getWindowManager().getDefaultDisplay().getRotation()​的值,可以是ROTATION_0、ROTATION_90、ROTATION_180、ROTATION_270。

  • 预览数据显示旋转角度:根据相机旋转角度屏幕显示旋转角度,我们即可计算预览数据显示旋转角度。顺时针旋转方向。

二、发现规律

分别选择后置和前置摄像头,将手机以各个角度握持,获得相机旋转角度屏幕显示旋转角度预览数据如下,那么对于预览数据后前后置摄像头的镜像差别,我们可以总结出预览数据显示旋转角度

相机相机旋转角度屏幕显示旋转角度预览帧数据预览数据显示旋转角度
后置90Surface.ROTATION_0 (portrait)

后置_portrait

90
后置90Surface.ROTATION_90 (landscape)

 后置_landscape

0
后置90Surface.ROTATION_180 (reverse-portrait)

后置_reverse-portrait

270
后置90Surface.ROTATION_270 (reverse-landscape)

 后置_reverse-landscape

180
前置270Surface.ROTATION_0 (portrait)

 (采集后的未镜像图像)前置_portrait

(先内部镜像)90
前置270Surface.ROTATION_90 (landscape)

  (采集后的未镜像图像)前置_landscape

内部镜像)0
前置270Surface.ROTATION_180 (reverse-portrait)

  (采集后的未镜像图像)前置_reverse-portrait

内部镜像)270
前置270Surface.ROTATION_270 (reverse-landscape)

(采集后的未镜像图像)前置_reverse-landscape

内部镜像)180
  • 内部镜像:
    对于后置摄像头,预览数据需要经过旋转后才能显示成正常效果,以竖屏情况为例:

    原始图预览数据显示旋转角度效果图

     后置_portrait

    90

    正常预览

    而对于前置摄像头,在旋转之前,我们要先进行左右镜像再旋转才能得到期望结果,也以竖屏情况为例:

    原始图镜像图预览数据显示旋转角度效果图

    前置_portrait

    ​镜像后数据

    90

    正常

三、总结

遍历分析了以上的所有情况后,我们也得出了以下结果:

  • 映射关系

    相机相机旋转角度屏幕显示旋转角度预览数据显示旋转角度
    后置90Surface.ROTATION_0 (portrait)90
    后置90Surface.ROTATION_90 (landscape)0
    后置90Surface.ROTATION_180 (reverse-portrait)270
    后置90Surface.ROTATION_270 (reverse-landscape)180
    前置270Surface.ROTATION_0 (portrait)90
    前置270Surface.ROTATION_90 (landscape)0
    前置270Surface.ROTATION_180 (reverse-portrait)270
    前置270Surface.ROTATION_270 (reverse-landscape)180
  • 总结函数
    如果只是简单总结下上面的数值映射关系,我们可以发现预览数据显示旋转角度似乎只和屏幕显示旋转角度有关,于是可以得出以下函数:

     private int getCameraOri(int rotation) {
          switch (rotation) {
              case Surface.ROTATION_0:
                  return 90;
              case Surface.ROTATION_90:
                  return 0;
              case Surface.ROTATION_180:
                  return 270;
              case Surface.ROTATION_270:
                  return 180;
              default:
                  return 0;
          }
      }
    

    但是真的足够了吗?可以看到,这个函数的入参仅仅只有rotation,并未考虑到cameraIdcameraOrientation,我们重新思考下:

    • 后置摄像头
      对于后置摄像头,旋转角度是90度,且不考虑镜像关系,也就是说:

      • rotationSurface.ROTATION_0时,预览数据需顺时针旋转90度(cameraOrientation)
      • rotationSurface.ROTATION_90时,预览数据不需要旋转,即旋转0度(cameraOrientation - 90)
      • rotationSurface.ROTATION_180时,预览数据需顺时针旋转270度(cameraOrientation - 180) + 360
      • rotationSurface.ROTATION_270时,预览数据需顺时针旋转180度(cameraOrientation - 270) + 360

        屏幕显示旋转角度每增加90度,预览数据显示旋转角度减少90度。
        因此后置摄像头的适配代码如下:
          int degrees = rotation * 90;
          switch (rotation) {
              case Surface.ROTATION_0:
                  degrees = 0;
                  break;
              case Surface.ROTATION_90:
                  degrees = 90;
                  break;
              case Surface.ROTATION_180:
                  degrees = 180;
                  break;
              case Surface.ROTATION_270:
                  degrees = 270;
                  break;
              default:
                  break;
          }
          // result 即为在camera.setDisplayOrientation(int)的参数
          result = (info.orientation - degrees + 360) % 360;
      
    • 前置摄像头
      对于前置摄像头,旋转角度是270度,也就是说:

      • rotationSurface.ROTATION_0时,预览数据需在左右镜像后顺时针旋转270度(cameraOrientation)
      • rotationSurface.ROTATION_90时,预览数据需在左右镜像后不需要旋转,即旋转0度(cameraOrientation + 90) - 360
      • rotationSurface.ROTATION_180时,预览数据需在左右镜像后顺时针旋转90度(cameraOrientation + 180) - 360
      • rotationSurface.ROTATION_270时,预览数据需在左右镜像后顺时针旋转180度(cameraOrientation + 270) - 360
        其中系统内部已经帮我们处理了镜像操作(可见下面第二段代码的注释),我们只需要传入旋转角度即可。
    • 综上,可得出Camera旋转角度适配代码如下:

   displayOrientation = getCameraOri(rotation,mCameraId);
   camera.setDisplayOrientation(displayOrientation);

   private int getCameraOri(int rotation, int cameraId) {
        int degrees = rotation * 90;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
            default:
                break;
        }

        // result 即为在camera.setDisplayOrientation(int)的参数
        int result;
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }

事实上,android.hardware.Camera类中早已帮我们实现了适配方案,且在public native final void setDisplayOrientation(int degrees)的注释中说明了在旋转前会做一次镜像操作:

Set the clockwise rotation of preview display in degrees. This affects
the preview frames and the picture displayed after snapshot. This method
is useful for portrait mode applications. Note that preview display of
front-facing cameras is flipped horizontally before the rotation
, that
is, the image is reflected along the central vertical axis of the camera
sensor. So the users can see themselves as looking into a mirror.

    /**
     * Set the clockwise rotation of preview display in degrees. This affects
     * the preview frames and the picture displayed after snapshot. This method
     * is useful for portrait mode applications. Note that preview display of
     * front-facing cameras is flipped horizontally before the rotation, that
     * is, the image is reflected along the central vertical axis of the camera
     * sensor. So the users can see themselves as looking into a mirror.
     *
     * <p>This does not affect the order of byte array passed in {@link
     * PreviewCallback#onPreviewFrame}, JPEG pictures, or recorded videos. This
     * method is not allowed to be called during preview.
     *
     * <p>If you want to make the camera image show in the same orientation as
     * the display, you can use the following code.
     * <pre>
     * public static void setCameraDisplayOrientation(Activity activity,
     *         int cameraId, android.hardware.Camera camera) {
     *     android.hardware.Camera.CameraInfo info =
     *             new android.hardware.Camera.CameraInfo();
     *     android.hardware.Camera.getCameraInfo(cameraId, info);
     *     int rotation = activity.getWindowManager().getDefaultDisplay()
     *             .getRotation();
     *     int degrees = 0;
     *     switch (rotation) {
     *         case Surface.ROTATION_0: degrees = 0; break;
     *         case Surface.ROTATION_90: degrees = 90; break;
     *         case Surface.ROTATION_180: degrees = 180; break;
     *         case Surface.ROTATION_270: degrees = 270; break;
     *     }
     *
     *     int result;
     *     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
     *         result = (info.orientation + degrees) % 360;
     *         result = (360 - result) % 360;  // compensate the mirror
     *     } else {  // back-facing
     *         result = (info.orientation - degrees + 360) % 360;
     *     }
     *     camera.setDisplayOrientation(result);
     * }
     * </pre>
     *
     * <p>Starting from API level 14, this method can be called when preview is
     * active.
     *
     * <p><b>Note: </b>Before API level 24, the default value for orientation is 0. Starting in
     * API level 24, the default orientation will be such that applications in forced-landscape mode
     * will have correct preview orientation, which may be either a default of 0 or
     * 180. Applications that operate in portrait mode or allow for changing orientation must still
     * call this method after each orientation change to ensure correct preview display in all
     * cases.</p>
     *
     * @param degrees the angle that the picture will be rotated clockwise.
     *                Valid values are 0, 90, 180, and 270.
     * @throws RuntimeException if setting orientation fails; usually this would
     *    be because of a hardware or other low-level error, or because
     *    release() has been called on this Camera instance.
     * @see #setPreviewDisplay(SurfaceHolder)
     */
    public native final void setDisplayOrientation(int degrees);

运行效果也确实如此,我们也可以翻下android源码看看内部的实现:

  • android.hardware.Camera.java
public native final void setDisplayOrientation(int degrees);
  • frameworks\base\core\jni\android_hardware_Camera.cpp
static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject thiz,
        jint value)
{
    ALOGV("setDisplayOrientation");
    sp<Camera> camera = get_native_camera(env, thiz, NULL);
    if (camera == 0) return;

    if (camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION, value, 0) != NO_ERROR) {
        jniThrowRuntimeException(env, "set display orientation failed");
    }
}
  • frameworks\av\camera\CameraClient.cpp

status_t CameraClient::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
    ...
    if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
        // Mirror the preview if the camera is front-facing.
        orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT);
        if (orientation == -1) return BAD_VALUE;

        if (mOrientation != orientation) {
            mOrientation = orientation;
            if (mPreviewWindow != 0) {
                mHardware->setPreviewTransform(mOrientation);
            }
        }
        return OK;
    } 
    ...
    return mHardware->sendCommand(cmd, arg1, arg2);
}

int CameraClient::getOrientation(int degrees, bool mirror) {
    if (!mirror) {
        if (degrees == 0) return 0;
        else if (degrees == 90) return HAL_TRANSFORM_ROT_90;
        else if (degrees == 180) return HAL_TRANSFORM_ROT_180;
        else if (degrees == 270) return HAL_TRANSFORM_ROT_270;
    } else {  // Do mirror (horizontal flip)
        if (degrees == 0) {           // FLIP_H and ROT_0
            return HAL_TRANSFORM_FLIP_H;
        } else if (degrees == 90) {   // FLIP_H and ROT_90
            return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
        } else if (degrees == 180) {  // FLIP_H and ROT_180
            return HAL_TRANSFORM_FLIP_V;
        } else if (degrees == 270) {  // FLIP_H and ROT_270
            return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
        }
    }
    ALOGE("Invalid setDisplayOrientation degrees=%d", degrees);
    return -1;
}

这里有一段关键代码,对于前置摄像头的不同旋转角度,设置水平镜像、垂直镜像、旋转角度。

        if (degrees == 0) {           // FLIP_H and ROT_0
            return HAL_TRANSFORM_FLIP_H;
        } else if (degrees == 90) {   // FLIP_H and ROT_90
            return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
        } else if (degrees == 180) {  // FLIP_H and ROT_180
            return HAL_TRANSFORM_FLIP_V;
        } else if (degrees == 270) {  // FLIP_H and ROT_270
            return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
        }

其中HAL_TRANSFORM_XXX的定义如下:

/**
 * Transformation definitions
 *
 * IMPORTANT NOTE:
 * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}.
 *
 */
typedef enum android_transform {
    /* flip source image horizontally (around the vertical axis) */
    HAL_TRANSFORM_FLIP_H    = 0x01,
    /* flip source image vertically (around the horizontal axis)*/
    HAL_TRANSFORM_FLIP_V    = 0x02,
    /* rotate source image 90 degrees clockwise */
    HAL_TRANSFORM_ROT_90    = 0x04,
    /* rotate source image 180 degrees */
    HAL_TRANSFORM_ROT_180   = 0x03,
    /* rotate source image 270 degrees clockwise */
    HAL_TRANSFORM_ROT_270   = 0x07,
    /* don't use. see system/window.h */
    HAL_TRANSFORM_RESERVED  = 0x08,
} android_transform_t;
  • frameworks\av\services\camera\libcameraservice\device1\CameraHardwareInterface.cpp
status_t CameraHardwareInterface::setPreviewTransform(int transform) {
    int rc = OK;
    mPreviewTransform = transform;
    if (mPreviewWindow != nullptr) {
        rc = native_window_set_buffers_transform(mPreviewWindow.get(),
                mPreviewTransform);
    }
    return rc;
}
  • frameworks\native\libs\nativewindow\include\system\window.h
/*
 * native_window_set_buffers_transform(..., int transform)
 * All buffers queued after this call will be displayed transformed according
 * to the transform parameter specified.
 */
static inline int native_window_set_buffers_transform(
        struct ANativeWindow* window,
        int transform)
{
    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
            transform);
}
  • frameworks/native/libs/gui/Surface.cpp
// ANativeWindow::perform 函数指向本地的 hook_perform 函数
Surface::Surface(
        const sp<IGraphicBufferProducer>& bufferProducer,
        bool controlledByApp)
    : mGraphicBufferProducer(bufferProducer),
      mCrop(Rect::EMPTY_RECT),
      mGenerationNumber(0),
      mSharedBufferMode(false),
      mAutoRefresh(false),
      mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
      mSharedBufferHasBeenQueued(false)
{
    ...
    ANativeWindow::perform          = hook_perform;
    ...
}
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
    va_list args;
    va_start(args, operation);
    Surface* c = getSelf(window);
    int result = c->perform(operation, args);
    va_end(args);
    return result;
}
int Surface::perform(int operation, va_list args)
{
    int res = NO_ERROR;
    switch (operation) {
    ...
    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
        res = dispatchSetBuffersTransform(args);
        break;
    ...
    }
}
int Surface::dispatchSetBuffersTransform(va_list args) {
    uint32_t transform = va_arg(args, uint32_t);
    return setBuffersTransform(transform);
}

// 最终是设置了mTransform的值
int Surface::setBuffersTransform(uint32_t transform)
{
    ATRACE_CALL();
    ALOGV("Surface::setBuffersTransform");
    Mutex::Autolock lock(mMutex);
    mTransform = transform;
    return NO_ERROR;
}
// 再看下mTransform在哪里被使用
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
    ...
    IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
            mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
            fence, mStickyTransform);
        if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
        input.setSurfaceDamage(Region::INVALID_REGION);
    } else {
        // Here we do two things:
        // 1) The surface damage was specified using the OpenGL ES convention of
        //    the origin being in the bottom-left corner. Here we flip to the
        //    convention that the rest of the system uses (top-left corner) by
        //    subtracting all top/bottom coordinates from the buffer height.
        // 2) If the buffer is coming in rotated (for example, because the EGL
        //    implementation is reacting to the transform hint coming back from
        //    SurfaceFlinger), the surface damage needs to be rotated the
        //    opposite direction, since it was generated assuming an unrotated
        //    buffer (the app doesn't know that the EGL implementation is
        //    reacting to the transform hint behind its back). The
        //    transformations in the switch statement below apply those
        //    complementary rotations (e.g., if 90 degrees, rotate 270 degrees).

        int width = buffer->width;
        int height = buffer->height;
        bool rotated90 = (mTransform ^ mStickyTransform) &
                NATIVE_WINDOW_TRANSFORM_ROT_90;
        if (rotated90) {
            std::swap(width, height);
        }

        Region flippedRegion;
        for (auto rect : mDirtyRegion) {
            int left = rect.left;
            int right = rect.right;
            int top = height - rect.bottom; // Flip from OpenGL convention
            int bottom = height - rect.top; // Flip from OpenGL convention
            switch (mTransform ^ mStickyTransform) {
                case NATIVE_WINDOW_TRANSFORM_ROT_90: {
                    // Rotate 270 degrees
                    Rect flippedRect{top, width - right, bottom, width - left};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
                case NATIVE_WINDOW_TRANSFORM_ROT_180: {
                    // Rotate 180 degrees
                    Rect flippedRect{width - right, height - bottom,
                            width - left, height - top};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
                case NATIVE_WINDOW_TRANSFORM_ROT_270: {
                    // Rotate 90 degrees
                    Rect flippedRect{height - bottom, left,
                            height - top, right};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
                default: {
                    Rect flippedRect{left, top, right, bottom};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
            }
        }

        input.setSurfaceDamage(flippedRegion);
    }

    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
    if (err != OK)  {
        ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
    }
    ...
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android Camera旋转角度 的相关文章

  • 如何从一个活动中完成一系列开放的子活动?

    我正在尝试为我的应用程序制作一个退出按钮 无论如何 我能够跟踪我的应用程序中的所有活动实例 然后完成它们 但在某些情况下 仍有一些活动仍然存在 不知道怎么办 有没有什么方法可以杀死android中的特定应用程序 或者我可以通过任何其他方式退
  • Google Play 商店中基于服务的 Android 应用程序

    我正在开发一个应用程序 该应用程序仅包含一些服务 没有任何活动 即没有 UI 基本上 当用户在他 她的设备上安装应用程序时 我希望有 2 到 3 个服务在后台运行 对此我有几个疑问 应用程序安装后我的服务将如何启动 我的BroadcastR
  • 如何使用retrofit2进行GET请求?

    我有一个在本地主机上运行的安静的 Web 服务 我想在该剩余 URL 上发出 Retrofit2 GET 请求 MainActivity java private void requestData public static final S
  • 导航组件参数默认值

    在导航组件中 将参数从第一个片段发送到第二个片段时 默认值不会从导航图中获取哪个集合 这是我的代码 导航图 xml
  • BluetoothAdapter.getDefaultAdapter() 不返回 null

    这是我的第一篇文章 所以如果我做了一些愚蠢的事情 请告诉我 这个问题可能看起来与其他帖子类似 但或多或 少与我所看到的所有内容相反 关于该项目的事情 我正在开发 android 4 0 4 4 应用程序 我正在使用蓝牙 我正在运行 andr
  • Android ListView 自定义适配器 ImageButton

    这可能不是正确的方法 如果有更好的方法请告诉我 我创建了一个自定义适配器类 在我的 getView 方法中我膨胀了我想要使用的视图 public View getView int position View convertView View
  • 从 BroadcastReceiver 获取方法来更新 UI

    我正在尝试根据变量的变化更新用户界面BroadcastReceiver 因此 我需要调用一个扩展类的方法 以获取我提到的变量 BroadcastReceiver in MainActivity取决于但我无法以任何方式获得真正的返回值 扩展的
  • 游标索引越界异常

    打开后出现光标索引越界错误 数据库 请任何人告诉我如何打开现有数据库 sqllite Android 我想在数据库上触发一个选择查询 检索一些信息 public void getPatient SQLiteDatabase db Strin
  • 如何使用键盘和鼠标控制相机 - Three.js

    我在 WEB GL 中有一个带有 Three js 的 3D 环境 并且我曾经使用 Orbitcontrols js http codepen io nireno pen cAoGI http codepen io nireno pen c
  • FLAG_ACTIVITY_REORDER_TO_FRONT 被忽略

    我有一个包含项目列表的 FragmentActivity 当应用程序处于后台时 可以推送该项目列表 发生这种情况时 我想创建一个状态栏通知并提醒用户更新 当用户单击通知时 活动应重新排序到前面并显示在屏幕上 同时在列表底部显示新项目 所以我
  • 在浏览器中打开 URL,即使我的应用程序为其注册了意图过滤器

    我的应用程序为某些 URL 注册了一个意图过滤器 因为它可以处理来自这些 URL 的数据 但是 在应用程序内部 我想提供一个按钮来在浏览器中打开这样的 URL 也就是说 如果设置了默认浏览器 则在默认浏览器中打开它 否则提供一个选择器 就像
  • 使用 RxJava 限制吞吐量

    我现在遇到的情况很难解释 所以我会写一个更简单的版本来解释这个问题 我有一个Observable from 它发出一系列由ArrayList文件数量 所有这些文件都应上传到服务器 为此 我有一个函数可以完成这项工作并返回一个Observab
  • 更改 Android 中的媒体音量?

    我可以更改媒体音量吗 如何 到目前为止我用过这个 setVolumeControlStream AudioManager STREAM MUSIC 但有一个搜索栏并且想要更改媒体音量 而不是铃声音量 那么有人可以告诉我如何更改媒体音量onC
  • 如何使用 Swipe 视图实现 Android TabLayout 设计支持库

    我将使用 android TabLayout 设计支持库 但我不知道如何使用滑动视图 这是我的代码 XML
  • Android:BATTERY_STATUS_DISCHARGING 和 BATTERY_STATUS_NOT_CHARGING 之间的区别

    我想知道这两个标志之间的区别 BatteryManager BATTERY STATUS DISCHARGING And BatteryManager BATTERY STATUS NOT CHARGING 我开发了一个使用这两个标志的应用
  • 使用 UPI url 调用 PSP 应用程序

    我正在尝试创建一个商家应用程序 它将根据 NPCI 的指南生成一个 url 此 url 将作为意图共享 并且 PSP 应用程序 任何注册的银行应用程序 应该能够侦听该 url 并被调用 我已经形成了这样的网址 upi pay pa icic
  • 如何在 kotlin 中检查 lambda 空值

    在 Kotlin 中如何检查 lambda 是否为空 例如 我有这样的签名 onError Throwable gt Unit 我如何区分它的默认值是应用于主体还是应用于此函数的值 您无法测试 lambda 的主体是否为空 因此它不包含源代
  • 如何在 onDraw() 方法中定义与像素无关的高度

    我扩展了 View 来构建自定义小部件 我想用独立的像素单位定义小部件的高度 我认为可以通过将像素密度乘以所需的高度来完成 但我不知道该怎么做 到目前为止我所拥有的 最小化 public class Timeline extends Vie
  • 动态创建 JSON 对象

    我正在尝试使用以下格式创建 JSON 对象 tableID 1 price 53 payment cash quantity 3 products ID 1 quantity 1 ID 3 quantity 2 我知道如何使用 JSONOb
  • 如何从灰度字节缓冲区图像创建位图?

    我正在尝试使用新的 Android 人脸检测移动视觉 API 来处理帧图像 所以我创建了自定义检测器来获取帧并尝试调用 getBitmap 方法 但它为空 所以我访问了帧的灰度数据 有没有办法从它或类似的图像持有者类创建位图 public

随机推荐

  • DataBinding源码解析

    DataBinding是Google发布的支持库 xff0c 它可以实现UI组件及数据源的双向绑定 使用DataBinding可以轻松实现MVVM模式 xff0c 当数据发生变化时会体现在View界面上 xff0c 反过来界面内容变化也会同
  • LiveData源码分析

    首先还是以一个示例开始 xff1a MutableLiveData lt String gt liveString 61 new MutableLiveData lt gt liveString observe mOwner new Obs
  • ViewModel源码分析

    首先 xff0c 还是先看一个例子 xff1a public class MyViewModel extends ViewModel private MutableLiveData lt List lt User gt gt users p
  • RxJava2源码分析——Map操作符

    本文章用的RxJava和RxAndroid版本如下 xff1a implementation 39 io reactivex rxjava2 rxjava 2 2 6 39 implementation 39 io reactivex rx
  • 交叉编译pytorch的aarch64版本

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 基础环境二 编译流程1 下载源码并配置TOOLCHAIN FILE内容2 预编译出protoc库和sleef库3 ana
  • 使用CSplitterWnd类静态分割的窗口的隐藏[转]

    标题略长 之前百度了很多 xff0c 也看过了很多程序 xff0c 那个时候稍微有点小青涩 xff0c 所以那些东西根本是看不懂什么意思 现在回过头来看 xff0c 其实还是很容易就实现的 当然 xff0c 话题很初级 xff0c 不是面向
  • Rxjava2源码-FlatMap操作符

    先来看一下使用demo Observable create new ObservableOnSubscribe lt String gt 64 Override public void subscribe ObservableEmitter
  • 代理设计模式

    代理模式的结构与实现 代理模式的结构比较简单 xff0c 主要是通过定义一个继承抽象主题的代理来包含真实主题 xff0c 从而实现对真实主题的访问 xff0c 下面来分析其基本结构和实现方法 1 模式的结构 代理模式的主要角色如下 抽象主题
  • 适配器模式

    模式的结构与实现 类适配器模式可采用多重继承方式实现 xff0c 如 C 43 43 可定义一个适配器类来同时继承当前系统的业务接口和现有组件库中已经存在的组件接口 xff1b Java 不支持多继承 xff0c 但可以定义一个适配器类来实
  • 装饰器模式

    装饰器模式的结构与实现 通常情况下 xff0c 扩展一个类的功能会使用继承方式来实现 但继承具有静态特征 xff0c 耦合度高 xff0c 并且随着扩展功能的增多 xff0c 子类会很膨胀 如果使用组合关系来创建一个包装对象 xff08 即
  • 享元设计模式

    享元模式的结构与实现 享元模式的定义提出了两个要求 xff0c 细粒度和共享对象 因为要求细粒度 xff0c 所以不可避免地会使对象数量多且性质相近 xff0c 此时我们就将这些对象的信息分为两个部分 xff1a 内部状态和外部状态 内部状
  • 组合设计模式

    组合模式的结构与实现 组合模式的结构不是很复杂 xff0c 下面对它的结构和实现进行分析 1 模式的结构 组合模式包含以下主要角色 抽象构件 xff08 Component xff09 角色 xff1a 它的主要作用是为树叶构件和树枝构件声
  • 模板方法模式

    模式的结构与实现 模板方法模式需要注意抽象类与具体子类之间的协作 它用到了虚函数的多态性技术以及 不用调用我 xff0c 让我来调用你 的反向控制技术 现在来介绍它们的基本结构 1 模式的结构 模板方法模式包含以下主要角色 1 xff09
  • 策略设计模式

    策略模式的结构与实现 策略模式是准备一组算法 xff0c 并将这组算法封装到一系列的策略类里面 xff0c 作为一个抽象策略类的子类 策略模式的重心不是如何实现算法 xff0c 而是如何组织这些算法 xff0c 从而让程序结构更加灵活 xf
  • 命令设计模式

    命令模式的结构与实现 可以将系统中的相关操作抽象成命令 xff0c 使调用者与实现者相关分离 xff0c 其结构如下 1 模式的结构 命令模式包含以下主要角色 抽象命令类 xff08 Command xff09 角色 xff1a 声明执行命
  • 状态设计模式

    状态模式的结构与实现 状态模式把受环境改变的对象行为包装在不同的状态对象里 xff0c 其意图是让一个对象在其内部状态改变的时候 xff0c 其行为也随之改变 现在我们来分析其基本结构和实现方法 1 模式的结构 状态模式包含以下主要角色 环
  • linux开启关闭端口(iptables 无法使用情况下)

    一 查看端口开启状态 查询已开放的端口 netstat anp root 64 localhost etc firewall cmd query port 61 8080 tcp 提示 yes xff0c 表示开启 xff1b no表示未开
  • 中介者模式

    模式的结构与实现 中介者模式实现的关键是找出 中介者 xff0c 下面对它的结构和实现进行分析 1 模式的结构 中介者模式包含以下主要角色 抽象中介者 xff08 Mediator xff09 角色 xff1a 它是中介者的接口 xff0c
  • YUV解析

    一般的视频采集芯片输出的码流一般都是 YUV 格式数据流 xff0c 后续视频处理也是对 YUV 数据流进行编码和解析 所以 xff0c 了解 YUV 数据流对做视频领域的人而言 xff0c 至关重要 在介绍 YUV 格式之前 xff0c
  • Android Camera旋转角度

    首先理解一下 info orientation 官方解释 官方定义 xff1a orientation 表示相机图像的方向 它的值是相机图像顺时针旋转到设备自然方向一致时的角度 例如假设设备是竖屏的 后置相机传感器是横屏安装的 当你面向屏幕