最近公司做项目,要求可以显示四路视频,如果 用 布局包裹的话 那会非常麻烦,后来在大神的带领下,知道了用recycleview可以实现,
具体的需求如下
具体的需求如上图 接下来用recycleview实现此功能,直接上代码
1, recycleview 继承类
public class VideoViewContainer extends RecyclerView {
public VideoViewContainer(Context context) {
super(context);
}
public VideoViewContainer(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public VideoViewContainer(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private VideoViewRecycleAdapter mVideoViewRecycleAdapter;
private boolean initAdapter(LinkedHashMap<String, StreamView> users) {
if (mVideoViewRecycleAdapter == null) {
mVideoViewRecycleAdapter = new VideoViewRecycleAdapter(getContext(), users);
return true;
}
return false;
}
public void initViewContainer(Context context, LinkedHashMap<String, StreamView> streamViewMap) {
/* ArrayList<StreamView> list = new ArrayList<StreamView>();
Collection<StreamView> collection = streamViewMap.values();
Iterator<StreamView> iterator = collection.iterator();
while (iterator.hasNext()) {
StreamView value = (StreamView) iterator.next();
list.add(value);
}*/
boolean newCreated = initAdapter(streamViewMap); //初始化adapter
if (!newCreated) {
mVideoViewRecycleAdapter.init(streamViewMap); //adapter里面的方法 主要是为了计算出 每个item的高度
}
this.setAdapter(mVideoViewRecycleAdapter);
int count = streamViewMap.size(); //streamViewMap为activity传过来的 装有surfaceview的 集合
if (count < 2) { // only local full view or or with one peer 当有一路视频或者没有视频的时候 布局
this.setLayoutManager(new LinearLayoutManager(context, RecyclerView.VERTICAL, false));
} else if (count == 2 || count == 4) { //当有两路视频或者四路视频的时候布局
// this.setLayoutManager(new GridLayoutManager(context, 2, RecyclerView.VERTICAL, false));
GridLayoutManager llmv;
llmv = new GridLayoutManager(context,2, GridLayoutManager.VERTICAL, false);
this.setLayoutManager(llmv);
}else if(count == 3){ //当有三路视频时候布局
GridLayoutManager llmv;
llmv = new GridLayoutManager(context,2, GridLayoutManager.VERTICAL, false);
llmv.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
//返回值:跨列数
if (position == 2){
return 2;
}
return 1;
}
});
this.setLayoutManager(llmv);
}
mVideoViewRecycleAdapter.notifyDataSetChanged();
}
其中标注已经非常清楚了 大家可以看一下
2..下面是adapter videoviewRecycleviewAdapter
public class VideoViewRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected LayoutInflater mInflater;
protected Context mContext;
public ArrayList<StreamView> mUsers;
public VideoViewRecycleAdapter(Context context, LinkedHashMap<String, StreamView> users) {
mContext = context;
mInflater = ((Activity) context).getLayoutInflater();
mUsers = new ArrayList<>();
if(users != null) {
for (LinkedHashMap.Entry<String, StreamView> entry : users.entrySet()) {
mUsers.add(entry.getValue());
}
}
}
protected int mItemWidth = 0;
protected int mItemHeight = 0;
/**
* 实现的方法 主要是初始化布局
* @param parent
* @param viewType
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d("VideoViewAdapter", "--------------------onCreateViewHolder " + viewType);
View v = mInflater.inflate(R.layout.item_recycle_video, parent, false);
Log.d("VideoViewAdapter", "--------------------onCreateViewHolder mInflaterend");
Log.d("VideoViewAdapter", "--------------------onCreateViewHolder container end");
v.getLayoutParams().width = mItemWidth;
v.getLayoutParams().height = mItemHeight;
return new VideoHolder(v);
}
/**
* 主要是检查传过来的view 有没有父类,如果有父类的话不删除的话 在addview会报错
* @param view
*/
protected final void stripSurfaceView(View view) {
if(view == null){
return;
}
ViewParent parent = view.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(view);
}
}
/**
* 计算出在不同路视频的情况下 item的高度
* @param users 这里主要是 装有surfacaview 的集合 streamview继承了surfaceview
*/
public void init(LinkedHashMap<String, StreamView> users){
int beforeCount = mUsers.size();
mUsers.clear();
for (LinkedHashMap.Entry<String, StreamView> entry : users.entrySet()) {
mUsers.add(entry.getValue());
}
WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(outMetrics);
int count = mUsers.size();
int DividerX = 1;
int DividerY = 1;
if (count > 1) {
DividerX = 2;
DividerY = 2;
}
mItemWidth = outMetrics.widthPixels / DividerX;
mItemHeight = outMetrics.heightPixels / DividerY;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
VideoHolder myHolder = ((VideoHolder) holder);
final StreamView user = mUsers.get(position);
V2Log.d("VideoViewAdapter","-----------------------------onBindViewHolder " + position + " " + user + " " + myHolder + " " + myHolder.itemView);
LinearLayout holderView = (LinearLayout) myHolder.itemView; //通过holder 获得每个item 布局
FrameLayout container;
container = (FrameLayout) holderView.findViewById(R.id.video_view_container);
if(mUsers.size()==2){ //当有两路视频时 布局的位置显示和显示大小
LinearLayout.LayoutParams layoutParams
= new LinearLayout.LayoutParams(mItemWidth, mItemHeight);
// (FrameLayout.LayoutParams) holderView.getLayoutParams();
layoutParams.setMargins(0, mItemHeight/2,0 , mItemHeight/2);
holderView.setLayoutParams(layoutParams);
}else if(position==2 && mUsers.size()==3){ //当有三路视频时 第三路布局的位置显示 和显示大小
Log.e("VideoViewAdapter","-----------------------------onBindViewHolder " + position + " " + user + " " + myHolder + " " + myHolder.itemView);
LinearLayout.LayoutParams layoutParams
= new LinearLayout.LayoutParams(mItemWidth, mItemHeight);
// (FrameLayout.LayoutParams) holderView.getLayoutParams();
layoutParams.setMargins(mItemWidth/2, 0,mItemWidth/2 , 0);
holderView.setLayoutParams(layoutParams);
}
if (container.getChildCount() == 0 && user!=null) { //当为一路视频时,全屏显示
stripSurfaceView(user);
container.addView(user, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
@Override
public int getItemCount() {
Log.d("VideoViewAdapter", "getItemCount " + mUsers.size());
int sizeLimit = mUsers.size();
if (sizeLimit >= 4) {
sizeLimit = 4;
}
return sizeLimit;
}
}
里面的注释已经很清楚了 ,
3, streamview 为surfaceview的封装类
public class StreamView extends RelativeLayout {
TextView textView,textViewName;
SurfaceView surfaceView;
boolean isLocalStream;
public long getStreamId() {
return streamId;
}
public long getUserId() {
return userId;
}
long streamId;
long userId;
public StreamView(@NonNull Context context, SurfaceView view, boolean local,long streamId, long userId) {
super(context);
isLocalStream = local;
this.streamId = streamId;
this.userId = userId;
textView = new TextView(context);
textViewName = new TextView(context);
surfaceView = view;
initSurfaceView();
}
public StreamView(@NonNull Context context, @Nullable AttributeSet attrs, SurfaceView view, boolean local,long streamId, long userId) {
super(context, attrs);
this.addView(view);
isLocalStream = local;
this.streamId = streamId;
this.userId = userId;
surfaceView = view;
textView = new TextView(context);
textViewName = new TextView(context);
initSurfaceView();
}
public StreamView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, SurfaceView view, boolean local,long streamId, long userId) {
super(context, attrs, defStyleAttr);
this.addView(view);
isLocalStream = local;
this.streamId = streamId;
this.userId = userId;
surfaceView = view;
textView = new TextView(context);
textViewName = new TextView(context);
initSurfaceView();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public StreamView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, SurfaceView view, boolean local,long streamId, long userId) {
super(context, attrs, defStyleAttr, defStyleRes);
isLocalStream = local;
this.streamId = streamId;
this.userId = userId;
surfaceView = view;
textView = new TextView(context);
textViewName = new TextView(context);
initSurfaceView();
}
public void initSurfaceView(){
LayoutParams layoutParams
=new LayoutParams(LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
if(surfaceView!= null) {
surfaceView.setLayoutParams(layoutParams);
}
this.addView(surfaceView);
LayoutParams layoutParams2
=new LayoutParams(LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutParams2.setMarginEnd(10);
}
layoutParams2.rightMargin = 10;
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
// layoutParams.addRule(RelativeLayout.);
textView.setLayoutParams(layoutParams2);
this.addView(textView);
LayoutParams layoutParams1 = new LayoutParams(layoutParams.WRAP_CONTENT,layoutParams.WRAP_CONTENT);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams1.rightMargin = 10;
textViewName.setLayoutParams(layoutParams1);
this.addView(textViewName);
}
里面主要是 加入了surfaceview textview显示姓名等
4.adapter 布局
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/video_view_border"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<FrameLayout
android:layout_margin="2dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:layout_marginLeft="2dp"
android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
android:gravity="center"
android:id="@+id/video_view_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
</FrameLayout>
</LinearLayout>
最后 通过上面的主要代码,就可以完成如图上面的需求拉 ,希望可以帮助到小伙伴们