Android自定义View——实现理财类APP七日年化收益折线图效果

这段时间的自定义View学习,学会了绘制柱状图、绘制折线图、绘制进度控件,那我们今天就来聊聊另外一种自定义的View,这就是我们常见的七日年化收益折线图效果。先看看长什么样。

这就是效果图了,元素相对而言还是比较多的,这里有线、柱状图、文字、折线、点等等。看起来好像很复杂,但是呢,只要一步一步的实现,那还是可以达到这种效果的,之前我们说过的, 自定义View,就像是在photo shop里面画图,想要什么就画什么,我们可以有很多的画笔工具,也可以有很多的图层。

先看看我们这一次用到哪些变量。

    private Paint mTextPaint, mLinePaint,mPathPaint,mPointPaint;
    //柱状图的宽度
    private float mPaintRectWidth;
    //路径
    private Path mPath;
    //高跟宽
    private float mWidth, mHeight;
    //柱状图的数量
    private final float mCount = 6;
    //偏移量
    private final float offsets=1;

    private float mRectHeight;
    //x轴的坐标
    private List<Float> xline=new ArrayList<Float>();
    //Y轴的坐标
    private List<Float> yline=new ArrayList<Float>();

    //左边文字
    private float []x={2.46f,2.45f,2.44f,2.43f,2.42f,2.41f,2.40f};
    //底部文字
    private String [] day={"07-01","07-02","07-03","07-04","07-05","07-06","07-07"};

注释标记的也是够详细了,为了大家能够看得懂,也为了提高的博客质量,我们注意到,这里有文字,有数量,基本涵盖了我们这里索要用到的所有的变量。

下面开始初始化操作。


    private void initView() {
        //绘制线和文字的颜色
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.parseColor("#cccccc"));
        mTextPaint.setTextSize(25);
        mTextPaint.setStrokeWidth(1);

        //绘制折线图的点
        mPointPaint= new Paint();
        mPointPaint.setAntiAlias(true);
        mPointPaint.setColor(Color.parseColor("#000000"));
        mPointPaint.setTextSize(25);
        mPointPaint.setStrokeWidth(5);

        //绘制柱状图的画笔
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);

        //绘制折线图的画笔
        mPathPaint= new Paint();
        mPathPaint.setAntiAlias(true);
        mPathPaint.setColor(Color.parseColor("#ff0000"));
        mPathPaint.setStyle(Style.STROKE);

        //折线图的路径
        mPath=new Path();

    }

这里主要就是给画笔初始化,可以看到每一个画笔的颜色、宽度都是不一样的,值的注意的是,这里面的画笔并没有设置镂空样式。

接下来我们看看怎么去赋值,老规矩,我们还是选择在onSizeChange()里面进行赋值,代码如下:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth=(float)(getWidth()-getWidth()*0.1);
        mHeight=(float)(getHeight()-getHeight()*0.1);
        mRectHeight=(float)(getHeight()-getHeight()*0.1);
        mPaintRectWidth=(float) (mWidth*0.8/mCount);
        mLinePaint.setStrokeWidth(mPaintRectWidth);
    }

跟之前的不一样,我们以前所要用的宽度跟高度,直接就是使用方法里的,但是现在我们没有这么做,例如:(float)(getWidth()-getWidth()*0.1);我们进行了一定的处理,这样可以提高页面的舒适性,提高用户体验。

好了,准备工作做好了,接下来看看onDraw方法里面的操作,代码如下:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        onDrawRect(canvas);
        onDrawLine(canvas);
        canvasPath(canvas);
    }

哟呵,怎么这么简单,就三个东西?对的,就是这么简单粗暴,从名字中也可以看出,第一个是绘制矩形,第二个绘制线,第三个就是绘制折线。是不是很快呢,下面看看具体的实现方式。

第一个方法:

    //绘制6个矩形
    private void onDrawRect(Canvas canvas) {
        for (int i = 0; i < 7; i++) {
            if (i%2==0) {
                mLinePaint.setColor(Color.parseColor("#eeeeee"));
            }else {
                mLinePaint.setColor(Color.parseColor("#ece1f3"));
            }
            float left  =(float) (mWidth * 0.4 / 2 + mPaintRectWidth * i + offsets);
            float right=(float) (mWidth * 0.4 / 2 + mPaintRectWidth* (i + 1));

            canvas.drawRect(left,(float)(mRectHeight*0.01),right, mHeight, mLinePaint);
        }

    }

第二个:

    //绘制网格线
    private void onDrawLine(Canvas canvas){
        //第一条线
        canvas.drawLine(mPaintRectWidth-mPaintRectWidth/2, (float)(mRectHeight*0.01), getWidth(),  (float)(mRectHeight*0.01), mTextPaint);
        //定义这里高度
        float height;
        //横七条
        for (float i = 0; i < 7; i++) {
            //从上到下
            if (i==0) {
                height=i;
            }else {
                height=mRectHeight*(i/6);
                float size=mTextPaint.measureText(x[(int)i]+"");
                //绘制线
                canvas.drawLine(mPaintRectWidth+mPaintRectWidth/2, height, getWidth(),  height, mTextPaint);
                //绘制左边Y轴的文字
                canvas.drawText(x[(int)i]+"", (float)(mPaintRectWidth-mPaintRectWidth*0.35), height+size/5, mTextPaint);
            }

        }
        //竖七条
        canvas.drawLine((float) (mPaintRectWidth-mPaintRectWidth/2),0, (float) (mPaintRectWidth-mPaintRectWidth/2),  mHeight, mTextPaint);
        for (float i = 0; i < 7; i++) {
            //从左到右
            canvas.drawLine((float) (mWidth * 0.4 / 2 + mPaintRectWidth * i),0, (float) (mWidth * 0.4 / 2 + mPaintRectWidth * i),  mHeight, mTextPaint);
            //绘制底边的日期文字
            canvas.drawText(day[(int) i], (float) (mWidth * 0.34 / 2 + mPaintRectWidth * i), (float)(mHeight+mHeight*0.1), mTextPaint);
            //准备好下面折线图的X轴坐标
            xline.add((float) (mWidth * 0.4 / 2 + mPaintRectWidth * i));
        }
        //折线图的第一个点
        xline.add((float) (mPaintRectWidth-mPaintRectWidth/2));

    }

这里的代码相对较多,我专门加了许多的注释,可以用心的看看。

第三个绘制折线:

    //绘制折线路径
    public void canvasPath(Canvas canvas){

        for (int j = 0; j < yline.size(); j++) {

            float x=xline.get(j);
            float y =yline.get(j);
            float aftery=Math.initData(y);
            if (j==0) {
                mPath.moveTo(x,aftery );
            }else{

                mPath.lineTo(x,aftery );
            }
            canvas.drawPoint(x, aftery, mPointPaint);
            float size=mPointPaint.measureText(y+"");
            canvas.drawText(y+"", (float)(x-size/2), (float)(aftery+size*0.25), mPointPaint);
        }
        canvas.drawPath(mPath, mPathPaint);
    }

这里的绘制折线包括绘制线、点、文字、还有文字的转换、但是这个转换可以根据自己的需求来进行定制。

最后向外面暴露一个方法,用于更新坐标。

    //用于设置Y轴的坐标值
    public void setDataY( List<Float> yline) {
        this.yline.clear();
        this.yline=yline;
    }
    //一个更新UI的方法
    public void invalidata(){
        invalidate();
    }

文字的转换我这里用一个自己的类去处理了,代码如下:

    public static float initData(float a){

        if (2.40f<=a&&a<2.41f) {
            a=540f-540f/(a/0.01f);
        }else if(2.41f<=a&&a<2.42f){
            a=480f-480f/(a/0.01f);
        }else if (2.42f<=a&&a<2.43f) {
            a=360f-360f/(a/0.01f);
        }else if (2.43f<=a&&a<2.44f) {
            a=270f-270f/(a/0.01f);
        }else if (2.44f<=a&&a<2.45f) {
            a=180f-180f/(a/0.01f);
        }else if (2.45f<=a&&a<2.46f) {
            a=90f-90f/(a/0.01f);
        }

        return a;

    }

以上就是逻辑的实现过程了。最后,我把所有代码贴上来:

AnnualyieldView.java

/**
 * 小瓶盖 2016年7月13日18:08:28
 *
 * @author Android自定义View——实现理财类APP七日年化收益折线图效果
 *
 *相关博客地址 http://blog.csdn.net/qq_25193681
 */
public class AnnualyieldView extends View {

    private Paint mTextPaint, mLinePaint,mPathPaint,mPointPaint;
    //柱状图的宽度
    private float mPaintRectWidth;
    //路径
    private Path mPath;
    //高跟宽
    private float mWidth, mHeight;
    //柱状图的数量
    private final float mCount = 6;
    //偏移量
    private final float offsets=1;

    private float mRectHeight;
    //x轴的坐标
    private List<Float> xline=new ArrayList<Float>();
    //Y轴的坐标
    private List<Float> yline=new ArrayList<Float>();

    //左边文字
    private float []x={2.46f,2.45f,2.44f,2.43f,2.42f,2.41f,2.40f};
    //底部文字
    private String [] day={"07-01","07-02","07-03","07-04","07-05","07-06","07-07"};

    public AnnualyieldView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initView();

    }

    public AnnualyieldView(Context context, AttributeSet attrs) {
        super(context, attrs);

        initView();
    }

    public AnnualyieldView(Context context) {
        super(context);

        initView();
    }

    private void initView() {
        //绘制线和文字的颜色
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.parseColor("#cccccc"));
        mTextPaint.setTextSize(25);
        mTextPaint.setStrokeWidth(1);

        //绘制折线图的点
        mPointPaint= new Paint();
        mPointPaint.setAntiAlias(true);
        mPointPaint.setColor(Color.parseColor("#000000"));
        mPointPaint.setTextSize(25);
        mPointPaint.setStrokeWidth(5);

        //绘制柱状图的画笔
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);

        //绘制折线图的画笔
        mPathPaint= new Paint();
        mPathPaint.setAntiAlias(true);
        mPathPaint.setColor(Color.parseColor("#ff0000"));
        mPathPaint.setStyle(Style.STROKE);

        //折线图的路径
        mPath=new Path();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth=(float)(getWidth()-getWidth()*0.1);
        mHeight=(float)(getHeight()-getHeight()*0.1);
        mRectHeight=(float)(getHeight()-getHeight()*0.1);
        mPaintRectWidth=(float) (mWidth*0.8/mCount);
        mLinePaint.setStrokeWidth(mPaintRectWidth);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        onDrawRect(canvas);
        onDrawLine(canvas);
        canvasPath(canvas);
    }
    //绘制6个矩形
    private void onDrawRect(Canvas canvas) {
        for (int i = 0; i < 7; i++) {
            if (i%2==0) {
                mLinePaint.setColor(Color.parseColor("#eeeeee"));
            }else {
                mLinePaint.setColor(Color.parseColor("#ece1f3"));
            }
            float left  =(float) (mWidth * 0.4 / 2 + mPaintRectWidth * i + offsets);
            float right=(float) (mWidth * 0.4 / 2 + mPaintRectWidth* (i + 1));

            canvas.drawRect(left,(float)(mRectHeight*0.01),right, mHeight, mLinePaint);
        }

    }
    //绘制网格线
    private void onDrawLine(Canvas canvas){
        //第一条线
        canvas.drawLine(mPaintRectWidth-mPaintRectWidth/2, (float)(mRectHeight*0.01), getWidth(),  (float)(mRectHeight*0.01), mTextPaint);
        //定义这里高度
        float height;
        //横七条
        for (float i = 0; i < 7; i++) {
            //从上到下
            if (i==0) {
                height=i;
            }else {
                height=mRectHeight*(i/6);
                float size=mTextPaint.measureText(x[(int)i]+"");
                //绘制线
                canvas.drawLine(mPaintRectWidth+mPaintRectWidth/2, height, getWidth(),  height, mTextPaint);
                //绘制左边Y轴的文字
                canvas.drawText(x[(int)i]+"", (float)(mPaintRectWidth-mPaintRectWidth*0.35), height+size/5, mTextPaint);
            }

        }
        //竖七条
        canvas.drawLine((float) (mPaintRectWidth-mPaintRectWidth/2),0, (float) (mPaintRectWidth-mPaintRectWidth/2),  mHeight, mTextPaint);
        for (float i = 0; i < 7; i++) {
            //从左到右
            canvas.drawLine((float) (mWidth * 0.4 / 2 + mPaintRectWidth * i),0, (float) (mWidth * 0.4 / 2 + mPaintRectWidth * i),  mHeight, mTextPaint);
            //绘制底边的日期文字
            canvas.drawText(day[(int) i], (float) (mWidth * 0.34 / 2 + mPaintRectWidth * i), (float)(mHeight+mHeight*0.1), mTextPaint);
            //准备好下面折线图的X轴坐标
            xline.add((float) (mWidth * 0.4 / 2 + mPaintRectWidth * i));
        }
        //折线图的第一个点
        xline.add((float) (mPaintRectWidth-mPaintRectWidth/2));

    }
    //绘制折线路径
    public void canvasPath(Canvas canvas){

        for (int j = 0; j < yline.size(); j++) {

            float x=xline.get(j);
            float y =yline.get(j);
            float aftery=Math.initData(y);
            if (j==0) {
                mPath.moveTo(x,aftery );
            }else{

                mPath.lineTo(x,aftery );
            }
            canvas.drawPoint(x, aftery, mPointPaint);
            float size=mPointPaint.measureText(y+"");
            canvas.drawText(y+"", (float)(x-size/2), (float)(aftery+size*0.25), mPointPaint);
        }
        canvas.drawPath(mPath, mPathPaint);
    }
    //用于设置Y轴的坐标值
    public void setDataY( List<Float> yline) {
        this.yline.clear();
        this.yline=yline;
    }
    //一个更新UI的方法
    public void invalidata(){
        invalidate();
    }
}

MainActivity.java

/**
 * 小瓶盖 2016年7月13日18:08:28
 *
 * @author Android自定义View——实现理财类APP七日年化收益折线图效果
 *
 *相关博客地址 http://blog.csdn.net/qq_25193681
 */
public class MainActivity extends Activity {
    private AnnualyieldView annualyieldView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        annualyieldView=(AnnualyieldView) findViewById(R.id.line);
        initdata();
    }
    private void initdata() {
        List<Float> yline=new ArrayList<Float>();
        yline.add(2.420123f);
        yline.add(2.444122f);
        yline.add(2.45359f);
        yline.add(2.4206f);
        yline.add(2.4357f);
        yline.add(2.4228f);
        yline.add(2.4350f);
        annualyieldView.setDataY(yline);

    }

}

布局文件activity_main.xml:

<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="com.example.annualyield.MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
         android:layout_margin="10dip"
        android:orientation="vertical" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="#888888"
            android:textSize="18sp"
            android:text="七日年化收益率(%)"
            />

        <com.example.annualyield.AnnualyieldView
            android:id="@+id/line"
            android:layout_width="match_parent"
            android:layout_height="200dip"
            android:layout_marginTop="10dip"
            android:layout_marginBottom="10dip" />
    </LinearLayout>

</RelativeLayout>

还有一个用于转换的类,Math.java

/**
 * 小瓶盖 2016年7月13日18:08:28
 *
 * @author Android自定义View——实现理财类APP七日年化收益折线图效果
 *
 *相关博客地址 http://blog.csdn.net/qq_25193681
 */
public class Math {

    public static float initData(float a){

        if (2.40f<=a&&a<2.41f) {
            a=540f-540f/(a/0.01f);
        }else if(2.41f<=a&&a<2.42f){
            a=480f-480f/(a/0.01f);
        }else if (2.42f<=a&&a<2.43f) {
            a=360f-360f/(a/0.01f);
        }else if (2.43f<=a&&a<2.44f) {
            a=270f-270f/(a/0.01f);
        }else if (2.44f<=a&&a<2.45f) {
            a=180f-180f/(a/0.01f);
        }else if (2.45f<=a&&a<2.46f) {
            a=90f-90f/(a/0.01f);
        }

        return a;

    }
}

以上就是代码的全部内容,我记得我大学的老实说,不懂的地方就自己多敲代码,多了就懂了,虽然那时候我不认同,但是现在觉得还是有一定道理,都是一样过来的,现在希望能够跟大家一起进步。

最后我附上源码:

时间: 2024-12-22 03:19:16

Android自定义View——实现理财类APP七日年化收益折线图效果的相关文章

Android自定义View之上拉、下拉列表 头部元素跟随 缩放、平移效果的实现

滑动ListView列表然后 listView上边的视图 跟随着上拉或者下拉的距离 自动放大或者缩小  视图里边元素自动平移的效果 思路很简单 根据listView 的滑动距离去计算图片和文字应该平移的距离 例如顶部视图本来高度为500px  我们定义视图收缩到200px为最小高度,那么视图向上滑动的最大距离就为300px 当我们将列表向上滑动300px的时候试图就应该滑到最小高度了   继续上滑,不继续缩小视图高度即可 同理 下拉的时候 下拉300px 此时视图应该从200px高度变为500p

Android 自定义View视图

创建全新的视图将满足我们独特的UI需求. 本文介绍在指南针开发中会用到的罗盘的界面UI,通过继承View类实现的自定义视图,以此来深刻了解自定义视图. 实现效果图: 源代码: 布局文件activity_main(其中CompassView继承View类): <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.

android 自定义View【2】对话框取色&amp;色盘取色的实现

android 自定义View[2]对话框取色&色盘取色的实现    上一篇文章基本介绍了android自定义view的流程:继承view,复写view的一些方法.实现简单的自定义view.这篇文章主要介绍的是系统对话框取色功能,然后顺便介绍升级版,色盘取色[类似于ps中的吸管,对图片点击相应位置,获取那个位置的颜色]. 一.概述:通过该例子了解以下内容: 1.进一步了解android 自定义view. 2.知道如何获取图片上的颜色值. 3.监听屏幕touch,实现移动的时候自动取色.[onDr

Android自定义View——自定义搜索框(SearchView)

概述 在Android开发中,当系统数据项比较多时,常常会在app添加搜索功能,方便用户能快速获得需要的数据.搜索栏对于我们并不陌生,在许多app都能见到它,比如豌豆荚 在某些情况下,我们希望我们的自动补全信息可以不只是纯文本,还可以像豌豆荚这样,能显示相应的图片和其他数据信息,因此Android给我们提供的AutoCompleteTextView往往就不够用,在大多情况下我们都需要自己去实现搜索框. 分析 根据上面这张图,简单分析一下自定义搜索框的结构与功能,有 1. 搜索界面大致由三部门组成

Android自定义view学习笔记02

Android自定义view学习笔记02 本文代码来自于张鸿洋老师的博客之Android 自定义View (二) 进阶 学习笔记,对代码进行些许修改,并补充一些在coding过程中遇到的问题.学习的新东西. 相关代码 //CustomImageView.java package mmrx.com.myuserdefinedview.textview; import android.content.Context; import android.content.res.TypedArray; im

Android自定义view教程05--自定义view的基础知识之LayoutInflater详解

前面的教程我们讲了一下android的动画 的使用,尤其是着重讲解了属性动画的使用.那么这章开始我们将会讲一下android 自定义view的一些基础知识.帮助大家理解. 首先我们来关注一下LayoutInflater这个类,经常使用开源控件的人对这个类应该很熟悉了,但是很少有人能把这个类讲明白 用清楚,今天我们就来深挖一下这个类. 首先我们定义一个button.xml 和button2.xml 1 <?xml version="1.0" encoding="utf-8

Android 自定义View使用示例(三)

转载 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu [email protected] 前言: 通过Android 自定义View和Android 自定义View使用示例(二),我们知道了如何使用自定义的View,以及Android绘制View的理论基础,其包含三个过程,测量View大小(通过onMeasure()方法实现),计算View位置(通过onLayout()方法实现),最后开始绘制(

【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑][下文简称(五),请先阅读完(五)再阅读本文],我们通过示例和log来分析了Android的事件分发机制.这些,我们只是看到了现象,如果要进一步了解事件分发机制,这是不够的,我们还需要透过现象看本质,去研究研究源码.本文将从源码(基

【朝花夕拾】Android自定义View篇之(八)多点触控(上)基础知识

前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11155259.html],谢谢! 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2.0开始引入的,在现在使用的Android手机上都是支持多点触控的.本系列文章将对常见的多点触控相关的重点知识进行总结,并使用多点触控来实现一些常见的效果,从而达到将