android 自定义带增长动画和点击弹窗提示效果的柱状图

来源:互联网 发布:sql语句not in的用法 编辑:程序博客网 时间:2024/06/05 02:11

     项目中最近用到各种图表,本来打算用第三方的,例如MPAndroid,这是一个十分强大的图表库,应用起来十分方便,但是最终发现和设计不太一样,没办法,只能自己写了。今天将写好的柱状图的demo贴在这,该柱状图可根据数据的功能有一下几点:

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

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

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

     好了,先上演示图:


     下边贴出相关代码:

     自定义柱状图类:

package com.example.histogram;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 com.example.histogram.UI.UI;import java.text.NumberFormat;/** * Created by ZHANGZDon 2016/6/16 0016. * 柱状图 */public class HistoGram 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 HistoGram(Context context) {        this(context, null);    }    public HistoGram(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public void setTitle(String[] title) {        this.xTitleString = title;    }    public HistoGram(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[num - 1 - 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[num - 1 - 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[num - 1 - 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;    }}
  在xml文件中的应用:

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"   android:orientation="vertical"    tools:context="com.example.histogram.MainActivity">        <TextView        android:layout_width="match_parent"        android:layout_height="40dp"        android:gravity="center"        android:text="繁忙度指示图(%)"        android:textSize="15sp"        android:textColor="#000000"        />        <com.example.histogram.HistoGram        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"/></LinearLayout>

   在activity中的实现:

package com.example.histogram;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;public class MainActivity extends AppCompatActivity {    private PopupWindow mPopupWindow;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final HistoGram histoGram = (HistoGram) findViewById(R.id.staticview);        String[] data ={"100","20","40","20","80","20","60","30","5","20","60","30","5","5","20","60","30","5"};        final String[] title = {"1","2","3","4","5","6","7","8","9","6","7","8","9","9","6","7","8","9"};        histoGram.setNum(title.length);        histoGram.setData(data);        histoGram.setxTitleString(title);        histoGram.setOnChartClickListener(new HistoGram.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 + "%\n" + title[num - 1]);                if(mPopupWindow != null) {                    mPopupWindow.dismiss();                }                mPopupWindow = new PopupWindow(inflate,140, 60, true);                mPopupWindow.setTouchable(true);                Log.e("ss","num" + num +";x" + x+";y"+ y + ";value" + value                        +";(int)((- histoGram.getHeight()) + y - 65)"                        +(int)((- histoGram.getHeight()) + y - 65)                + "histoGram.getHeight()" + histoGram.getHeight());                // 设置好参数之后再show//                Toast.makeText(MainActivity.this, "num" + num +";x" + x+";y"+ y + ";value" + value//                        +";popupWindow.getWidth()"+ mPopupWindow.getWidth()+";"+ mPopupWindow.getHeight(), Toast.LENGTH_SHORT).show();                mPopupWindow.showAsDropDown(histoGram,(int)(x - 65),(int)((- histoGram.getHeight()) + y - 65) );                mPopupWindow.setBackgroundDrawable(getResources().getDrawable(R.mipmap.databg_busyness));                new Handler().postDelayed(new Runnable(){                    public void run() {                        mPopupWindow.dismiss();                    }                }, 1000);            }        });    }}

     好了,这就是所有代码,写的比较简单,也存在一些问题,希望大家多多指教吐舌头



3 1
原创粉丝点击