Android自己定义控件(状态提示图表)
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处。尊重分享成果】
1 背景
前面分析那么多系统源代码了。也该暂停下来歇息一下,趁昨晚闲着看见一个有意思的需求就操练一下分析源代码后的实例演练—-自己定义控件。
这个实例非常适合新手入门自己定义控件。先看下效果图:
横屏模式例如以下:
竖屏模式例如以下:
看见没有。这个控件全然自己定义的,连文字等都是自己定义的,没有不论什么图片等资源,就仅仅是一个小的java文件,这个界面仅仅有一个控件。例如以下咱们看下实现代码。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处。尊重分享成果】
2 实例代码
例如以下就是整个project的源代码了。
自己定义上面展示的控件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 总结
上面代码非常easy。核心的都已经凝视了。不须要过多解释。核心思路就是一些坐标点的计算。该控件支持设置mergin及width与hight等属性,支持自己定义全部颜色及显示及坐标区分等。唯一缺陷就是没来得及写attr属性xml设置这些值。有兴趣的自己实现吧。我是没时间了。
能够发现,自己定义View无非就是重写前面文章分析的那三个方法而已。
重点仅仅提供实现思路,详细细节没时间优化。有需求的能够在以下讨论。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
Android自己定义控件(状态提示图表)的更多相关文章
- Android自己定义控件系列五:自己定义绚丽水波纹效果
尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...
- android 自己定义控件
Android自己定义View实现非常easy 继承View,重写构造函数.onDraw.(onMeasure)等函数. 假设自己定义的View须要有自己定义的属性.须要在values下建立attrs ...
- Android自己定义控件皮肤
Android自己定义控件皮肤 对于Android的自带控件,其外观仅仅能说中规中矩,而我们平时所示Android应用中,一个简单的button都做得十分美观.甚至于很多button在按下时的外观都有 ...
- Android自己定义控件:进度条的四种实现方式
前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源代码下载)http://down ...
- android 自己定义控件属性(TypedArray以及attrs解释)
近期在捣鼓android 自己定义控件属性,学到了TypedArray以及attrs.在这当中看了一篇大神博客Android 深入理解Android中的自己定义属性.我就更加深入学习力一番.我就沿着这 ...
- Android自己定义控件系列二:自己定义开关button(一)
这一次我们将会实现一个完整纯粹的自己定义控件,而不是像之前的组合控件一样.拿系统的控件来实现.计划分为三部分:自己定义控件的基本部分,自己定义控件的触摸事件的处理和自己定义控件的自己定义属性: 以下就 ...
- Android自己定义控件系列三:自己定义开关button(二)
接上一篇自己定义开关button(一)的内容继续.上一次实现了一个开关button的基本功能.即自己定义了一个控件.开关button,实现了点击切换开关状态的功能.今天我们想在此基础之上.进一步实现触 ...
- Android自己定义控件之应用程序首页轮播图
如今基本上大多数的Android应用程序的首页都有轮播图.就是像下图这种(此图为转载的一篇博文中的图.拿来直接用了): 像这种组件我相信大多数的应用程序都会使用到,本文就是自己定义一个这种组件,能够动 ...
- Android 自己定义控件开发入门(二)
上一次我们讲了一堆实现自己定义控件的理论基础.列举了View类一些能够重写的方法,我们对这些方法的重写是我们继承View类来派生自己定义控件的关键 我通过一个最简单的样例给大家展示了这一个过程,不管是 ...
随机推荐
- 【转】Nginx+Tomcat搭建高性能负载均衡集群
最近对负载均衡比较感兴趣,研究了公司的负载均衡的配置,用的是阿里的SLB,相当于不用运维,只需要在后台进行简单的配置就能完成Tomcat的负载均衡,索性在网上找了几篇文章去尝试搭建一个集群,然而很多都 ...
- VC++实现生成右键菜单及添加图标
用VC++实现弹出菜单比较简单,这里介绍其中的一种来实现一个鼠标右键弹出菜单,效果如下图所示: 步骤: 一.新建一个基于对话框的MFC应用程序-----PopMenu 二.添加一个菜单资源------ ...
- GNU自动补全模块readline解析
readline模块定义了一系列函数用来读写Python解释器中历史命令,并提供自动补全命令功能.这个模块可以通过relcompleter模块直接调用,模块中的设置会影响解释器中的交互提示,以及内置函 ...
- 设计模式值六大原则——开闭原则(OCP)
开闭原则(Open Closed Principle)是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的.灵活的系统. 定义: 一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. S ...
- System.Web.Security 在winform中是什么命名空间呢
des.Key = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStorin ...
- 【随手记-有空整理】Linux随手记
1. CentOS6.5安装g++:yum install gcc-c++ 注意如果写成yum install g++会提示No package g++ available. 2. XShell下打开 ...
- __call重载方法
<?php class Person { function du() { echo "这是一个存在的方法"; } //该方法有两个参数,第一个参数 $function_nam ...
- day1作业脚本
1.编写登录接口: - 输入用户名和密码 - 认证成功后显示欢迎信息 - 输错三次后锁定 2.编写多级菜单 - 三级菜单 - 可依次进入子菜单 第一次写python脚本,因为没有学到函数,所以写的有点 ...
- java学习之网络编程之echo程序
服务端的实现 package com.gh.echo; import java.io.*; import java.net.*; /** * echo服务器程序 * 实现 不断接收字符串 ,然后返回一 ...
- python文件I/O
file = open(filename,mode) ,python使用open或者file:打开文件,打开文件有几种模式,譬如酱紫: test = open(“txt.txt”,”w”),更多的参数 ...