文章目录
- 一、引言
- 二、详细设计
- 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