android - Android - 使用 SurfaceView 和 Camera2 API 处理屏幕方向
问题描述
我有一个关于当我们将 Camera2 API 与 SurfaceView 结合使用时如何处理 Android 中的屏幕方向的问题。我在https://github.com/googlesamples/android-HdrViewfinder上玩了一下官方的HdrViewfinder
google 示例代码。在那个项目中,他们使用了一个名为的类,它是. 但是该项目仅在活动的 screenOrientation (AndroidManifest) 处于横向模式而不是纵向模式时才能正确显示相机预览。将属性设置为纵向会以一种奇怪的方式交换预览。我如何修改该代码才能在纵向模式下正确查看相机预览?FixedAspectSurfaceView
SurfaceView
所以,这个FixedAspectSurfaceView.java
类看起来像这样:
public class FixedAspectSurfaceView extends SurfaceView {
/**
* Desired width/height ratio
*/
private float mAspectRatio;
private GestureDetector mGestureDetector;
public FixedAspectSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// Get initial aspect ratio from custom attributes
TypedArray a =
context.getTheme().obtainStyledAttributes(attrs,
R.styleable.FixedAspectSurfaceView, 0, 0);
setAspectRatio(a.getFloat(
R.styleable.FixedAspectSurfaceView_aspectRatio, 1.f));
a.recycle();
}
/**
* Set the desired aspect ratio for this view.
*
* @param aspect the desired width/height ratio in the current UI orientation. Must be a
* positive value.
*/
public void setAspectRatio(float aspect) {
if (aspect <= 0) {
throw new IllegalArgumentException("Aspect ratio must be positive");
}
mAspectRatio = aspect;
requestLayout();
}
/**
* Set a gesture listener to listen for touch events
*/
public void setGestureListener(Context context, GestureDetector.OnGestureListener listener) {
if (listener == null) {
mGestureDetector = null;
} else {
mGestureDetector = new GestureDetector(context, listener);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// General goal: Adjust dimensions to maintain the requested aspect ratio as much
// as possible. Depending on the measure specs handed down, this may not be possible
// Only set one of these to true
boolean scaleWidth = false;
boolean scaleHeight = false;
// Sort out which dimension to scale, if either can be. There are 9 combinations of
// possible measure specs; a few cases below handle multiple combinations
if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
// Can't adjust sizes at all, do nothing
} else if (widthMode == MeasureSpec.EXACTLY) {
// Width is fixed, heightMode either AT_MOST or UNSPECIFIED, so adjust height
scaleHeight = true;
} else if (heightMode == MeasureSpec.EXACTLY) {
// Height is fixed, widthMode either AT_MOST or UNSPECIFIED, so adjust width
scaleWidth = true;
} else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
// Need to fit into box <= [width, height] in size.
// Maximize the View's area while maintaining aspect ratio
// This means keeping one dimension as large as possible and shrinking the other
float boxAspectRatio = width / (float) height;
if (boxAspectRatio > mAspectRatio) {
// Box is wider than requested aspect; pillarbox
scaleWidth = true;
} else {
// Box is narrower than requested aspect; letterbox
scaleHeight = true;
}
} else if (widthMode == MeasureSpec.AT_MOST) {
// Maximize width, heightSpec is UNSPECIFIED
scaleHeight = true;
} else if (heightMode == MeasureSpec.AT_MOST) {
// Maximize height, widthSpec is UNSPECIFIED
scaleWidth = true;
} else {
// Both MeasureSpecs are UNSPECIFIED. This is probably a pathological layout,
// with width == height == 0
// but arbitrarily scale height anyway
scaleHeight = true;
}
// Do the scaling
if (scaleWidth) {
width = (int) (height * mAspectRatio);
} else if (scaleHeight) {
height = (int) (width / mAspectRatio);
}
// Override width/height if needed for EXACTLY and AT_MOST specs
width = View.resolveSizeAndState(width, widthMeasureSpec, 0);
height = View.resolveSizeAndState(height, heightMeasureSpec, 0);
// Finally set the calculated dimensions
setMeasuredDimension(width, height);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureDetector != null) {
return mGestureDetector.onTouchEvent(event);
}
return false;
}
}
我将文件中的screenOrientation
属性更改为. 我还更改了 activity_main.xml 布局文件:AndroidManifest
portrait
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<com.celik.abdullah.project.utils.FixedAspectSurfaceView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:aspectRatio="0.75"/>
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:text="next"/>
</FrameLayout>
当我将screenOrientation
属性留在清单文件中时landscape
,相机预览很好,但应用程序当然会以“横向”模式打开。当我将其设置screenOrientation
为纵向时,相机预览“将视图向左交换”。我不知道如何描述它,但它绝对是奇怪的。为什么我切换到预览版portrait
?我怎样才能修改项目以便它也可以在纵向模式下使用?
解决方案
对于 FixedAspectSurfaceView,您应该能够将其纵横比设置为您在横向中使用的纵横比的倒数。因此,如果在横向中将其设置为 4/3,则将其设置为 3/4 以进行纵向布局。
camera-to-SurfaceView 路径应该为你处理所有的旋转,你只需要保持 SurfaceView 的形状正确。
推荐阅读
- r - R:unnest 函数如何工作以及如何克服不兼容的长度错误?
- vue.js - 如何在保留原始模板的同时覆盖 Bootstrap Vue 组件的方法?
- terraform - 如何在 Terraform 中重用资源中的数据?
- coq - if-then-else 条件下的案例拆分
- asp.net-core-webapi - 发布时无法命中 AspNetCore 3.1 MVC API 端点
- groovy - 编写 DRY Spock 测试
- javascript - Heroku 在部署时进行故障排除
- mysql - 如何从 MySQL 中的同一张表中计算两个不同的值?
- javascript - 社交网络网站的数据库架构?mysql?
- java - 匿名类可以完全不可变吗?