【Android项目开发】聊天功能-主界面设计(对标企业需求)

news2024/11/27 11:27:04

文章目录

    • 一、引言
    • 二、详细设计
      • 1、解决需求
        • (1)图形问题
        • (2)文本长度问题
        • (3)时间转换问题
      • 2、UI设计
        • (1)主界面
        • (2)适配器
      • 3、Adapter适配器
      • 4、测试参数
    • 三、附录
      • 1、源代码

一、引言

  • 描述:写一个聊天模块UI
  • 需求:
    1、将一个正方形 or 长方形的图片渲染成圆形图片,并且能保持原先的图片内容。
    2、预显示文本要自适应屏幕宽度,不能叠加成两行或者多行,多出的部分可以用"…"表示。
    3、近三天的时间转换为 “上午”、“下午”、“昨天”、“前天”。
  • 难度:初级
  • 知识点:
    1、简单的按需时间转换算法
    2、Adapter适配器的使用
    3、继承ImageView重写方法
  • 效果图

在这里插入图片描述

优化后的界面

在这里插入图片描述

二、详细设计

1、解决需求

(1)图形问题

创建一个类CircleImageView继承ImageView,通过Canvas进行图形操作

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class CircleImageView extends ImageView {

    private Paint paint; // 画笔

    private int radius; // 半径

    private float scale; // 缩放比例

    public CircleImageView(Context context) {
        super(context);
    }

    public CircleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 宽高保持一致
        int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
        radius = size / 2;
        setMeasuredDimension(size, size);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        paint = new Paint();
        Bitmap bitmap = drawableToBitmap(getDrawable());

        // 初始化BitmapShader
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        // 缩放比例
        scale = (radius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth());

        Matrix matrix = new Matrix();
        matrix.setScale(scale, scale);
        bitmapShader.setLocalMatrix(matrix);


        paint.setShader(bitmapShader);

        // 圆形操作 定中心点坐标、半径、画笔
        canvas.drawCircle(radius, radius, radius, paint);
    }

    /**
     * Drawable转 BitMap
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        }
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

}

(2)文本长度问题

	// 获取手机屏幕宽度,建议是放在主函数里,只计算一遍
	// WindowManager manager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
    // int width = manager.getDefaultDisplay().getWidth();
    
	/**
     * 防止文本过长,影响UI美观
     * @param text
     * @return
     */
    private String handleText(String text) {
        int endIndex = width/90;
        // 根据屏幕宽度,截取预显示文字
        String string = text;
        if (string.length() > endIndex) {
            string = string.substring(0, endIndex) + "...";
        }
        return string;
    }

(3)时间转换问题

	/**
     * 时间转换                  Calendar ca = Calendar.getInstance();
     * @param mYear 获取系统年   ca.get(Calendar.YEAR)
     * @param mMonth 获取系统月  ca.get(Calendar.MONTH) + 1
     * @param mDay 获取系统日    ca.get(Calendar.DAY_OF_MONTH)
     * @param time chat.getTime()
     * @return
     */
    private String algTime(int mYear, int mMonth, int mDay, String time) {
        String temp = "";
        // 切割时间 例:2023-6-2 9:00
        String[] times = time.split(" ");
        // 判断时间正确性
        if (times.length == 2) {
            String[] strings = times[0].split("\\-");
            int year = Integer.valueOf(strings[0]);
            int month = Integer.valueOf(strings[1]);
            int day = Integer.valueOf(strings[2]);
            // 如果日期为每月前两天,则需要欠上一个月
            int d;
            if (mMonth - month == 1 && mDay < 3) {
                d = mDay + algDay(year, month) - day;
            } else {
                d = mDay - day;
            }
            if (year == mYear && mMonth - month < 2  && d < 3) {  // 判断年月
                String[] mHour = times[1].split("\\:");
                switch (d) {
                    case 0:
                        if (Integer.valueOf(mHour[0]) <= 12) {
                            temp += "上午 " + times[1];
                        } else {
                            temp += "下午 " + (Integer.valueOf(mHour[0]) - 12) + ":" + mHour[1];
                        }
                        break;
                    case 1:
                        temp = "昨天 " + times[1];
                        break;
                    case 2:
                        temp = "前天 " + times[1];
                        break;
                    default:
                        break;
                }
            } else {
                temp = times[0];
            }
        }
        return temp;
    }

    /**
     * 计算:当前年月的天数
     * @param year
     * @param month
     * @return
     */
    private int algDay(int year, int month) {
        boolean isRunYear;
        int day = 0;
        // 判断是否为闰年
        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
            isRunYear = true;
        } else {
            isRunYear = false;
        }
        // 判断月份
        if (month > 0 && month < 13) {
            if (month != 2) {
                switch (month) {
                    case 1:
                    case 3:
                    case 5:
                    case 7:
                    case 8:
                    case 10:
                    case 12:
                        day = 31;
                        break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:
                        day = 30;
                        break;
                    default:
                        break;
                }
            } else {
                if (isRunYear) {
                    day = 29;
                } else {
                    day = 28;
                }
            }
        }
        return day;
    }

2、UI设计

(1)主界面

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:background="#F0F0F0">

        <com.hngy.xpq.chatdemo.view.CircleImageView
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_marginTop="20dp"
            android:src="@drawable/index"/>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="云端new守夜人"
                android:textSize="14dp"
                android:textStyle="bold"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:text="[5G在线]"
                android:textSize="12dp"/>

        </LinearLayout>

    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/chat"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:background="#FFF"/>

</LinearLayout>

(2)适配器

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:orientation="horizontal">

    <com.hngy.xpq.chatdemo.view.CircleImageView
        android:id="@+id/chatImage"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@drawable/c"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginLeft="20dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/chatName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20dp"
                android:textStyle="bold"
                android:text="学习群"/>

            <TextView
                android:id="@+id/chatTime"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="right"
                android:text="上午 9:00"/>

        </LinearLayout>

        <TextView
            android:id="@+id/chatText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="14dp"
            android:text="许...:小卷不是卷"/>

    </LinearLayout>

</LinearLayout>

3、Adapter适配器

老是将重复的代码粘贴进来,就显得博客很水,那么学习地址:http://t.csdn.cn/fdLea

4、测试参数

	private List<Chat> getListChat() {
        // 获取聊天数据(本地数据获取<推荐> or 服务器数据获取)
        // 图片按道理应该是String -> 存储地址 or Url -> 网络地址,这里使用drawable取代
        List<Chat> list = new ArrayList<>();
        Chat chat1 = new Chat((long) 1001, String.valueOf(R.drawable.d), "学习群", "许...:小卷不算卷", "2023-6-2 9:00");
        list.add(chat1);
        Chat chat2 = new Chat((long) 1002, String.valueOf(R.drawable.a), "Man", "Man:儿子,吃饭没?", "2023-6-2 18:00");
        list.add(chat2);
        Chat chat3 = new Chat((long) 1003, String.valueOf(R.drawable.b), "Dad", "Dad:过的怎么样,没人欺负你吧.有什么事,都和我说.", "2023-6-1 21:00");
        list.add(chat3);
        Chat chat4 = new Chat((long) 1004, String.valueOf(R.drawable.c), "导师", "收到", "2023-5-31 18:10");
        list.add(chat4);
        Chat chat5 = new Chat((long) 1005, String.valueOf(R.drawable.c), "导师2-测试时间计算", "收到", "2023-5-30 18:10");
        list.add(chat5);
        // 获取系统时间
        Calendar ca = Calendar.getInstance();
        int mYear = ca.get(Calendar.YEAR);
        int mMonth = ca.get(Calendar.MONTH) + 1;
        int mDay = ca.get(Calendar.DAY_OF_MONTH);
        // 处理数据
        for (Chat c:list) {
            c.setTime(algTime(mYear, mMonth, mDay, c.getTime()));
        }
        return list;
    }

三、附录

1、源代码

下载地址:https://download.csdn.net/download/weixin_48916759/87855518

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/601352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

龙芯2K1000实战开发-USB/PCIe/HDMI外设开发

文章目录 概要整体架构流程技术名词解释技术细节小结概要 提示:这里可以添加技术概要 本文主要针对2k1000的PCIE和USB外设的国产化设计 整体架构流程 提示:这里可以添加技术整体架构 使用2k1000自带的以太网pcie控制器,USB控制器。 考虑到龙芯没有HDMI接口,选用龙讯半…

从小白走向进阶:如何系统的学习it技术

无论是初学者还是有经验的专业人士&#xff0c;在学习一门新的IT技术时&#xff0c;都需要采取一种系统性的学习方法。那么作为一名技术er&#xff0c;你是如何系统的学习it技术的呢。 一、it技术介绍 1. Spring、SpringMVC、MyBatis、MyBatis-Plus、tkMapper&#xff0c;Spri…

分享一组真实的按钮

先看效果图&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>发光的按钮</title><style>* {border: 0;box-sizing: border-box;margin: 0;padding: 0;…

VMware ESXi 8.0U1a macOS Unlocker OEM BIOS (标准版和厂商定制版)

VMware ESXi 8.0 Update 1a macOS Unlocker & OEM BIOS (标准版和厂商定制版) ESXi 8.0U1 标准版&#xff0c;Dell HPE 联想 浪潮 定制版 请访问原文链接&#xff1a; https://sysin.org/blog/vmware-esxi-8-u1-oem/&#xff0c;查看最新版。原创作品&#xff0c;转载请保…

pytorch实战 -- 初窥张量

张量的创建 张量&#xff08;Tensors&#xff09;类似于NumPy的ndarrays&#xff0c;但张量可以在GPU上进行计算。 所以从本质上来说&#xff0c;PyTorch是一个处理张量的库。一个张量是一个数字、向量、矩阵或任何n维数组。 下面分别展示了0维张量到n位张量&#xff1a; im…

SVN客户端的下载和安装(图文超详细)

目录 0.准备工作 1.SVN客户端安装包安装 2.安装语言包 0.准备工作 博主安装环境&#xff1a;windows x86 SVN客户端下载地址&#xff1a;下载 SVN (tortoisesvn.net) 【下载地址中需下载符合电脑版本的安装包&#xff0c;以及语言包】 注&#xff1a;下载两个包后&#…

Linux操作系统相关介绍

目录 一、认识Linux 二、Linux特点总结 三、Linux版本 &#xff08;1&#xff09;Linux内核版 &#xff08;2&#xff09;Linux发行版 一、认识Linux • 1991年&#xff0c;芬兰的一名大学生Linus Torvalds开发了linux内核 • Linux是一种开放源代码的、自由的、免费的类…

Zookeeper面试这一篇就够了

谈下你对 Zookeeper 的认识&#xff1f; ZooKeeper 是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。 ZooKeeper 的目标就是封装好…

面试官:“你知道什么情况下 HTTPS 不安全么”

面试官&#xff1a;“HTTPS的加密过程你知道么&#xff1f;” 我&#xff1a;“那肯定知道啊。” 面试官&#xff1a;“那你知道什么情况下 HTTPS 不安全么” 我&#xff1a;“这....” 越面觉得自己越菜&#xff0c;继续努力学习&#xff01;&#xff01;&#xff01; 什麽是中…

STM32使用HAL库,串口收发一段时间后出错问题及解决

STM32使用HAL库&#xff0c;串口收发一段时间后出错 问题及解决方法问题1&#xff1a;串口溢出解决方法问题2&#xff1a;串口同时收发&#xff0c;一段时间后串口接收不工作解决办法 问题及解决方法 当STM32使用HAL库进行开发时&#xff0c;偶尔会遇到串口收发数据量大时&…

开源创新 协同融合|2023 开放原子全球开源峰会开源协作平台分论坛即将启幕

由开放原子开源基金会主办&#xff0c;阿里云、CSDN 等单位共同承办的开源协作平台分论坛即将于 6 月 12 日上午在北京经开区北人亦创国际会展中心隆重召开。作为 2023 开放原子全球开源峰会的重要组成部分&#xff0c;开源协作平台分论坛将聚焦于开源代码平台的创新功能、用户…

[Halcon3D] 3D重要算子及简单处理点云模型求高度示例讲解

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

(转载)基于粒子群算法的PID控制器优化设计(matlab实现)

1 理论基础 PID控制器应用广泛&#xff0c;其一般形式为 可见&#xff0c;PID控制器的性能取决于Kp、Ki、Kd这3个参数是否合理&#xff0c;因此&#xff0c;优化PID控制器参数具有重要意义。目前&#xff0c;PID控制器参数主要是人工调整&#xff0c;这种方法不仅费时&#x…

PaaS平台iuap——数智底座支撑企业的全球化业务拓展

数智化转型是全球化企业非常关注的话题&#xff0c;数智化转型过程中suo 面临的问题与挑战也绝非一套简单的产品能够解决的&#xff0c;必须配合组织、人员、目标制度采用达成目标。iuap平台是整个企业数智化转型的底座&#xff0c;形象来说是我们的土壤&#xff0c;在这个土壤…

借助Aspose.BarCode,轻松实现QR和众多二维码在线扫描

Aspose.BarCode for .NET 是一个功能强大的API&#xff0c;可以从任意角度生成和识别多种图像类型的一维和二维条形码。开发人员可以轻松添加条形码生成和识别功能&#xff0c;以及在.NET应用程序中将生成的条形码导出为高质量的图像格式。 Aspose API支持流行文件格式处理&am…

postgresql多行转一列,再转多列

问题&#xff1a;某个单据明细表需要加上两列审批人 已有数据&#xff1a;单据表&#xff0c;审批记录表 实现&#xff1a; select billno ,split_part(string_agg(checkman,,),,,1) checkman1 ,split_part(string_agg(checkman,,),,,2) checkman2 from ods_workflownote GR…

指数对数计算公式基础知识

指数对数实际中也是比较重要的公式

浅谈智能配电系统在水务行业的应用

关注acrelzxz 摘要&#xff1a;在构建智慧水务和“双碳”时代背景下&#xff0c;智能配电系统在水务行业中发挥日益突出的重要作用。本文首先回顾了智能配电系统在水务行业的发展历程&#xff0c;并对其应用现状进行了分析&#xff0c;进而展望了智能配电系统在水务行业的发展…

不要再犯这些错误了!电脑使用误区大揭秘

本文是向大家介绍关于电脑的误区&#xff0c;电脑是较为贵重的一个电子产品&#xff0c;熟练掌握基本的电脑操作技巧应该是每一个人必备技能。在使用技巧之外&#xff0c;有一些误区是需要我们知道并避免的。 误区一&#xff1a;重装系统对电脑损耗极大 重装系统的损耗和玩游戏…

基于java语言编写的爬虫程序

Java语言可以使用Jsoup、HttpClient等库进行网络爬虫开发&#xff0c;其中Jsoup提供了HTML解析和DOM操作的功能&#xff0c;HttpClient则提供了HTTP协议的支持。你可以通过使用这些库&#xff0c;构建网络爬虫程序来爬取指定网站的数据。需要注意的是&#xff0c;应该遵守网站的…