The introduction

* In a blink of an eye, the winter of 2016 with the Internet and the haze of the imperial capital will soon be over. I wonder how you have been this year? Recently, the ticket circle was flooded with smog in various cities. It was inevitable that my heart would be in turmoil. I felt glad that I had come out a year earlier, and ALSO worried about my future career planning. It doesn’t matter, since you have chosen this industry, I think we should stick to it, the road is their own way, even in the winter, as long as you are good enough, enough hard work, I believe you will eventually find their own satisfactory job. And finally, I’d like to thank all of you who voted for me this year. Thank you so much. So without further ado, today’s topic is that — yes, custom View bar diagrams. Why don’t you tell me what I’ve been doing? I haven’t written my blog for a long time. Recently I have two projects in my hand, and I have been busy with them in my spare time. I have encapsulated my own Library, and I will share it with you. The company’s project has also been busy, and today’s bar chart is used in the company’s project. Let’s take a look at the effect


*

background

At the beginning of the demand, I went to the Internet to find some open source projects, such as MpChart HelloCharts – Android AndroidCharts and so on. However, in the end, WHY DID I choose to customize? Firstly, these open source libraries are very “heavy” and many of them are useless. Second, there are few double-bar bars; Three to also want to improve their own, a careful study found that it is not so difficult. If you are interested, check out the open source charts, but I have thought about modifying them based on the above ones and found it difficult to do so.


The specific implementation

As can be seen, today’s bar chart is divided into three categories: double vertical bar chart, single vertical bar chart and single horizontal bar chart. In fact, the principle is the same. Let’s see how to implement and draw such a bar chart in detail.

Two vertical

We can see that the bar chart mainly includes the following aspects:

  1. Double bar chart
  2. Abscissa month
  3. Click tips to display specific values
  4. Grey shadow (the code is not shown)
  5. Histogram gradient, rounded corner, click color change

Ok, so the above five points are all that the requirements and UI bring up, so let’s start drawing.


1. First we define some resource styles for use

Include leftColor Top color of the left bar chart leftColorBottom bottom color of the left bar chart rightColor Top color of the right bar chart rightColorBottom bottom color of the right bar chart selectRightColor Click on the left side to select the color selectRightColor and click on the right side to select the color xyColor

The bottom and top colors are used for gradients


        
        
        
        
        
        
        
    
    Copy the code

2. Now let’s look at the specific code. The comments are very detailed.

  • Initialize properties, brushes, size used, and so on
  • Measure and calculate the height and width, etc
  • Draw axes, draw months, draw bars, draw shadows
  • Histogram gradient and click color change
  • The touch click event determines which month the click belongs to, and the interface callback displays the specific month value to the activity

    Note: this method onWindowVisibilityChanged (when screen focus changes to lateral starting position, must be rewritten time method, or when focus change histogram will run to the outside of the screen)Copy the code

Let’s focus on the drawing part

Ontouch ()

We put the index of each onTouch bar into the selectIndexRoles array, and then when that array contains the index of the drawn bar we set no color and no gradient; We also shaded the space between each pair of bars; Finally, drawRoundRect () draws a rounded rectangle.

For (int I = 0; i < list.size(); i++) { int size = mHeight / 120; If (selectIndexRoles. The contains (I)) {/ / even mChartPaint setShader (null); if (i % 2 == 0) { mChartPaint.setColor(selectLeftColor); } else { mChartPaint.setColor(selectRightColor); }} else {LinearGradient LG = new LinearGradient(mChartWidth, mChartWidth + mSize, 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); / / draw a shadow 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); }Copy the code
All the code
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 tangyangkai on 2016/12/2.
 */

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 list = new ArrayList<>();//柱状图高度占比
    private Rect rect;//柱状图矩形
    private getNumberListener listener;//点击接口
    private int number = 1000;//柱状图最大值
    private int selectIndex = -1;//点击选中柱状图索引
    private List selectIndexRoles = new ArrayList<>();

    public void setList(List 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;
    }
}Copy the code

3. Specific use:

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 (" income: "+ chartlist. get(number * 2 + 1)); llChart.measure(0, 0); / / call the method to get the width of the layout 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 ()) {// Set the layout width to the left screen minus the layout width params.leftMargin = relativeLayout.getWidth() - llChart.getMeasuredWidth(); } llChart.setLayoutParams(params); relativeLayout.addView(llChart); }}); }Copy the code

After the above steps, our two vertical bars are drawn and displayed. In fact, they sit down and carefully take a pen to calculate, draw, it is not so difficult to imagine. As for the single bar and horizontal implementation principle is the same, much simpler than this. By the way, I just customize a horizontal bar View and use ListView to display the specific content of each department. Ok, finally, thanks to my friend Young_Kai for helping me a lot. You can also check out his blog, which is very good.

I have uploaded the code to my Github, welcome everyone star, fork: github.com/Hankkin/MyC…

Finally, I recommend the MPChart analyzed by Lao Liu

blog.csdn.net/qq_26787115