Recently, the company made a project, which requires that four-way video can be displayed. It would be very troublesome to use layout package. Later, under the leadership of the God, I learned that recycling view can be used,
The specific requirements are as follows
The specific requirements are as shown in the figure above. Next, recycle view is used to realize this function and directly upload the code
1. Recycleview inheritance class
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); //Initialize adapter if (!newCreated) { mVideoViewRecycleAdapter.init(streamViewMap); //The main purpose of the method in adapter is to calculate the height of each item } this.setAdapter(mVideoViewRecycleAdapter); int count = streamViewMap.size(); //streamViewMap is the collection with surfaceview passed from activity if (count < 2) { // Only local full view or with one peer this.setLayoutManager(new LinearLayoutManager(context, RecyclerView.VERTICAL, false)); } else if (count == 2 || count == 4) { //Layout when there are two-way or four-way videos // 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){ //Layout when there are three videos GridLayoutManager llmv; llmv = new GridLayoutManager(context,2, GridLayoutManager.VERTICAL, false); llmv.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { //Return value: number of cross columns if (position == 2){ return 2; } return 1; } }); this.setLayoutManager(llmv); } mVideoViewRecycleAdapter.notifyDataSetChanged(); }
The labels are very clear. Let's have a look
2. The following is adapter videoviewrecycleview adapter
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; /** * The main method of implementation is to initialize the layout * @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); } /** * The main task is to check whether the passed view has a parent class. If the parent class is not deleted, an error will be reported in addview * @param view */ protected final void stripSurfaceView(View view) { if(view == null){ return; } ViewParent parent = view.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(view); } } /** * Calculate the height of the item in the case of different videos * @param users Here, the collection streamview with surface view inherits the surface view */ 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; //Get the layout of each item through holder FrameLayout container; container = (FrameLayout) holderView.findViewById(R.id.video_view_container); if(mUsers.size()==2){ //Position display and display size of layout when there are two videos 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){ //Position display and display size of the third layout when there are three videos 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) { //When it is a one-way video, full screen display 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; } }The notes are clear,
3. Streamview is the encapsulation class of 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); }Surface view textview is added to display names
4.adapter layout
<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>
Finally, through the above main code, you can complete the requirements pull as shown in the figure above, hoping to help the partners