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

作者:zzd_92 时间:2022-10-16 17:10:25 

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

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

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

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

     好了,先上演示图:

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

     下边贴出相关代码:

     自定义柱状图类:


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);
     }
   });
 }
}

以上所述是小编给大家介绍的Android自定义带增长动画和点击弹窗提示效果的柱状图,实现一个模拟后台数据登入的效果网站的支持!

来源:http://blog.csdn.net/u011450295/article/details/53095613

标签:android,柱状图
0
投稿

猜你喜欢

  • 解析JDK14中的java tools简介

    2023-04-18 10:28:37
  • c#源码的执行过程详解

    2023-10-01 17:03:37
  • C#调用WebService的方法介绍

    2022-06-05 01:10:30
  • Android中子线程和UI线程通信详解

    2023-03-21 00:05:10
  • 总结Android App内存优化之图片优化

    2022-07-01 22:52:16
  • springcloud项目占用内存好几个G导致服务器崩溃的问题

    2023-03-30 09:54:25
  • 详解用RxJava实现事件总线(Event Bus)

    2022-02-13 16:43:18
  • JDK10中的局部变量类型推断var

    2022-06-16 20:32:48
  • Android Handler runWithScissors 梳理流程解析

    2023-01-29 11:51:27
  • springboot做代理分发服务+代理鉴权的实现过程

    2021-06-28 03:22:14
  • springboot+mybatis-plus 两种方式打印sql语句的方法

    2022-12-29 13:41:11
  • Android Camera开发实现可复用的相机组件

    2023-04-08 20:34:56
  • java实现字符串反转案例

    2021-11-20 04:45:55
  • Java将Date日期类型字段转换成json字符串的方法

    2023-02-18 19:57:09
  • C#抓取网页数据 解析标题描述图片等信息 去除HTML标签

    2023-03-13 17:23:07
  • C#中图片、二进制与字符串的相互转换方法

    2023-05-16 13:07:50
  • 花样使用Handler与源码分析

    2023-07-30 08:36:31
  • Java并发包线程池ThreadPoolExecutor的实现

    2022-11-10 09:52:41
  • JAVA Optional类用法分享

    2022-01-07 02:36:08
  • springboot 按月分表的实现方式

    2023-11-25 00:03:47
  • asp之家 软件编程 m.aspxhome.com