Android自定义View实现折线图效果_Android

下面就是结果图(每种状态用一个表情图片表示):

一、主页面的布局文件如下:

<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"
 tools:context=".MainActivity"
 xmlns:app="http://schemas.android.com/apk/res/ting.example.linecharview">
 <ting.example.linecharview.LineCharView
 android:id="@+id/test"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:xytextcolor="@color/bg"
 app:xytextsize="20sp"
 app:interval="80dp"
 />
</RelativeLayout> 

其中linecharview就是自定义的View,而app:xx就是这个View的各种属性。

二、在values的attrs文件中加入如下xml,来定义linecharview的各种属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="LineChar">
 <attr name="xylinecolor" format="color"/><!-- xy坐标轴颜色 -->
 <attr name="xylinewidth" format="dimension"/><!-- xy坐标轴宽度 -->
 <attr name="xytextcolor" format="color"/><!-- xy坐标轴文字颜色 -->
 <attr name="xytextsize" format="dimension"/><!-- xy坐标轴文字大小 -->
 <attr name="linecolor" format="color"/><!-- 折线图中折线的颜色 -->
 <attr name="interval" format="dimension"/><!-- x轴各个坐标点水平间距 -->
 <attr name="bgcolor" format="color"/><!-- 背景颜色 -->
 </declare-styleable>
</resources> 

三、接下来建个类LineCharView 继承View,并申明如下变量:

<span style="white-space:pre"> </span>private int xori;//圆点x坐标
 private int yori;//圆点y坐标
 private int xinit;//第一个点x坐标
 private int minXinit;//在移动时,第一个点允许最小的x坐标
 private int maxXinit;//在移动时,第一个点允许允许最大的x坐标
 private int xylinecolor;//xy坐标轴颜色
 private int xylinewidth;//xy坐标轴大小
 private int xytextcolor;//xy坐标轴文字颜色
 private int xytextsize;//xy坐标轴文字大小
 private int linecolor;//折线的颜色
 private int interval;//坐标间的间隔
 private int bgColor;//背景颜色
 private List<String> x_coords;//x坐标点的值
 private List<String> x_coord_values;//每个点状态值 

 private int width;//控件宽度
 private int heigth;//控件高度
 private int imageWidth;//表情的宽度
 private float textwidth;//y轴文字的宽度
 float startX=0;//滑动时候,上一次手指的x坐标 

在构造函数中读取各个属性值:

public LineCharView(Context context, AttributeSet attrs) {
 super(context, attrs);
 TypedArray typedArray= context.obtainStyledAttributes(attrs, R.styleable.LineChar);
 xylinecolor=typedArray.getColor(R.styleable.LineChar_xylinecolor, Color.GRAY);
 xylinewidth=typedArray.getInt(R.styleable.LineChar_xylinewidth, 5);
 xytextcolor=typedArray.getColor(R.styleable.LineChar_xytextcolor, Color.BLACK);
 xytextsize=typedArray.getLayoutDimension(R.styleable.LineChar_xytextsize, 20);
 linecolor=typedArray.getColor(R.styleable.LineChar_linecolor, Color.GRAY);
 interval=typedArray.getLayoutDimension(R.styleable.LineChar_interval, 100);
 bgColor=typedArray.getColor(R.styleable.LineChar_bgcolor, Color.WHITE);
 typedArray.recycle();
 x_coords=new ArrayList<String>();
 x_coord_values=new ArrayList<String>();
} 

四、接下来可以重写onLayout方法,来计算控件宽高和坐标轴的原点坐标,坐标轴原点的x坐标可以通过y轴文字的宽度,y轴宽度,和距离y的水平距离进行计算,这里y轴文字只有4种状态(A、B、C、D),可以使用下面方法来计算出原点的x坐标:

Paint paint=new Paint();
paint.setTextSize(xytextsize);
textwidth= paint.measureText("A");
xori=(int) (textwidth+6+2*xylinewidth);//6 为与y轴的间隔 

原点的y坐标也可以用类似的方法计算出来:

yori=heigth-xytextsize-2*xylinewidth-3; //3为x轴的间隔,heigth为控件高度。 

当需要展示的数据量多时候,无法全部展示时候,需要通过滑动折线图进行展示,我们只需要控制第一点x坐标,就可以通过interval这个属性计算出后面每个点的坐标,但是为了防止将所有的数据滑动出界面外,需要在滑动时进行控制,其实就是控制第一个点x坐标的范围,第一个点的x坐标的最小值可以通过控件的宽度减去原点x坐标再减去所有折线图的水平距离,代码如下:

minXinit=width-xori-x_coords.size()*interval; 

控件在默认第一个展示时,第一个点与y轴的水平距离等于interval的一半,在滑动时候如果第一个点出现在这个位置了,就不允许再继续向右滑动,所以第一个点x坐标的最大值就等这个起始x坐标。

xinit=interval/2+xori;
maxXinit=xinit; 

重写onLayout方法的代码如下:

@Override
 protected void onLayout(boolean changed, int left, int top, int right,
 int bottom) {
 if(changed){
 width=getWidth();
 heigth=getHeight();
 Paint paint=new Paint();
 paint.setTextSize(xytextsize);
 textwidth= paint.measureText("A");
 xori=(int) (textwidth+6+2*xylinewidth);//6 为与y轴的间隔
 yori=heigth-xytextsize-2*xylinewidth-3;//3为x轴的间隔
 xinit=interval/2+xori;
 imageWidth= BitmapFactory.decodeResource(getResources(), R.drawable.facea).getWidth();
 minXinit=width-xori-x_coords.size()*interval;
 maxXinit=xinit;
 setBackgroundColor(bgColor);
 }
 super.onLayout(changed, left, top, right, bottom);
 } 

五、接下来就可以画折线、x坐标轴上的小圆点和折线上表情

代码如下:

//画X轴坐标点,折线,表情
 @SuppressLint("ResourceAsColor")
 private void drawX (Canvas canvas) {
 Paint x_coordPaint =new Paint();
 x_coordPaint.setTextSize(xytextsize);
 x_coordPaint.setStyle(Paint.Style.FILL);
 Path path=new Path();
 //画坐标轴上小原点,坐标轴文字
 for(int i=0;i<x_coords.size();i++){
 int x=i*interval+xinit;
 if(i==0){
 path.moveTo(x, getYValue(x_coord_values.get(i)));
 }else{
 path.lineTo(x, getYValue(x_coord_values.get(i)));
 }
 x_coordPaint.setColor(xylinecolor);
 canvas.drawCircle(x, yori, xylinewidth*2, x_coordPaint);
 String text=x_coords.get(i);
 x_coordPaint.setColor(xytextcolor);
 canvas.drawText(text, x-x_coordPaint.measureText(text)/2, yori+xytextsize+xylinewidth*2, x_coordPaint);
 } 

 x_coordPaint.setStyle(Paint.Style.STROKE);
 x_coordPaint.setStrokeWidth(xylinewidth);
 x_coordPaint.setColor(linecolor);
 //画折线
 canvas.drawPath(path, x_coordPaint); 

 //画表情
 for(int i=0;i<x_coords.size();i++){
 int x=i*interval+xinit;
 canvas.drawBitmap(getYBitmap(x_coord_values.get(i)), x-imageWidth/2, getYValue(x_coord_values.get(i))-imageWidth/2, x_coordPaint);
 } 

 //将折线超出x轴坐标的部分截取掉
 x_coordPaint.setStyle(Paint.Style.FILL);
 x_coordPaint.setColor(bgColor);
 x_coordPaint.setXfermode(new PorterDuffXfermode( PorterDuff.Mode.SRC_OVER));
 RectF rectF=new RectF(0, 0, xori, heigth);
 canvas.drawRect(rectF, x_coordPaint); 

 } 

以上代码首先通过遍历x_coordsx_coord_values这两个List集合,来画坐标点,折线,表情,由于在向左滑动的时候有可能会将坐标点,折线绘制到y轴的左边,所以需要对其进行截取。其中getYValue和getYBitmap方法,可以通过x_coord_values的值计算y坐标和相应的表情。两方法如:

//得到y坐标
 private float getYValue(String value)
 {
 if(value.equalsIgnoreCase("A")){
 return yori-interval/2;
 }
 else if(value.equalsIgnoreCase("B")){
 return yori-interval;
 }
 else if(value.equalsIgnoreCase("C")){
 return (float) (yori-interval*1.5);
 }
 else if(value.equalsIgnoreCase("D")){
 return yori-interval*2;
 }else{
 return yori;
 }
 } 

 //得到表情图
 private Bitmap getYBitmap(String value){
 Bitmap bitmap=null;
 if(value.equalsIgnoreCase("A")){
 bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.facea);
 }
 else if(value.equalsIgnoreCase("B")){
 bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.faceb);
 }
 else if(value.equalsIgnoreCase("C")){
 bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.facec);
 }
 else if(value.equalsIgnoreCase("D")){
 bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.faced);
 }
 return bitmap;
 } 

六、画好了坐标点,折线,表情,接下来就简单,就可以画x y轴了,x y轴只要确定的原点坐标,就非常简单了,代码如下:

//画坐标轴
private void drawXY(Canvas canvas){
 Paint paint=new Paint();
 paint.setColor(xylinecolor);
 paint.setStrokeWidth(xylinewidth);
 canvas.drawLine(xori, 0, xori, yori, paint);
 canvas.drawLine(xori, yori, width, yori, paint);
} 

七、最后就可以画y轴上的坐标小原点和y轴的文字了:

//画Y轴坐标点
 private void drawY(Canvas canvas){
 Paint paint=new Paint();
 paint.setColor(xylinecolor);
 paint.setStyle(Paint.Style.FILL);
 for(int i=1;i<5 ;i++){
 canvas.drawCircle(xori, yori-(i*interval/2), xylinewidth*2, paint);
 } 

 paint.setTextSize(xytextsize);
 paint.setColor(xytextcolor);
 canvas.drawText("D",xori-textwidth-6-xylinewidth , yori-(2*interval)+xytextsize/2, paint);
 canvas.drawText("C",xori-textwidth-6-xylinewidth , (float) (yori-(1.5*interval)+xytextsize/2), paint);
 canvas.drawText("B",xori-textwidth-6-xylinewidth , yori-interval+xytextsize/2, paint);
 canvas.drawText("A",xori-textwidth-6-xylinewidth , (float) (yori-(0.5*interval)+xytextsize/2), paint);
 } 

八、写完了以上三个方法:只需要重写onDraw方法,就可以进行绘制了。

@Override
 protected void onDraw(Canvas canvas) {
 drawX(canvas);
 drawXY(canvas);
 drawY(canvas);
 } 

九、为了可以进行水平滑动,需要重写控件的onTouchEvent方法,在滑动时候,实时计算手指滑动的距离来改变第一个点的x坐标,然后调用invalidate();就可以刷新控件,重新绘制达到滑动效果。

@Override
 public boolean onTouchEvent(MotionEvent event) { 

 //如果不用滑动就可以展示所有数据,就不让滑动
 if(interval*x_coord_values.size()<=width-xori){
 return false;
 } 

 switch (event.getAction()) {
 case MotionEvent.ACTION_DOWN:
 startX=event.getX();
 break; 

 case MotionEvent.ACTION_MOVE:
 float dis=event.getX()-startX;
 startX=event.getX();
 if(xinit+dis>maxXinit){
 xinit=maxXinit;
 }else if(xinit+dis<minXinit){
 xinit=minXinit;
 }else{
 xinit=(int) (xinit+dis);
 }
 invalidate(); 

 break;
 }
 return true;
 } 

十、最后添加一个设置数据源的方法,设置x_coordsx_coord_values这个两个List集合,在设置完成之后调用invalidate() ,进行控件刷新:

/**
 * 设置坐标折线图值
 * @param x_coords 横坐标坐标点
 * @param x_coord_values 每个点的值
 */
public void setValue( List<String> x_coords ,List<String> x_coord_values) {
 if(x_coord_values.size()!=x_coords.size()){
 throw new IllegalArgumentException("坐标轴点和坐标轴点的值的个数必须一样!");
 }
 this.x_coord_values=x_coord_values;
 this.x_coords=x_coords;
 invalidate();
} 

总结

以上就是Android自定义View实现折线图效果的全部内容,希望对大家开发Android能有所帮助。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, 自定义折线图
, 自定义折线
view画折线图
自定义view画折线图、android 自定义折线图、android 自定义 折线、android 实现折线图、自定义view实现动画,以便于您获取更多的相关知识。

时间: 2024-10-28 06:03:58

Android自定义View实现折线图效果_Android的相关文章

Android自定义View实现折线图效果

下面就是结果图(每种状态用一个表情图片表示): 一.主页面的布局文件如下: <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=&quo

Android自定义View实现弹性小球效果_Android

照例先看效果图 自定义代码示例 public class BezierView extends View { Paint paint;//画笔 Path path;//路径 int radius = 50;//圆的半径 int time = 100;//计数时长 int index; int offsetIndex; float viewX, viewY;//图形中心点坐标 float width;//屏幕宽度 float partWidth;//屏幕宽度的1/4 int paddingLeft

Android自定义View实现水面上涨效果_Android

实现效果如下: 实现思路: 1.如何实现圆中水面上涨效果:利用Paint的setXfermode属性为PorterDuff.Mode.SRC_IN画出进度所在的矩形与圆的交集实现 2.如何水波纹效果:利用贝塞尔曲线,动态改变波峰值,实现"随着进度的增加,水波纹逐渐变小的效果" 话不多说,看代码. 首先是自定义属性值,有哪些可自定义属性值呢? 圆的背景颜色:circle_color,进度的颜色:progress_color,进度显示文字的颜色:text_color,进度文字的大小:tex

Android自定义View简易折线图控件(二)

继续练习自定义View,这次带来的是简易折线图,支持坐标点点击监听,效果如下: 画坐标轴.画刻度.画点.连线..x.y轴的数据范围是写死的 1 <= x <= 7 ,1 <= y <= 70 ..写活的话涉及到坐标轴刻度的动态计算.坐标点的坐标修改,想想就头大,这里只练习自定义View. 1.在res/values文件夹下新建attrs.xml文件,编写自定义属性: <?xml version="1.0" encoding="utf-8"

Android自定义View实现圆环交替效果_Android

下面请先看效果图: 看上去是不很炫的样子,它的实现上也不是很复杂,重点在与onDraw()方法的绘制. 首先是我们的attrs文件: <?xml version="1.0" encoding="utf-8"?> <resources> <attr name="firstColor" format="color"/> <attr name="secondColor"

Android自定义VIew实现卫星菜单效果浅析_Android

 一 概述: 最近一直致力于Android自定义VIew的学习,主要在看<android群英传>,还有CSDN博客鸿洋大神和wing大神的一些文章,写的很详细,自己心血来潮,学着写了个实现了类似卫星效果的一个自定义的View,分享到博客上,望各位指点一二.写的比较粗糙,见谅.(因为是在Linux系统下写的,效果图我直接用手机拍的,难看,大家讲究下就看个效果,勿喷). 先来看个效果图,有点不忍直视: 自定义VIew准备: (1)创建继承自View的类; (2)重写构造函数; (3)定义属性. (

Android自定义VIew实现卫星菜单效果浅析

一 概述: 最近一直致力于Android自定义VIew的学习,主要在看<android群英传>,还有CSDN博客鸿洋大神和wing大神的一些文章,写的很详细,自己心血来潮,学着写了个实现了类似卫星效果的一个自定义的View,分享到博客上,望各位指点一二.写的比较粗糙,见谅.(因为是在Linux系统下写的,效果图我直接用手机拍的,难看,大家讲究下就看个效果,勿喷). 先来看个效果图,有点不忍直视: 自定义VIew准备: (1)创建继承自View的类; (2)重写构造函数; (3)定义属性. (4

Android自定义view制作绚丽的验证码_Android

废话不多说了,先给大家展示下自定义view效果图,如果大家觉得还不错的话,请继续往下阅读. 怎么样,这种验证码是不是很常见呢,下面我们就自己动手实现这种效果,自己动手,丰衣足食,哈哈~ 一. 自定义view的步骤 自定义view一直被认为android进阶通向高手的必经之路,其实自定义view好简单,自定义view真正难的是如何绘制出高难度的图形,这需要有好的数学功底(后悔没有好好学数学了~),因为绘制图形经常要计算坐标点及类似的几何变换等等.自定义view通常只需要以下几个步骤: 写一个类继承

Android自定义View实现字母导航栏_Android

很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章. 思路分析: 1.自定义View实现字母导航栏 2.ListView实现联系人列表 3.字母导航栏滑动事件处理 4.字母导航栏与中间字母的联动 5.字母导航栏与ListView的联动 效果图: 首先,我们先甩出主布局文件,方便后面代码的说明 <!--?xml version="1.0" encoding=&qu