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

news/2025/2/23 4:46:31/

文章目录

    • 一、引言
    • 二、详细设计
      • 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);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 宽高保持一致int size = Math.min(getMeasuredWidth(), getMeasuredHeight());radius = size / 2;setMeasuredDimension(size, size);}@Overrideprotected void onDraw(Canvas canvas) {paint = new Paint();Bitmap bitmap = drawableToBitmap(getDrawable());// 初始化BitmapShaderBitmapShader 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:00String[] 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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:padding="10dp"android:background="#F0F0F0"><com.hngy.xpq.chatdemo.view.CircleImageViewandroid:layout_width="45dp"android:layout_height="45dp"android:layout_marginTop="20dp"android:src="@drawable/index"/><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:layout_marginTop="20dp"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="云端new守夜人"android:textSize="14dp"android:textStyle="bold"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:text="[5G在线]"android:textSize="12dp"/></LinearLayout></LinearLayout><androidx.recyclerview.widget.RecyclerViewandroid: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"?>
<LinearLayoutxmlns: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.CircleImageViewandroid:id="@+id/chatImage"android:layout_width="60dp"android:layout_height="60dp"android:src="@drawable/c"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:layout_marginLeft="20dp"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/chatName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20dp"android:textStyle="bold"android:text="学习群"/><TextViewandroid:id="@+id/chatTime"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="right"android:text="上午 9:00"/></LinearLayout><TextViewandroid: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.ppmy.cn/news/168485.html

相关文章

自带显示大屏 富士通ScanSnap iX1500扫描仪初体验

【试用】对于大部分人来说&#xff0c;日常办公所需要的打印&#xff0c;扫描和复印工作都由一台具备多种功能的复合机所代替。但是像这种配置的复合机器应付普通办公的扫描需求确实绰绰有余了&#xff0c;但是当遇到对于扫描作业需求较高的场合&#xff0c;这些复合机设备所搭…

富士通员工待遇

我就是从bfs出来的&#xff0c;也算个老员工了&#xff0c;和一般的小公司比&#xff0c;bfs还比较正规。不过期望不要太高&#xff0c;没什么发展前途~如果只想求份稳定的工作还是不错的~ 总的说来有如下特点&#xff1a; 1&#xff0c;待遇&#xff0c;以前不高&#xff0c;现…

Fujitsu(富士通)扫描仪——fi-6130z 无感安装设置

前言 众所周知&#xff0c;富士通的扫描仪是真的好用。但是设置也比较繁琐。不富裕的我&#xff0c;弄了一个二手的fi-6130z,在网上找了一圈&#xff0c;发现没有相关的设置教程&#xff0c;无奈只能官网找客服。冷淡的客服并没有完全的解答我的疑问&#xff0c;且态度不是很好…

基于AT89C51单片机的6位电子密码锁详细设计

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87855657?spm=1001.2014.3001.5503 源码获取 目录 1绪论 1 1.1 课题背景 1 1.2 课题设计目标 1 2系统方案论证 2 2.1 主控部分的选择 2 2.2 密码输入方式的选择 2 3 系统总体…

关于人力资源管理职能,你需要知道的事

每个成功的企业都有一个称职的人力资源部门。它是任何企业的重要组成部分&#xff0c;是员工和管理层之间的纽带。人力资源涵盖影响组织人员的所有任务&#xff0c;从基本的人力资源活动到战略决策。 对于任何希望可持续发展的企业来说&#xff0c;人力资源管理职能的重要性不…

jwt加密权限验证

1.pom文件 <!--JWT依赖--> <dependency><groupId>com.nimbusds</groupId><artifactId>nimbus-jose-jwt</artifactId><version>6.0</version> </dependency> 2.在工具类中设置密钥和有效时间 //密钥 private static f…

AUTOSAR架构介绍

简介 AUTOSAR&#xff08;AUTomotive Open System ARchitecture&#xff09;是一种面向汽车电子系统的软件架构标准。AUTOSAR为汽车电子系统提供一种开放式的软件架构标准&#xff0c;以促进汽车电子系统的可重用性、互操作性和可扩展性。它包括一系列的规范和标准&#xff0c…

100W无线电耦合功率测试实验

▌01 无线功率耦合 根据 Experimental Test on a Contactless Power Transfer System 中的方案&#xff0c;进行无线功率发送实验。 ⊙ 耦合线圈 1. 无线节能线圈参数以及相互之间耦合初步测试   2. 有互感的电感的串并联   3. 多股Litz线制作无线耦合线圈测试 ⊙ 高频功…