背景
现在有一个需求:无论横竖屏模式,textureView 的相对位置应该和图片背景保持一致。
如图所示:
产品要求图片居中不拉伸,保证图片的背景效果 & 可以接受黑边。
那么这最外层的约束布局中的bg就不能直接引用 android:background
来设置图片背景,因为就呈现拉伸的状态。
那么就得使用imageView
来实现并设置scaleType为centerInside
就可以满足产品需求。
但是
无论横竖屏模式,textureView 的相对位置应该和图片背景保持一致 这个需求就得知道当前手机实际展示图片大小。
这是本次需求的重难点:
获取当前实际展示的图片大小并构建一个子约束布局来处理视频流(即上图的人物)。
解决
核心代码如下:
/**
* 该方法保障subLayout 和 imageView
* 实际展示的图片大小完全重合
* 以保证横竖屏切换的情况下都是完全贴合的
*
* @param imageView imageview对象
* @param subLayout 约束布局
*/
private void makeConstraintLayoutSuitable(ImageView imageView, ConstraintLayout subLayout) {
// 获取ImageView的Drawable对象
Drawable drawable = imageView.getDrawable();
// 获取Drawable的大小
int drawableWidth = drawable.getBounds().width();
int drawableHeight = drawable.getBounds().height();
//获得ImageView中Image的变换矩阵
Matrix m = imageView.getImageMatrix();
float[] values = new float[10];
m.getValues(values);
//Image在绘制过程中的变换矩阵,从中获得x和y方向的缩放系数
float sx = values[0];
float sy = values[4];
// 刷新约束layout 布局大小
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) subLayout.getLayoutParams();
//计算Image在屏幕上实际绘制的宽高
layoutParams.width = (int) (drawableWidth * sx);
layoutParams.height = (int) (drawableHeight * sy);
// 当前的图片的展示宽高 数据刷新
realBgWidth = layoutParams.width;
realBgHeight = layoutParams.height;
subLayout.setLayoutParams(layoutParams);
}
但是调用的时机要非常准确,,那么我们可以在 RoomMiddleView 中重写 onSizeChanged 方法,在横竖屏切换后获取到实际展示的 imageView 中的大小和缩放比例,然后根据这些信息重新设置 subLayout 的大小和约束条件。具体实现可以参考以下代码:来保证横竖屏的时候调用时机不会太早,使用旧的ImageView(即没有横屏或者竖屏后)的大小数据来加工的。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
makeConstraintLayoutSuitable(ivRoomBg, subLayout);
}
完整代码:
public class RoomMiddleView extends ConstraintLayout {
// textureView 的数据容器:根据房间配置数据来保证长度
private List<TextureView> textureViewList = new ArrayList<>();
public List<TextureView> getTextureViewList() {
return textureViewList;
}
public RoomMiddleView(@NonNull Context context) {
super(context);
}
public RoomMiddleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public RoomMiddleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private ImageView ivRoomBg;
// 子约束布局用来保证 textureView的
private ConstraintLayout subLayout;
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// TODO: 2023/4/28 在横竖屏的时候需要考虑不同的texture的布局
}
// 当前的图片背景bg的展示宽高
int realBgWidth;
int realBgHeight;
public void init(RoomInternalItem roomItem) {
LayoutInflater.from(getContext()).inflate(R.layout.fs_room_middle, this);
ivRoomBg = findViewById(R.id.iv_room_bg);
RoomTypeConfig config = roomItem.getSubItem().getConfig();
// 背景更换
ivRoomBg.setImageResource(roomItem.getSubItem().getDrawable());
subLayout = findViewById(R.id.sub_layout);
// 保证初始化完成之后才调用,这样获取到的imageview 的宽高 数据就不会是0
post(() -> {
makeConstraintLayoutSuitable(ivRoomBg,subLayout);
boolean isLandScape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
refreshTextureViews(config,subLayout,isLandScape);
});
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
/*
* 重要!!!
* 无论是横竖屏情况下都能保证sub子约束布局 和 图片实际展示位置的大小
* 完成覆盖重合
*/
post(() -> makeConstraintLayoutSuitable(ivRoomBg,subLayout));
}
private void refreshTextureViews(RoomTypeConfig config, ConstraintLayout subLayout,boolean isLandscape) {
// 前一个下标 TextureView
TextureView preTexture = null;
//TextureView 的初始化
for(int i = 0; i< config.getMaxMemberCount(); i++){
TextureView textureView = new TextureView(getContext());
// 默认 设置透明 方便直接供SDK使用
textureView.setOpaque(false);
// 数据容器添加
textureViewList.add(textureView);
// 添加指定的id
textureView.setId(View.generateViewId());
// 添加到约束布局的视图里面
subLayout.addView(textureView);
// 布局管理
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(subLayout);
int textureViewWidth = 100;
int textureViewHeight = 100;
// TODO: 2023/4/13 算法研究textureView的布局待定
// 这个TextureView的宽高
constraintSet.constrainWidth(textureView.getId(), textureViewWidth);
constraintSet.constrainHeight(textureView.getId(), textureViewHeight);
// 设置约束效果
constraintSet.connect(textureView.getId(), ConstraintSet.TOP, subLayout.getId(),ConstraintSet.TOP);
constraintSet.connect(textureView.getId(), ConstraintSet.BOTTOM, subLayout.getId(), ConstraintSet.BOTTOM);
if(i==0){
constraintSet.connect(textureView.getId(), ConstraintSet.START, subLayout.getId(),ConstraintSet.START);
}else {
constraintSet.connect(textureView.getId(), ConstraintSet.START, preTexture.getId(), ConstraintSet.END);
}
// TODO: 2023/4/16 还得考虑横竖屏的切换导致的View约束效果
constraintSet.applyTo(subLayout);// 将修改后的约束条件应用到布局中
preTexture = textureView;
}
}
/**
* 该方法保障subLayout 和 imageView
* 实际展示的图片大小完全重合
* 以保证横竖屏切换的情况下都是完全贴合的
*
* @param imageView imageview对象
* @param subLayout 约束布局
*/
private void makeConstraintLayoutSuitable(ImageView imageView, ConstraintLayout subLayout) {
// 获取ImageView的Drawable对象
Drawable drawable = imageView.getDrawable();
// 获取Drawable的大小
int drawableWidth = drawable.getBounds().width();
int drawableHeight = drawable.getBounds().height();
//获得ImageView中Image的变换矩阵
Matrix m = imageView.getImageMatrix();
float[] values = new float[10];
m.getValues(values);
//Image在绘制过程中的变换矩阵,从中获得x和y方向的缩放系数
float sx = values[0];
float sy = values[4];
// 刷新约束layout 布局大小
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) subLayout.getLayoutParams();
//计算Image在屏幕上实际绘制的宽高
layoutParams.width = (int) (drawableWidth * sx);
layoutParams.height = (int) (drawableHeight * sy);
// 当前的图片的展示宽高 数据刷新
realBgWidth = layoutParams.width;
realBgHeight = layoutParams.height;
subLayout.setLayoutParams(layoutParams);
}
}
视图文件:
<?xml version="1.0" encoding="utf-8"?>
<!-- fs 房间内的 中间显示框-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/fs_common_black">
<!--图片布局 竖屏:centerInside 横屏:fixXY -->
<ImageView
android:id="@+id/iv_room_bg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerInside"
android:src="@drawable/fs_pic_study_horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sub_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>