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

1 背景

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

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

横屏模式例如以下:



竖屏模式例如以下:

看见没有。这个控件全然自己定义的,连文字等都是自己定义的,没有不论什么图片等资源,就仅仅是一个小的java文件,这个界面仅仅有一个控件。例如以下咱们看下实现代码。

!!!!!!! 下载Demoproject源代码点击我

【工匠若水 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无非就是重写前面文章分析的那三个方法而已。

!!!!!!! 下载Demoproject源代码点击我

重点仅仅提供实现思路,详细细节没时间优化。有需求的能够在以下讨论。

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

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

  1. Android自己定义控件系列五:自己定义绚丽水波纹效果

    尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...

  2. android 自己定义控件

    Android自己定义View实现非常easy 继承View,重写构造函数.onDraw.(onMeasure)等函数. 假设自己定义的View须要有自己定义的属性.须要在values下建立attrs ...

  3. Android自己定义控件皮肤

    Android自己定义控件皮肤 对于Android的自带控件,其外观仅仅能说中规中矩,而我们平时所示Android应用中,一个简单的button都做得十分美观.甚至于很多button在按下时的外观都有 ...

  4. Android自己定义控件:进度条的四种实现方式

    前三种实现方式代码出自: http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/ (源代码下载)http://down ...

  5. android 自己定义控件属性(TypedArray以及attrs解释)

    近期在捣鼓android 自己定义控件属性,学到了TypedArray以及attrs.在这当中看了一篇大神博客Android 深入理解Android中的自己定义属性.我就更加深入学习力一番.我就沿着这 ...

  6. Android自己定义控件系列二:自己定义开关button(一)

    这一次我们将会实现一个完整纯粹的自己定义控件,而不是像之前的组合控件一样.拿系统的控件来实现.计划分为三部分:自己定义控件的基本部分,自己定义控件的触摸事件的处理和自己定义控件的自己定义属性: 以下就 ...

  7. Android自己定义控件系列三:自己定义开关button(二)

    接上一篇自己定义开关button(一)的内容继续.上一次实现了一个开关button的基本功能.即自己定义了一个控件.开关button,实现了点击切换开关状态的功能.今天我们想在此基础之上.进一步实现触 ...

  8. Android自己定义控件之应用程序首页轮播图

    如今基本上大多数的Android应用程序的首页都有轮播图.就是像下图这种(此图为转载的一篇博文中的图.拿来直接用了): 像这种组件我相信大多数的应用程序都会使用到,本文就是自己定义一个这种组件,能够动 ...

  9. Android 自己定义控件开发入门(二)

    上一次我们讲了一堆实现自己定义控件的理论基础.列举了View类一些能够重写的方法,我们对这些方法的重写是我们继承View类来派生自己定义控件的关键 我通过一个最简单的样例给大家展示了这一个过程,不管是 ...

随机推荐

  1. BZOJ 1669: [Usaco2006 Oct]Hungry Cows饥饿的奶牛( LIS )

    裸的LIS ----------------------------------------------------------------- #include<cstdio> #incl ...

  2. 数据层使用DBHelper.dll来减少工作量

    目前在需求确定了以后进行开发的步骤一般是这样的:建立数据表,建立model,构建数据操作层,最后在页面中进行调用.关于数据操作层,因为大量的操作都集中在增加.更新.删除等简易操作,而我之前的写法是每次 ...

  3. Android 屏幕尺寸知识

    转自:http://www.zcool.com.cn/article/ZNjI3NDQ=.html 1.了解几个概念 (1)分辨率.分辨率就是手机屏幕的像素点数,一般描述成屏幕的“宽×高”,安卓手机屏 ...

  4. Android 开发笔记 “广播组件使用”

    在Activity中,注册广播的一个Demo. 总共分3步 第一步:定义一个BroadcastReceiver广播接收类: private BroadcastReceiver mBroadcastRe ...

  5. ELK 之四:搭建集群处理日PV 四亿次超大访问量优化方法

    最近公司的网站访问量越来越大,采用4台高配置服务器做后端Server,前端使用一个负载,日志从后端4台服务器收集到ELK统计,但是最近Logstash经常出问题,每次启动运行三四个小时就挂了,分析是由 ...

  6. Python之路Day14

    主要内容:jQuery进阶.CSS伪类和伪元素.jQuery插件 tab菜单样式 checkbox全选.反选 位置:scrollTop和offset 事件:两种绑定事件的方式和委托delegate a ...

  7. java-StringTokenizer类

    StringTokenizer类别可以通过某个字符或者多个字符作为分界符,来将字符串划分为多个标记(token). package com.example.helloworld; import jav ...

  8. Microsoft Jet 数据库引擎找不到对象'Sheet1$_'。请确定对象是否存在,并正确地写出它的名称和路径

    We have a CRM add-on for Importing Price Lists into CRM. For this tool, we expect the details to be ...

  9. Android 6.0 新特性 整理 资料来自网络

    Android 6.0新特性 Runtime Permissions Doze and App Standby Apache HTTP Client Removal BoringSSL Access ...

  10. java学习之IO文件分割

    package om.gh.homework; import java.io.*; /** * 实现分割文件; * @param file */ public class HomeWork { /** ...