美文网首页
2019-03-05 自定义View绘制线性表

2019-03-05 自定义View绘制线性表

作者: j江柯 | 来源:发表于2019-03-05 09:33 被阅读0次

          自定义View绘图,学的就是用的,怎么方便怎么来,就像随手涂鸦一样,以后都不会再

    拾起,所以没怎么封装,也不标准。
    第一次写个稍微完整点的例子,写的不好还请多多指教..

    1、先来效果图:

    图1 图2 素材1 素材2 素材3

    2、图1的源码:

    package com.lipy.linechart;

    import android.content.Context;

    import android.graphics.Canvas;

    import android.graphics.Color;

    import android.graphics.Paint;

    import android.support.annotation.Nullable;

    import android.util.AttributeSet;

    import android.util.TypedValue;

    import android.view.View;

    import android.widget.Switch;

    import java.util.ArrayList;

    import java.util.List;

    public class TrendLineChartView extends View {

        private List<Integer> trendData;    //走势数据

        private Paint redPaint,blackPaint,greenPaint; //圆点画笔

        private Paint realLinePaint,dashLinePaint;  //实线和灰线画笔

        private int defaultPadding = this.dp2px(getContext(),5); //默认内边距

        private int dotRadius = this.dp2px(getContext(),4);    //圆点的半径

        /**

        * 设置走势数据

        * 列表首项是最远天数,画在图表的最左边

        * 数据值对应  1:红点,0:绿点,-1:黑点

        */

        public void setTrendData(List<Integer> trendData){

            this.trendData = trendData;

            invalidate();

        }

        public TrendLineChartView(Context context) {

            this(context,null);

        }

        public TrendLineChartView(Context context, @Nullable AttributeSet attrs) {

            super(context, attrs);

            init();

        }

        private void init() {

            dashLinePaint = new Paint();

            dashLinePaint.setColor(Color.GRAY);

            dashLinePaint.setAlpha(100);

            dashLinePaint.setStrokeWidth(1.5f);

            realLinePaint = new Paint(dashLinePaint);

            realLinePaint.setColor(Color.parseColor("#555555"));

            realLinePaint.setAntiAlias(true);

            realLinePaint.setStrokeWidth(1.8f);

            redPaint = new Paint(realLinePaint);

            redPaint.setColor(Color.RED);

            redPaint.setStyle(Paint.Style.FILL);

            greenPaint = new Paint(redPaint);

            greenPaint.setColor(Color.GREEN);

            blackPaint = new Paint(greenPaint);

            blackPaint.setColor(Color.BLACK);

        }

        @Override

        protected void onDraw(Canvas canvas) {

            super.onDraw(canvas);

            drawBackground(canvas);

            drawData(canvas);

        }

        //绘制数据

        private void drawData(Canvas canvas) {

            int width = canvas.getWidth();

            int height = canvas.getHeight();

            if (trendData == null){

                return;

            }

            if (trendData.size() <= 1){

                //数据只有一条时,默认画在中间

                canvas.drawCircle(width/2,getDrawHeight(height,trendData.get(0)),dotRadius,getPaint(trendData.get(0)));

            }

            else{

                float space = (width-defaultPadding*2)/(trendData.size()-1);

                //画折线

                for (int i = 0; i < trendData.size()-1; i++) {

                    canvas.drawLine(defaultPadding+i*space,getDrawHeight(height,trendData.get(i)),defaultPadding+(i+1)*space,getDrawHeight(height,trendData.get(i+1)),realLinePaint);

                }

                //画圆点

                for (int i = 0; i < trendData.size(); i++) {

                    canvas.drawCircle(defaultPadding+i*space,getDrawHeight(height,trendData.get(i)),dotRadius,getPaint(trendData.get(i)));

                }

            }

        }

        //画三条虚线组成的背景

        private void drawBackground(Canvas canvas) {

            int width = canvas.getWidth();

            int height = canvas.getHeight();

            canvas.drawLine(defaultPadding,defaultPadding,width-defaultPadding,defaultPadding,dashLinePaint);

            canvas.drawLine(defaultPadding,height/2,width-defaultPadding,height/2,dashLinePaint);

            canvas.drawLine(defaultPadding,height-defaultPadding,width-defaultPadding,height-defaultPadding,dashLinePaint);

        }

        /**

        * 更具trendData数据获得对应颜色的画笔

        */

        private Paint getPaint(int result){

            if (result == -1){

                return blackPaint;

            }

            else if (result == 0){

                return  greenPaint;

            }

            else if (result == 1){

                return redPaint;

            }

            return greenPaint;

        }

        /**

        * @param height    控件的总高度

        * @param result    数据的走势

        * @return  计算出该圆点的Y坐标

        */

        private int getDrawHeight(int height,int result){

            if (result == -1){

                return height - defaultPadding;

            }

            else if (result == 0){

                return  height/2;

            }

            else if (result == 1){

                return defaultPadding;

            }

            return height/2;

        }

        private int dp2px(Context context, int dip) {

            final float scale = context.getResources().getDisplayMetrics().density;

            return (int) (dip * scale + 0.5f);

        }

        private int sp2px(Context context, float spValue) {

            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());

        }

    }

    3、图2的源码:

    package com.lipy.linechart;

    import android.content.Context;

    import android.graphics.Bitmap;

    import android.graphics.BitmapFactory;

    import android.graphics.BitmapShader;

    import android.graphics.Canvas;

    import android.graphics.Color;

    import android.graphics.LinearGradient;

    import android.graphics.Paint;

    import android.graphics.Path;

    import android.graphics.Rect;

    import android.graphics.Shader;

    import android.graphics.drawable.ShapeDrawable;

    import android.graphics.drawable.shapes.PathShape;

    import android.support.annotation.Nullable;

    import android.util.AttributeSet;

    import android.util.Log;

    import android.util.TypedValue;

    import android.view.View;

    import java.util.Arrays;

    import java.util.List;

    public class HitRateLineGridView extends View {

        private List<Integer> hitRateData;    //命中率数据,  固定的4条数据。

        private String[] xLabels;  //x轴的标签

        private int maxIndex = 0;  //命中率最高的一项的索引,设置数据时就会计算

        private Bitmap bmLeftTip, bmCenterTip,bmRightTip;      //位于三个不同方位的提示图片

        private Paint hollowCirclePaint,solidCirclePaint; //空心元和实心圆画笔

        private Paint labelFontPaint,tipFontPaint;  //标签文字画笔和提示文字画笔

        private Paint realLinePaint,dashLinePaint;  //折线画笔和背景灰线画笔

        private Paint shaderBgPaint;    //阴影背景画笔

        private int dataColor = Color.parseColor("#d92e48"); //数据颜色,红色

        private int defaultPadding = this.dp2px(getContext(),20); //默认边距

        private int fontSize = this.sp2px(getContext(),8); //默认字体颜色

        private Path shaderPath;    //渐变阴影的路径,在绘制折线的时候记录路径

        /**

        * 设置命中率数据,取最近的7天

        * 不需要这个方法,已标记为过时

        */

        @Deprecated

        public void setHitRateData(List<Integer> hitRateData){

            this.hitRateData = hitRateData;

            if (hitRateData.size()<=7){

                //this.hitRateData = hitRateData;

                //++计算统计后再赋值给 hitRateData变量

            }

            else{

                //this.hitRateData = hitRateData.subList(hitRateData.size() - 7,hitRateData.size());

                //++计算统计后再赋值给 hitRateData变量

            }

            invalidate();

        }

        /**

        * 设置命中率数据,已经统计过的数据,注意!只接收固定的4条数据

        *分别是近7天的命中率,近5天的命中率,近3天的命中率,和近2天的命中率

        */

        public void setHitRateDataCounted(List<Integer> hitRateData){

            this.hitRateData = hitRateData;

            for (int i = 0; i < hitRateData.size(); i++) {

                if (hitRateData.get(maxIndex) < hitRateData.get(i)){

                    maxIndex = i;

                }

            }

            invalidate();

        }

        /**

        * @param xLabels x轴底部的标签,最多取前4个

        */

        private void setXLabels(String[] xLabels){

            if (xLabels.length<=4){

                this.xLabels = xLabels;

            }

            else{

                this.xLabels = Arrays.copyOfRange(xLabels,0,4);

            }

            invalidate();

        }

        public HitRateLineGridView(Context context) {

            this(context,null);

        }

        public HitRateLineGridView(Context context, @Nullable AttributeSet attrs) {

            super(context, attrs);

            init();

        }

        private void init() {

            dashLinePaint = new Paint();

            dashLinePaint.setColor(Color.GRAY);

            dashLinePaint.setAlpha(100);

            dashLinePaint.setStrokeWidth(1.5f);

            labelFontPaint = new Paint();

            labelFontPaint.setColor(Color.GRAY);

            labelFontPaint.setTextSize(fontSize);

            setXLabels(new String[]{"近7","近5","近3","近2"} );

            realLinePaint = new Paint();

            realLinePaint.setColor(dataColor);

            realLinePaint.setStrokeWidth(4f);

            realLinePaint.setAntiAlias(true);

            solidCirclePaint = new Paint(realLinePaint);

            solidCirclePaint.setStrokeWidth(1.5f);

            //solidCirclePaint.setStyle(Paint.Style.FILL);    //设置实心

            hollowCirclePaint = new Paint(realLinePaint);

            hollowCirclePaint.setColor(Color.WHITE);    //采取在一个大实心圆里画一个小白圆来实现空心圆效果的策略

            hollowCirclePaint.setStrokeWidth(1.8f);

            //hollowCirclePaint.setStyle(Paint.Style.STROKE); //设置空心

            tipFontPaint = new Paint();

            tipFontPaint.setTextSize(sp2px(getContext(),10));

            tipFontPaint.setColor(Color.WHITE);

            bmLeftTip = getTipBitmap(R.drawable.bubble_red_left,defaultPadding * 3,defaultPadding);

            bmCenterTip = getTipBitmap(R.drawable.bubble_red_mid,defaultPadding * 3,defaultPadding);

            bmRightTip = getTipBitmap(R.drawable.bubble_red_right,defaultPadding * 3,defaultPadding);

            shaderPath = new Path();

            shaderBgPaint = new Paint();

            shaderBgPaint.setAntiAlias(true);

            shaderBgPaint.setStyle(Paint.Style.FILL);

        }

        @Override

        protected void onDraw(Canvas canvas) {

            super.onDraw(canvas);

            drawBackground(canvas);

            drawXLabels(canvas);

            drawData(canvas);

        }

        //绘制渐变阴影

        private void drawShader(Canvas canvas) {

            int width = canvas.getWidth();

            int height = canvas.getHeight();

            LinearGradient lg = new LinearGradient(width/2,height,width/2,0,

                Color.parseColor("#00d92e48"),

                Color.parseColor("#99d92e48"),

                Shader.TileMode.CLAMP);// CLAMP重复最后一个颜色至最后

            shaderBgPaint.setShader(lg);

            canvas.drawPath(shaderPath,shaderBgPaint);

        }

        //绘制数据

        private void drawData(Canvas canvas) {

            int width = canvas.getWidth();

            int height = canvas.getHeight();

            int gridHeight = height - defaultPadding*2;

            if (hitRateData == null){

                return;

            }

            float w_space = (width-defaultPadding*2)/3;

            //画折线

            for (int i = 0; i < 3; i++) {

                float startX = defaultPadding+i*w_space;

                float startY = (100-hitRateData.get(i)) *gridHeight/100 + defaultPadding;

                float stopX = defaultPadding+(i+1)*w_space;

                float stopY = (100-hitRateData.get(i+1))*gridHeight/100 + defaultPadding;

                canvas.drawLine(startX,startY,stopX,stopY,realLinePaint);

                //阴影

                if (i == 0){

                    shaderPath.moveTo(startX,startY);

                }

                else if(i == 1){

                    shaderPath.lineTo(startX,startY);

                }

                else if (i == 2){

                    shaderPath.lineTo(startX,startY);

                    shaderPath.lineTo(stopX,stopY);

                    shaderPath.lineTo(stopX,height-defaultPadding);

                    shaderPath.lineTo(defaultPadding,height-defaultPadding);

                    shaderPath.close();

                }

            }

            //画阴影

            drawShader(canvas);

            //画圆点

            for (int i = 0; i < 4; i++) {

                if (i == maxIndex){    //最大项单独处理

                    //画空心圆

                    canvas.drawCircle(defaultPadding+i*w_space,(100-hitRateData.get(i))*gridHeight/100 + defaultPadding,dp2px(getContext(),5),solidCirclePaint);

                    canvas.drawCircle(defaultPadding+i*w_space,(100-hitRateData.get(i))*gridHeight/100 + defaultPadding,dp2px(getContext(),3),hollowCirclePaint);

                    //画提示图

                    int tipTopValue = (100-hitRateData.get(i))*gridHeight/100 - dp2px(getContext(),6);  //提示图y轴方向的位置

                    if (maxIndex == 0){

                        float tipLeftValue = i*w_space + dp2px(getContext(),1);

                        canvas.drawBitmap(bmLeftTip,tipLeftValue , tipTopValue, tipFontPaint);

                        canvas.drawText("命中率"+hitRateData.get(i)+"%", tipLeftValue + dp2px(getContext(),3), tipTopValue + dp2px(getContext(),12), tipFontPaint);

                    }

                    else if (maxIndex == 1){

                        float tipLeftValue = i*w_space - defaultPadding/2;

                        canvas.drawBitmap(bmCenterTip, tipLeftValue, tipTopValue, tipFontPaint);

                        canvas.drawText("命中率"+hitRateData.get(i)+"%", tipLeftValue + dp2px(getContext(),3), tipTopValue  + dp2px(getContext(),12), tipFontPaint);

                    }

                    else if(maxIndex == 2){

                        float tipLeftValue = i*w_space - defaultPadding/2;

                        canvas.drawBitmap(bmCenterTip, tipLeftValue, tipTopValue, tipFontPaint);

                        canvas.drawText("命中率"+hitRateData.get(i)+"%", tipLeftValue + dp2px(getContext(),3), tipTopValue  + dp2px(getContext(),12), tipFontPaint);

                    }

                    else if (maxIndex == 3){

                        float tipLeftValue = i*w_space - defaultPadding - dp2px(getContext(),1);

                        canvas.drawBitmap(bmRightTip, tipLeftValue, tipTopValue, tipFontPaint);

                        canvas.drawText("命中率"+hitRateData.get(i)+"%", tipLeftValue + dp2px(getContext(),3), tipTopValue  + dp2px(getContext(),12), tipFontPaint);

                    }

                } else{

                    canvas.drawCircle(defaultPadding+i*w_space,(100-hitRateData.get(i))*gridHeight/100 + defaultPadding,dp2px(getContext(),3),solidCirclePaint);

                }

            }

        }

        //绘制x轴下方的标签

        private void drawXLabels(Canvas canvas) {

            int width = canvas.getWidth();

            int height = canvas.getHeight();

            if (xLabels == null){

                return;

            }

            int space = (width-defaultPadding - defaultPadding) / 3;

            Path path = new Path();

            for (int i = 0; i < xLabels.length; i++) {

                // path.reset();    //用路径也没法实现垂直绘制文字,得拆分了画

                // path.moveTo(i*space + defaultPadding,height - defaultPadding + 3);

                //path.lineTo(i*space + defaultPadding,height - 3);

                // canvas.drawTextOnPath(xLabels[i],path,0f,0f,labelFontPaint);

                canvas.drawText(xLabels[i],i*space + defaultPadding/2 + 3,height- defaultPadding/2 + 3,labelFontPaint);

            }

        }

        //绘制背景4*3表格,

        private void drawBackground(Canvas canvas) {

            int width = canvas.getWidth();

            int height = canvas.getHeight();

            //画5横

            canvas.drawLine(defaultPadding,defaultPadding,width-defaultPadding,defaultPadding,dashLinePaint);

            canvas.drawLine(defaultPadding,(height - defaultPadding - defaultPadding)/4 + defaultPadding ,width-defaultPadding,(height - defaultPadding - defaultPadding)/4 + defaultPadding,dashLinePaint);

            canvas.drawLine(defaultPadding,height/2 ,width-defaultPadding, height/2,dashLinePaint);

            canvas.drawLine(defaultPadding,(height - defaultPadding - defaultPadding)*3/4 + defaultPadding ,width-defaultPadding,(height - defaultPadding - defaultPadding)*3/4 + defaultPadding,dashLinePaint);

            canvas.drawLine(defaultPadding,height-defaultPadding,width-defaultPadding,height-defaultPadding,dashLinePaint);

            //画4竖

            canvas.drawLine(defaultPadding,defaultPadding,defaultPadding,height - defaultPadding,dashLinePaint);

            canvas.drawLine((width - defaultPadding - defaultPadding) / 3 + defaultPadding,defaultPadding,(width - defaultPadding - defaultPadding) / 3 + defaultPadding,height - defaultPadding,dashLinePaint);

            canvas.drawLine((width - defaultPadding - defaultPadding) *2/ 3 + defaultPadding,defaultPadding,(width - defaultPadding - defaultPadding)*2 / 3 + defaultPadding,height - defaultPadding,dashLinePaint);

            canvas.drawLine(width-defaultPadding,defaultPadding,width-defaultPadding,height - defaultPadding,dashLinePaint);

        }

        /**

        * @param sourceId  图片的资源id

        * @param width  指定宽度

        * @param height  指定高度

        * @return  适应控件大小的图片

        */

        private Bitmap getTipBitmap(int sourceId,int width,int height){

            Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),sourceId);

            if (bitmap.getWidth() == width && bitmap.getHeight() == height){    //写这个判断,避createScaledBitmap()的坑

                return bitmap;

            }

            return Bitmap.createScaledBitmap(bitmap,width,height,true);

        }

        private int dp2px(Context context, int dip) {

            final float scale = context.getResources().getDisplayMetrics().density;

            return (int) (dip * scale + 0.5f);

        }

        private int sp2px(Context context, float spValue) {

            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());

        }

    }

    4、后话:

            这也没啥技术含量,就是官方的api用用,基本的计算公式摆摆,也不多废话了。

    反正用代码画图挺好玩的,文章里可能会有些你一时没想到的小技巧吧。

    相关文章

      网友评论

          本文标题:2019-03-05 自定义View绘制线性表

          本文链接:https://www.haomeiwen.com/subject/pjnguqtx.html