具有正确纵横比的android textureview全屏预览
Posted
技术标签:
【中文标题】具有正确纵横比的android textureview全屏预览【英文标题】:android textureview full screen preview with correct aspect ratio 【发布时间】:2016-11-26 21:11:58 【问题描述】:我一直在使用camera2 api demo from google,不幸的是,示例应用程序被构建为在屏幕高度的大约 70% 处显示纹理视图预览,环顾四周后,我能够确定这是由 AutoFitTextureView 覆盖引起的onMeasure()
方法如下图:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight)
setMeasuredDimension(width, height);
else
if (width < height * mRatioWidth / mRatioHeight)
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
else
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
我试图通过在setMeasuredDimension(width, height);
中设置正确的高度和宽度来纠正这个问题,这解决了高度问题并给了我从纹理视图的全屏预览,但是纵横比在每个设备上都完全被破坏和扭曲,什么是解决这个问题的标准方法吗?我看到 Play 商店中的许多应用都找到了解决此问题的方法,但还没有找到解决方法,任何帮助都会有很大帮助,谢谢。
【问题讨论】:
嘿,您找到任何解决方案了吗?面临同样的问题..请帮忙。 答案更新如下 【参考方案1】:我可以通过切换 setMeasuredDimension(); 来解决这个问题
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight)
setMeasuredDimension(width, height);
else
if (width < height * mRatioWidth / mRatioHeight)
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
else
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
【讨论】:
您好 Edmund,您所说的“切换 setMeasuredDimension()”是什么意思?我对camera2的全屏也有同样的问题。您能否提供您在 onMeasure 方法中使用的代码以使全屏正常工作而不会出现任何失真? 感谢您提供更新的答案,但此解决方案会延长预览时间。 感谢您的解决方案。您应该将其标记为答案。 切换似乎确实有效,但稍微解释一下为什么会真的很有帮助。此外,这会使 Camera2Raw 应用程序代码中的 FrameLayout 视图脱离屏幕。 不要忘记在xml中设置视图的高度和宽度为wrap_content
【参考方案2】:
下面是我们用来测量支持 4:3、16:9 和 1:1 预览尺寸的预览的代码。高度是缩放的,因为应用程序是纵向的,它不会旋转到横向。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
Log.d(TAG, "[onMeasure] Before transforming: " + width + "x" + height);
int rotation = ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRotation();
boolean isInHorizontal = Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation;
int newWidth;
int newHeight;
Log.d(TAG, "[onMeasure] Get measured dimensions: " + getMeasuredWidth() + "x" + getMeasuredHeight());
if (isInHorizontal)
newHeight = getMeasuredHeight();
if (mAspectRatioOneOne) newWidth = getMeasuredHeight();
else newWidth = (int) (newHeight * mAspectRatio);
else
newWidth = getMeasuredWidth();
if (mAspectRatioOneOne) newHeight = getMeasuredWidth();
else newHeight = (int) (newWidth * mAspectRatio);
setMeasuredDimension(newWidth, newHeight);
Log.d(TAG, "[onMeasure] After transforming: " + getMeasuredWidth() + "x" + getMeasuredHeight());
【讨论】:
【参考方案3】:我在使用 Camera2 实现时遇到了类似的问题。我使用 TextureView 的 AutoFitTextureView 实现作为相机的预览,并希望调整到正确的比例。我的问题来自方法的不同实现并传播到 onSurfaceTextureSizeChanged。 [1]com.google.android.cameraview.CameraView#onMeasure(..) -> [2]com.google.android.cameraview.AutoFitTextureView#onMeasure(..) -> [3]onSurfaceTextureSizeChanged(..)
if [1] 与 [2] 不同的问题。 我在 [1] 中替换:
if (height < width * ratio.getY() / ratio.getX())
到
if (!(height < width * ratio.getY() / ratio.getX()))
因为 [2] 如果基于宽度而不是高度
if (width < height * mRatioWidth / mRatioHeight)
验证方法 onMeasure(..) 是否正确实现。
或者你可以像 Edmund Rojas 那样做 - 上面
【讨论】:
【参考方案4】:如果您想要全屏、不失真的预览,则必须为相机选择与屏幕纵横比相匹配的预览分辨率。
但是,如果您的屏幕尺寸不是标准尺寸(基本上是 16:9 或 4:3),尤其是当您减去软按钮和通知栏后(假设您不是完全全屏),那么让预览充满整个屏幕的唯一选择就是剪掉其中的一部分。
您应该能够通过查看视图的宽度和高度以及选定的预览宽度和高度来修改 TextureView 的变换矩阵以消除失真,但这必然会减少一些失真。
【讨论】:
嘿,你会怎么去“放大”,以保持纵横比,但用相机预览填满整个屏幕?您在最后一段中有所描述,但我不明白如何正确修改变换矩阵。 变换矩阵是齐次坐标的标准图形 4x4 矩阵。要填满屏幕,您只需要均匀地放大图像直到黑条消失(注意这会使一些图像数据离开屏幕)。这是来自内存,但比例矩阵只是 [s 0 0 0; 0 秒 0 0; 0 0 0 0; 0 0 0 1] 其中 s 是放大系数(因此 s=2,对于双倍尺寸)。将该矩阵与您从 getTransform 获得的变换矩阵相乘(矩阵相乘,而不是逐元素),并使用 setTransform 将其设置为变换。 好吧,我理解了 matrix.setScale(x, y) 方法。我将在明天尝试这个“将该矩阵与变换矩阵相乘......”部分,看看我是否让它工作。我不是 100% 确定你的意思。我可能会再次打你,到目前为止非常感谢! 如何计算变换矩阵的 X 和 Y 比例,使其适合任一屏幕方向(横向/纵向)?我有屏幕宽度+高度和预览宽度+高度。我必须以某种方式获得 X 和 Y 的值(取决于方向),其中一个是“1”,另一个稍大一些,大多数时候大约是 1,2。当表面更改回调被触发时,我正在设置转换。 Mhhh 等等,这个“任何一个值为 1”可能是错误的【参考方案5】:布局中的AutoFitTextureView应该使用wrap_content
<AutoFitTextureView
android:id="@+id/texture"
android:layout_
android:layout_/>
/**
* A [TextureView] that can be adjusted to a specified aspect ratio.
*/
class AutoFitTextureView : TextureView
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : super(context, attrs, defStyle)
private var ratioWidth = 0
private var ratioHeight = 0
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* @param width Relative horizontal size
* @param height Relative vertical size
*/
fun setAspectRatio(width: Int, height: Int)
require(!(width < 0 || height < 0)) "Size cannot be negative."
ratioWidth = width
ratioHeight = height
requestLayout()
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
if (ratioWidth == 0 || ratioHeight == 0)
setMeasuredDimension(width, height)
else
if (width < height * ratioWidth / ratioHeight)
setMeasuredDimension(width, width * ratioHeight / ratioWidth)
else
setMeasuredDimension(height * ratioWidth / ratioHeight, height)
【讨论】:
以上是关于具有正确纵横比的android textureview全屏预览的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 启动活动中保持 windowBackground 的正确纵横比