源:Android自定义控件(状态提示图表)

源:Android应用开发

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

1  背景

前面分析那么多系统源码了,也该暂停下来休息一下,趁昨晚闲着看见一个有意思的需求就操练一下分析源码后的实例演练—-自定义控件。

这个实例很适合新手入门自定义控件。先看下效果图:

横屏模式如下: 竖屏模式如下:

看见没有,这个控件完全自定义的,连文字等都是自定义的,没有任何图片等资源,就仅仅是一个小的java文件,这个界面只有一个控件。如下咱们看下实现代码。

!!!!!!! 下载Demo工程源码点击我

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

2 实例代码

如下就是整个工程的源码了。

自定义上面展示的控件AreaChartsView源码:

/**
* Author : yanbo
* Date : 2015-06-03
* Time : 09:22
* Description : 自定义区域描述图表View
*/
public class AreaChartsView extends View {
private Paint mPaint; private int[] mZeroPos = new int[2];
private int[] mMaxYPos = new int[2];
private int[] mMaxXPos = new int[2]; private int mWidth, mHight;
private int mRealWidth, mRealHight;
private String mTitleY, mTitleX; private ArrayList<Integer> mXLevel = new ArrayList<>();
private ArrayList<Integer> mYLevel = new ArrayList<>();
private ArrayList<String> mGridLevelText = new ArrayList<>();
private ArrayList<Integer> mGridColorLevel = new ArrayList<>();
private ArrayList<Integer> mGridTxtColorLevel = new ArrayList<>(); private int mGridLevel = mXLevel.size() - 1; //title字符大小
private int mXYTitleTextSize = 40; private int mMeasureXpos, mMeasureYpos; public AreaChartsView(Context context, AttributeSet attrs) {
super(context, attrs); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
} @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mWidth = getWidth();
mHight = getHeight();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); initPosition();
drawXYTitle(canvas);
drawXYLine(canvas);
drawContent(canvas);
} private void initPosition() {
//初始化坐标图的xy交点原点坐标
mZeroPos[0] = mXYTitleTextSize * 2;
mZeroPos[1] = mHight - mXYTitleTextSize * 4;
//初始化坐标图的X轴最大值坐标
mMaxXPos[0] = mWidth;
mMaxXPos[1] = mHight - mXYTitleTextSize * 4;
//初始化坐标图的Y轴最大值坐标
mMaxYPos[0] = mXYTitleTextSize * 2;
mMaxYPos[1] = mXYTitleTextSize * 2;
} private void drawXYTitle(Canvas canvas) {
mPaint.setColor(Color.parseColor("#1FB0E7"));
mPaint.setTextSize(mXYTitleTextSize);
mPaint.setTextAlign(Paint.Align.LEFT);
//画Y轴顶的title
canvas.drawText(mTitleY, mMaxYPos[0] - mXYTitleTextSize * 2, mMaxYPos[1] - mXYTitleTextSize, mPaint);
mPaint.setTextAlign(Paint.Align.RIGHT);
//画X轴顶的title
canvas.drawText(mTitleX, mMaxXPos[0], mMaxXPos[1] + mXYTitleTextSize * 2, mPaint);
} private void drawXYLine(Canvas canvas) {
mPaint.setColor(Color.DKGRAY);
mPaint.setTextAlign(Paint.Align.RIGHT);
//画XY轴
canvas.drawLine(mMaxYPos[0], mMaxYPos[1], mZeroPos[0], mZeroPos[1], mPaint);
canvas.drawLine(mZeroPos[0], mZeroPos[1], mMaxXPos[0], mMaxXPos[1], mPaint);
} private void drawContent(Canvas canvas) {
mGridLevel = mXLevel.size() - 1;
//计算出偏移title等显示尺标后的真实XY轴长度,便于接下来等分
mRealWidth = (mWidth - mXYTitleTextSize * 2);
mRealHight = (mHight - mXYTitleTextSize * 4);
//算出等分间距
int offsetX = mRealWidth/(mGridLevel);
int offsetY = mRealHight/(mGridLevel+1);
//循环绘制content
for (int index=0; index<mGridLevel+1; index++) {
mPaint.setColor(Color.DKGRAY);
mPaint.setTextAlign(Paint.Align.RIGHT);
mPaint.setTextSize(mXYTitleTextSize-5);
//绘制X轴的那些坐标区间点,包含0点坐标
canvas.drawText(String.valueOf(mXLevel.get(index)), mZeroPos[0]+(index*offsetX), mZeroPos[1] + mXYTitleTextSize, mPaint); if (index != 0) {
//绘制Y轴坐标区间点,不包含0点坐标,X轴已经画过了
canvas.drawText(String.valueOf(mYLevel.get(index)), mZeroPos[0], mZeroPos[1]-(index*offsetY), mPaint);
} if (index == mGridLevel) {
//坐标区间 = 真实区间 + 1
break;
} mPaint.setColor(mGridColorLevel.get(mGridLevel - 1 - index));
mPaint.setStyle(Paint.Style.FILL);
//绘制区间叠加图谱方块,从远到0坐标,因为小的图会覆盖大的图
canvas.drawRect(mMaxYPos[0], mMaxYPos[1] + index*offsetY, mMaxXPos[0]-index*offsetX, mMaxXPos[1], mPaint); mPaint.setColor(mGridTxtColorLevel.get(index));
mPaint.setTextAlign(Paint.Align.RIGHT);
mPaint.setTextSize(mXYTitleTextSize);
//绘制每个方块状态区间的提示文字
canvas.drawText(mGridLevelText.get(index), mMaxXPos[0] - index * offsetX - mXYTitleTextSize,
mMaxYPos[1] + index * offsetY + mXYTitleTextSize, mPaint);
}
//绘制当前坐标
drawNotice(canvas, offsetX, offsetY);
} private void drawNotice(Canvas canvas, int offsetX, int offsetY) {
int realPosX = 0;
int realPosY = 0;
//计算传入的x值与真实屏幕坐标的像素值的百分比差值转换
for (int index=0; index<mGridLevel; index++) {
if (mMeasureXpos >= mXLevel.get(index) && mMeasureXpos < mXLevel.get(index+1)) {
int subValue = mMeasureXpos - mXLevel.get(index);
int offset = mXLevel.get(index+1) - mXLevel.get(index);
realPosX = mZeroPos[0] + index*offsetX + (subValue / offset);
break;
}
}
//计算传入的y值与真实屏幕坐标的像素值的百分比差值转换
for (int index=0; index<mGridLevel; index++) {
if (mMeasureYpos >= mYLevel.get(index) && mMeasureYpos < mYLevel.get(index+1)) {
int subValue = mMeasureYpos - mYLevel.get(index);
int offset = mYLevel.get(index+1) - mYLevel.get(index);
realPosY = mZeroPos[1] - index*offsetY - (offsetY - (subValue / offset));
break;
}
}
//画我们传入的坐标点的标记小红点
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(realPosX, realPosY, 8, mPaint); int[] centerPos = {mZeroPos[0] + mRealWidth/2, mZeroPos[1] - mRealHight/2}; mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
RectF rectF = null;
Path path = new Path();
//画红点旁边的提示框和文字,有四个区域,然后提示框的小三角指标方位不同
if (realPosX <= centerPos[0] && realPosY >= centerPos[1]) {
//left-bottom
//画三角形
path.moveTo(realPosX+5, realPosY+5);
path.lineTo(realPosX+15, realPosY+15);
path.lineTo(realPosX+15, realPosY-15);
//画矩形背景
rectF = new RectF(realPosX+15, realPosY-40, realPosX+200, realPosY + 30);
canvas.drawRoundRect(rectF, 15, 15, mPaint);
//画提示框的文字
mPaint.reset();
mPaint.setColor(Color.RED);
mPaint.setTextSize(mXYTitleTextSize - 5);
canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX+30, realPosY, mPaint);
}
else if (realPosX <= centerPos[0] && realPosY < centerPos[1]) {
//left-top
path.moveTo(realPosX+5, realPosY+5);
path.lineTo(realPosX+15, realPosY+15);
path.lineTo(realPosX + 15, realPosY - 15); rectF = new RectF(realPosX+15, realPosY - 20, realPosX+200, realPosY + 50);
canvas.drawRoundRect(rectF, 15, 15, mPaint); mPaint.reset();
mPaint.setColor(Color.RED);
mPaint.setTextSize(mXYTitleTextSize - 5);
canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX+30, realPosY+20, mPaint);
}
else if (realPosX > centerPos[0] && realPosY >= centerPos[1]) {
//right-bottom
path.moveTo(realPosX-5, realPosY+5);
path.lineTo(realPosX-15, realPosY+15);
path.lineTo(realPosX - 15, realPosY - 15); rectF = new RectF(realPosX-200, realPosY-40, realPosX-15, realPosY + 30);
canvas.drawRoundRect(rectF, 15, 15, mPaint); mPaint.reset();
mPaint.setColor(Color.RED);
mPaint.setTextSize(mXYTitleTextSize - 5);
canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX-180, realPosY, mPaint);
}
else if (realPosX > centerPos[0] && realPosY < centerPos[1]) {
//right-top
path.moveTo(realPosX-5, realPosY+5);
path.lineTo(realPosX-15, realPosY+15);
path.lineTo(realPosX - 15, realPosY - 15); rectF = new RectF(realPosX-200, realPosY - 20, realPosX-15, realPosY + 50);
canvas.drawRoundRect(rectF, 15, 15, mPaint); mPaint.reset();
mPaint.setColor(Color.RED);
mPaint.setTextSize(mXYTitleTextSize - 5);
canvas.drawText("("+mMeasureXpos+", "+mMeasureYpos+")", realPosX-180, realPosY+30, mPaint);
} path.close();
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawPath(path, mPaint);
} //设置当前比值
public void updateValues(int x, int y) {
mMeasureXpos = x;
mMeasureYpos = y; postInvalidate();
} //设置XY轴顶角的title字体大小
public void setTitleTextSize(int size) {
mXYTitleTextSize = size;
} //初始化X轴的坐标区间点值,可以不均等分
public void initXLevelOffset(ArrayList<Integer> list) {
mXLevel.clear();
mXLevel.addAll(list);
} //初始化Y轴的坐标区间点值,可以不均等分
public void initYLevelOffset(ArrayList<Integer> list) {
mYLevel.clear();
mYLevel.addAll(list);
} //初始化每个区间的提示文字,如果不想显示可以设置""
public void initGridLevelText(ArrayList<String> list) {
mGridLevelText.clear();
mGridLevelText.addAll(list);
} //初始化每个区间的颜色
public void initGridColorLevel(ArrayList<Integer> list) {
mGridColorLevel.clear();
mGridColorLevel.addAll(list);
} //初始化每个区间的提示文字颜色
public void initGridTxtColorLevel(ArrayList<Integer> list) {
mGridTxtColorLevel.clear();
mGridTxtColorLevel.addAll(list);
} //初始化XY轴title
public void initTitleXY(String x, String y) {
mTitleX = x;
mTitleY = y;
}
}

再来看下布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"> <com.yanbober.customerviewdemo.areachartsview.AreaChartsView
android:id="@+id/area_charts_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"/> </RelativeLayout>

再看看主界面:

public class MainActivity extends AppCompatActivity {
private AreaChartsView mAreaChartsView;
private Timer timer; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mAreaChartsView = (AreaChartsView) this.findViewById(R.id.area_charts_view); //初始化自定义图表的规格和属性
ArrayList<Integer> mXLevel = new ArrayList<>();
ArrayList<Integer> mYLevel = new ArrayList<>();
ArrayList<String> mGridLevelText = new ArrayList<>();
ArrayList<Integer> mGridColorLevel = new ArrayList<>();
ArrayList<Integer> mGridTxtColorLevel = new ArrayList<>();
//初始化x轴坐标区间
mXLevel.add(0);
mXLevel.add(60);
mXLevel.add(90);
mXLevel.add(100);
mXLevel.add(110);
mXLevel.add(120);
//初始化y轴坐标区间
mYLevel.add(0);
mYLevel.add(90);
mYLevel.add(140);
mYLevel.add(160);
mYLevel.add(180);
mYLevel.add(200);
//初始化区间颜色
mGridColorLevel.add(Color.parseColor("#1FB0E7"));
mGridColorLevel.add(Color.parseColor("#4FC7F4"));
mGridColorLevel.add(Color.parseColor("#4FDDF2"));
mGridColorLevel.add(Color.parseColor("#90E9F4"));
mGridColorLevel.add(Color.parseColor("#B2F6F1"));
//初始化区间文字提示颜色
mGridTxtColorLevel.add(Color.parseColor("#EA8868"));
mGridTxtColorLevel.add(Color.parseColor("#EA8868"));
mGridTxtColorLevel.add(Color.parseColor("#EA8868"));
mGridTxtColorLevel.add(Color.WHITE);
mGridTxtColorLevel.add(Color.BLACK);
//初始化区间文字
mGridLevelText.add("异常");
mGridLevelText.add("过高");
mGridLevelText.add("偏高");
mGridLevelText.add("正常");
mGridLevelText.add("偏低"); mAreaChartsView.initGridColorLevel(mGridColorLevel);
mAreaChartsView.initGridLevelText(mGridLevelText);
mAreaChartsView.initGridTxtColorLevel(mGridTxtColorLevel);
mAreaChartsView.initXLevelOffset(mXLevel);
mAreaChartsView.initYLevelOffset(mYLevel);
mAreaChartsView.initTitleXY("投入量(H)", "产出量(H)");
} @Override
protected void onStart() {
super.onStart();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Random random = new Random();
int x = random.nextInt(120) % (120 + 1) + 0;
Random randomy = new Random();
int y = randomy.nextInt(200) % (200 + 1) + 0;
//随机模拟赋值
mAreaChartsView.updateValues(x, y);
}
}, 0, 1000);
} @Override
protected void onPause() {
super.onPause();
timer.cancel();
}
}

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

3  总结

上面代码很简单,核心的都已经注释了,不需要过多解释。核心思路就是一些坐标点的计算。该控件支持设置mergin及width与hight等属性,支持自定义所有颜色及显示及坐标区分等,唯一缺陷就是没来得及写attr属性xml设置这些值,有兴趣的自己实现吧,我是没时间了。

可以发现,自定义View无非就是重写前面文章分析的那三个方法而已。

!!!!!!! 下载Demo工程源码点击我

重点只提供实现思路,具体细节没时间优化。有需求的可以在下面讨论。

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

Android自定义控件(状态提示图表) (转)的更多相关文章

  1. Android自己定义控件(状态提示图表)

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重分享成果] 1 背景 前面分析那么多系统源代码了.也该暂停下来歇息一下,趁昨晚闲着看见一个有意思的需求就操 ...

  2. Android之hint提示字体大小修改,显示完全

    Android之hint提示字体大小修改,显示完全 1.工作中遇到一个问题,就是自定义EditText的hint提示在超大字体下会显示不全, 2.然后在网上搜索了一下,在这里记录一下,分享给大家,在此 ...

  3. Android自定义控件 开源组件SlidingMenu的项目集成

    在实际项目开发中,定制一个菜单,能让用户得到更好的用户体验,诚然菜单的样式各种各样,但是有一种菜单——滑动菜单,是被众多应用广泛使用的.关于这种滑动菜单的实现,我在前面的博文中也介绍了如何自定义去实现 ...

  4. Android自定义控件实现带有清除按钮的EditText

    首先声明我也是参考了别人的思路,只是稍微做了下修改,增加显示密码与隐藏密码,没有输入字符串时让EditText进行抖动,废话少说这里附上效果图 效果很赞有木有 那么怎么实现这种效果呢?那就跟着我一起来 ...

  5. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  6. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  7. Android自定义控件之自定义属性

    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...

  8. Android自定义控件之基本原理

    前言: 在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理. 自 ...

  9. [Android]Dagger2Metrics - 测量DI图表初始化的性能(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5098943.html Dagger2Metrics - 测量D ...

随机推荐

  1. POJ 2115 C Looooops (扩展欧几里德 + 线性同余方程)

    分析:这个题主要考察的是对线性同余方程的理解,根据题目中给出的a,b,c,d,不难的出这样的式子,(a+k*c) % (1<<d) = b; 题目要求我们在有解的情况下求出最小的解,我们转 ...

  2. Windows Batch Scripts

    Some simple examples: simple.bat @echo off set _var1=3 set _var2=5 set /a _result=%_var1%+%_var2% ec ...

  3. Oracle Sql优化之Merge 改写优化Update

    1.待改写语句如下 update table1 f )),) ,),)), f.jine2 )),) from table2 e where e.kjqj=f.kjqj=e.gs=f.gs and e ...

  4. index.do为后缀的是什么开发语言? 有什么技术特点?

    @Override 重写父类的方法.@Nullable 表示定义的字段可以为空. 一般情况下扩展名可以体现出一个网站使用的技术,***.html?id=***,这个就是普通的html页面,然后通过ja ...

  5. 解决Android Studio Gradle Build特别慢的问题

    解决Android Studio Gradle Build 特别慢的问题 C:\Users\你的用户名\.gradle目录下新建一个文件名为gradle.properties的文件.内容为:org.g ...

  6. getWriter() has already been called for this response 的解决办法

    getWriter() has already been called for this response response已经被其他对象调用了,导致无法继续使用如下 类似的方法 PrintWrite ...

  7. The Wall (medium)

    The Wall (medium) Heidi the Cow is aghast: cracks in the northern Wall? Zombies gathering outside, f ...

  8. Windows环境下使用VS2005编译OpenSSL

    如何Windows环境下,使用VS2005编译OpenSSL,虽然这个问题在Baidu.Google上一堆,但安装中还是遇到些问题,在这里 记录下来希望能帮助大家不要在走弯路.注:我是在WinXP S ...

  9. MyEclipse中提示SpringMVC的XML配置文件出错解决方法

    手动添加schema文件,方法如下: 1,依次选择:windwos->preferences->myeclipse->files and editors->xml->xm ...

  10. windows下常用软件

    1  pdf转word solid converter pdf  特点:功能强大,能转换页面页眉等,还有表格.