网站首页 编程语言 正文
近期公司要做报表功能,在网上搜索下表格的样式后便自己写了一个自定义的表格控件,该表格控件能根据设置的数据中数据的最大值自动设置左侧信息栏显示的值,使得条形图能尽量的充满控件,条形图部分支持左右滑动,数据的长度可能超过控件本身所能容纳的长度,所以在绘制的时候做了判断,当需要绘制的部分不再控件范围内则不进行绘制,具体请阅读代码,目前只支持一个名称对应一条数据,如有不足之处,大家提出帮忙修改
使用方法如下:
在xml文件中定义控件属性
在Activity中设置控件要显示的数据、设置显示的样式
public class MainActivity extends AppCompatActivity {
private MyChatView mMyChatView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyChatView = (MyChatView) this.findViewById(R.id.chatView);
mMyChatView.setData(getData1())
.setDataCompany("美元")
.setDataTitle("测试")
.setLeftTextColor(Color.RED)
.setBottomTextColor(Color.BLUE)
.setmDataTopTextColor(Color.RED) .setDataBackgroundColor(Color.parseColor("#ABCDEF"))
.setDataColor(Color.RED)
.setTitleColor(Color.BLUE)
.setLeftTextSize(12)
.setBottomTextSize(15)
.setDataTopTextSize(10)
.setTitleTextSize(20)
.setSpanBottomText(15);
}
/**
* 获取测试数据
* @return 测试用的数据
*/
private List自定义控件代码:
public class MyChatView extends View {
/** 数据集合中的 Map 集合存放信息的键 */
public static final String NAME = "name";
/** 数据集合中的 Map 集合存放数据的键 */
public static final String VALUE = "value";
/** 上下文 */
private Context mContext;
/** 控件的高度 */
private int mHeight;
/** 控件的宽度 */
private int mWidget;
/** 数据 */
private List> mData;
/** 数据单位 */
private String mDataCompany = "单位: ";
/** 底部表格名称 */
private String mDataTitle = null;
/** 底部信息栏文字的大小 */
private int mBottomTextSize;
/** 左侧等分信息栏文字的大小 */
private int mLeftTextSize;
/** 柱状图顶部文字的大小 */
private int mDataTopTextSize;
/** 表格标题文字大小 */
private int mTitleTextSize;
/** 左侧文字与数据区域的间隔 */
private int mSpanLeftText;
/** 柱状图顶部文字与柱状图的间隔 */
int mSpanDataTopText;
/** 底部信息字符串间隔 */
int mSpanBottomText;
/** 底部信息字符串与控件底部间隔 */
int mSpanBottom;
/** 绘制数据部分的背景颜色 */
private int mDataBackgroundColor = Color.WHITE;
/** 底部信息字符串颜色 */
private int mBottomTextColor = Color.BLACK;
/** 柱状图柱状部分颜色 */
private int mDataColor = Color.BLACK;
/** 左边信息栏文字颜色 */
private int mLeftTextColor = Color.BLACK;
/** 柱状图顶部文字颜色 */
private int mDataTopTextColor = Color.BLACK;
/** 标题颜色 */
private int mTitleColor = Color.BLACK;
/** 表格移动的位置 */
private int mChartMovedSize = 0;
/** 用户按下时 X 方向位置 */
private int mDownX = 0;
/** 用户松手是 X 方向位置 */
private int mUpX = 0;
/** 表格 X 方向移动的最大距离 */
private int mChartMaxMovedLengthX;
public MyChatView(Context context) {
this(context, null);
}
public MyChatView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyChatView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
this.mBottomTextSize = dpToPx(context, 15);
this.mLeftTextSize = dpToPx(context, 10);
this.mDataTopTextSize = dpToPx(context, 10);
this.mSpanLeftText = dpToPx(context, 2);
this.mSpanDataTopText = dpToPx(context, 3);
this.mSpanBottomText = dpToPx(context, 10);
this.mSpanBottom = dpToPx(context, 8);
this.mTitleTextSize = dpToPx(context, 20);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.mHeight = h;
this.mWidget = w;
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
mUpX = (int) event.getX();
shouldMoveChart();
break;
case MotionEvent.ACTION_UP:
mUpX = (int) event.getX();
shouldMoveChart();
break;
}
return true;
}
/**
* 判断移动的距离大于规定距离就移动表格
*/
private void shouldMoveChart (){
if (mChartMaxMovedLengthX=size || (mDownX-mUpX)>=size ) {
mChartMovedSize += (mUpX - mDownX);
mDownX = mUpX;
if (mChartMovedSize>=0) {
mChartMovedSize = 0;
}
if (mChartMovedSize<-mChartMaxMovedLengthX) {
mChartMovedSize = -mChartMaxMovedLengthX;
}
this.invalidate();
}
}
private InnerDraw innerDraw;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (innerDraw==null) {
innerDraw = new InnerDraw(canvas);
} else {
innerDraw.initData(canvas);
}
innerDraw.drawLeftMenue();
innerDraw.drawDataBackground();
innerDraw.drawDataTitle();
innerDraw.drawCompany();
int dataSize = mData.size();
for (int i=0; imaxLengthString.length()) {
maxLengthString = tempString;
}
}
if (maxLengthString.contains(".")) {
maxLengthString = maxLengthString.substring(0, maxLengthString.indexOf('.')+2);
}
return maxLengthString;
}
/**
* 获取最长信息字符串
* @return 底部信息栏中最长字符串
*/
private String getBottomMaxLegthString (){
int size = mData.size();
String maxString = "你好我好大家好才是真的好";
String tempString;
for (int i=0; itempString.length()) {
maxString = tempString;
}
}
return maxString;
}
/**
* 获取数据中的最大值
* @return 数据中的最大值 int float 类型
*/
private int getMaxValue (){
int maxValue = Float.valueOf(mData.get(0).get(VALUE)).intValue();
int tempValue;
for (Map map : mData) {
tempValue = Float.valueOf(map.get(VALUE)).intValue();
if (maxValue map : mData) {
xTotalLength += getTextWidth(paint, map.get(NAME), mBottomTextSize);
xTotalLength = xTotalLength + mSpanBottomText;
}
return xTotalLength;
}
/**
* 根据数据中的最大值将数据分成10等分,每等分为10的倍数
* @param maxValue 数据中的最大值
* @return 左侧等分栏的每一等分的数值 int 类型
*/
private int getPiceValue (int maxValue){
int piceValue = 1;
while (maxValue>10) {
maxValue = maxValue/10;
piceValue = piceValue * 10;
}
if (maxValue<=5) {
piceValue = piceValue / 2;
}
return piceValue;
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
* @param context 上下文
* @param dpValue 要转换的 dp 值
* @return 转换后的 px 值
*/
private int dpToPx(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 设置数据
* @param data 表中的数据
* data集合中的Map
* "name"存放名称,使用MyCharView中的常量 NAME
* "value"存放数据,数据为int类型的字符串,使用MyCharView中的常量 VALUE
*/
public MyChatView setData (List> data){
try {
this.mData = data==null? new ArrayList>():data;
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置绘制数据部分背景颜色
* @param dataBackgroundColor 16进制 int 类型颜色
* @return 对象本身
*/
public MyChatView setDataBackgroundColor (int dataBackgroundColor){
try {
this.mDataBackgroundColor = dataBackgroundColor;
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置左边信息栏文字颜色
* @param leftTextColor 16进制 int 类型颜色
* @return 对象本身
*/
public MyChatView setLeftTextColor (int leftTextColor){
try {
this.mLeftTextColor = leftTextColor;
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置底部信息文字颜色
* @param bottomTextColor 16进制 int 类型颜色
* @return 对象本身
*/
public MyChatView setBottomTextColor (int bottomTextColor){
try {
this.mBottomTextColor = bottomTextColor;
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置柱状条的背景颜色
* @param dataColor 16进制 int 类型颜色
* @return 对象本身
*/
public MyChatView setDataColor (int dataColor){
try {
this.mDataColor = dataColor;
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置柱状条顶部文字颜色
* @param dataTopTextColor 16进制 int 类型颜色
* @return 对象本身
*/
public MyChatView setmDataTopTextColor (int dataTopTextColor){
try {
this.mDataTopTextColor = dataTopTextColor;
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置标题颜色
* @param titleColor 颜色16进制的 int 类型
* @return 对象本身
*/
public MyChatView setTitleColor (int titleColor){
try {
this.mTitleColor = titleColor;
}catch (Exception e){
e.printStackTrace();
}
return this;
}
/**
* 设置底部信息文字大小
* @param bottomTextSize int 类型 dp
* @return 对象本身
*/
public MyChatView setBottomTextSize (int bottomTextSize){
try {
this.mBottomTextSize = dpToPx(mContext, bottomTextSize);
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置左侧信息文字大小
* @param leftTextSize int 类型 dp
* @return 对象本身
*/
public MyChatView setLeftTextSize (int leftTextSize){
try {
this.mLeftTextSize = dpToPx(mContext, leftTextSize);
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置柱状条顶部文字大小
* @param dataTopTextSize int 类型 dp
* @return 对象本身
*/
public MyChatView setDataTopTextSize (int dataTopTextSize){
try {
this.mDataTopTextSize = dpToPx(mContext, dataTopTextSize);
}catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置底部表格标题文字大小
* @param titleTextSize 标题文字大小
* @return 对象本身
*/
public MyChatView setTitleTextSize (int titleTextSize){
try {
this.mTitleTextSize = dpToPx(mContext,titleTextSize);
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置表格数据单位
* @param dataCompany 数据单位
* @return 对象本身
*/
public MyChatView setDataCompany (String dataCompany){
try {
this.mDataCompany += dataCompany==null? "空":dataCompany;
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置表格标题
* @param dataTitle 表格标题
* @return 对象本身
*/
public MyChatView setDataTitle (String dataTitle){
try {
mDataTitle = dataTitle;
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
/**
* 设置底部信息栏文字的间隔 默认为 10dp
* @param spanBottomText 间隔距离 dp
* @return 对象本身
*/
public MyChatView setSpanBottomText (int spanBottomText){
try {
this.mSpanBottomText = dpToPx(mContext, spanBottomText);
} catch (Exception e){
e.printStackTrace();
}
return this;
}
//****************************** 绘制表格的类 **********************************
/**
* 绘制控件界面的类
*/
private class InnerDraw{
private Canvas canvas;
private Paint paint;
/** 记录绘制到的位置 */
private int bottomTextPainted;
/** 底部标题高度 */
private int bottomTitleHeight;
/** 底部信息的高度 */
private int bottomMessageHeight;
/** 左侧信息栏最长文字 */
private String leftValueMaxString;
/** 表格中柱状图上文字高度 */
private int dataTopTextHeight;
/** 左侧信息栏宽度 */
private int leftValueWidth;
/** 绘制数据部分的高度 */
private int chartDataHeight;
/** 左侧等分栏的每等分高度 */
private int piceValueHeight;
/** 左侧等分栏每等分的数值 */
private int piceValue;
/** 柱状的宽度 */
private int dataBarWidth;
/** 底部信息 */
String bottomMessage;
/** 底部信息的宽度 */
private int bottomMessageWidth;
/** 数据信息 */
Map data;
/** 要绘制信息的位置 */
int index;
public InnerDraw (Canvas canvas){
initData(canvas);
}
/**
* 初始化数据
*/
public void initData(Canvas canvas){
this.canvas = canvas;
this.paint = new Paint();
this.bottomTitleHeight = mDataTitle==null? 0:(getTextHeight(paint, mTitleTextSize)+mSpanBottom/2);
this.bottomTextPainted = 0;
this.bottomMessageHeight = getTextHeight(paint, mBottomTextSize) + mSpanBottom + bottomTitleHeight;
this.leftValueMaxString = getLeftValueMaxString();
this.dataTopTextHeight = getTextHeight(paint, mDataTopTextSize);
this.leftValueWidth = getTextWidth(paint, leftValueMaxString , mLeftTextSize) + mSpanLeftText;
this.chartDataHeight = mHeight - bottomMessageHeight - mSpanBottom;
this.piceValueHeight = (chartDataHeight-dataTopTextHeight-getTextHeight(paint, mSpanDataTopText))/10;
this.piceValue = getPiceValue(getMaxValue());
this.dataBarWidth = getTextWidth(paint, getBottomMaxLegthString(), mBottomTextSize);
this.bottomMessage = "";
this.bottomMessageWidth = 0;
this.data = new HashMap<>();
this.index = 0;
}
/**
* 绘制左侧等分栏
*/
public void drawLeftMenue(){
paint.setColor(mLeftTextColor);
paint.setTextSize(mLeftTextSize);
int textLeft;
int textTop;
String valueStr;
int strLength;
String maxValueStr = String.valueOf(piceValue * 10);
int textMaxLength = maxValueStr.length();
int topTextHeight = getTextHeight(paint, mDataTopTextSize);
int leftTextHeight = getTextHeight(paint, mLeftTextSize);
for (int i=0; i<=10; i++) {
textLeft = 0;
valueStr = String.valueOf(i * piceValue);
strLength = valueStr.length();
if (strLength data, int index){
this.data = data;
this.bottomMessage = data.get(NAME).trim();
this.bottomMessageWidth = getTextWidth(paint, bottomMessage, mBottomTextSize);
this.index = index;
int bottomMsgBegainDrawX = (index + 1) * mSpanBottomText + mChartMovedSize + leftValueWidth + bottomTextPainted;
if ((bottomMsgBegainDrawX+bottomMessageWidth)mWidget) {//需要绘制的区域超出了控件的右边,结束绘制
return -1;
}
return 1;
}
/**
* 结束绘制
*/
public void endDrawBody(){
bottomTextPainted += bottomMessageWidth;
}
/**
* 绘制底部信息栏
*/
public void drawBottomMessage() {
paint.setColor(mBottomTextColor);
paint.setTextSize(mBottomTextSize);
int bottomLeft = (index + 1) * mSpanBottomText + mChartMovedSize + leftValueWidth + bottomTextPainted;
int bottomTop = chartDataHeight + bottomMessageHeight - bottomTitleHeight - dpToPx(mContext, 2);
if (bottomLeft >= leftValueWidth && bottomLeft < mWidget) {
canvas.drawText(bottomMessage, bottomLeft, bottomTop, paint);
} else if ((bottomLeft+bottomMessageWidth)>leftValueWidth) {
int index = (leftValueWidth-bottomLeft)*bottomMessage.length()/bottomMessageWidth+1;
if (index>=0 && index leftValueWidth) {
dataLeft = leftValueWidth;
}
Rect dataRect = new Rect(dataLeft, dataTop, dataRight, dataBottom);
if (dataRight>leftValueWidth) {
canvas.drawRect(dataRect, paint);
}
}
/**
* 绘制条形数据顶部文字
*/
public void drawDataTopText() {
String topTextMessage = data.get(VALUE);
int topTextWidth = getTextWidth(paint, topTextMessage, mDataTopTextSize);
paint.setColor(mDataTopTextColor);
paint.setTextSize(mDataTopTextSize);
int bottomLeft = (index + 1) * mSpanBottomText + mChartMovedSize + leftValueWidth + bottomTextPainted;
int topTextLeft = bottomLeft + (bottomMessageWidth - topTextWidth) / 2;
int dataValue = Float.valueOf(data.get(VALUE)).intValue();
int dataTop = chartDataHeight - (dataValue*(chartDataHeight-dataTopTextHeight - mSpanDataTopText)/piceValue)/10;
int topTextTop = dataTop - mSpanDataTopText * 2 / 3;
if (topTextLeft >= leftValueWidth && bottomLeft < mWidget) {
canvas.drawText(topTextMessage, topTextLeft, topTextTop, paint);
} else if ((topTextLeft+topTextWidth)>leftValueWidth) {
int index = (leftValueWidth-topTextLeft)*topTextMessage.length()/topTextWidth+1;
if (index>=0 && index 实现效果如下图

原文链接:https://blog.csdn.net/zhoujiulong90/article/details/51106658
相关推荐
- 2022-07-16 new FileOutputStream(“data\\test2.txt“,true),true是
- 2023-05-23 numpy中的transpose函数中具体使用方法_python
- 2023-03-26 react组件实例属性props实例详解_React
- 2023-08-01 TypeScript 中的字面量类型和联合类型特性
- 2022-10-08 Python使用plt.boxplot()函数绘制箱图、常用方法以及含义详解_python
- 2022-06-14 深入解析docker三种网络模式_docker
- 2022-12-05 Android开发InputManagerService创建与启动流程_Android
- 2022-04-04 react报错‘react-scripts‘ 不是内部或外部命令,也不是可运行的程序
- 最近更新
-
- window11 系统安装 yarn
- 超详细win安装深度学习环境2025年最新版(
- Linux 中运行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存储小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基础操作-- 运算符,流程控制 Flo
- 1. Int 和Integer 的区别,Jav
- spring @retryable不生效的一种
- Spring Security之认证信息的处理
- Spring Security之认证过滤器
- Spring Security概述快速入门
- Spring Security之配置体系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置权
- redisson分布式锁中waittime的设
- maven:解决release错误:Artif
- restTemplate使用总结
- Spring Security之安全异常处理
- MybatisPlus优雅实现加密?
- Spring ioc容器与Bean的生命周期。
- 【探索SpringCloud】服务发现-Nac
- Spring Security之基于HttpR
- Redis 底层数据结构-简单动态字符串(SD
- arthas操作spring被代理目标对象命令
- Spring中的单例模式应用详解
- 聊聊消息队列,发送消息的4种方式
- bootspring第三方资源配置管理
- GIT同步修改后的远程分支
