项目中遇到动态添加view的需求来展示视频画面,整理一下供大家参考
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.cloudroom.cloudroomvideosdk.model.UsrVideoId;
import com.example.chenhe.s.sxlivevideo.R;
import com.example.chenhe.s.sxlivevideo.common.Constants;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author:chenhe
* @date:2019/4/1 11:44
*/
public class FlowTagLayout extends ViewGroup {
private Context context;
private Rect[] childrenBounds;
// private List<String> tagList;
private ArrayList<UsrVideoId> tagWatchableVideos;
ArrayList<UsrVideoId> WatchVideos = new ArrayList<UsrVideoId>();//所有视频流
private OnTagClickListener onTagClickListener;
private int defaultColor = Color.parseColor("#666666");
private float defaultTextSize = Utils.spToPx(16);
/****************** 自定义的Attribute *************************/
private int leftMargin;
private int rightMargin;
private int topMargin;
private int bottomMargin;
private int leftPadding;
private int rightPadding;
private int topPadding;
private int bottomPadding;
private int background;
private int textColor;
private float textSize;
public static int devd_w = 2;
public static int devd_h = 1;
private int screenWidth;
private int screenHeight;
public FlowTagLayout(Context context) {
super(context);
this.context = context;
}
public FlowTagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
getScreenSize();
}
public FlowTagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init(attrs);
getScreenSize();
}
private void init(AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowTagLayout);
leftMargin = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_leftMargin, 0);
rightMargin = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_rightMargin, 0);
topMargin = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_topMargin, 0);
bottomMargin = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_bottomMargin, 0);
leftPadding = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_leftPadding, 0);
rightPadding = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_rightPadding, 0);
topPadding = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_topPadding, 0);
bottomPadding = (int) typedArray.getDimension(R.styleable.FlowTagLayout_item_bottomPadding, 0);
background = typedArray.getResourceId(R.styleable.FlowTagLayout_item_background, -1);
textColor = typedArray.getColor(R.styleable.FlowTagLayout_item_textColor, defaultColor);
textSize = typedArray.getDimension(R.styleable.FlowTagLayout_item_textSize, defaultTextSize);
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.e("onMeasure", "----------------------onMeasure----------------------");
// 已用宽度
int widthUsed = getPaddingLeft();
// 已用高度
int heightUsed = getPaddingTop();
// 当前行高度
int lineHeight = 0;
// 当前行宽度
int lineWidth = 0;
// 本身的宽度
int parentWidth = 0;
// 本身的高度
int parentHeight = 0;
int specWidth = MeasureSpec.getSize(widthMeasureSpec);
int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int specHeight = MeasureSpec.getSize(heightMeasureSpec);
int specHeightMode = MeasureSpec.getMode(heightMeasureSpec);
int childCount = getChildCount();
if (childrenBounds == null) {
childrenBounds = new Rect[childCount];
} else if (childrenBounds.length < childCount) {
childrenBounds = Arrays.copyOf(childrenBounds, childCount);
}
// 遍历测量子view
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
// 测量子view
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 判断是否换行(1.不是非限制 2.已用宽度+当前添加的child宽度+左右的padding 是否大于 父view自身测量的宽度)
int paddingRight = getPaddingRight();
Log.e("20000", paddingRight + ":padding");
Log.e("20000", lp.leftMargin + ":margin");
Log.e("20000", child.getMeasuredWidth() + ":width");
widthUsed = widthUsed + leftMargin;
if (specWidthMode != MeasureSpec.UNSPECIFIED &&
widthUsed + child.getMeasuredWidth() + rightMargin + getPaddingRight() > specWidth) {
parentWidth = specWidth;
widthUsed = getPaddingLeft() + leftMargin;
// 已用高度 = 之前已用高度 + 当前行的高度
heightUsed = heightUsed + lineHeight;
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
Rect childBounds = childrenBounds[i];
if (childBounds == null) {
childBounds = childrenBounds[i] = new Rect();
}
lineHeight = child.getMeasuredHeight() + topMargin + bottomMargin;
// 保存child的位置
childBounds.set(widthUsed, heightUsed + topMargin,
widthUsed + child.getMeasuredWidth(), heightUsed + lineHeight - bottomMargin);
// 当前行已用的宽度
widthUsed = widthUsed + child.getMeasuredWidth() + rightMargin;
}
// 因为换行parentWidth会被赋值,所以需要对比是否换行过赋值后的parentWidth
parentWidth = Math.max(parentWidth, widthUsed + getPaddingRight());
// 父view的高度 = 已用高度 + 当前行高度 + padding
parentHeight = heightUsed + lineHeight + getPaddingBottom();
setMeasuredDimension(resolveSizeAndState(parentWidth, widthMeasureSpec, 0),
resolveSizeAndState(parentHeight, heightMeasureSpec, 0));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
Rect childRect = childrenBounds[i];
view.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
/**
* 必须重写这个方法,不然measure的时候child.getLayoutParams()的margin是为0的
*
* @param attrs
* @return
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
/**
* 添加tags列表
*
* @param watchableVideos
*/
public void addTags(ArrayList<UsrVideoId> watchableVideos) {
Log.e("调试方法","--------addTags"+watchableVideos.toString());
if (tagWatchableVideos == null) {
tagWatchableVideos = new ArrayList<>();
} else {
tagWatchableVideos.clear();
}
// tagWatchableVideos.addAll(watchableVideos);
tagWatchableVideos = watchableVideos;
setDevid(tagWatchableVideos.size());
loadTagView();
}
/**
* 添加控件数量
*
* @param num
*/
public void addTags(int num) {
Log.e("视频","addTags(int num)");
setDevid(num);
for (int i = 0; i < num; i++) {
setTagContent(i);
}
}
/**
* 初始化tags
*/
private void setDevid(int num) {
switch (num) {
case 0:
case 1:
devd_w = 1;
devd_h = 1;
break;
case 2:
devd_w = 2;
devd_h = 1;
break;
case 3:
devd_w = 2;
devd_h = 2;
break;
case 4:
devd_w = 2;
devd_h = 2;
break;
case 5:
devd_w = 3;
devd_h = 2;
break;
case 6:
devd_w = 3;
devd_h = 2;
break;
case 7:
devd_w = 4;
devd_h = 2;
break;
case 8:
devd_w = 4;
devd_h = 2;
break;
case 9:
devd_w = 3;
devd_h = 3;
break;
default:
devd_w = 2;
devd_h = 2;
break;
}
}
/**
* 初始化tags
*/
private void loadTagView() {
removeAllViews();
Log.e("调试方法","--------loadTagView");
for (int i = 0; i < tagWatchableVideos.size(); i++) {
setTagContent(i);
}
}
/**
* 添加到尾部
*
* @param label
*/
public void addTag(UsrVideoId label) {
Log.e("调试方法","--------addTaglabel");
if (tagWatchableVideos == null) {
tagWatchableVideos = new ArrayList<>();
}
tagWatchableVideos.add(label);
setDevid(tagWatchableVideos.size());
setTagContent(tagWatchableVideos.size() - 1);
}
/**
* 添加到尾部
*
* @param videolist
*/
public void addTag( ArrayList<UsrVideoId> videolist,int pos) {
Log.e("视频","周到这李addtag");
WatchVideos.add(videolist.get(pos));
setDevid(WatchVideos.size());
setTagContent(WatchVideos.size() - 1);
// int childCount = getChildCount();
// WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// int screenWidth = wm.getDefaultDisplay().getWidth();
// int screenHeight = wm.getDefaultDisplay().getHeight();
//
// // 遍历测量子view
// for (int i = 0; i < childCount; i++) {
// View child = getChildAt(i);
// VideoView videoView = child.findViewById(R.id.item_videoview);
//
// LinearLayout.LayoutParams Params = (LinearLayout.LayoutParams) videoView.getLayoutParams();
// Params.height = (int) screenHeight / devd_h;
// Params.width = (int) screenWidth / devd_w;
// videoView.setLayoutParams(Params);
//
// if (i<=WatchVideos.size()){
// videoView.setUsrVideoId(WatchVideos.get(i));
// }
//
// }
}
/**
* 添加tag到index位置
*
* @param index
* @param label
*/
public void addTagOfIndex(int index, UsrVideoId label) {
if (tagWatchableVideos == null) {
tagWatchableVideos = new ArrayList<>();
}
tagWatchableVideos.add(index, label);
setTagOfIndex(index);
}
/**
* 初始化item
*
* @param i
*/
@SuppressLint("NewApi")
private void setTagContent(int i) {
Log.e("调试方法","--------setTagContent"+i);
LayoutInflater.from(context).inflate(R.layout.item_videoview, this);
// 这里用是getChildAt(index)获取子view,不能直接拿上面inflate的view
VideoView videoView = getChildAt(i).findViewById(R.id.item_videoview);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.setMargins(leftPadding, topPadding, rightPadding, bottomPadding);
lp.height = (int) screenHeight / devd_h;
lp.width = (int) screenWidth / devd_w;
videoView.setLayoutParams(lp);
Log.e("调试方法","--------setTagContent"+tagWatchableVideos.get(i).toString());
videoView.setUsrVideoId(tagWatchableVideos.get(i));
// textView.setText(tagList.get(i));
// textView.setTextColor(textColor);
// textView.setTextSize(Utils.pxToSp(textSize));
refreshTag(tagWatchableVideos.size());
final int finalI = i;
if (background == -1) {
getChildAt(i).setBackground(null);
} else {
getChildAt(i).setBackground(getResources().getDrawable(background));
}
getChildAt(i).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onTagClickListener != null) {
onTagClickListener.tagClick(finalI);
}
}
});
}
/**
* 添加到index位置,使用的是addView(view ,index),最后需要把点击事件重置
*
* @param i
*/
@SuppressLint("NewApi")
private void setTagOfIndex(int i) {
View view = LayoutInflater.from(context).inflate(R.layout.item_tag, null, false);
addView(view, i);
// 这里用是getChildAt(index)获取子view,不能直接拿上面inflate的view
// TextView textView = getChildAt(i).findViewById(R.id.item_text);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.setMargins(leftPadding, topPadding, rightPadding, bottomPadding);
// textView.setLayoutParams(lp);
// textView.setText(tagList.get(i));
// textView.setTextColor(textColor);
// textView.setTextSize(Utils.pxToSp(textSize));
if (background == -1) {
getChildAt(i).setBackground(null);
} else {
getChildAt(i).setBackground(getResources().getDrawable(background));
}
for (int j = 0; j < getChildCount(); j++) {
// 去掉点击事件
getChildAt(j).setOnClickListener(null);
final int finalJ = j;
// 重新绑定点击监听
getChildAt(j).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onTagClickListener != null) {
onTagClickListener.tagClick(finalJ);
}
}
});
}
}
/**
* 移除最后一个tag
*/
public void removeTag(int s) {
// getChildAt(tagList.size() - 1).setOnClickListener(null);
// removeViewAt(tagList.size() - 1);
// tagList.remove(tagList.size() - 1);
getChildAt(s).setOnClickListener(null);
removeViewAt(s);
tagWatchableVideos.remove(s);
}
/**
* 更新布局
*/
public void refreshTag(int num) {
Log.e("调试方法","refreshTag----->"+num);
setDevid(num);
// this.devd = devd;
int childCount = getChildCount();
int randomnum = (int) (Math.random() * 10) + 1;//0到10随机数
// 遍历测量子view
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
VideoView videoView = child.findViewById(R.id.item_videoview);
LinearLayout.LayoutParams Params = (LinearLayout.LayoutParams) videoView.getLayoutParams();
Params.height = (int) screenHeight / devd_h;
Params.width = (int) screenWidth / devd_w;
videoView.setLayoutParams(Params);
}
}
/**
* 更新布局
*/
public void refreshTag( ArrayList<UsrVideoId> watchableVideos ) {
setDevid(watchableVideos.size());
// this.devd = devd;
int childCount = getChildCount();
// 遍历测量子view
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
VideoView videoView = child.findViewById(R.id.item_videoview);
LinearLayout.LayoutParams Params = (LinearLayout.LayoutParams) videoView.getLayoutParams();
Params.height = (int) screenHeight / devd_h;
Params.width = (int) screenWidth / devd_w;
videoView.setLayoutParams(Params);
if (i<=watchableVideos.size()){
videoView.setUsrVideoId(watchableVideos.get(i));
}
}
}
/**
* 移除index位置的tag
*
* @param usrVideoId
*/
public void removeTagOfIndex(UsrVideoId usrVideoId) {
int index = 0;
for (int i = 0;i<tagWatchableVideos.size();i++){
if (tagWatchableVideos.get(i).userId.equals(usrVideoId.userId)){
index = i;
}
}
getChildAt(index).setOnClickListener(null);
removeViewAt(index);
tagWatchableVideos.remove(index);
for (int j = index; j < getChildCount(); j++) {
// 去掉点击事件
getChildAt(j).setOnClickListener(null);
final int finalJ = j;
// 重新绑定点击监听
getChildAt(j).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onTagClickListener != null) {
onTagClickListener.tagClick(finalJ);
}
}
});
}
refreshTag(tagWatchableVideos.size());
}
public void removeAllViews(){
for (int i = 0;i<tagWatchableVideos.size();i++){
getChildAt(i).setOnClickListener(null);
removeViewAt(i);
// tagWatchableVideos.remove(i);
for (int j = i; j < getChildCount(); j++) {
// 去掉点击事件
getChildAt(j).setOnClickListener(null);
final int finalJ = j;
// 重新绑定点击监听
getChildAt(j).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onTagClickListener != null) {
onTagClickListener.tagClick(finalJ);
}
}
});
}
}
tagWatchableVideos.clear();
refreshTag(0);
}
public void setTagClickListener(OnTagClickListener onTagClickListener) {
this.onTagClickListener = onTagClickListener;
}
public interface OnTagClickListener {
void tagClick(int position);
}
private void getScreenSize(){
DisplayMetrics dm = getResources().getDisplayMetrics();
screenHeight = dm.heightPixels;
screenWidth = dm.widthPixels;
int realScreenH = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {//获取真实高度 包括导航栏 状态栏
WindowManager wmManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wmManager.getDefaultDisplay().getRealMetrics(dm);
screenHeight = dm.heightPixels;
}
}
}
//新增
flowTagLayout.addTag(new_otherWatchVideos.get(i));
//移除
flowTagLayout.removeTagOfIndex(old_otherWatchVideos.get(j));
//删除所有
flowTagLayout.removeAllViews();
demo