首页 > 系统 > Android > 正文

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

2019-12-12 04:44:44
字体:
来源:转载
供稿:网友

项目中最近用到各种图表,本来打算用第三方的,例如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);       }     });   } } 

以上所述是小编给大家介绍的Android自定义带增长动画和点击弹窗提示效果的柱状图,实现一个模拟后台数据登入的效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表