Android实现自定义曲线图

一般来说应用中比较常见的是折线图,直方图这种比较多,今天来写一个项目中的需求曲线图,也是在之前的折线图基础上改进而来,看效果图

主要考虑曲线的实现以及阴影部分的实现

先看代码:

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by wangchang on 2019/4/29.
 */
public class StudyBendLine extends View {

    private float sumHeight;//总控件的高度
    private float sumWidth;//总空间的宽度
    private float maxTime;//最大的时间 用来划分单位的 最小就是20 X1.2是为了给上方和下方预留空间
    private Paint linePaint;//线的画笔
    private Paint shadowPaint;//阴影的画笔
    private Paint mPaint;//曲线画笔
    private Paint circlePaint;//圆点画笔
    private Paint circlePaint2;//圆点画笔
    private Paint textPaint;//文字的画笔
    private ArrayList<Integer> timeList;//读书时间
    private ArrayList<String> dataList;//底部的时间
    private float oneHeight; //每一个小段所要分成的高
    private float oneWidth;//每一个小段所要分成的宽
    private float buttomHeiht; //给底部一排日期预留出的时间
    private Path baseLinePath;//折线路径
    private float smoothness = 0.36f; //折线的弯曲率
    private Paint baseShadow;//折线下的阴影的画笔
    private ArrayList<PointF> xyList;//储存定好的坐标点的集合

    public StudyBendLine(Context context) {
        super(context);
        initPaint();
    }

    public StudyBendLine(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    /**
     * 初始化画笔
     *
     * @linpaint 线条画笔
     * @shadowPaint 阴影画笔
     */
    private void initPaint() {
        //画线的画笔
        linePaint = new Paint();
        linePaint.setColor(Color.parseColor("#17CAAA"));
        linePaint.setAntiAlias(true);
        linePaint.setTextSize(dp2px(getContext(), 12));
        linePaint.setStrokeWidth(dp2px(getContext(), 1));
        //画背景的画笔
        shadowPaint = new Paint();
        shadowPaint.setColor(Color.parseColor("#CBF2ED"));
        shadowPaint.setAntiAlias(true);
        //画最下方文字的画笔
        textPaint = new Paint();
        textPaint.setColor(Color.parseColor("#999999"));
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(dp2px(getContext(), 11));

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(Color.parseColor("#8BE4D4"));
        circlePaint.setStrokeWidth(dp2px(getContext(), 2));
        circlePaint.setStyle(Paint.Style.STROKE);

        circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint2.setColor(Color.WHITE);
        circlePaint2.setStyle(Paint.Style.FILL);

        baseShadow = new Paint();
        baseShadow.setAntiAlias(true);
        baseShadow.setColor((Color.WHITE & 0x40FFFFFF) | 0x10000000);
        baseShadow.setStyle(Paint.Style.FILL);

        buttomHeiht = dp2px(35);//线距离底部高度

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.parseColor("#17CAAA"));
        mPaint.setStrokeWidth(dp2px(getContext(), 2));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setColor(Color.parseColor("#17CAAA"));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        sumHeight = getMeasuredHeight();
        sumWidth = getMeasuredWidth();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

    }

    private void measure() {
        maxTime = 100;//最大分数为100
        for (int i = 0; i < 7; i++) {
            if (maxTime <= timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime < 20) {
            maxTime = 20;
        }
        String text = "分";
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        oneHeight =((sumHeight-buttomHeiht-2*rect.height()) / maxTime);
        oneWidth = sumWidth / 28;
    }

    /**
     * 更新阅读时间
     */

    public void updateTime(ArrayList<Integer> timeList, ArrayList<String> bottomList) {
        this.timeList = timeList;
        this.dataList = bottomList;
        if (this.timeList!=null&&this.timeList.size()>0&&dataList!=null&&dataList.size()>0) {
            invalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (timeList == null || dataList == null) return;
        maxTime = getMaxTime(timeList);
        measure();
        toGetXy();//获取x和y的坐标

        toDrawLine(canvas);
    }

    private void toGetXy() {
        int leftWidth = dp2px(13);//距离右边宽度
        xyList = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            float x = oneWidth + i * 4 * oneWidth;
            float time = timeList.get(i);//每点时间
            float y = (sumHeight - (oneHeight * time));
            xyList.add(new PointF(x + leftWidth, y - buttomHeiht));
        }
    }

    /**
     * 画线
     */
    private void toDrawLine(Canvas canvas) {
        if (xyList == null || xyList.size() == 0) {
            return;
        }
        List<PointF> NewPoints = new ArrayList<>();
        NewPoints.addAll(xyList);
        float lX = 0;
        float lY = 0;
        baseLinePath = new Path();
        baseLinePath.moveTo(NewPoints.get(0).x, NewPoints.get(0).y);
        for (int i = 1; i < NewPoints.size(); i++) {
            PointF p = NewPoints.get(i);
            PointF firstPointF = NewPoints.get(i - 1);
            float x1 = firstPointF.x + lX;
            float y1 = firstPointF.y + lY;

            PointF secondPointF = NewPoints.get(i + 1 < NewPoints.size() ? i + 1 : i);
            lX = (secondPointF.x - firstPointF.x) / 2 * smoothness;
            lY = (secondPointF.y - firstPointF.y) / 2 * smoothness;
            float x2 = p.x - lX;
            float y2 = p.y - lY;
            if (y1 == p.y) {
                y2 = y1;
            }
            baseLinePath.cubicTo(x1, y1, x2, y2, p.x, p.y);
        }
        canvas.drawPath(baseLinePath, mPaint);
        for (int i = 0; i < NewPoints.size(); i++) {
            float x = NewPoints.get(i).x;
            float y = NewPoints.get(i).y;
            int time = timeList.get(i);
            String mThumbText = String.valueOf(time);
            Rect rect = new Rect();
            linePaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect);
            canvas.drawText(mThumbText, x - rect.width() / 2, y - dp2px(6), linePaint);//画最上面字体
            canvas.drawCircle(x, y, dp2px(3), circlePaint);
            canvas.drawCircle(x, y, dp2px(2), circlePaint2);
            Rect rectf = new Rect();
            textPaint.getTextBounds(dataList.get(i), 0, dataList.get(i).length(), rectf);
            canvas.drawText(dataList.get(i), x - rectf.width() / 2, sumHeight - dp2px(2), textPaint);//画最下方的字体
        }
        drawArea(canvas, NewPoints);
        drawBottomLine(canvas);
        drawLine(canvas);

    }

    /**
     * 分割线
     *
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        mPaint.setColor(Color.parseColor("#DDDDDD"));
        mPaint.setStrokeWidth(dp2px(0.5f));
        //
        String text = "0";
        Rect rect = new Rect();
        mPaint.getTextBounds(text, 0, text.length(), rect);
        //0
        canvas.drawText(text, 0, sumHeight - buttomHeiht - rect.height() / 2, textPaint);
        //60
        text = "60";
        float y = (sumHeight - (oneHeight * 60));
        canvas.drawText(text, 0, y - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y - buttomHeiht, sumWidth, y - buttomHeiht, mPaint);
        //100
        text = "100(分)";
        float y2 = (sumHeight - (oneHeight * 100));
        canvas.drawText(text, 0, y2 - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y2 - buttomHeiht, sumWidth, y2 - buttomHeiht, mPaint);
    }

    /**
     * 底部标线
     *
     * @param canvas
     */
    private void drawBottomLine(Canvas canvas) {
        mPaint.setColor(Color.parseColor("#EBEBEB"));
        mPaint.setStrokeWidth(dp2px(1));
        Rect rect = new Rect();
        mPaint.getTextBounds("0", 0, "0".length(), rect);
        canvas.drawLine(0, sumHeight - dp2px(15) - rect.height() / 2, sumWidth, sumHeight - dp2px(15) - rect.height() / 2, mPaint);//画底部灰线
    }

    /**
     * 阴影层叠
     *
     * @param canvas
     * @param Points
     */

    private void drawArea(Canvas canvas, List<PointF> Points) {
        LinearGradient mShader = new LinearGradient(0, 0, 0, getMeasuredHeight(), new int[]{Color.parseColor("#BAEFE6"), Color.parseColor("#D7F5F0"), Color.parseColor("#F9FEFD")}, new float[]{0.5f, 0.65f, 0.85f}, Shader.TileMode.REPEAT);
        baseShadow.setShader(mShader);
        if (Points.size() > 0 && xyList != null && xyList.size() > 0) {
            baseLinePath.lineTo(xyList.get(Points.size() - 1).x, sumHeight - buttomHeiht);
            baseLinePath.lineTo(xyList.get(0).x, sumHeight - buttomHeiht);
            baseLinePath.close();
            canvas.drawPath(baseLinePath, baseShadow);
        }

    }

    public int dp2px(float dp) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    /**
     * 取出时间里面的最大的一个用来计算总长度
     *
     * @param timeList
     * @return
     */
    public float getMaxTime(List<Integer> timeList) {
        maxTime = 0;
        for (int i = 0; i < 7; i++) {
            if (maxTime < timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime <= 10) {
            maxTime = 10;
        }
        maxTime *= 1.2;
        return maxTime;
    }

    public  int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }
    public  float dp2px(Resources resources, float dp) {
        final float scale = resources.getDisplayMetrics().density;
        return  dp * scale + 0.5f;
    }

    public  float sp2px(Resources resources, float sp){
        final float scale = resources.getDisplayMetrics().scaledDensity;
        return sp * scale;
    }
}

首先在onMeasure获取宽高,这里主要说一下getMeasuredHeight,getMeasuredWidth与 getWidth(),getHeight()的区别,getMeasuredHeight表示view的测量高度,getHeight表示view在屏幕中的高度,当屏幕能包含view时,二者大小是一致的,当超出屏幕时,getHeight只表示在屏幕中可见部分的高度。

首先将数据整理成PointF坐标点集合,遍历坐标点集合,通过path.moveto,移动到起始点位置,通过计算贝塞尔曲线的控制点然后调用cubicTo方法实现贝塞尔曲线。

然后调用drawpath绘制整个曲线

阴影层叠通过LinearGradient 来实现

private void drawArea(Canvas canvas, List<PointF> Points) {
        LinearGradient mShader = new LinearGradient(0, 0, 0, getMeasuredHeight(), new int[]{Color.parseColor("#BAEFE6"),    Color.parseColor("#D7F5F0"), Color.parseColor("#F9FEFD")}, new float[]{0.5f, 0.65f, 0.85f}, Shader.TileMode.REPEAT);
        baseShadow.setShader(mShader);
        if (Points.size() > 0 && xyList != null && xyList.size() > 0) {
            baseLinePath.lineTo(xyList.get(Points.size() - 1).x, sumHeight - buttomHeiht);
            baseLinePath.lineTo(xyList.get(0).x, sumHeight - buttomHeiht);
            baseLinePath.close();
            canvas.drawPath(baseLinePath, baseShadow);
        }

}

然后你以为这就完了?no,可以看到ondraw方法中为了处理一些问题,在ondraw中进行了实例化的对象创建,这么做是错误的。

so,需要把所有的初始化操作都放置在构造方法中,因为刷新的时候会调用ondraw方法,就会导致对象重复创建,这也是自定义view优化的点。看优化之后的代码

package com.example.android.testviewpager;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by wangchang on 2019/4/29.
 */
public class StudyBendLine extends View {

    private float sumHeight;//总控件的高度
    private float sumWidth;//总空间的宽度
    private float maxTime;//最大的时间 用来划分单位的 最小就是20 X1.2是为了给上方和下方预留空间
    private Paint linePaint;//线的画笔
    private Paint shadowPaint;//阴影的画笔
    private Paint mPaint;//曲线画笔
    private Paint circlePaint;//圆点画笔
    private Paint circlePaint2;//圆点画笔
    private Paint scorePaint;
    private Paint textPaint;//文字的画笔
    private ArrayList<Integer> timeList;//读书时间
    private ArrayList<String> dataList;//底部的时间
    private float oneHeight; //每一个小段所要分成的高
    private float oneWidth;//每一个小段所要分成的宽
    private float buttomHeiht; //给底部一排日期预留出的时间
    private Path baseLinePath;//折线路径
    private float smoothness = 0.36f; //折线的弯曲率
    private Paint baseShadow;//折线下的阴影的画笔
    private ArrayList<PointF> xyList;//储存定好的坐标点的集合

    public StudyBendLine(Context context) {
        super(context);
        initPaint();
    }

    public StudyBendLine(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    /**
     * 初始化画笔
     *
     * @linpaint 线条画笔
     * @shadowPaint 阴影画笔
     */
    private void initPaint() {
        //画线的画笔
        linePaint = new Paint();
        linePaint.setColor(Color.parseColor("#17CAAA"));
        linePaint.setAntiAlias(true);
        linePaint.setTextSize(dp2px(getContext(), 12));
        linePaint.setStrokeWidth(dp2px(getContext(), 1));
        //画背景的画笔
        shadowPaint = new Paint();
        shadowPaint.setColor(Color.parseColor("#CBF2ED"));
        shadowPaint.setAntiAlias(true);
        //画最下方文字的画笔
        textPaint = new Paint();
        textPaint.setColor(Color.parseColor("#999999"));
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(dp2px(getContext(), 11));

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(Color.parseColor("#8BE4D4"));
        circlePaint.setStrokeWidth(dp2px(getContext(), 2));
        circlePaint.setStyle(Paint.Style.STROKE);

        circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint2.setColor(Color.WHITE);
        circlePaint2.setStyle(Paint.Style.FILL);

        baseShadow = new Paint();
        baseShadow.setAntiAlias(true);
        baseShadow.setColor((Color.WHITE & 0x40FFFFFF) | 0x10000000);
        baseShadow.setStyle(Paint.Style.FILL);

        buttomHeiht = dp2px(35);//线距离底部高度

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.parseColor("#17CAAA"));
        mPaint.setStrokeWidth(dp2px(getContext(), 2));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        scorePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scorePaint.setStyle(Paint.Style.STROKE);
        scorePaint.setStrokeCap(Paint.Cap.ROUND);
        scorePaint.setColor(Color.parseColor("#DDDDDD"));
        scorePaint.setStrokeWidth(dp2px(0.5f));
        baseLinePath = new Path();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        sumHeight = getMeasuredHeight();
        sumWidth = getMeasuredWidth();

    }

    private void measure() {
        maxTime = 100;//最大分数为100
        for (int i = 0; i < 7; i++) {
            if (maxTime <= timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime < 20) {
            maxTime = 20;
        }
        String text = "分";
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        oneHeight =((sumHeight-buttomHeiht-2*rect.height()) / maxTime);
        oneWidth = sumWidth / 28;
    }

    /**
     * 更新阅读时间
     */

    public void updateTime(ArrayList<Integer> timeList, ArrayList<String> bottomList) {
        this.timeList = timeList;
        this.dataList = bottomList;
        if (this.timeList!=null&&this.timeList.size()>0&&dataList!=null&&dataList.size()>0) {
            invalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (timeList == null || dataList == null) return;
        maxTime = getMaxTime(timeList);
        measure();
        toGetXy();//获取x和y的坐标

        toDrawLine(canvas);
    }

    private void toGetXy() {
        int leftWidth = dp2px(13);//距离右边宽度
        xyList = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            float x = oneWidth + i * 4 * oneWidth;
            float time = timeList.get(i);//每点时间
            float y = (sumHeight - (oneHeight * time));
            xyList.add(new PointF(x + leftWidth, y - buttomHeiht));
        }
    }

    /**
     * 画线
     */
    private void toDrawLine(Canvas canvas) {
        if (xyList == null || xyList.size() == 0) {
            return;
        }
        List<PointF> NewPoints = new ArrayList<>();
        NewPoints.addAll(xyList);
        float lX = 0;
        float lY = 0;
        baseLinePath.reset();
        baseLinePath.moveTo(NewPoints.get(0).x, NewPoints.get(0).y);
        for (int i = 1; i < NewPoints.size(); i++) {
            PointF p = NewPoints.get(i);
            PointF firstPointF = NewPoints.get(i - 1);
            float x1 = firstPointF.x + lX;
            float y1 = firstPointF.y + lY;

            PointF secondPointF = NewPoints.get(i + 1 < NewPoints.size() ? i + 1 : i);
            lX = (secondPointF.x - firstPointF.x) / 2 * smoothness;
            lY = (secondPointF.y - firstPointF.y) / 2 * smoothness;
            float x2 = p.x - lX;
            float y2 = p.y - lY;
            if (y1 == p.y) {
                y2 = y1;
            }
            baseLinePath.cubicTo(x1, y1, x2, y2, p.x, p.y);
        }
        canvas.drawPath(baseLinePath, mPaint);
        for (int i = 0; i < NewPoints.size(); i++) {
            float x = NewPoints.get(i).x;
            float y = NewPoints.get(i).y;
            int time = timeList.get(i);
            String mThumbText = String.valueOf(time);
            Rect rect = new Rect();
            linePaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect);
            canvas.drawText(mThumbText, x - rect.width() / 2, y - dp2px(6), linePaint);//画最上面字体
            canvas.drawCircle(x, y, dp2px(3), circlePaint);
            canvas.drawCircle(x, y, dp2px(2), circlePaint2);
            Rect rectf = new Rect();
            textPaint.getTextBounds(dataList.get(i), 0, dataList.get(i).length(), rectf);
            canvas.drawText(dataList.get(i), x - rectf.width() / 2, sumHeight - dp2px(2), textPaint);//画最下方的字体
        }
        drawArea(canvas, NewPoints);
        drawBottomLine(canvas);
        drawLine(canvas);

    }

    /**
     * 分割线
     *
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        //
        String text = "0";
        Rect rect = new Rect();
        scorePaint.getTextBounds(text, 0, text.length(), rect);
        //0
        canvas.drawText(text, 0, sumHeight - buttomHeiht - rect.height() / 2, textPaint);
        //60
        text = "60";
        float y = (sumHeight - (oneHeight * 60));
        canvas.drawText(text, 0, y - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y - buttomHeiht, sumWidth, y - buttomHeiht, scorePaint);
        //100
        text = "100(分)";
        float y2 = (sumHeight - (oneHeight * 100));
        canvas.drawText(text, 0, y2 - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y2 - buttomHeiht, sumWidth, y2 - buttomHeiht, scorePaint);
    }

    /**
     * 底部标线
     *
     * @param canvas
     */
    private void drawBottomLine(Canvas canvas) {
        Rect rect = new Rect();
        scorePaint.getTextBounds("0", 0, "0".length(), rect);
        canvas.drawLine(0, sumHeight - dp2px(15) - rect.height() / 2, sumWidth, sumHeight - dp2px(15) - rect.height() / 2, scorePaint);//画底部灰线
    }

    /**
     * 阴影层叠
     *
     * @param canvas
     * @param Points
     */

    private void drawArea(Canvas canvas, List<PointF> Points) {
        LinearGradient mShader = new LinearGradient(0, 0, 0, getMeasuredHeight(), new int[]{Color.parseColor("#BAEFE6"), Color.parseColor("#D7F5F0"), Color.parseColor("#F9FEFD")}, new float[]{0.5f, 0.65f, 0.85f}, Shader.TileMode.REPEAT);
        baseShadow.setShader(mShader);
        if (Points.size() > 0 && xyList != null && xyList.size() > 0) {
            baseLinePath.lineTo(xyList.get(Points.size() - 1).x, sumHeight - buttomHeiht);
            baseLinePath.lineTo(xyList.get(0).x, sumHeight - buttomHeiht);
            baseLinePath.close();
            canvas.drawPath(baseLinePath, baseShadow);
        }

    }

    public int dp2px(float dp) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    /**
     * 取出时间里面的最大的一个用来计算总长度
     *
     * @param timeList
     * @return
     */
    public float getMaxTime(List<Integer> timeList) {
        maxTime = 0;
        for (int i = 0; i < 7; i++) {
            if (maxTime < timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime <= 10) {
            maxTime = 10;
        }
        maxTime *= 1.2;
        return maxTime;
    }

    public  int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }
    public  float dp2px(Resources resources, float dp) {
        final float scale = resources.getDisplayMetrics().density;
        return  dp * scale + 0.5f;
    }

    public  float sp2px(Resources resources, float sp){
        final float scale = resources.getDisplayMetrics().scaledDensity;
        return sp * scale;
    }
}

实现的效果图

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

(0)

相关推荐

  • Android 自定义View实现芝麻分曲线图效果

    1.简介 其实这个效果几天之前就写了,但是一直没有更新博客,本来想着把芝麻分雷达图也做好再发博客的,然后今天看到鸿洋的微信公众号有朋友发了芝麻分的雷达图,所以就算了,算是一个互补吧.平时文章也写的比较少,所以可能有点杂乱,有什么需要改进的地方欢迎给出建议,不胜感激. 效果图: 2.步骤: 初始化View的属性 初始化画笔 绘制代表最高分和最低分的两根虚线 绘制文字 绘制代表月份的属性 绘制芝麻分折线 绘制代表芝麻分的圆点 绘制选中分数的悬浮文字以及背景 处理点击事件 3.编码: 初始化View属

  • Android利用MPAndroidChart绘制曲线图表的基础教程

    前言 最近看了一下 Android 上的图表控件,去年做过一款应用也已上架了,也用到了图表控件,但是只是按照官方 demo 集成了,并没有过多的研究. 我最近基本上是面向 Github 编程了,拿别人造好的轮子拿来用,不能直接用的就改改再用,实在不好实现的就劝说产品去改需求,基本这是个套路. 既然是图表控件,就上 github 一番搜索,关键字 chart,按照 java 一筛选,再按照 start 数量排序,由高到低.基本上 start 从高到低的图表控件就是 MPAndroidChart.h

  • Android 曲线图的绘制示例代码

    本文介绍了Android 曲线图的绘制示例代码,分享给大家,具体如下: 效果展示 效果展示.gif 使用方式 // 初始化数据表格相关 with(mTableView) { // 配置坐标系 setupCoordinator("日", "人", /*这里是横坐标的值*/0f, 5f, 10f, 15f, 20f, 25f, 30f) // 添加曲线, 确保纵坐标的数值位数相等 addWave(ContextCompat.getColor(this@MainActiv

  • Android 游戏开发中绘制游戏触摸轨迹的曲线图

    本篇文章主要来讲解怎样绘制游戏触摸轨迹的曲线图. 我们在onTouchEvent方法中,可以获取到触摸屏幕时手指触摸点的x.y坐标,如何用这些点形成一条无规则轨迹并把这条无规则轨迹曲线显示在屏幕上就是本篇文章的主旨内容. Android Path类 Android提供了一个Path类 , 顾名思义这个类可以设置曲线路径轨迹.任何无规则的曲线实际上都是由若干条线段组成,而线段的定义为两点之间最短的一条线.path类就 可以记录这两点之间的轨迹,那么若干个Path 就是我们须要绘制的无规则曲线. 下

  • Android实现简易的柱状图和曲线图表实例代码

    前言 之前有写过一个图表lib,但是开发的速度,大多很难跟上产品需求变化的脚步,所以修改了下原先的图表库,支持图表下面能整合table显示对应的类目,用曲线替换了折线,支持多曲线的显示,增加了显示的动画,增加了一些可定制的属性,支持水平柱状图和叠加柱状图,以及多曲线图和饼状图的显示,下面话不多说了,来一起看看详细的介绍吧. 1.效果图 2.各种图表的使用方式 1.饼状图 这个和原先的使用一样,只不过增加了一个动画,可以参看之前的文章,饼状图使用. 2.水平多柱状图 2.1 xml布局 <well

  • Android实现价格走势自定义曲线图

    本文是引用开源图表库框架 MPAndroidChart的LineChart 地址:https://github.com/PhilJay/MPAndroidChart 1.需求: (1)动态添加RadioButton,点击改变下面的LineChart数据 (2)LineChart绘制价格走势图,只显示最低点的小圆点和View,手指滑动,MarkView数据变化. (3) 服务端返回端数据,不是每一天端数据,但是x轴显示的必须是每一天的数据,这里是有我自己处理过的.返回里需要显示点的数组,之前的时间

  • Android实现自定义曲线图

    一般来说应用中比较常见的是折线图,直方图这种比较多,今天来写一个项目中的需求曲线图,也是在之前的折线图基础上改进而来,看效果图 主要考虑曲线的实现以及阴影部分的实现 先看代码: import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Line

  • Android动态自定义圆形进度条

    效果图: A.绘制圆环,圆弧,文本 //1.画圆环 //原点坐标 float circleX = width / 2; float circleY = width / 2; //半径 float radius = width / 2 - roundWidth / 2; //设置画笔的属性 paint.setColor(roundColor); paint.setStrokeWidth(roundWidth); paint.setStyle(Paint.Style.STROKE); canvas.

  • Android编程自定义Notification实例分析

    本文实例讲述了Android编程自定义Notification的用法.分享给大家供大家参考,具体如下: Notification是一种让你的应用程序在不使用Activity的情况下警示用户,Notification是看不见的程序组件警示用户有需要注意的事件发生的最好途径. 作为UI部分,Notification对移动设备来说是最适合不过的了.用户可能随时都带着手机在身边.一般来说,用户会在后台打开几个程序,但不会注意它们.在这样的情形下,当发生需要注意的事件时,能够通知用户是很重要的. Noti

  • Android中自定义水平进度条样式之黑色虚线

    以下内容给大家介绍Android中自定义水平进度条样式之黑色虚线,对代码实现方法感兴趣的朋友一起学习吧. 布局layout中使用: <ProgressBar android:id="@+id/progress_bar" style="?android:attr/progressBarStyleHorizontal" <!--必须设置为水平--> android:progressDrawable="@drawable/myprogress&

  • Android编程自定义圆角半透明Dialog的方法

    本文实例讲述了Android编程自定义圆角半透明Dialog的方法.分享给大家供大家参考,具体如下: 效果图如下: 只是在实例化的时候使用带样式的构造函数即可 new MyDialog(GameActivity.this, R.style.dialog); 在value文件夹中添加mydialogthemes.xml <?xml version="1.0″ encoding=" utf-8″?> <resources> <style name="

  • Android使用自定义View绘制渐隐渐现动画

    实现了一个有趣的小东西:使用自定义View绘图,一边画线,画出的线条渐渐变淡,直到消失.效果如下图所示: 用属性动画或者渐变填充(Shader)可以做到一笔一笔的变化,但要想一笔渐变(手指不抬起边画边渐隐),没在Android中找到现成的API可用.所以,自己做了一个. 基本的想法是这样的: 在View的onTouchEvent中记录触摸点,生成一条一条的线LineElement,放在一个List中.给每个LineElement配置一个Paint实例. 在onDraw中绘制线段. 变换LineE

  • Android实现自定义的卫星式菜单(弧形菜单)详解

    一.前言 Android 实现卫星式菜单也叫弧形菜单,主要要做的工作如下: 1.动画的处理 2.自定义ViewGroup来实现卫星式菜单View (1)自定义属性 a. 在attrs.xml中定义属性 b. 在布局中使用自定义属性 c. 在自定义View中读取布局文件中的自定义属性 (2)onMeasure 测量 child 即测量主按钮以及菜单项 (3)onLayout 布局 child 即布局主按钮以及菜单项 (4)设置主按钮的选择动画 a.为菜单项menuItem添加平移动画和旋转动画 b

  • Android编程自定义扁平化对话框示例

    本文实例讲述了Android编程自定义扁平化对话框.分享给大家供大家参考,具体如下: 平时我们开发的大多数的Android.iOS的APP,它们的风格都是拟物化设计.如Android 4.X.iOS 7.WP8采用的是扁平化设计,可以看出扁平化设计是未来UI设计的趋势.其实扁平化设计要比拟物化设计要简单一点,扁平化设计更加的简约,给人视觉上更加舒服. Shamoo想到在Android平台上弄一个扁平化的对话框.参考过一篇帖子,然后改了一下. 这个Demo比较简单,首先是一个dialog的布局文件

  • Android中自定义PopupWindow实现弹出框并带有动画效果

    使用PopupWindow来实现弹出框,并且带有动画效果 首先自定义PopupWindow public class LostPopupWindow extends PopupWindow { public Lost lost; public void onLost(Lost lost){ this.lost = lost; } private View conentView; public View getConentView() { return conentView; } public L

  • Android开发自定义TextView省略号样式的方法

    本文实例讲述了Android开发自定义TextView省略号样式的方法.分享给大家供大家参考,具体如下: 在布局xml中设置textView的字段 android:maxLines="2"  android:ellipsize="end"字段之后,textview会自动压缩行数,并且对压缩掉的部分用...显示.如果不想用...而想用...或者... ...就需要自定义这个省略号的样式,不需要自定义控件,方法如下. 首先是布局文件 <TextView andro

随机推荐