精华内容
下载资源
问答
  • 本篇文章主要介绍了angular实现IM聊天图片发送实例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 自定义ImageView IM聊天 图片圆角 三角角标 先看下效果图 矩形圆角大小可自行设置 三角角标可设置左右 上下距离可设置 三角的大小可看源码 着需求而定 这里采用的是BitmapShader,BitmapShader是Shader的...

    自定义ImageView IM聊天 图片圆角 三角角标

    先看下效果图

    这IMImageView效果图![描述](ss)


    矩形圆角大小可自行设置 三角角标可设置左右 上下距离可设置 三角的大小可看源码 着需求而定


    这里采用的是BitmapShader,BitmapShader是Shader的子类,Shader在三维软件中我们称之为着色器,所以通俗的理解,Shader的作用是给图像着色或者上色,BitmapShader允许我们载入一张图片来给图像着色,具体不做过多的解释,结尾贴出关于Shader的具体使用的文章

    所以其实根据上面对于BitmapShader的描述,其实就可以对圆角ImageView有一定的思路了吧,画一个圆角矩形,然后把本来画上去的图像着色到圆角矩形上,这样就实现了圆角的ImageView

    圆角核心代码

        //绘制圆角
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        float scale = 1.0f;
        if (!(bitmap.getWidth() == (getWidth() - cornerMarkWidth) && bitmap.getHeight() == getHeight())) {
            // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
            if (AngleLocation == 1 || AngleLocation == 2) {
                scale = Math.max((getWidth() - cornerMarkWidth) * 1.0f / bitmap.getWidth(),
                        getHeight() * 1.0f / bitmap.getHeight());
            } else {
                scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(),
                        getHeight() * 1.0f / bitmap.getHeight());
            }
        }
        // shader的变换矩阵,我们这里主要用于放大或者缩小
        mMatrix.setScale(scale, scale);
        // 设置变换矩阵
        mBitmapShader.setLocalMatrix(mMatrix);
        // 设置shader
        rectF.left = getPaddingLeft();
        rectF.right = getWidth() - getPaddingRight();
        if (AngleLocation == 1) {//画在左边
            rectF.left = getPaddingLeft() + cornerMarkWidth;
        } else if (AngleLocation == 2) {//右边
            rectF.right = getWidth() - getPaddingRight() - cornerMarkWidth;
        }
        rectF.top = getPaddingTop();
        rectF.bottom = getHeight() - getPaddingBottom();
        mPaint.setShader(mBitmapShader);
        canvas.drawRoundRect(rectF, circleRadius, circleRadius,
                mPaint);
    

    下面就是画三角 分在左边和右边 代码如下

         //绘制凸出三角
        if (AngleLocation == 2) {
            path.moveTo(rectF.right, rectF.top + mAnglePaddingTop);
            path.lineTo(rectF.right + cornerMarkWidth, rectF.top + mAnglePaddingTop + cornerMarkHeight / 2);
            path.lineTo(rectF.right, rectF.top + mAnglePaddingTop + cornerMarkHeight);
            path.lineTo(rectF.right, rectF.top + mAnglePaddingTop);
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mPaint.setAntiAlias(true);
            mPaint.setDither(true);
            canvas.drawPath(path, mPaint);
        } else if (AngleLocation == 1) {
            path.moveTo(rectF.left, rectF.top + mAnglePaddingTop);
            path.lineTo(rectF.left - cornerMarkWidth, rectF.top + mAnglePaddingTop + cornerMarkHeight / 2);
            path.lineTo(rectF.left, rectF.top + mAnglePaddingTop + cornerMarkHeight);
            path.lineTo(rectF.left, rectF.top + mAnglePaddingTop);
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mPaint.setAntiAlias(true);
            mPaint.setDither(true);
            canvas.drawPath(path, mPaint);
        }
    

    这是自定义的属性 也可以自己添加三角的高度 和宽度

         <declare-styleable name="IMImageView">
        <!--圆角半径-->
        <attr name="circleRadius" format="dimension" />
        <!--左边三角 还是右边三角-->
        <attr name="AngleLocation"  >
                <enum name="Left" value="1" />
                <enum name="Right" value="2" />
                <enum name="Gone" value="3" />
        </attr>
        <!--三角距离顶部位置-->
        <attr name="anglePaddingTop" format="dimension" />
    </declare-styleable>
    

    下面是全部的代码

    package xxx.xxxx.view.im;
    
    package xxx.xxxx.view.im;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.ColorDrawable;
    import android.graphics.drawable.Drawable;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    
    import xxxx.xxxx.xxxx.R;
    import xxxx.xxxx.xxxx.view.ImageView;
    
    /**
     * Created by ZhangYan on 2018/3/28.
     */
    
    public class IMImageView extends ImageView {
        private final Context context;
        private float mCircleRadius;
        // 1 左边 2 右边 3 隐藏
        private int AngleLocation;
        //三角距离自身顶部的距离
        private float mAnglePaddingTop;
        private Paint mPaint;
        private BitmapShader mBitmapShader;
        private Matrix mMatrix;
        private RectF mRectF;
        //角标的宽度
        private int mCornerMarkWidth = 20;
        //角标的高度
        private int mCornerMarkHeight = 40;
        private Path mPath;
    
        public IMImageView(Context context) {
            this(context, null);
        }
    
        public IMImageView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public IMImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.context = context;
            init(attrs);
        }
    
        private void init(AttributeSet attrs) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IMImageView);
            mCircleRadius = typedArray.getDimensionPixelOffset(R.styleable.IMImageView_circleRadius, 10);
            AngleLocation = typedArray.getInt(R.styleable.IMImageView_AngleLocation, 1);
            mAnglePaddingTop = typedArray.getDimension(R.styleable.IMImageView_anglePaddingTop, 10f);
            typedArray.recycle();
            mMatrix = new Matrix();
            mPaint = new Paint();
            mRectF = new RectF();
            mPaint.setAntiAlias(true);
            mPath = new Path();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if (AngleLocation == 1 || AngleLocation == 2) {
                int size = MeasureSpec.getSize(widthMeasureSpec);
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(size + mCornerMarkWidth, MeasureSpec.getMode(widthMeasureSpec));
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
        }
    
        @SuppressLint("DrawAllocation")
        @Override
        protected void onDraw(Canvas canvas) {
            Bitmap bitmap = drawableToBitmap(getDrawable());
            if (bitmap == null) {
                return;
            }
    
            //绘制圆角
            mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            float scale = 1.0f;
            if (!(bitmap.getWidth() == (getWidth() - mCornerMarkWidth) && bitmap.getHeight() == getHeight())) {
                // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
                if (AngleLocation == 1 || AngleLocation == 2) {
                    scale = Math.max((getWidth() - mCornerMarkWidth) * 1.0f / bitmap.getWidth(),
                            getHeight() * 1.0f / bitmap.getHeight());
                } else {
                    scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(),
                            getHeight() * 1.0f / bitmap.getHeight());
                }
            }
            // shader的变换矩阵,我们这里主要用于放大或者缩小
            mMatrix.setScale(scale, scale);
            // 设置变换矩阵
            mBitmapShader.setLocalMatrix(mMatrix);
            // 设置shader
            mRectF.left = getPaddingLeft();
            mRectF.right = getWidth() - getPaddingRight();
            if (AngleLocation == 1) {//画在左边
                mRectF.left = getPaddingLeft() + mCornerMarkWidth;
            } else if (AngleLocation == 2) {//右边
                mRectF.right = getWidth() - getPaddingRight() - mCornerMarkWidth;
            }
            mRectF.top = getPaddingTop();
            mRectF.bottom = getHeight() - getPaddingBottom();
            mPaint.setShader(mBitmapShader);
            canvas.drawRoundRect(mRectF, mCircleRadius, mCircleRadius,
                    mPaint);
            //绘制凸出三角
            if (AngleLocation == 2) {
                mPath.moveTo(mRectF.right, mRectF.top + mAnglePaddingTop);
                mPath.lineTo(mRectF.right + mCornerMarkWidth, mRectF.top + mAnglePaddingTop + mCornerMarkHeight / 2);
                mPath.lineTo(mRectF.right, mRectF.top + mAnglePaddingTop + mCornerMarkHeight);
                mPath.lineTo(mRectF.right, mRectF.top + mAnglePaddingTop);
                mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
                mPaint.setAntiAlias(true);
                mPaint.setDither(true);
                canvas.drawPath(mPath, mPaint);
            } else if (AngleLocation == 1) {
                mPath.moveTo(mRectF.left, mRectF.top + mAnglePaddingTop);
                mPath.lineTo(mRectF.left - mCornerMarkWidth, mRectF.top + mAnglePaddingTop + mCornerMarkHeight / 2);
                mPath.lineTo(mRectF.left, mRectF.top + mAnglePaddingTop + mCornerMarkHeight);
                mPath.lineTo(mRectF.left, mRectF.top + mAnglePaddingTop);
                mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
                mPaint.setAntiAlias(true);
                mPaint.setDither(true);
                canvas.drawPath(mPath, mPaint);
            }
    
        }
    
    
        private Bitmap drawableToBitmap(Drawable drawable) {
            if (drawable == null) {
                return null;
            } else if (drawable instanceof BitmapDrawable) {
                return ((BitmapDrawable) drawable).getBitmap();
            } else {
                try {
                    Bitmap bitmap;
                    if (drawable instanceof ColorDrawable) {
                        bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
                    } else {
                        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
                    }
    
                    Canvas canvas = new Canvas(bitmap);
                    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
                    drawable.draw(canvas);
                    return bitmap;
                } catch (Exception var4) {
                    var4.printStackTrace();
                    return null;
                }
            }
        }
    }
    

    使用


     <xxxx.xxxx.view.im.IMImageView
     xmlns:app="http://schemas.android.com/apk/res-auto"
                    android:id="@+id/image"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    app:anglePaddingTop="@dimen/x20"
                    app:AngleLocation="Right"
                    app:circleRadius="@dimen/x10"
                    android:src="@drawable/img_default_page"
                    android:scaleType="fitXY"
                    />
    

    展开全文
  • 朋友在问如何在IM即时通讯中实现发送图片视频语音和表情呢,今天小编通过本文给大家详细介绍下,感兴趣的朋友一起看看吧
  • IM聊天教程:发送图片/视频/语音/表情 经常有朋友问起,如何在IM即时通讯中实现发送图片、视频、语音和表情? 为此,小编特意写了一个vue版本的Demo,实现了图片视频文件和表情的的发送,参考这个Demo源代码,相信你...

    IM聊天教程:发送图片/视频/语音/表情

    经常有朋友问起,如何在IM即时通讯中实现发送图片、视频、语音和表情?

    为此,小编特意写了一个vue版本的Demo,实现了图片视频文件和表情的的发送,参考这个Demo源代码,相信你就可以轻松的用Uniapp和小程序完成类似的功能。

    本文的Demo全套的源码已经开源在码云上,供大家clone或者下载:https://gitee.com/goeasy-io/GoEasyDemo-vue-AudioPictureVideo

    一、图片/视频/语音发送

    对于语音、视频和图片的发送,您如果有注意的话,在使用QQ或者微信的时候,当有朋友发送图片和视频给您时,收到后,需要等一会儿才能显示出来。就是因为在发送的时候,只发送了文件的路径,您收到后,需要加载才能显示出来。因为当前主流的IM包括微信,QQ等对于图片和视频的发送,通常的做法都是:

    1. 上传文件到文件服务器
    2. 推送文件路径
    3. 收到文件路径
    4. 加载文件

    并不会通过网络直接传送源文件,因为对于大文件的传输,会影响消息的即时性。

    对于文件的上传,您可以选择直接上传到您自己的服务器,也可以选择上传到各种云服务的对象存储服务,也就是OSS上。

    参考源码:

    DemoService.prototype.sendFileMessage = function (type,content) {
        let uploadResult = restapi.uploadFile(content);
        let message = new Message(type, uploadResult.url);
        uploadResult.promise.then(() => {
            this.publish(message);
        },() =>  {
            var error = new Message(MessageType.TEXT, "文件上传失败.");
            this.messages.unshift(error)
        });
        return uploadResult.promise;
    };
    

    云服务的OSS具有更好的稳定性和高可用性,上传的速度也有保证,另外也可以和CDN配合,所以我们建议用GoEasy配合OSS服务来实现图片和视频的发送。

    在本文的源码里,选择了使用阿里云的OSS作为文件上传服务器,您也可以切换为您自己实现的文件上传服务器,或者选择其他云服务的OSS,原理都是一样的。

    二、发送表情

    表情的发送也是非常简单的,只是对于一些第一次实现表情发送的同学来说,需要一个思路而已。

    细心点的朋友,肯定有发现,当我们在QQ上聊天的时候,我们输入一个反斜杠+“cy”, 就像这样:/cy ,QQ就会立即显示为一个呲牙的表情,就像下图一样:

    image

    哈哈哈,相信你已经心里已经明白了十之八九了,对吧?

    没错,表情在发送的过程中其实就是发一个像“/cy”这样定义好的的字符串,在对方收到后“翻译”成表情而已。

    那为什么不直接发图片,而要进行这么复杂的“翻译”呢?

    因为字符串比图片更小,发送的速度更快,用户体验更好。一个系统中的用户成千上万,用字符串可以节约大量的带宽,节约系统资源。

    原理讲明白了,我们就开始干活儿吧:

    第一步、定义表情

    定义一个key value的对象,key作为表情标签,value则为每个表情标签对应的图片:

    let expressions = {
        "[risus]": './images/risus.png',
        "[kiss]": './images/kiss.png',
        "[cry]": './images/cry.png',
        "[die]": './images/die.png',
        "[anger]": './images/anger.png',
    }
    

    然后画一个表情选择的界面:

    image

    第二步、选择表情

    为每个图片的onclick事件中传入这个表情的字符串标签,当用户点击的时候,将表情的标签写入输入框,就成为了一个普通的字符串。在发送的时候,发送的其实就是这个表情的标签,也就是一个字符串。

    <div class="goeasy-expression">
                <div :class="[appearanceClass, 'goeasy-appearance']" @click="show = true">{{text}}</div>
                <div class="expression-container" v-show="show">
                    <div class="expression-icon-content">
                        <div class="expression-icon__item"
                             v-for="expression in list"
                             :key="expression.id"
                             @click="selectExpression(expression)">
                            <img :src="expressions[expression.tag]">
                        </div>
    
                    </div>
                    <div class="close-expression" @click="show = false"></div>
                </div>
            </div>
    

    第三步、收到表情和展示表情

    当对方收到一个字符串后,跟第一步定义的key-value列表去匹配,如果能找到对应的表情,就在页面上展示对应的表情图片,如果找不到,就是一个普通的文本信息。

    原理讲清楚了后,具体实现是不是很简单了?

    参考我们提供的Demo源代码,相信你很快就能掌握实现方法。

    Demo源码:https://gitee.com/goeasy-io/GoEasyDemo-vue-AudioPictureVideo

    GoEasy相关Demo推荐:

    1、uniapp版本的直播间聊天室(支持打包成安卓/ios的app、微信小程序):https://gitee.com/goeasy-io/GoEasyDemo-Uniapp-LiveChatRoom
    2、uniapp版本的即时通讯IM(支持打包成安卓/ios的app、微信小程序):https://gitee.com/goeasy-io/GoEasyDemo-Uniapp-IM-Chat
    3、微信小程序版本的即时通讯IM:https://gitee.com/goeasy-io/GoEasyDemo-wxapp-IM-Chat
    4、VUE版本的即时通讯IM:https://gitee.com/goeasy-io/GoEasyDemo-Vue-IM-Chat
    5、H5版本的即时通讯IM:https://gitee.com/goeasy-io/GoEasyDemo-IM-Chat
    6、H5版本的直播间聊天室:https://gitee.com/goeasy-io/GoEasyDemo-Live-Chatroom

    展开全文
  • 【本im与其它im的对比】:本im是支持微服务的,当然说im直接微服务是有点矛盾的,因为channel不能序列化,所以 应该是其它模块都是微服务,数据流服务是集群,当然并不只是简单的集群,例如channel原始池是独立的...

     

    后端涉及的框架和技术】:springboot2.1.1, dubbo2.7.3(很新),netty4,mysql,redis,mongodb,fastdfs,oauth2,swagger2,mybatis

    源码已经发布到码云上了,希望的童鞋去下载,有不足之处还望指正  

    https://gitee.com/wangdefu/angular_chat

    【前端涉及的技术和框架】:由于本人前端技术有限,app使用的是混合app,框架是ionic4(angualr版本)

    【本im与其它im的对比】:本im是支持微服务的,当然说im直接微服务是有点矛盾的,因为channel不能序列化,所以

    应该是其它模块都是微服务,数据流服务是集群,当然并不只是简单的集群,例如channel原始池是独立的,因为这个没法序列表,不过使用了redis记录每个用户所在的池子,就你使用微服务来参与进行数据与服务的调度,目前以及实现了如下功能

    添加好友,搜索好友,添加群,搜索群,单聊,群聊,处理加群与好友申请的请求,头像上传处理,获取好友列表,注册用户,短信发送(目前只集成了阿里大于的短信),。。。功能太多了,就不一一列举了,如果你喜欢自己查看源代码吧

    【性能与安全】:微服务框架采用的dubbo,因为我喜欢它的性能。聊天等不是很重要的内容都是存储到了mongodb数据库,其它的都存储到了mysql,聊天等实时性消息的拉取均走socket。部分高频率访问的数据会存储到redis,图片,视频等文件存储到fastdfs。

    【代码规范】:完全遵循restful风格,代码严格分层,新手也能看懂

     

     本源代码本来只是正对开发人员的,所以描述的都是关键信息,显得有些简略

    【源码地址】:码云地址:https://gitee.com/wangdefu/angular_chat

    【技术交流群】:604655085

    展开全文
  • IM多人聊天-群聊头像合成方法

    千次阅读 2017-06-12 16:01:50
    IM多人聊天群聊头像合成方法 群聊中将多个成员的头像合成为群聊的头像:ViewController.m // // ViewController.m // imageMerge // // Created by xxx on 2017/6/12. // Copyright © 2017年 xxxx. All rights ...

    IM多人聊天群聊头像合成方法

    群聊中将多个成员的头像合成为群聊的头像:

    这里写图片描述

    ViewController.m
    //
    //  ViewController.m
    //  imageMerge
    //
    //  Created by xxx on 2017/6/12.
    //  Copyright © 2017年 xxxx. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "UIImageView+imageMerge.h"
    
    #define DeviceWidth [UIScreen mainScreen].bounds.size.width
    #define DeviceHeight [UIScreen mainScreen].bounds.size.height
    
    const CGFloat iconViewSideLength = 100.0f;
    
    @interface ViewController ()
    {
        CGFloat _beginningY;
        CGFloat _iconViewMargin;
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        _iconViewMargin = (DeviceWidth - iconViewSideLength * 3) / 4;
        _beginningY = (DeviceHeight - iconViewSideLength * 3 - _iconViewMargin * 2) / 2;
    
        [self createiconViews];
    }
    
    - (void)createiconViews {
        int maxRow = 3;
        int maxColumn = 3;
    
        for (int i = 0; i < 8; i++)
        {
            int row = floor((float)i / maxRow);
            int column = i % maxColumn;
    
            CGFloat originX = _iconViewMargin + column * iconViewSideLength + column * _iconViewMargin;
            CGFloat originY = _beginningY + row * iconViewSideLength + row * _iconViewMargin;
    
            NSMutableArray * imageNamedArray = @[].mutableCopy;
            for (int loop = 1; loop <= i+1; loop++)
            {
                [imageNamedArray addObject:[NSString stringWithFormat:@"%d", loop]];
            }
    
            UIImageView * exampleView = [UIImageView imageViewMergedByImages:imageNamedArray imageViewRadius:0.5*iconViewSideLength ];
            exampleView.frame  = CGRectMake(originX, originY, iconViewSideLength, iconViewSideLength);
    
            [self.view addSubview:exampleView];
        }
    }
    
    @end
    //
    //  UIImageView+imageMerge.h
    //  imageMerge
    //
    //  Created by xxx on 2017/6/12.
    //  Copyright © 2017年 xxxx. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface UIImageView (imageMerge)
    
    
    /**
     *  多个image合并为一个图像视图
     *
     *  @param imageNames image数组
     *  @param radius   生成的UIImageView半径
     *
     *  @return <#return value description#>
     */
    + (UIImageView *)imageViewMergedByImages:(NSArray *)imageNames imageViewRadius:(CGFloat)radius;
    
    @end
    //
    //  UIImageView+imageMerge.m
    //  imageMerge
    //
    //  Created by xxx on 2017/6/12.
    //  Copyright © 2017年 xxxx. All rights reserved.
    //
    
    #import "UIImageView+imageMerge.h"
    
    #define kMaxCount   5
    
    @implementation UIImageView (imageMerge)
    
    + (UIImageView *)imageViewMergedByImages:(NSArray *)imageNames imageViewRadius:(CGFloat)radius
    {
        UIImageView * canvasView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 2*radius, 2*radius)];
        canvasView.layer.cornerRadius = radius;
        canvasView.layer.masksToBounds = YES;
        canvasView.backgroundColor = [UIColor colorWithWhite:0.839 alpha:1.000];
    
        NSArray * subImageViews = [self generatorSubViews:imageNames mageViewRadius:radius];
        for (UIImageView * subImageView in subImageViews)
        {
            [canvasView addSubview:subImageView];
        }
        return canvasView;
    }
    
    
    + (NSArray *)generatorSubViews:(NSArray *)imageNames mageViewRadius:(CGFloat)radius
    {
        NSInteger count = 0;
        NSMutableArray * subImageViews       = @[].mutableCopy;
        NSMutableArray * subImageViewOrigins = @[].mutableCopy;
    
        for (int loop = 0; loop < imageNames.count && loop < kMaxCount; loop++)
        {
            NSString * imageName = [imageNames objectAtIndex:loop];
            UIImageView * subImageView = [[UIImageView alloc]init];
    
            subImageView.image         = [UIImage imageNamed:imageName];
            [subImageViews addObject:subImageView];
        }
        count = subImageViews.count;
    
        CGFloat originX = 0;
        CGFloat originY = 0;
        CGFloat smallRadius = radius;
    
        switch (count)
        {
            case 1:
            {
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
                break;
            }
            case 2:
            {
                smallRadius = 0.6*radius;
                originX = radius-sqrt(2)*radius/2+sqrt(2)*smallRadius/2-smallRadius;
                originY = originX;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX += sqrt(2)*(radius-smallRadius);
                originY = originX;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
                break;
            }
            case 3:
            {
                smallRadius = 0.5*radius;
                originX = radius - smallRadius;
                originY = 0;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = radius - sqrt(3)*smallRadius/2 - smallRadius;
                originY = 1.5*smallRadius;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = radius + sqrt(3)*smallRadius/2-smallRadius;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
                break;
            }
            case 4:
            {
                smallRadius = radius*0.45;
                originX = 0.293*(radius-smallRadius);
                originY = originX;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = 1.707*(radius-smallRadius);
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = 0.293*(radius-smallRadius);
                originY = 1.707*(radius-smallRadius);
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = 1.707*(radius-smallRadius);
                originY = 1.707*(radius-smallRadius);
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
                break;
            }
            case 5:
            {
                smallRadius = radius*0.45;
                originX = radius - smallRadius;
                originY = 0;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = (radius - smallRadius)*0.049;
                originY = smallRadius;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = (radius - smallRadius)*1.951;
                originY = smallRadius;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = radius - (radius-smallRadius)*0.588-smallRadius;
                originY = radius + (radius-smallRadius)*0.809-smallRadius;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
    
                originX = radius + (radius-smallRadius)*0.588-smallRadius;
                originY = radius + (radius-smallRadius)*0.809-smallRadius;
                [subImageViewOrigins addObject:@{@"origin.x":@(originX),@"origin.y":@(originY)}];
                break;
            }
    
            default:
                break;
        }
    
        for (int loop = 0; loop < subImageViews.count; loop++)
        {
            UIImageView * imageView   = [subImageViews objectAtIndex:loop];
            NSDictionary * originDict = [subImageViewOrigins objectAtIndex:loop];
    
            imageView.frame = CGRectMake([originDict[@"origin.x"] floatValue], [originDict[@"origin.y"] floatValue], 2*smallRadius, 2*smallRadius);
            [[imageView layer] setCornerRadius:smallRadius];
            [[imageView layer] setMasksToBounds:YES];
        }
    
        return subImageViews;
    }
    @end
    
    

    注:最多合成5张图片,群聊中如成员多余5个,则只取前5个成员的头像去合成群聊的头像。

    展开全文
  • 项目里需要用到一个客服的功能,其实就是一个在线聊天,当时也是花了很多功夫,最后是借助三方平台融云的IM来实现的,不过当时时间很紧,于是没有将过程记录下来,正好最近接触到了云信,于是...
  • IM群聊头像九宫格实现方式

    千次阅读 2018-08-21 15:32:36
    最近一直在做与IM相关的项目,所以就遇到了群聊头像九宫格的问题。 具体思路: 1:创建ContentView:vi用来显示九宫格头像布局。 2:循环加载头像并添加到vi布局中。 3:在所有头像全部加载完成后开启画布...
  • Skype IM聊天源码

    2018-10-10 11:27:07
    IM即时通讯框架android端源码,实现了发送文本信息、图片信息、音频信息以及语音通话等功能
  • 最近几个月团队主要开发了基于websocket的网页版即时聊天IM系统,...IM.pngIM.pngIM即时消息分发,同时包含通讯录,可以进行文字、语音、图片、附件、小视频等聊天,功能基本参照网页版微信、QQ、钉钉这类的应用,...
  • react+redux仿微信聊天IM实战|react仿微信界面|react多人群聊天室 最近一直捣鼓react开发,就运用react开发了个仿微信聊天室reactChatRoom项目,基于react+react-dom+react-router-dom+redux+react-redux+webpack...
  • 独立IM:个性化定制,私有化部署,全功能,高并发,快速拥有自己的微信、QQ或陌陌。 I M+模块:Web、H5、App、小程序,简单几步实现消息、动态、点赞评论,变身社交+电商、资讯、游戏、支付、区块链…… 独立部署!...
  • 要实现一整套能用于大用户量、高并发场景下的IM群聊,技术难度远超IM系统中的其它功能,原因在于:IM群聊消息的实时写扩散特性带来了一系列技术难题。 举个例子:如一个2000人群里,一条普通消息的...
  • im修复腾讯通聊天窗口拖动滚动条时图片闪烁的BUG 使用方法:关闭腾讯通,直接解压到腾讯通安装目录即可. 其他情况下导致闪烁的问题暂未修正.
  • 评估了一下团队实力后,并不允许,所以找了一个IM第三方框架,采用的是腾讯云的即时通信IM,但是目前腾讯云IM的小程序SDK并没有包含UI,因此就需要自己来开发页面样式和交互,腾讯云IM自己也提供了一个demo,但是...
  • IM聊天系统

    2016-05-19 23:10:00
    大概功能有,单点消息支持文本/图片/截图/音频/视频发送直接播放/视频聊天/大文件传输/动态自定义表情等。持久化群成员,功能支持会议视频和上述消息功能。辅助功能有,签名头像皮肤广播消息等等。由于时间问题不...
  • 最近在做一个IM聊天项目,这个项目中也涉及到了发送表情的需求,就正好去研究了一下正则表达式,现在就和大家分享一下一个简单的demo(若有更简便的方法,希望各位朋友能指点哈) 先看看效果图 1、自己新建一个用于...
  • 最近因为项目中用到了IM聊天的功能,由于项目中并不准备集成第三方的sdk ,所以就自己写了一个ui界面来实现消息发送接收。大家如果需要的话直接移到自己的项目中就行,先展示一下实现的效果,然后再简单介绍一下怎么实现...
  • 高仿qq IM通讯源码,包括聊天,发现附近好友,发送表情图片等,放心下载,直接运行。
  • IM传送文件图片备份实现功能:设置策略的客户端使用常用聊天工具发送的文件和图片(不包含表情)都会备份到服务器,可以在文档操作日志中进行查看 IM传送文件图片禁止实现功能:设置策略的客户端无法使用常用聊天...
  • 高度可定制化 IM聊天界面设计

    千次阅读 2017-02-17 22:48:49
    IM聊天,对接其他部门业务需求,老是接到需要定制个性化的消息UI,接手这块业务时候,以前的代码写的比较死,怎么样的呢: 一个BaseAdapter,包含2个view (RecvView,SendView)每次来消息不断的根据消息类型去...
  • Go-im实现 简单介绍 一个即时通讯服务器,基于WebSocket协议,使用Golang语言完成, 实现功能 登录注册 验证码 上传文件 单聊/群聊 文字、表情、图片、语音等消息推送 添加好友 创建群/加入群 七牛云对象存储 消息持久...
  • 1、前言群聊已经成为主流IM软件的基本功能,不管是QQ群、还是微信群,一个群友在群内发了一条消息,那么对于IM服务器来说需要保证:在线的群友能第一时间收到消息;离线的群友能在登陆后收到消息。由于“消息风暴...
  • 抛弃SDK,采用http的方式封装的极光IM聊天,整个工具体积特别小,并且在项目中可以减少极光jar的冲突及依赖,因为是http请求,所以可扩展性极强,代码简洁
  • 作者KKKiller,源码IMInputView,IM即时通讯聊天输入框,包括自增长输入框,插入图片,输入语音等模块,可自我拓展表情等其他模块。
  • 【需要源代码请加个人微信】:xw2876001800提供前端和... ... 【升级版本】:整合ionic4,支持打包双系统app,接近原生app的体验,真正的即时通讯,真正的聊天app,对任何想整合im的项目都是零侵入性,随时随地整合聊天...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,945
精华内容 3,978
关键字:

im群聊图片