Android仿微信多人音视频通话界面

工作中需要实现一个类似微信多人视频通话功能的界面,分别使用自定义viewgroup和自定义layoutManager的方式进行了实现。最终工作中采用了layoutManager,因为可以使用payload更新单个布局控件,效率更好。下面放出两种具体的实现效果代码。

1、使用自定义ViewGroup方式实现

下面是三个人通话时候的效果,其他的可以参考微信多人音视频通话界面。

package com.dnaer.android.telephone.widgets;

import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

import com.anbetter.log.MLog;

public class MultiVideoChatLayout extends ViewGroup implements CommLayoutAdapter.OnDataChangedListener {

 private CommLayoutAdapter mCommLayoutAdapter;

 private int mScreenWidth;

 //人数为2,3,4状态下的宽高度
 private int mSizeModel1;

 //人数为5,6,7,8,9状态下的宽高度
 private int mSizeModel2;

 public MultiVideoChatLayout(Context context) {
 this(context, null);
 }

 public MultiVideoChatLayout(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
 }

 public MultiVideoChatLayout(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 initialize(context);
 }

 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 public MultiVideoChatLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
 super(context, attrs, defStyleAttr, defStyleRes);
 initialize(context);
 }

 private void initialize(Context context) {
 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
 DisplayMetrics metrics = new DisplayMetrics();
 wm.getDefaultDisplay().getMetrics(metrics);
 mScreenWidth = metrics.widthPixels;

 mSizeModel1 = mScreenWidth / 2;
 mSizeModel2 = mScreenWidth / 3;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 //宽度默认给屏幕的宽度,高度直接取宽度,形成一个正方形
 final int width = MeasureSpec.makeMeasureSpec(mScreenWidth, MeasureSpec.EXACTLY);
 final int height = MeasureSpec.makeMeasureSpec(mScreenWidth, MeasureSpec.EXACTLY);
 setMeasuredDimension(width, height);

 MLog.d("width: " + width + ", height:" + height);

 final int childWidth = MeasureSpec.makeMeasureSpec(mScreenWidth / 3, MeasureSpec.EXACTLY);
 final int childHeight = MeasureSpec.makeMeasureSpec(mScreenWidth / 3, MeasureSpec.EXACTLY);

 final int childWidth2 = MeasureSpec.makeMeasureSpec(mScreenWidth / 2, MeasureSpec.EXACTLY);
 final int childHeight2 = MeasureSpec.makeMeasureSpec(mScreenWidth / 2, MeasureSpec.EXACTLY);

 if (getChildCount() > 4) {
  for (int i = 0; i < getChildCount(); i++) {
  View child = getChildAt(i);
  child.measure(childWidth, childHeight);
  }
 } else {
  for (int i = 0; i < getChildCount(); i++) {
  View child = getChildAt(i);
  child.measure(childWidth2, childHeight2);
  }
 }
 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 if (getChildCount() <= 4) {
  layoutModel1();
 } else {
  layoutModel2();
 }
 }

 private void layoutModel2() {
 int currentWidth = 0;
 for (int i = 0; i < getChildCount(); i++) {
  View item = getChildAt(i);
  if (i % 3 == 0) {
  currentWidth = 0;
  item.layout(0, i / 3 * mSizeModel2, mSizeModel2, i / 3 * mSizeModel2 + mSizeModel2);
  } else {
  item.layout(currentWidth + mSizeModel2, i / 3 * mSizeModel2, currentWidth + 2 * mSizeModel2, i / 3 * mSizeModel2 + mSizeModel2);
  currentWidth = currentWidth + mSizeModel2;
  }
 }
 }

 private void layoutModel1() {
 if (getChildCount() == 3) {
  for (int i = 0; i < getChildCount(); i++) {
  View item = getChildAt(i);
  MLog.d("width: " + item.getMeasuredWidth() + ", height: " + item.getMeasuredHeight() + ", mSizeModel1: " + mSizeModel1);
  if (i == 0) {
   item.layout(0, 0, mSizeModel1, mSizeModel1);
  } else if (i == 1) {
   item.layout(mSizeModel1, 0, mSizeModel1 * 2, mSizeModel1);
  } else if (i == 2) {
   item.layout(mSizeModel1 / 2, mSizeModel1, mSizeModel1 + mSizeModel1 / 2, mSizeModel1 * 2);
  }
  }
 } else {
  for (int i = 0; i < getChildCount(); i++) {
  View item = getChildAt(i);
  if (i % 2 == 0) {
   item.layout(0, i / 2 * mSizeModel1, mSizeModel1, i / 2 * mSizeModel1 + mSizeModel1);
  } else {
   item.layout(mSizeModel1, i / 2 * mSizeModel1, 2 * mSizeModel1, i / 2 * mSizeModel1 + mSizeModel1);
  }
  }
 }
 }

 public void setAdapter(CommLayoutAdapter adapter) {
 mCommLayoutAdapter = adapter;
 mCommLayoutAdapter.setOnDataChangedListener(this);
 changedAdapter();
 }

 @Override
 public void onChanged() {
 changedAdapter();
 }

 private void changedAdapter() {
 removeAllViews();
 CommLayoutAdapter layoutAdapter = mCommLayoutAdapter;
 for (int i = 0; i < layoutAdapter.getCount(); i++) {
  View view = layoutAdapter.getView(this, i, layoutAdapter.getItem(i));
  view.setDuplicateParentStateEnabled(true);
  addView(view);
 }
 }
}

2、使用自定义LayoutManager方式实现

package org.fireking.customgridlayoutmanager

import android.content.res.Resources
import android.support.v7.widget.RecyclerView
import java.lang.IllegalArgumentException

class MultiChatLayoutManager : RecyclerView.LayoutManager() {

 private var leftMargin = 0
 private var rightMargin = 0
 private var mScreenWidth = 0

 override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
 return RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT)
 }

 override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) {
 super.onLayoutChildren(recycler, state)
 if (itemCount == 0) {
  detachAndScrapAttachedViews(recycler!!)
  return
 }
 if (childCount == 0 && state!!.isPreLayout) {
  return
 }
 val params = recycler!!.getViewForPosition(0).layoutParams as RecyclerView.LayoutParams
 leftMargin = params.leftMargin
 rightMargin = params.rightMargin
 detachAndScrapAttachedViews(recycler)
 layoutItem(recycler)
 }

 private fun layoutItem(recycler: RecyclerView.Recycler) {

 if (itemCount > 9) {
  throw IllegalArgumentException("${javaClass.simpleName}最多支持9个item布局, 请检查你的item个数是否正确")
 }

 mScreenWidth = Resources.getSystem().displayMetrics.widthPixels

 val itemSize = if (itemCount > 4) {
  mScreenWidth / 3
 } else {
  mScreenWidth / 2
 }

 if (itemCount <= 4) {
  if (itemCount == 3) {
  for (i in 0 until itemCount) {
   val view = recycler.getViewForPosition(i)
   addView(view) // 因为detach过所以重新添加
   measureChildWithMargins(view, 0, 0)
   when (i) {
   0 -> layoutDecoratedWithMargins(view, 0, 0, itemSize, itemSize)
   1 -> layoutDecoratedWithMargins(view, itemSize, 0, itemSize * 2, itemSize)
   else -> layoutDecoratedWithMargins(
    view,
    itemSize / 2,
    itemSize,
    itemSize + itemSize / 2,
    itemSize * 2
   )
   }
  }
  } else {
  for (i in 0 until itemCount) {
   val view = recycler.getViewForPosition(i)
   addView(view) // 因为detach过所以重新添加
   measureChildWithMargins(view, 0, 0)
   if (i % 2 == 0) {
   layoutDecoratedWithMargins(view, 0, i / 2 * itemSize, itemSize, i / 2 * itemSize + itemSize)
   } else {
   layoutDecoratedWithMargins(
    view,
    itemSize,
    i / 2 * itemSize,
    2 * itemSize,
    i / 2 * itemSize + itemSize
   )
   }
  }
  }
 } else {
  var currentWidth = 0
  for (i in 0 until itemCount) {
  val view = recycler.getViewForPosition(i)
  addView(view) // 因为detach过所以重新添加
  measureChildWithMargins(view, 0, 0)
  if (i % 3 == 0) {
   currentWidth = 0
   layoutDecoratedWithMargins(view, 0, i / 3 * itemSize, itemSize, i / 3 * itemSize + itemSize)
  } else {
   layoutDecoratedWithMargins(
   view,
   currentWidth + itemSize,
   i / 3 * itemSize,
   currentWidth + 2 * itemSize,
   i / 3 * itemSize + itemSize
   )
   currentWidth += itemSize
  }
  }
 }
 }

 //因为这个布局不需要有滚动,所以直接将横竖两个方向的滚动全部取消了
 override fun canScrollHorizontally(): Boolean {
 return false
 }

 override fun canScrollVertically(): Boolean {
 return false
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android仿微信拍摄短视频

    近期做项目需要添加上传短视频功能,功能设置为类似于微信,点击开始拍摄,设置最长拍摄时间,经过研究最终实现了这个功能,下面就和大家分享一下,希望对你有帮助. 1.视频录制自定义控件: /** * 视频播放控件 */ public class MovieRecorderView extends LinearLayout implements OnErrorListener { private SurfaceView mSurfaceView; private SurfaceHolder mSurfa

  • Android仿微信录制小视频

    本文实例为大家分享了Android仿微信录制小视频的具体代码,供大家参考,具体内容如下 先上张图片看看效果 简单叙述下 首先通过Camera类调用系统相机 通过surfaceview绘制出来 通过MediaRecorder来录制视频 闪光灯 和 切换摄像头 需要重新配置Camera的参数 Camera预览界面画面拉升的原因是因为Surfaceview的大小与设定的比例不一致的 **本次版本更新了 切换前置摄像头录制视频问题 Android部分手机录制视频适配IOS手机问题 (OPPO手机部分不适

  • Android实现微信朋友圈发本地视频功能

    一.前言 前一篇文章已经详细介绍了如何使用Xposed框架编写第一个微信插件:摇骰子和猜拳作弊器 本文继续来介绍如何使用Xposed框架编写第二个微信插件,可以将本地小视频发布到朋友圈的功能.在这之前我们还是要有老套路,准备工作要做好,这里还是使用微信6.3.9版本进行操作,准备工作: 1.使用apktool工具进行反编译,微信没有做加固防护,所以这个版本的微信包反编译是没有任何问题的. 2.借助于可视化反编译工具Jadx打开微信包,后续几乎重要分析都是借助这个工具来操作的. 二.猜想与假设 做

  • Android 微信小视频录制功能实现详细介绍

    Android 微信小视频录制功能 开发之前 这几天接触了一下和视频相关的控件, 所以, 继之前的微信摇一摇, 我想到了来实现一下微信小视频录制的功能, 它的功能点比较多, 我每天都抽出点时间来写写, 说实话, 有些东西还是比较费劲, 希望大家认真看看, 说得不对的地方还请大家在评论中指正. 废话不多说, 进入正题. 开发环境 最近刚更新的, 没更新的小伙伴们抓紧了 Android Studio 2.2.2 JDK1.7 API 24 Gradle 2.2.2 相关知识点 视频录制界面 Surf

  • Android仿微信多人音视频通话界面

    工作中需要实现一个类似微信多人视频通话功能的界面,分别使用自定义viewgroup和自定义layoutManager的方式进行了实现.最终工作中采用了layoutManager,因为可以使用payload更新单个布局控件,效率更好.下面放出两种具体的实现效果代码. 1.使用自定义ViewGroup方式实现 下面是三个人通话时候的效果,其他的可以参考微信多人音视频通话界面. package com.dnaer.android.telephone.widgets; import android.co

  • Android仿微信之界面导航篇(1)

    微信是现在比较流行的应用了,在各大安卓市场几乎都是名列前茅了. 说实话不得不羡慕腾讯庞大的用户群体,只要腾讯敢做,就会有很多人去用. 废话不多说,下面就开始说一下如何实现微信的第一次启动程序的用户导航,ViewPager相信大家都不陌生了,是google放出的一个安卓低版本的兼容包android-support-v4.jar,里面有很多类我们可以去使用.那这个导航就是使用这个类来辅助完成的,在每一个View里放置一个图片,当我们使用ViewPager滑动界面的时候,就会看到一张张图片,从而实现这

  • Android 仿微信图像拍摄和选择界面功能(代码分享)

    插件运行后的画面如下: 下面这张图对图像进行筛选,根据照片产生的源头分(QQ和微信和相机) 点击某文件夹后,可以查看该文件夹下包含的所有的图片 图片选择界面 选中后就跳到已经选择界面的窗口,并且可以对该吃图片上传进行简要的描述 首先我想说明的是这个插件默认是不进行图片筛选的,打开app后会有几十个文件夹,但是个人认为开发中常用的图片基本都来自于QQ中拍摄的照片,微信中拍摄的照片,以及相机直接拍摄的照片,因此我对这个插件进行过滤以及文件夹名称的更改,具体做法,主要是对AlbumHelper类bui

  • Android仿微信主界面的实现方法

    本文实例为大家分享了Android模仿微信主界面展示的具体代码,供大家参考,具体内容如下 先看一下效果图 实现的原理: ViewPager+FragmentPagerAdapter 主界面可分为三部分: top标题栏就是一个TextView 中间的ViewPager作为显示的容器,填充Fragment bottom是一个RadioGroup 这里为了布局的优化,将top和bottom抽取出来 ,然后用include将其导入主布局,如下 <LinearLayout xmlns:android=&quo

  • Android仿微信公众号界面

    最近在做一个关于微信公众平台服务号的小项目,主要用来实现排队叫号功能.一直都对微信公众号开发比较好奇,于是趁这次机会仔细研究了一下公众号的开发流程和逻辑架构. 微信公众平台现在分为3类:订阅号,服务号和企业号.其中,服务号和企业号的开放权限比较高,可以实现自定义菜单功能,调用摄像头以及LBS等API. 基本通信架构如图: 在项目的功能设计阶段本想搭建一个服务号Demo用来展示,但微信服务号的认证手续太麻烦,而且我也没有那个资质去开通服务号.于是打算自己做一个仿微信公众号的基本界面,先实现菜单功能

  • Android仿微信语音对讲录音功能

    自微信出现以来取得了很好的成绩,语音对讲的实现更加方便了人与人之间的交流.今天来实践一下微信的语音对讲的录音实现,这个也比较容易实现.在此,我将该按钮封装成为一个控件,并通过策略模式的方式实现录音和界面的解耦合,以方便我们在实际情况中对录音方法的不同需求(例如想要实现wav格式的编码时我们也就不能再使用MediaRecorder,而只能使用AudioRecord进行处理). 效果图: 实现思路: 1.在微信中我们可以看到实现语音对讲的是通过点按按钮来完成的,因此在这里我选择重新自己的控件使其继承

  • Android仿微信底部菜单栏功能显示未读消息数量

    底部菜单栏很重要,我看了一下很多应用软件都是用了底部菜单栏,这里使用了tabhost做了一种通用的(就是可以像微信那样显示未读消息数量的,虽然之前也做过但是layout下的xml写的太臃肿,这里去掉了很多不必要的层,个人看起来还是不错的,所以贴出来方便以后使用). 先看一下做出来之后的效果: 以后使用的时候就可以换成自己项目的图片和字体了,主框架不用变哈哈, 首先是要布局layout下xml文件 main.xml: <?xml version="1.0" encoding=&qu

  • Android仿微信通讯录滑动快速定位功能

    先给大家展示下效果图: 实现代码如下: 下面简单说下实现原理. public class IndexBar extends LinearLayout implements View.OnTouchListener { private static final String[] INDEXES = new String[]{"#", "A", "B", "C", "D", "E", &qu

  • Android仿微信滑动退出Activity

    效果图: 原理: 原理一句话就能描述清楚.重写Activity的dispatchTouchEvent,滑动的时候拿到Activity栈中栈顶Activity的上一个Acticity的ContentView添加到栈顶Activity的DecorView中,滑动的过程中做视图平移,滑动结束之后把前面拿过来用的ContentView归还给上一个Activity,然后finish当前Activity. ActivityStack: 实现 Application.ActivityLifecycleCall

  • Android仿微信发朋友圈浏览图片效果

    先看一下效果吧: 下面就来说一下具体怎么实现的: 实现思路 1.首先我们要获取数据源,数据源就是我们的每条说说(包括姓名.标题.图片数组) 2.自定义适配器(ListView嵌套着GridView) 3.图片点击浏览图片(Fragment+ViewPager) 具体实现 1.初始化数据源,设置适配器,看一下代码: public class MyActivity extends Activity { /*图片显示列表*/ private ListView listView; /*图片URL数组*/

随机推荐