Android開發之自訂View專題(一):自訂直條圖

來源:互聯網
上載者:User

標籤:報表   直條圖   自訂view   柱狀圖   橫條圖   

博主之前做的項目中,需要用到報表功能,之前在網上百度Google各種結果,沒有一個是能夠滿足博主的老闆的需求的,無奈之前博主只好自己去研究。終於研究出了一個不錯的結果。先上:

這是博主的一個項目的一個報表。老闆要求可以點擊左側的月份進行月份比較,也可以點擊選擇月份,右邊直條圖條狀是可以響應點擊事件的,並且可以左右滑動切換月份,如果直條圖內容過長超出介面,也是可以左右滑動查看未顯示全的內容的。這裡只講直條圖的產生,其他介面效果博主就不講解了。實際效果和這樣有點差異,但不大,主要是實際效果在直條圖的頂端都還會顯示數值。

自訂直條圖提供兩種定義方式,可以在XML檔案中寫入,也可以再代碼中new出來後加入一個容器中。這裡博主就不在詳細描述了,博主花了1個多小時才將該view從項目中獨立出來。有興趣的同學可以下載完整的項目下來學習。

github:https://github.com/victorfreedom1989/FreedomHistogram

CSDN:http://download.csdn.net/detail/victorfreedom/8309505



1、自訂直條圖第一步,自訂柱形條:

package com.freedom.histogram;import java.text.DecimalFormat;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;/** * @ClassName: FreedomHistogram * @author victor_freedom ([email protected]) * @createddate 2014-12-28 下午10:44:52 * @Description:  */public class FreedomHistogram {// 柱形條寬度private int with;// 柱形條高度private int height;// 柱形條對應數值private double count;// 起始Y座標private int axisY;// 標題起始Y座標private int titleY;// 柱形條起始X座標private int axisX;private Context context;private DecimalFormat df;private Paint mPaint;public FreedomHistogram(Context context, int with, float axisY) {this.with = with;this.axisY = ScreenUtil.dip2px(context, axisY);this.titleY = ScreenUtil.dip2px(context, axisY + 35);this.context = context;this.df = new DecimalFormat("#0.0");mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setColor(Color.WHITE);}public FreedomHistogram() {};public int getAxisX() {return axisX;}public void setAxisX(int axisX) {this.axisX = axisX;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public double getCount() {return count;}public void setCount(double count) {this.count = count;}public void drawHistogram(Canvas canvas, Paint paint) {// 繪製第一層canvas.drawRect(axisX - 1, axisY - height - 1, with + axisX + 1, axisY,mPaint);// 繪製第二層,讓柱形條有白邊,好看,根據個人喜好可以不繪製第二層。canvas.drawRect(axisX, axisY - height, with + axisX, axisY - 1, paint);// 繪製柱形條對應的數值canvas.drawText(df.format(count),axisX - ScreenUtil.dip2px(context, 5), axisY - height - 10,paint);}public void drawTitle(Canvas canvas, Paint paint, String name) {// 繪製標題canvas.drawRect(axisX, titleY - height, with + axisX, titleY - 1, paint);canvas.drawText(name, with + axisX + ScreenUtil.dip2px(context, 5),titleY - 1, paint);}}
2、自訂直條圖第二步:構建直條圖

package com.freedom.histogram;import java.util.ArrayList;import java.util.List;import java.util.Map;import android.annotation.SuppressLint;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.DashPathEffect;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.PathEffect;import android.graphics.PointF;import android.graphics.Rect;import android.util.AttributeSet;import android.util.FloatMath;import android.view.MotionEvent;import android.view.View;/** * @ClassName: FreedomHistogramView * @author victor_freedom ([email protected]) * @createddate 2014-12-28 下午10:47:33 * @Description: TODO */public class FreedomHistogramView extends View {private HistogramOnClickListener histogramOnClickListener;public void setHistogramOnClickListener(HistogramOnClickListener listner) {this.histogramOnClickListener = listner;}// 直條圖總資料private Map<Integer, Double[]> datas;// 直條圖每一組資料private Double[] perData;// 最大值private double max;// 平局值private Double average;// 資料有多少組集合private ArrayList<Integer> dataKeys;// 每組資料的集合private ArrayList<Double[]> valuese;// 每組資料對應的地區private ArrayList<Rect> rects;// 資料有多少組private int flag;// 平均線數值private double avergerLine;// 每組資料對應的顏色private int[] colors = new int[] { 0xff8B008B, 0xff00BFFF, 0xff4800FF,0xff333333, 0xff006400, 0xff668B8B, 0xffFF83FA, 0xff363636,0xff000080, 0xff008B8B, 0xffFFDAB9, 0xff90EE90 };// 直條圖標題對應相距值private int margin;// 柱形條private FreedomHistogram freedomHistogram;private Paint paint;// 是否左下角繪製標題private boolean isTitle = false;private String title[];// 每組資料柱形條對應的X軸座標說明private List<String> nodes;// 柱形條寬度private int charWith;// 柱形條間距(配合margin使用)private int withX;// Y周對應座標點的個數private int numberY;// Y軸各座標點之間的距離private float withY;public FreedomHistogramView(Context context, AttributeSet attrs,int defStyle) {super(context, attrs, defStyle);}/** * Title: Description:在XML檔案中的構建方法 *  * @param context * @param attrs */public FreedomHistogramView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.FreedomHistogramView);charWith = a.getInteger(R.styleable.FreedomHistogramView_charWith, 20);withX = a.getInteger(R.styleable.FreedomHistogramView_withX, 20);numberY = a.getInteger(R.styleable.FreedomHistogramView_numberY, 10);withY = a.getFloat(R.styleable.FreedomHistogramView_withY, 20);isTitle = a.getBoolean(R.styleable.FreedomHistogramView_isTitle, false);paint = new Paint();paint.setAntiAlias(true);// xPaint.setAntiAlias(true);// 建立一個柱形條freedomHistogram = new FreedomHistogram(context, ScreenUtil.dip2px(context, charWith), withY * (numberY - 1) + 10);a.recycle();}public FreedomHistogramView(Context context) {super(context);}public FreedomHistogramView(Context context, Map<Integer, Double[]> datas,int charWith, int withX, List<String> nodes, int numberY,float withY, boolean isTitle) {super(context);margin = 0;this.datas = datas;this.withX = withX;this.nodes = nodes;this.numberY = numberY;this.withY = withY;this.charWith = charWith;this.isTitle = isTitle;paint = new Paint();paint.setAntiAlias(true);// xPaint.setAntiAlias(true);// 建立一個柱形條freedomHistogram = new FreedomHistogram(context, ScreenUtil.dip2px(context, charWith), withY * (numberY - 1) + 10);}public Map<Integer, Double[]> getDatas() {return datas;}public String[] getTitle() {return title;}public void setTitle(String[] title) {this.title = title;}public void setDatas(Map<Integer, Double[]> datas) {this.datas = datas;// invalidate();}public boolean isTitle() {return isTitle;}public void setTitle(boolean isTitle) {this.isTitle = isTitle;}public List<String> getNodes() {return nodes;}public void setNodes(List<String> nodes) {this.nodes = nodes;// invalidate();}public int getCharWith() {return charWith;}public void setCharWith(int charWith) {this.charWith = charWith;}public int getwithX() {return withX;}public void setwithX(int withX) {this.withX = withX;}public float getNumberY() {return numberY;}public void setNumberY(int numberY) {this.numberY = numberY;}public float getWithY() {return withY;}public void setWithY(float withY) {this.withY = withY;}public void iniData() {// 取出數值Integer k;Double[] v;dataKeys = new ArrayList<Integer>();valuese = new ArrayList<Double[]>();for (Map.Entry<Integer, Double[]> entry : datas.entrySet()) {k = entry.getKey(); // keydataKeys.add(k);v = entry.getValue(); // valuevaluese.add(v);}this.flag = dataKeys.size();double total = 0;int sum = 0;for (Double[] dataMax : valuese) {max = dataMax[0];for (double data : dataMax) {total += data;max = max > data ? max : data;if (data != 0.0) {sum++;}}}max = dataChange(max);average = max / (numberY - 1);avergerLine = total / sum;}private double dataChange(double data) {StringBuilder sb = new StringBuilder();String dataString = String.valueOf(data);String dateInt = dataString.substring(0, dataString.lastIndexOf("."));int length = dateInt.length();String newDatafirst = dataString.substring(0, 1);if (newDatafirst.equals("9")) {boolean isChange = false;for (int i = 1; i < dataString.length(); i++) {String dataPer = dataString.substring(i, i + 1);if (dataPer.equals("0")) {continue;} else {isChange = true;}}if (isChange) {sb.append("1");for (int i = 0; i < length; i++) {sb.append("0");}return Double.valueOf(sb.toString());} else {return data;}} else {boolean isChange = false;for (int i = 1; i < dataString.length(); i++) {String dataPer = dataString.substring(i, i + 1);if (dataPer.equals("0")) {continue;} else {isChange = true;break;}}if (isChange) {newDatafirst = (Integer.valueOf(newDatafirst) + 1) + "";sb.append(newDatafirst);for (int i = 0; i < length - 1; i++) {sb.append("0");}return Double.valueOf(sb.toString());} else {return data;}}}public void drawAxis(Canvas canvas) {paint.setColor(Color.DKGRAY);paint.setStrokeWidth(ScreenUtil.dip2px(getContext(), 2));paint.setTextSize(ScreenUtil.sp2px(getContext(), 10));canvas.drawLine(ScreenUtil.dip2px(getContext(), 50),ScreenUtil.dip2px(getContext(), withY * (numberY - 1) + 10),ScreenUtil.dip2px(getContext(), (withX + (flag - 1) * charWith)* (nodes.size() + 1)),ScreenUtil.dip2px(getContext(), withY * (numberY - 1) + 10),paint);canvas.drawLine(ScreenUtil.dip2px(getContext(), 51),ScreenUtil.dip2px(getContext(), 5),ScreenUtil.dip2px(getContext(), 51),ScreenUtil.dip2px(getContext(), withY * (numberY - 1) + 10),paint);int x = ScreenUtil.dip2px(getContext(), (float) (59 + flag * charWith/ 2));int y = ScreenUtil.dip2px(getContext(), withY * (numberY - 1) + 10);for (int i = 0; i < nodes.size(); i++) {canvas.drawText(nodes.get(i),x,ScreenUtil.dip2px(getContext(), withY * (numberY - 1) + 25),paint);x += ScreenUtil.dip2px(getContext(), withX + (flag - 1) * charWith);}for (int i = 0; i < numberY; i++) {canvas.drawText(average.intValue() * i + "",ScreenUtil.dip2px(getContext(), 3), y, paint);y -= ScreenUtil.dip2px(getContext(), withY);}}public void drawChart(Canvas canvas) {rects = new ArrayList<Rect>();rects.clear();paint.setTextSize(ScreenUtil.sp2px(getContext(), 8));for (int j = 0; j < flag; j++) {paint.setColor(colors[j]);// paint.setStyle(Style.FILL_AND_STROKE);// paint.setStrokeWidth(1);perData = valuese.get(j);int temp_screen = ScreenUtil.dip2px(getContext(), 60 + j * charWith);for (int i = 0; i < perData.length; i++) {freedomHistogram.setCount(perData[i]);freedomHistogram.setHeight(ScreenUtil.dip2px(getContext(),(int) (perData[i].intValue() / max * (withY* (numberY - 1) + 10))));if (margin == 0) {freedomHistogram.setAxisX(temp_screen);} else {temp_screen += ScreenUtil.dip2px(getContext(), withX+ (flag - 1) * charWith);freedomHistogram.setAxisX(temp_screen);}freedomHistogram.drawHistogram(canvas, paint);margin = ScreenUtil.dip2px(getContext(), 10);Rect r = new Rect(temp_screen- ScreenUtil.dip2px(getContext(),(withX - charWith) / 3),ScreenUtil.dip2px(getContext(), withY * (numberY - 1))- ScreenUtil.dip2px(getContext(),(int) (perData[i].intValue() / max* withY * (numberY - 1))),temp_screen+ ScreenUtil.dip2px(getContext(), charWith)+ ScreenUtil.dip2px(getContext(),(withX - charWith) / 3),ScreenUtil.dip2px(getContext(), withY * (numberY - 1)));rects.add(r);}margin = 0;}}public void drawline(Canvas canvas) {// 虛線// Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);Paint xPaint = new Paint(Paint.ANTI_ALIAS_FLAG);xPaint.setAntiAlias(true);xPaint.setStyle(Style.STROKE);xPaint.setColor(Color.RED);xPaint.setStrokeWidth(2);PathEffect effects = new DashPathEffect(new float[] {ScreenUtil.dip2px(getContext(), 5),ScreenUtil.dip2px(getContext(), 5),ScreenUtil.dip2px(getContext(), 5),ScreenUtil.dip2px(getContext(), 5) }, 1);xPaint.setPathEffect(effects);canvas.drawLine(ScreenUtil.dip2px(getContext(), 51), ScreenUtil.dip2px(getContext(),withY* (numberY - 1)+ 10- (int) (avergerLine / max * (withY* (numberY - 1) + 10))), ScreenUtil.dip2px(getContext(),(withX + (flag - 1) * charWith) * (nodes.size() + 1)),ScreenUtil.dip2px(getContext(),withY* (numberY - 1)+ 10- (int) (avergerLine / max * (withY* (numberY - 1) + 10))), xPaint);}public void drawtitle(Canvas canvas) {paint.setTextSize(ScreenUtil.sp2px(getContext(), 12));int title_screen = ScreenUtil.dip2px(getContext(), charWith);for (int i = 0; i < dataKeys.size(); i++) {paint.setColor(colors[i]);freedomHistogram.setHeight(ScreenUtil.dip2px(getContext(), 10));if (margin == 0) {freedomHistogram.setAxisX(title_screen+ ScreenUtil.dip2px(getContext(), 30) + margin);} else {freedomHistogram.setAxisX(title_screen+ ScreenUtil.dip2px(getContext(), 40) + margin);}freedomHistogram.drawTitle(canvas, paint, title[i]);margin = ScreenUtil.dip2px(getContext(), 10);title_screen = freedomHistogram.getAxisX();}margin = 0;}/** * 需要得到控制項的寬和高 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 設定開關的寬和高為背景圖片的寬高setMeasuredDimension(ScreenUtil.dip2px(getContext(), 60 + (withX + flag * charWith)* (nodes.size() + 1)),ScreenUtil.dip2px(getContext(), withY * numberY + 50));}@Overridepublic void onDraw(Canvas canvas) {canvas.drawColor(Color.TRANSPARENT);iniData();drawChart(canvas);drawAxis(canvas);if (flag == 1) {drawline(canvas);}if (isTitle) {drawtitle(canvas);}super.onDraw(canvas);}private float scale;private float lastScale;private boolean firstTouch = true;private boolean guide = true;private boolean move = false;private int mode = 0;private int DRAG = 1;private int ZOOM = 2;private float startDis;int x1 = 0;int y1 = 0;private int startX = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:mode = DRAG;startX = (int) event.getRawX();scale = this.getScaleX();move = false;break;case MotionEvent.ACTION_MOVE:if (mode == DRAG) {int newX = (int) event.getRawX();int dx = newX - startX;if (dx > 10) {move = true;}int l = this.getLeft() + dx;int t = this.getTop();int r = this.getRight() + dx;int b = this.getBottom();this.layout(l, t, r, b);startX = (int) event.getRawX();} else if (mode == ZOOM) {float endDis = distance(event);if (endDis > 10f) {lastScale = scale;scale = endDis / startDis;if (firstTouch) {if (scale > lastScale) {guide = true;} else {guide = false;}firstTouch = false;}if (guide) {if (scale < lastScale) {scale = lastScale;}} else {if (scale > lastScale) {scale = lastScale;}scale = scale * lastScale;if (scale < 1) {lastScale = 1;scale = 1;}}}}break;case MotionEvent.ACTION_UP:if (scale == 1 && dataKeys.size() == 1 && !move) {x1 = (int) event.getX();y1 = (int) event.getY();getPosition(x1, y1);}break;// 當螢幕上已經有一個觸點了,再有一個手指按下螢幕,就會相應case MotionEvent.ACTION_POINTER_DOWN:mode = ZOOM;firstTouch = true;move = true;startDis = distance(event);if (startDis > 10f) {// midPoint = mid(event);}break;// 手指離開螢幕,當螢幕還有一個手指在螢幕上時,就相應case MotionEvent.ACTION_POINTER_UP:mode = 0;break;}if (mode == ZOOM) {if (scale > 2) {lastScale = 2;scale = 2;}this.setScaleX(scale);if (scale == 1) {int l = 0;int t = 0;int r = this.getWidth();int b = this.getHeight();this.layout(l, t, r, b);}}return true;}private void getPosition(int x12, int y12) {if (null == rects) {return;}for (int i = 1; i <= rects.size(); i++) {Rect r = rects.get(i - 1);if (r.contains(x12, y12)) {if (null != histogramOnClickListener) {histogramOnClickListener.onClick(i);}}}}@SuppressLint("FloatMath")public float distance(MotionEvent event) {float dx = event.getX(1) - event.getX(0);float dy = event.getY(1) - event.getY(0);float dz = FloatMath.sqrt(dx * dx + dy * dy);return dz;}private PointF mid(MotionEvent event) {float dx = event.getX(1) + event.getX(0);float dy = event.getY(1) + event.getY(0);return new PointF(dx / 2, dy / 2);}}







Android開發之自訂View專題(一):自訂直條圖

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.