为了账号安全,请及时绑定邮箱和手机立即绑定

改良网友的自定义增长柱状图

标签:
Android

改良的原因:

1.根据他写的程序出现Bug,程序崩溃
2.X柱和Y柱数据不对称

改良后:

1.调整程序中出现的个别Bug
2.调整X柱Y柱分别对称

本程序主要讲述:

     1. 根据数据的多少,动态的绘制柱状图柱子的条数;

     2. 柱状图每条柱子的绘制都有动态的动画效果;

     3. 每条柱子有点击事件,点击时弹出提示框,显示相关信息,规定时间后,弹窗自动消失。

5baa30fa00011e4910801920.jpg

图1:本来打开可以动画增长,但是电脑尚没有制作gif的图片

5baa30fc0001292410801920.jpg

图2:点击柱子出现数据

下面直接上代码:(完整的程序代码)

柱状图类:


package chart.hanwei.com.zhuchart.chart;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.text.NumberFormat;

/**
 * Created by 陈苗辉 on 2016/12/8.
 */
public class HorizontalChart extends View implements Runnable {

    private Handler handler = new Handler(); // 用于延时更新,实现动画
    private float animHeight; // 进度条动画高度
    private Paint axisLinePaint; // 坐标轴画笔
    private Paint hLinePaint; // 内部水平虚线画笔
    private Paint textPaint; // 绘制文本的画笔
    private Paint recPaint; // 绘制柱状图阴影背景的画笔
    private Paint dataPaint; // 绘制柱状图的画笔
    private Paint textPaint2; // 绘制白色文本的画笔
    private Paint textPaint3; // 绘制坐标的画笔
    private Paint textPaint4;  // 绘制x轴上的白色竖线的画笔

    private String[] xTitleString; // x轴刻度
    private String[] yTitleString; // y轴刻度
    private String[] data; // 接口返回的indicatordata,用于计算柱子高度

    NumberFormat numberFormat; //用于格式化数字

    private float currentHeight; // 当前柱状图应有的高度,应由计算得来
    private int num = -1; // 画多少条柱子,因为存在刚开机数据不足24条的情况
    private float mRelativePxInHeight;
    private float mRelativePxInWidth;
    private OnChartClickListener listener;
    private int mDist;

    public void setNum(int num) {
        this.num = num;
        invalidate();
    }

    public void setData(String[] data) {
        this.data = data;
        invalidate();
    }

    public void setxTitleString(String[] title) {
        this.xTitleString = title;
        invalidate();


    }

    public HorizontalChart(Context context) {

        this(context, null);
    }

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

    }

    public void setTitle(String[] title) {
        this.xTitleString = title;
    }

    public HorizontalChart(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    /**
     * 进行相关初始化操作
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        axisLinePaint = new Paint();
        hLinePaint = new Paint();
        textPaint = new Paint();
        recPaint = new Paint();
        dataPaint = new Paint();
        textPaint2 = new Paint();
        textPaint3 = new Paint();
        textPaint4 = new Paint();

        numberFormat = NumberFormat.getNumberInstance();
        numberFormat.setMinimumFractionDigits(3);  //设置打印时保留三位小数
        axisLinePaint.setColor(Color.parseColor("#dbdde4"));  //设置坐标轴的颜色为白色
        hLinePaint.setARGB(51, 255, 255, 255);
        textPaint.setColor(Color.parseColor("#8593a1"));
//        textPaint.setTextSize(29);
        textPaint.setTextSize(UI.dip2px(getContext(), 12));
        recPaint.setColor(Color.parseColor("#f2f5fc"));
        dataPaint.setColor(Color.CYAN);
        textPaint2.setColor(Color.WHITE);
        textPaint2.setTextSize(UI.dip2px(getContext(), 12));
        textPaint3.setColor(Color.parseColor("#000000"));
        textPaint3.setTextSize(UI.dip2px(getContext(), 9));
        textPaint4.setColor(Color.parseColor("#8593a1"));
        textPaint4.setTextSize(UI.dip2px(getContext(), 6));

        axisLinePaint.setAntiAlias(true);
        hLinePaint.setAntiAlias(true);
        textPaint.setAntiAlias(true);
        recPaint.setAntiAlias(true);
        dataPaint.setAntiAlias(true);
        textPaint2.setAntiAlias(true);
        textPaint3.setAntiAlias(true);
        textPaint4.setAntiAlias(true);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if(data == null || xTitleString == null || num < 0 ) {
            return;
        }

        //绘制y轴刻度
        Paint.FontMetrics metrics = textPaint3.getFontMetrics();
        int decent = (int) metrics.descent;
        float width = getWidth();
        float height = getHeight();

        //根据原型图得出,图中每px高度在实际中的相对尺寸
        mRelativePxInHeight = height / 470;
        //根据原型图得出,图中每px宽度在实际中的相对尺寸
        mRelativePxInWidth = width / 690;

        textPaint3.setTextAlign(Paint.Align.RIGHT);


        //绘制纵坐标
        yTitleString = new String[6];
        yTitleString[5] = "0";
        yTitleString[4] = "20";
        yTitleString[3] = "40";
        yTitleString[2] = "60";
        yTitleString[1] = "80";
        yTitleString[0] = "100";

        for (int i = 0; i < yTitleString.length; i++) {
            canvas.drawText(yTitleString[i], 88 * mRelativePxInWidth, (72 + i * 56) * mRelativePxInHeight + decent, textPaint3);
        }


        //绘制x轴刻度
        textPaint3.setTextAlign(Paint.Align.CENTER);
        textPaint4.setTextAlign(Paint.Align.CENTER);
        TextPaint textPaint = new TextPaint();
        textPaint.setColor(Color.parseColor("#000000"));
        textPaint.setTextSize(UI.dip2px(getContext(), 9));
        //计算柱子之间的间隔
        //最左侧位置100 * mRelativePxInWidth,最右侧位置630 ePxInWidth,
        float totalWidth = 630 - 100;
        // 柱子与之子之间的间隔
        mDist = (int) (totalWidth / (xTitleString.length + 1));
        for (int i = 0; i < xTitleString.length; i++) {
            //绘制白色竖线
            canvas.drawLine((100 + (i+1) * mDist) * mRelativePxInWidth, 348 * mRelativePxInHeight, (100 + (i+1) * mDist) * mRelativePxInWidth, 352 * mRelativePxInHeight, axisLinePaint);
            //绘制x轴文字
            canvas.drawText(xTitleString[i], (100 + (i+1) * mDist) * mRelativePxInWidth, 370 * mRelativePxInHeight, textPaint3);
        }


//        绘制矩形阴影
        for (int i = 0; i < num; i++) {
            RectF rectF = new RectF();
//            rectF.left = 111 * relativePxInWidth + i * 22 * relativePxInWidth;
//            rectF.right = 121 * relativePxInWidth + i * 22 * relativePxInWidth;
            rectF.left = 95 * mRelativePxInWidth + (i+1) * mDist * mRelativePxInWidth;
            rectF.right = 105 * mRelativePxInWidth +(i+1) * mDist * mRelativePxInWidth;
            rectF.top = 70 * mRelativePxInHeight;
            rectF.bottom = 338 * mRelativePxInHeight;
            canvas.drawRoundRect(rectF, 10, 10, recPaint);
        }

        //        绘制x轴坐标线
        for (int i = 0; i < 6; i++) {
            canvas.drawLine(100 * mRelativePxInWidth, (66 + i * 56) * mRelativePxInHeight + decent, 630 * mRelativePxInWidth, (66 + i * 56) * mRelativePxInHeight + decent, axisLinePaint);
        }


//        延时绘制,实现动画效果。数字越大,延时越久,动画效果就会越慢
        handler.postDelayed(this, 1);
        for (int i = 0; i < num; i++) {
            RectF dataRectF = new RectF();
            dataRectF.left = 95 * mRelativePxInWidth + (i + 1) * mDist * mRelativePxInWidth;
            dataRectF.right = 105 * mRelativePxInWidth + (i + 1) * mDist * mRelativePxInWidth;

            dataPaint.setColor(Color.parseColor("#3ac2d9"));
            //获取柱子高度
            currentHeight = Float.parseFloat(data[i]);
            if (currentHeight == 0) {
                dataRectF.top = 346 * mRelativePxInHeight;
            } else if (currentHeight == 100) {
                dataRectF.top = 70 * mRelativePxInHeight;
            } else {
                if (animHeight >= currentHeight) {
                    dataRectF.top = 346 * mRelativePxInHeight - currentHeight / 100 * 276 * mRelativePxInHeight;
                } else {
                    dataRectF.top = 346 * mRelativePxInHeight - 276 * mRelativePxInHeight * (animHeight / 100);
                }
            }
            dataRectF.bottom = 346 * mRelativePxInHeight;
//                限制最高高度
            if (dataRectF.top < 70 * mRelativePxInHeight) {
                dataRectF.top = 70 * mRelativePxInHeight;
            }
            canvas.drawRoundRect(dataRectF, 10, 10, dataPaint);

        }
    }

    //实现柱子增长的动画效果
    @Override
    public void run() {
        animHeight += 1;

        if (animHeight >= 276 * mRelativePxInHeight) {
            return;
        } else {
            invalidate();
        }
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                //获取点击坐标
                float x = event.getX();
                float y = event.getY();
                //判断点击点的位置
                float leftx = 0;
                float rightx = 0;

                for (int i = 0; i < num; i++) {
                    leftx = 95 * mRelativePxInWidth + (i+ 1) * mDist * mRelativePxInWidth - mDist/2 * mRelativePxInWidth;
                    rightx = 105 * mRelativePxInWidth + (i+ 1) * mDist * mRelativePxInWidth + mDist/2 * mRelativePxInWidth;
                    if (x < leftx) {
                        continue;
                    }
                    if (leftx <= x && x <= rightx) {
                        //获取点击的柱子区域的y值
                        float top = 346 * mRelativePxInHeight - Float.parseFloat(data[i])/ 100 * 276 * mRelativePxInHeight;
                        float bottom = 346 * mRelativePxInHeight;


                        if (y >= top && y <= bottom) {
                            //判断是否设置监听
                            //将点击的第几条柱子,点击柱子顶部的坐值,用于弹出dialog提示数据,还要返回百分比currentHeidht = Float.parseFloat(data[num - 1 - i])
                            if(listener != null) {
                                Log.e("ss","x" + x +";y:" + y);
                                listener.onClick(i + 1, leftx + mDist/2,top,Float.parseFloat(data[i]));
                            }
                            break;

                        }
                    }

                }
                break;
            }
            case MotionEvent.ACTION_MOVE:
                Log.e("touch", "ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
                Log.e("touch", "ACTION_UP");
                break;
        }

        return true;
    }

    /**
     * 柱子点击时的监听接口
     */
    public interface OnChartClickListener {
        void onClick(int num, float x, float y, float value);

    }

    /**
     * 设置柱子点击监听的方法
     * @param listener
     */
    public void setOnChartClickListener(OnChartClickListener listener)  {
        this.listener = listener;
    }
}

在MainActivity中的xml应用:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <chart.hanwei.com.zhuchart.chart.HorizontalChart
        android:id="@+id/staticview"
        android:layout_width="400dp"
        android:layout_height="500dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="14dp"
        android:layout_marginTop="5dp"/>
</RelativeLayout>

activity的实现:


package chart.hanwei.com.zhuchart.activity;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.PopupWindow;
import android.widget.TextView;

import chart.hanwei.com.zhuchart.R;
import chart.hanwei.com.zhuchart.chart.HorizontalChart;

public class MainActivity extends AppCompatActivity {
    private PopupWindow mPopupWindow;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final HorizontalChart histoGram = (HorizontalChart) findViewById(R.id.staticview);
        String[] data ={"100","20","40","20","80","20","60","30","5","20","60","30","5","5","20","60","30","20","10"};
        final String[] title = {"1","2","3","4","5","6","7","8","9","6","7","8","9","9","6","7","8","9","10"};

        histoGram.setNum(title.length);
        histoGram.setData(data);
        histoGram.setxTitleString(title);
        histoGram.setOnChartClickListener(new HorizontalChart.OnChartClickListener() {
            @Override
            public void onClick(int num, float x, float y, float value) {

                //显示提示窗
                View inflate = View.inflate(MainActivity.this, R.layout.popupwindow, null);
                TextView textView = (TextView) inflate.findViewById(R.id.main_tv);
                textView.setText(value + "%" );//+ title[num - 1]x柱上的数
                if(mPopupWindow != null) {
                    mPopupWindow.dismiss();
                }
                mPopupWindow = new PopupWindow(inflate,140, 60, true);
                mPopupWindow.setTouchable(true);
                mPopupWindow.showAsDropDown(histoGram,(int)(x - 65),(int)((- histoGram.getHeight()) + y - 65) );
                new Handler().postDelayed(new Runnable(){
                    public void run() {
                        mPopupWindow.dismiss();
                    }
                }, 1000);


            }
        });
    }
}

自动获取屏幕dp转成px

package chart.hanwei.com.zhuchart.chart;

import android.content.Context;

/**
 * Created by 陈苗辉 on 2016/12/8.
 */
public class UI {

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
}

activity中的popupwindow


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/main_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="100"
        />
</LinearLayout>

原文链接:http://www.apkbus.com/blog-784586-62749.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消