全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

详解Android自定义View--自定义柱状图

绪论

转眼间,2016伴随着互联网寒冬和帝都的雾霾马上就过去了,不知道大家今年一整年过得怎么样?最近票圈被各个城市的雾霾刷屏,内心难免会动荡,庆幸自己早出来一年,也担忧着自己的未来的职业规划。无所谓了,既然选择了这个行业,我觉得大家就应该坚持下去,路是自己走的,及时再寒冬,只要你足够优秀,足够努力,相信你最后还是会找到自己满意的工作的。最后还要感谢今年博客之星大家对我的投票支持,非常感谢。不多说了,今天的主题是它–对,自定义View柱状图。

先来说说我最近在做什么吧?好久没有写博客了,最近手里有两个项目,闲的时候一直在忙着做项目,也封装了属于自己的一套Library,抽下来我会把它分享出来的。公司的项目也一直在忙,今天的柱状图就是公司的项目所用到的。先来看一下效果吧

具体实现

可以看到,今天的柱状图分为三类:双条竖向柱状图、单条竖向柱状图以及单条横向柱状图,其实原理都是一样的,下面我们具体看一下怎么实现,怎么去画一个这样的柱状图。

双条竖向

我们可以看到这个柱状图主要包括下面几个方面:

  • 双条柱状图
  • 横坐标月份
  • 点击tips显示具体数值
  • 灰色阴影(图上没有显示具体看代码)
  • 柱状图渐变、圆角、点击变色

好了上面五点就是需求和UI所提出来的所有东西,我们开始着手去“画”吧。

1.首先我们定义一些资源style供使用

包括

  • leftColor 左侧柱状图顶部颜色
  • leftColorBottom 左侧柱状图底部颜色
  • rightColor 右侧柱状图顶部颜色
  • rightColorBottom 右侧柱状图底部颜色
  • selectRightColor 左侧点击选中颜色
  • selectRightColor 右侧点击选中颜色
  • xyColor 横轴字体颜色

底部和顶部颜色是用于渐变用的

<declare-styleable name="MyChartView">
    <attr name="leftColor" format="color"></attr>
    <attr name="leftColorBottom" format="color"></attr>
    <attr name="selectLeftColor" format="color"></attr>
    <attr name="rightColor" format="color"></attr>
    <attr name="rightColorBottom" format="color"></attr>
    <attr name="selectRightColor" format="color"></attr>
    <attr name="xyColor" format="color"></attr>
  </declare-styleable>

2.接下来我们看具体代码,注释写的很详细了,仔细看:

  • 初始化属性、画笔、所用的size等
  • 测量计算高宽度等
  • 画坐标轴、画月份、画柱状图、画阴影
  • 柱状图渐变以及点击变色
  • touch点击事件判断点击所属哪个月份,接口回调给activity显示具体月份数值

注意:onWindowVisibilityChanged这个方法(当屏幕焦点变化时重新侧向起始位置,必须重写次方法,否则当焦点变化时柱状图会跑到屏幕外面)

下面主要说一下绘制部分吧

OnDraw()部分

我们将每次onTouch的条的索引放到selectIndexRoles数组中,然后当这个数组包含该绘制的柱状图的索引是我们设置不用颜色以及不设置渐变;

同时我们给每两个双条之间的的空白处绘制成阴影;

最后drawRoundRect()就绘制了一个圆角的矩形。

//画柱状图
    for (int i = 0; i < list.size(); i++) {
      int size = mHeight / 120;
      if (selectIndexRoles.contains(i)) {
        //偶数
        mChartPaint.setShader(null);
        if (i % 2 == 0) {
          mChartPaint.setColor(selectLeftColor);
        } else {
          mChartPaint.setColor(selectRightColor);
        }
      } else {
        //偶数
        if (i % 2 == 0) {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), lefrColorBottom, leftColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        } else {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), rightColorBottom, rightColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        }
      }

      mChartPaint.setStyle(Paint.Style.FILL);
      //画阴影
      if (i == number * 2 || i == number * 2 + 1) {
        mShadowPaint.setColor(Color.BLUE);
      } else {
        mShadowPaint.setColor(Color.WHITE);
      }

      //画柱状图
      RectF rectF = new RectF();
      rectF.left = mChartWidth;
      rectF.right = mChartWidth + mSize;
      rectF.bottom = mHeight - 100;
      rectF.top = (float) (mHeight - 100 - list.get(i) * size);
      canvas.drawRoundRect(rectF, 10, 10, mChartPaint);
      //canvas.drawRect(mChartWidth, mHeight - 100 - list.get(i) * size, mChartWidth + mSize, mHeight - 100, mChartPaint)
      // ;// 长方形
      mChartWidth += (i % 2 == 0) ? (3 + getWidth() / 39) : (getWidth() / 13 - 3 - mSize);
    }

全部代码

package com.hankkin.mycartdemo.chatview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.hankkin.mycartdemo.R;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Hankkin on 2016/12/10.
 */

public class MyChartView extends View {

  private int leftColor;//双柱左侧
  private int rightColor;//双柱右侧
  private int lineColor;//横轴线
  private int selectLeftColor;//点击选中左侧
  private int selectRightColor;//点击选中右侧
  private int lefrColorBottom;//左侧底部
  private int rightColorBottom;//右侧底部
  private Paint mPaint, mChartPaint, mShadowPaint;//横轴画笔、柱状图画笔、阴影画笔
  private int mWidth, mHeight, mStartWidth, mChartWidth, mSize;//屏幕宽度高度、柱状图起始位置、柱状图宽度
  private Rect mBound;
  private List<Float> list = new ArrayList<>();//柱状图高度占比
  private Rect rect;//柱状图矩形
  private getNumberListener listener;//点击接口
  private int number = 1000;//柱状图最大值
  private int selectIndex = -1;//点击选中柱状图索引
  private List<Integer> selectIndexRoles = new ArrayList<>();

  public void setList(List<Float> list) {
    this.list = list;
    mSize = getWidth() / 39;
    mStartWidth = getWidth() / 13;
    mChartWidth = getWidth() / 13 - mSize - 3;
    invalidate();
  }

  public void setListener(getNumberListener listener) {
    this.listener = listener;
  }

  public MyChartView(Context context) {
    this(context, null);
  }

  public MyChartView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public MyChartView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //获取我们自定义的样式属性
    TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyChartView, defStyleAttr, 0);
    int n = array.getIndexCount();
    for (int i = 0; i < n; i++) {
      int attr = array.getIndex(i);
      switch (attr) {
        case R.styleable.MyChartView_leftColor:
          // 默认颜色设置为黑色
          leftColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_selectLeftColor:
          // 默认颜色设置为黑色
          selectLeftColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_rightColor:
          rightColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_selectRightColor:
          selectRightColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_xyColor:
          lineColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_leftColorBottom:
          lefrColorBottom = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_rightColorBottom:
          rightColorBottom = array.getColor(attr, Color.BLACK);
          break;
      }
    }
    array.recycle();
    init();
  }

  //初始化画笔
  private void init() {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mBound = new Rect();
    mChartPaint = new Paint();
    mChartPaint.setAntiAlias(true);
    mShadowPaint = new Paint();
    mShadowPaint.setAntiAlias(true);
    mShadowPaint.setColor(Color.WHITE);
  }

  //测量高宽度
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width;
    int height;
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    if (widthMode == MeasureSpec.EXACTLY) {
      width = widthSize;
    } else {
      width = widthSize * 1 / 2;
    }
    if (heightMode == MeasureSpec.EXACTLY) {
      height = heightSize;
    } else {
      height = heightSize * 1 / 2;
    }

    setMeasuredDimension(width, height);
  }

  //计算高度宽度
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    mWidth = getWidth();
    mHeight = getHeight();
    mStartWidth = getWidth() / 13;
    mSize = getWidth() / 39;
    mChartWidth = getWidth() / 13 - mSize;
  }

  //重写onDraw绘制
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    mPaint.setColor(lineColor);
    //画坐标轴
    //canvas.drawLine(0, mHeight - 100, mWidth, mHeight - 100, mPaint);
    for (int i = 0; i < 12; i++) {
      //画刻度线
      //canvas.drawLine(mStartWidth, mHeight - 100, mStartWidth, mHeight - 80, mPaint);
      //画数字
      mPaint.setTextSize(35);
      mPaint.setTextAlign(Paint.Align.CENTER);
      mPaint.getTextBounds(String.valueOf(i + 1) + "", 0, String.valueOf(i).length(), mBound);
      canvas.drawText(String.valueOf(i + 1) + "月", mStartWidth - mBound.width() * 1 / 2,
        mHeight - 60 + mBound.height() * 1 / 2, mPaint);
      mStartWidth += getWidth() / 13;
    }
    //画柱状图
    for (int i = 0; i < list.size(); i++) {
      int size = mHeight / 120;
      if (selectIndexRoles.contains(i)) {
        //偶数
        mChartPaint.setShader(null);
        if (i % 2 == 0) {
          mChartPaint.setColor(selectLeftColor);
        } else {
          mChartPaint.setColor(selectRightColor);
        }
      } else {
        //偶数
        if (i % 2 == 0) {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), lefrColorBottom, leftColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        } else {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), rightColorBottom, rightColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        }
      }

      mChartPaint.setStyle(Paint.Style.FILL);
      //画阴影
      if (i == number * 2 || i == number * 2 + 1) {
        mShadowPaint.setColor(Color.BLUE);
      } else {
        mShadowPaint.setColor(Color.WHITE);
      }

      //画柱状图
      RectF rectF = new RectF();
      rectF.left = mChartWidth;
      rectF.right = mChartWidth + mSize;
      rectF.bottom = mHeight - 100;
      rectF.top = (float) (mHeight - 100 - list.get(i) * size);
      canvas.drawRoundRect(rectF, 10, 10, mChartPaint);
      //canvas.drawRect(mChartWidth, mHeight - 100 - list.get(i) * size, mChartWidth + mSize, mHeight - 100, mChartPaint)
      // ;// 长方形
      mChartWidth += (i % 2 == 0) ? (3 + getWidth() / 39) : (getWidth() / 13 - 3 - mSize);
    }
  }

  @Override
  public void onWindowFocusChanged(boolean hasWindowFocus) {
    super.onWindowFocusChanged(hasWindowFocus);
    if (hasWindowFocus) {

    }
  }

  /**
   * 注意:
   * 当屏幕焦点变化时重新侧向起始位置,必须重写次方法,否则当焦点变化时柱状图会跑到屏幕外面
   */

  @Override
  protected void onWindowVisibilityChanged(int visibility) {
    super.onWindowVisibilityChanged(visibility);
    if (visibility == VISIBLE) {
      mSize = getWidth() / 39;
      mStartWidth = getWidth() / 13;
      mChartWidth = getWidth() / 13 - mSize - 3;
    }
  }

  /**
   * 柱状图touch事件
   * 获取触摸位置计算属于哪个月份的
   * @param ev
   * @return
   */
  @Override
  public boolean onTouchEvent(MotionEvent ev) {

    int x = (int) ev.getX();
    int y = (int) ev.getY();
    int left = 0;
    int top = 0;
    int right = mWidth / 12;
    int bottom = mHeight - 100;
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        for (int i = 0; i < 12; i++) {
          rect = new Rect(left, top, right, bottom);
          left += mWidth / 12;
          right += mWidth / 12;
          if (rect.contains(x, y)) {
            listener.getNumber(i, x, y);
            number = i;
            selectIndex = i;
            selectIndexRoles.clear();
            ;
            selectIndexRoles.add(selectIndex * 2 + 1);
            selectIndexRoles.add(selectIndex * 2);
            invalidate();
          }
        }
        break;
      case MotionEvent.ACTION_UP:

        break;
    }
    return true;
  }

  public interface getNumberListener {
    void getNumber(int number, int x, int y);
  }

  public int getLeftColor() {
    return leftColor;
  }

  public void setLeftColor(int leftColor) {
    this.leftColor = leftColor;
  }

  public int getRightColor() {
    return rightColor;
  }

  public void setRightColor(int rightColor) {
    this.rightColor = rightColor;
  }

  public int getLineColor() {
    return lineColor;
  }

  public void setLineColor(int lineColor) {
    this.lineColor = lineColor;
  }

  public int getSelectLeftColor() {
    return selectLeftColor;
  }

  public void setSelectLeftColor(int selectLeftColor) {
    this.selectLeftColor = selectLeftColor;
  }

  public int getSelectRightColor() {
    return selectRightColor;
  }

  public void setSelectRightColor(int selectRightColor) {
    this.selectRightColor = selectRightColor;
  }

  public int getLefrColorBottom() {
    return lefrColorBottom;
  }

  public void setLefrColorBottom(int lefrColorBottom) {
    this.lefrColorBottom = lefrColorBottom;
  }

  public int getRightColorBottom() {
    return rightColorBottom;
  }

  public void setRightColorBottom(int rightColorBottom) {
    this.rightColorBottom = rightColorBottom;
  }
}

3.具体使用:

private void initChatView() {

    myChartView.setLefrColorBottom(getResources().getColor(R.color.leftColorBottom));
    myChartView.setLeftColor(getResources().getColor(R.color.leftColor));
    myChartView.setRightColor(getResources().getColor(R.color.rightColor));
    myChartView.setRightColorBottom(getResources().getColor(R.color.rightBottomColor));
    myChartView.setSelectLeftColor(getResources().getColor(R.color.selectLeftColor));
    myChartView.setSelectRightColor(getResources().getColor(R.color.selectRightColor));
    myChartView.setLineColor(getResources().getColor(R.color.xyColor));
    chartList = new ArrayList<>();

    relativeLayout = (RelativeLayout) findViewById(R.id.linearLayout);
    relativeLayout.removeView(llChart);
    Random random = new Random();
    while (chartList.size() < 24) {
      int randomInt = random.nextInt(100);
      chartList.add((float) randomInt);
    }
    myChartView.setList(chartList);
    myChartView.setListener(new MyChartView.getNumberListener() {
      @Override
      public void getNumber(int number, int x, int y) {
        relativeLayout.removeView(llChart);
        //反射加载点击柱状图弹出布局
        llChart = (LinearLayout) LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_shouru_zhichu, null);
        TextView tvZhichu = (TextView) llChart.findViewById(R.id.tv_zhichu);
        TextView tvShouru = (TextView) llChart.findViewById(R.id.tv_shouru);
        tvZhichu.setText((number + 1) + "月支出" + " " + chartList.get(number * 2));
        tvShouru.setText ( "收入: " + chartList.get(number * 2 + 1));
        llChart.measure(0, 0);//调用该方法后才能获取到布局的宽度
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
          RelativeLayout.LayoutParams.WRAP_CONTENT);
        params.leftMargin = x - 100;
        if (x - 100 < 0) {
          params.leftMargin = 0;
        } else if (x - 100 > relativeLayout.getWidth() - llChart.getMeasuredWidth()) {
          //设置布局距左侧屏幕宽度减去布局宽度
          params.leftMargin = relativeLayout.getWidth() - llChart.getMeasuredWidth();
        }
        llChart.setLayoutParams(params);
        relativeLayout.addView(llChart);
      }
    });
  }

经过以上步骤,我们的双条竖向柱状图就绘制完成了,也显示出来了。其实自己坐下来仔细拿笔算一下,画一下,也没有想象的那么难。至于单条和横向的实现原理都一样,比这个要简单的多。哦对了,横向的我只是自定义了一个横向的柱状图View,然后用ListView显示的各个部门的具体内容。

代码下载:demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# android  # 柱状图  # 自定义柱状图  # Android自定义柱状图表的方法实例  # Android实现简易的柱状图和曲线图表实例代码  # Android自定义view实现动态柱状图  # 100行Android代码轻松实现带动画柱状图  # Android自定义带增长动画和点击弹窗提示效果的柱状图DEMO  # MPAndroidChart开源图表库的使用介绍之饼状图、折线图和柱状图  # Android自定义圆角柱状图  # 自己的  # 自定义  # 重写  # 单条  # 跑到  # 可以看到  # 看一下  # 设置为  # 先来  # 画一  # 圆角  # 互联网  # 好了  # 我会  # 我觉得  # 说了  # 做什么  # 不多  # 把它 


相关文章: 如何在Ubuntu系统下快速搭建WordPress个人网站?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  如何快速搭建个人网站并优化SEO?  如何快速搭建安全的FTP站点?  成都响应式网站开发,dw怎么把手机适应页面变成网页?  c# await 一个已经完成的Task会发生什么  在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?  建站之星北京办公室:智能建站系统与小程序生成方案解析  建站之星价格显示格式升级,你的预算足够吗?  品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?  如何在沈阳梯子盘古建站优化SEO排名与功能模块?  如何在云主机上快速搭建网站?  建站之星2.7模板:企业网站建设与h5定制设计专题  建站之星下载版如何获取与安装?  宿州网站制作公司兴策,安徽省低保查询网站?  建站VPS能否同时实现高效与安全翻墙?  如何在局域网内绑定自建网站域名?  三星网站视频制作教程下载,三星w23网页如何全屏?  如何快速搭建高效WAP手机网站吸引移动用户?  在线教育网站制作平台,山西立德教育官网?  中山网站制作网页,中山新生登记系统登记流程?  齐河建站公司:营销型网站建设与SEO优化双核驱动策略  Python lxml的etree和ElementTree有什么区别  建站之星IIS配置教程:代码生成技巧与站点搭建指南  如何快速查询域名建站关键信息?  如何用低价快速搭建高质量网站?  制作网站外包平台,自动化接单网站有哪些?  如何零基础在云服务器搭建WordPress站点?  设计网站制作公司有哪些,制作网页教程?  网站制作模板下载什么软件,ppt模板免费下载网站?  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  教程网站设计制作软件,怎么创建自己的一个网站?  如何在Windows虚拟主机上快速搭建网站?  深圳 网站制作,深圳招聘网站哪个比较好一点啊?  建站主机空间推荐 高性价比配置与快速部署方案解析  沈阳个人网站制作公司,哪个网站能考到沈阳事业编招聘的信息?  北京制作网站的公司,北京铁路集团官方网站?  如何在万网自助建站中设置域名及备案?  如何选择CMS系统实现快速建站与SEO优化?  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  浅谈Javascript中的Label语句  电影网站制作价格表,那些提供免费电影的网站,他们是怎么盈利的?  建站之星安全性能如何?防护体系能否抵御黑客入侵?  如何快速搭建响应式可视化网站?  网站制作软件免费下载安装,有哪些免费下载的软件网站?  如何通过FTP服务器快速搭建网站?  深圳网站制作培训,深圳哪些招聘网站比较好?  如何快速生成高效建站系统源代码?  小米网站链接制作教程,请问miui新增网页链接调用服务有什么用啊?  如何选择长沙网站建站模板?H5响应式与品牌定制哪个更优? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。