前言:前段时间做的工程,自己一个人小打小闹的修改着,代码不完善之处请在评论指出,谢谢!
工程来源:github AndroidCharts:https://github.com/dacer/AndroidCharts
修改:使得BarView可以显示数值和颜色
我的需求:将每间教室的总座位数,空座位数显示在图上
效果图:
代码1: 修改后的BarView.java
package com.androidcharts;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
/**
* Created by Dacer on 11/11/13.
*/
public class BarView extends View {
private ArrayList<Float> percentList;
private ArrayList<Float> targetPercentList;
private ArrayList<Float> AllpercentList;
private ArrayList<Float> AlltargetPercentList;
private Paint textPaint;
private Paint BlankNumPaint;
private Paint AllNumPaint;
private Paint bgPaint;
private Paint fgPaint;
private Paint AllfgPaint;
private Rect rect;
private int barWidth;
// private boolean showSideMargin = true;
private int bottomTextDescent;
private boolean autoSetWidth = true;
private int topMargin;
private int bottomTextHeight;
private int clickarea; //点击坐标轴上的区域的数值
public boolean clickaarea = false;
public String clickroom = null;
private ArrayList<String> bottomTextList;
private ArrayList<String> BlankSeatList;
private ArrayList<String> AllSeatList;
private final int MINI_BAR_WIDTH;
private final int BAR_SIDE_MARGIN;
private final int TEXT_TOP_MARGIN;
private final int TEXT_COLOR = Color.parseColor("#9B9A9B");
private final int NUM_COLOR = Color.parseColor("#1E90FF");
private final int BACKGROUND_COLOR = Color.parseColor("#F6F6F6");
private final int FOREGROUND_COLOR = Color.parseColor("#00FF7F");
private final int AllFOREGROUND_COLOR = Color.parseColor("#AFEEEE");
public interface DeliverRoomListener {
public void DeliverRoom(String classname);
}
private DeliverRoomListener mDeliverRoomListener;
//
public void setRoomClickedListener(DeliverRoomListener listener){
mDeliverRoomListener = listener;
}
private Runnable animator = new Runnable() {
@Override
public void run() {
boolean needNewFrame = false;
for (int i=0; i<targetPercentList.size();i++) {
if (percentList.get(i) < targetPercentList.get(i)) {
percentList.set(i,percentList.get(i)+0.02f);
needNewFrame = true;
} else if (percentList.get(i) > targetPercentList.get(i)){
percentList.set(i,percentList.get(i)-0.02f);
needNewFrame = true;
}
if(Math.abs(targetPercentList.get(i)-percentList.get(i))<0.02f){
percentList.set(i,targetPercentList.get(i));
}
}
if (needNewFrame) {
postDelayed(this, 20);
}
invalidate();
}
};
/*
* 座位总数
*/
private Runnable Allanimator = new Runnable() {
@Override
public void run() {
boolean needNewFrame = false;
for (int i=0; i<AlltargetPercentList.size();i++) {
if (AllpercentList.get(i) < AlltargetPercentList.get(i)) {
AllpercentList.set(i,AllpercentList.get(i)+0.02f);
needNewFrame = true;
} else if (AllpercentList.get(i) > AlltargetPercentList.get(i)){
AllpercentList.set(i,AllpercentList.get(i)-0.02f);
needNewFrame = true;
}
if(Math.abs(AlltargetPercentList.get(i)-AllpercentList.get(i))<0.02f){
AllpercentList.set(i,AlltargetPercentList.get(i));
}
}
if (needNewFrame) {
postDelayed(this, 20);
}
invalidate();
}
};
public BarView(Context context){
this(context,null);
}
public BarView(Context context, AttributeSet attrs){
super(context, attrs);
bgPaint = new Paint();
bgPaint.setAntiAlias(true);
bgPaint.setColor(BACKGROUND_COLOR);
fgPaint = new Paint(bgPaint);
fgPaint.setColor(FOREGROUND_COLOR);
AllfgPaint = new Paint(bgPaint);
AllfgPaint.setColor(AllFOREGROUND_COLOR);
rect = new Rect();
topMargin = MyUtils.dip2px(context, 5);
int textSize = MyUtils.sp2px(context, 15);
int numSize = MyUtils.sp2px(context, 13);
barWidth = MyUtils.dip2px(context,22);
MINI_BAR_WIDTH = MyUtils.dip2px(context,22);
BAR_SIDE_MARGIN = MyUtils.dip2px(context,22);
TEXT_TOP_MARGIN = MyUtils.dip2px(context, 5);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(TEXT_COLOR);
textPaint.setTextSize(textSize);
textPaint.setTextAlign(Paint.Align.CENTER);
BlankNumPaint = new Paint();
BlankNumPaint.setAntiAlias(true);
BlankNumPaint.setColor(NUM_COLOR);
BlankNumPaint.setTextSize(numSize);
BlankNumPaint.setTextAlign(Paint.Align.CENTER);
AllNumPaint = new Paint();
AllNumPaint.setAntiAlias(true);
AllNumPaint.setColor(NUM_COLOR);
AllNumPaint.setTextSize(numSize);
AllNumPaint.setTextAlign(Paint.Align.CENTER);
percentList = new ArrayList<Float>();
AllpercentList = new ArrayList<Float>();
}
/**
* dataList will be reset when called is method.
* @param bottomStringList The String ArrayList in the bottom.
*/
public void setBottomTextList(ArrayList<String> bottomStringList){
// this.dataList = null;
this.bottomTextList = bottomStringList;
Rect r = new Rect();
bottomTextDescent = 0;
barWidth = MINI_BAR_WIDTH;
for(String s:bottomTextList){
textPaint.getTextBounds(s,0,s.length(),r);
BlankNumPaint.getTextBounds(s,0,s.length(),r);
AllNumPaint.getTextBounds(s,0,s.length(),r);
if(bottomTextHeight<r.height()){
bottomTextHeight = r.height();
}
if(autoSetWidth&&(barWidth<r.width())){
barWidth = r.width();
}
if(bottomTextDescent<(Math.abs(r.bottom))){
bottomTextDescent = Math.abs(r.bottom);
}
}
setMinimumWidth(2);
postInvalidate();
}
public void setBlankSeatList(ArrayList<String> BlankSeatStringList){
this.BlankSeatList = BlankSeatStringList;
}
public void setAllSeatList(ArrayList<String> AllSeatStringList){
this.AllSeatList = AllSeatStringList;
}
/**
*
* @param list The ArrayList of Integer with the range of [0-max].
*/
public void setDataList(ArrayList<Integer> list, int max){
targetPercentList = new ArrayList<Float>();
if(max == 0) max = 1;
for(Integer integer : list){
targetPercentList.add(1-(float)integer/(float)max);
}
// Make sure percentList.size() == targetPercentList.size()
if(percentList.isEmpty() || percentList.size()<targetPercentList.size()){
int temp = targetPercentList.size()-percentList.size();
for(int i=0; i<temp;i++){
percentList.add(1f);
}
} else if (percentList.size()>targetPercentList.size()){
int temp = percentList.size()-targetPercentList.size();
for(int i=0; i<temp;i++){
percentList.remove(percentList.size()-1);
}
}
setMinimumWidth(2);
removeCallbacks(animator);
post(animator);
}
/*
* 同理,将每个教室的座位总数也传进来
*/
public void setAllDataList(ArrayList<Integer> list, int max){
AlltargetPercentList = new ArrayList<Float>();
if(max == 0) max = 1;
for(Integer integer : list){
AlltargetPercentList.add(1-(float)integer/(float)max);
}
// Make sure percentList.size() == targetPercentList.size()
if(AllpercentList.isEmpty() || AllpercentList.size()<AlltargetPercentList.size()){
int temp = AlltargetPercentList.size()-AllpercentList.size();
for(int i=0; i<temp;i++){
AllpercentList.add(1f);
}
} else if (AllpercentList.size()>AlltargetPercentList.size()){
int temp = AllpercentList.size()-AlltargetPercentList.size();
for(int i=0; i<temp;i++){
AllpercentList.remove(AllpercentList.size()-1);
}
}
setMinimumWidth(2);
removeCallbacks(Allanimator);
post(Allanimator);
}
@Override
protected void onDraw(Canvas canvas) {
int i = 1,j = 1,k = 1;
if(percentList != null && !percentList.isEmpty()){
for(Float f:percentList){
rect.set(BAR_SIDE_MARGIN*i+barWidth*(i-1),
topMargin,
(BAR_SIDE_MARGIN+barWidth)* i,
getHeight()-bottomTextHeight-TEXT_TOP_MARGIN);
canvas.drawRect(rect,bgPaint);
rect.set(BAR_SIDE_MARGIN*i+barWidth*(i-1),
topMargin+(int)((getHeight()-topMargin)*AllpercentList.get(i-1)),
(BAR_SIDE_MARGIN+barWidth)* i,
getHeight()-bottomTextHeight-TEXT_TOP_MARGIN);
canvas.drawRect(rect,AllfgPaint);
rect.set(BAR_SIDE_MARGIN*i+barWidth*(i-1),
topMargin+(int)((getHeight()-topMargin)*percentList.get(i-1)),
(BAR_SIDE_MARGIN+barWidth)* i,
getHeight()-bottomTextHeight-TEXT_TOP_MARGIN);
canvas.drawRect(rect,fgPaint);
i++;
}
}
if(bottomTextList != null && !bottomTextList.isEmpty()){
i = 1;j = 1;k = 1;
//底部数字
for(String s:bottomTextList){
canvas.drawText(s,BAR_SIDE_MARGIN*i+barWidth*(i-1)+barWidth/2,
getHeight()-bottomTextDescent,textPaint);
i++;
}
//空座位数值
for(String s:BlankSeatList){
float y=( topMargin+(int)((getHeight()-topMargin)*percentList.get(j-1)) + getHeight()-bottomTextHeight-TEXT_TOP_MARGIN)/2;
if (y> (getHeight()-bottomTextHeight-TEXT_TOP_MARGIN-topMargin-15))
y=getHeight()-bottomTextHeight-TEXT_TOP_MARGIN-topMargin;
canvas.drawText(s,BAR_SIDE_MARGIN*j+barWidth*(j-1)+barWidth/2,
y,BlankNumPaint);
j++;
}
//总座位数值
for(String s:AllSeatList){
float y=topMargin+(int)((getHeight()-topMargin)*AllpercentList.get(k-1));
canvas.drawText(s,BAR_SIDE_MARGIN*k+barWidth*(k-1)+barWidth/2,
y,AllNumPaint);
k++;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mViewWidth = measureWidth(widthMeasureSpec);
int mViewHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(mViewWidth,mViewHeight);
}
private int measureWidth(int measureSpec){
int preferred = 0;
if(bottomTextList != null){
preferred = bottomTextList.size()*(barWidth+BAR_SIDE_MARGIN);
}
return getMeasurement(measureSpec, preferred);
}
private int measureHeight(int measureSpec){
int preferred = 222;
return getMeasurement(measureSpec, preferred);
}
private int getMeasurement(int measureSpec, int preferred){
int specSize = MeasureSpec.getSize(measureSpec);
int measurement;
switch(MeasureSpec.getMode(measureSpec)){
case MeasureSpec.EXACTLY:
measurement = specSize;
break;
case MeasureSpec.AT_MOST:
measurement = Math.min(preferred, specSize);
break;
default:
measurement = preferred;
break;
}
return measurement;
}
//处理横轴标注的点击事件
@Override
public boolean onTouchEvent(MotionEvent event) {
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
Region rv = new Region();
int a,b,c,d;
if(bottomTextList != null || !bottomTextList.isEmpty()){
for(int i=0;i<bottomTextList.size();i++)
{
a=BAR_SIDE_MARGIN*(i+1)+barWidth*i-BAR_SIDE_MARGIN/2; //L
b=a+BAR_SIDE_MARGIN+barWidth; //R
c=getHeight()-bottomTextHeight-TEXT_TOP_MARGIN-20; //T
d=getHeight()+20; //B
rv.set(a,c,b,d);
if (rv.contains(point.x,point.y) && event.getAction() == MotionEvent.ACTION_DOWN){
clickarea = i;
}else if (event.getAction() == MotionEvent.ACTION_UP){
if (rv.contains(point.x,point.y)){
//把点击的教室名传进接口
clickroom = bottomTextList.get(clickarea);
if (mDeliverRoomListener != null)
mDeliverRoomListener.DeliverRoom(clickroom);
System.out.println("click:"+clickarea);
}
}
}
}
if (event.getAction() == MotionEvent.ACTION_DOWN ||
event.getAction() == MotionEvent.ACTION_UP){
postInvalidate();
}
return true;
}
}
说明:
1.通过以下5个方法将数据传进BarView实例内:
public void setBottomTextList(ArrayList<String> bottomStringList) //底部横坐标数据
public void setBlankSeatList(ArrayList<String> BlankSeatStringList) //空座位个数 string 用来显示在图上
public void setAllSeatList(ArrayList<String> AllSeatStringList) //总座位个数 string 用来显示在图上
public void setDataList(ArrayList<Integer> list, int max) //空座位数据 int 用来转化成高度,画图
public void setAllDataList(ArrayList<Integer> list, int max) //总座位数据 int 用来转化成高度,画图
我比较懒,因为联网拿到的json就是string,所以直接将要显示在图上的数据给传进来了,同时通过setDataList将int类型的数据传进来,其实数据是一样的。大家可以自己修改一下。
其中max这个参数是这组数据中最大的,用来确定BarView的高度,看过BarView源码的应该都清楚。
2. 在onDraw中 绘制,包括横坐标数据,两组数字,3组颜色
这个根据大家的需求看着修改。
3. 代码中重载了onTouchEvent函数,旨在点击横轴坐标可以打开一个WebView,不需要的请注释掉这个函数,这里目的是向大家展示如果获取横轴坐标的数据的位置。
代码2:嵌入了BarView的 Activity 相关代码,主要是实例化BarView以及传数据
实例化:
final BarView barView = (BarView)this.findViewById(R.id.lib_bar_view);
barView.setRoomClickedListener(this);
传数据:
SetCurrentBlankNum(barView);
private void SetCurrentBlankNum(BarView barView){
int r = dataList.length;
int max = 0;
totalblanknum = 0;
bottomList.clear();
barDataList.clear();
BlankNumList.clear();
AllbarDataList.clear();
AllNumList.clear();
for (int i=0; i<r/3; i++){
bottomList.add(dataList[i*3]);
barDataList.add(Integer.parseInt(dataList[1+i*3]));
BlankNumList.add(dataList[1+i*3]);
totalblanknum = totalblanknum + Integer.parseInt(dataList[1+i*3]);
//找到最大教室的座位个数
max = (Integer.parseInt(dataList[2+i*3])>max) ? Integer.parseInt(dataList[2+i*3]) : max;
AllbarDataList.add(Integer.parseInt(dataList[2+i*3]));
AllNumList.add(dataList[2+i*3]);
}
barView.setBottomTextList(bottomList); //横轴area名称
barView.setBlankSeatList(BlankNumList); //空座位数目 string
barView.setAllSeatList(AllNumList); //总座位数目 string
barView.setDataList(barDataList,max); //绘制上层bar的数据 int
barView.setAllDataList(AllbarDataList,max+10);//绘制底层bar的数据 int
}
说明:
1. dataList:
是一个 String[] 类型的数组,我是存放进过初步处理的json的。json格式:name1,blanknum1,totalnum1,name2,blanknum2,totalnum2,,,
所以dataList数据也是这样的。
2.max的取值:
max值传进BarView中确定了 绘制的Bar的高度,这里max+10 是因为总座位数的数据高度。
如有错误或这需要改进的地方望指出。