Android自定義柱狀圖,帶有標註和左右滑動效果

最近自己改進了一下柱狀圖的繪製,支持多跟柱子,自己設置顏色,並且添加了手動滑動效果(在屏幕繪製寬度不夠時啓動滑動),下圖是整體效果,下面我分析一下代碼:

1、設置內容:

 tagging:標註

 xRawData:x軸座標

 yRawData:爲柱形圖的內容,通過List<Float>... yRawData傳入,可以有多個


/**
	 * 設置值 tagging:標註 xRawData:x軸座標 yRawData:爲柱形圖的內容
	 */
	public void setData(List<String> tagging, List<String> xRawData,
			List<Float>... yRawData) {
		occupyingText = null;
		this.xRawDatas.clear();
		this.yRawData.clear();
		this.xRawDatas.addAll(xRawData);
		this.tagging = tagging;
		Collections.addAll(this.yRawData, yRawData);
		updateTagging();
		updateCutoffwidth();
		scroll();
		invalidate();
	}

2、調用方法:

BarChartView kanner = (BarChartView) findViewById(R.id.kanner);
		List<String> taggings = new ArrayList<>(); 
		taggings.add("北京");
		taggings.add("上海");
		taggings.add("深圳");
		List<String> xRawDatas = new ArrayList<>();  //橫座標值
		xRawDatas.add("2016-01");
		xRawDatas.add("2016-02");
		xRawDatas.add("2016-03");
		xRawDatas.add("2016-04");
		xRawDatas.add("2016-05");
		xRawDatas.add("2016-06");
		xRawDatas.add("2016-07");
		xRawDatas.add("2016-08");
		xRawDatas.add("2016-09");
		List<Float> yRawDatas1 = new ArrayList<>();
		List<Float> yRawDatas2 = new ArrayList<>();
		List<Float> yRawDatas3 = new ArrayList<>();
		yRawDatas1.add(12.0f);
		yRawDatas1.add(7.0f);
		yRawDatas1.add(-12.0f);
		yRawDatas1.add(6.0f);
		yRawDatas1.add(12.0f);
		yRawDatas1.add(17.0f);
		yRawDatas1.add(-5.0f);
		yRawDatas1.add(9.0f);
		yRawDatas1.add(2.0f);
		
		yRawDatas2.add(-12.0f);
		yRawDatas2.add(7.0f);
		yRawDatas2.add(6.0f);
		yRawDatas2.add(7.0f);
		yRawDatas2.add(8.0f);
		yRawDatas2.add(-13.0f);
		yRawDatas2.add(5.0f);
		yRawDatas2.add(9.0f);
		yRawDatas2.add(2.8f);
		
		yRawDatas3.add(3.0f);
		yRawDatas3.add(6.0f);
		yRawDatas3.add(9.0f);
		yRawDatas3.add(6.0f);
		yRawDatas3.add(11.0f);
		yRawDatas3.add(-7.0f);
		yRawDatas3.add(6.66f);
		yRawDatas3.add(6.0f);
		yRawDatas3.add(-6.0f);
		kanner.setData(taggings, xRawDatas, yRawDatas1,yRawDatas2,yRawDatas3);

這裏我做了一些假數據,實際開發中根據接口請求到的值賦值就可以了。



3、這裏我對一些關鍵技術說明一下:

private float mDownPosX = 0;
	private float mDownPosY = 0;

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		if (offsetWidthMax != 0) {
			final float x = event.getX();
			final float y = event.getY();
			final int action = event.getAction();
			switch (action) {
			case MotionEvent.ACTION_DOWN:
				mDownPosX = x;
				mDownPosY = y;
				break;
			case MotionEvent.ACTION_MOVE:
				final float deltaX = Math.abs(x - mDownPosX);
				final float deltaY = Math.abs(y - mDownPosY);
				if (deltaX > deltaY) {
					getParent().requestDisallowInterceptTouchEvent(true);
				} else {
					getParent().requestDisallowInterceptTouchEvent(false);
					return true;
				}
				break;
			}
			mGestureDetector.onTouchEvent(event);
			return true;
		} else {
			getParent().requestDisallowInterceptTouchEvent(false);
			return super.dispatchTouchEvent(event);
		}
		
	}

重寫dispatchTouchEvent,如果在滑動視圖裏,會與系統的滑動事件衝突,這裏就用到了事件分發機制,因爲柱狀圖都是左右滑動的,所以我們只需要攔截左右滑動就可以了,在這裏我們判斷如果左右滑動距離大於上下滑動,就執行左右滑動,否則不執行滑動事件。具體Android事件分發我整理一片博客http://blog.csdn.net/yulu5216/article/details/51306160,感興趣的可以看看


onSizeChanged,當佈局高寬改變時會重新繪製圖片高寬:

@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		this.canvasHeight = h;
		this.canvasWidth = w;
		coordinateRect = new RectF(marginLeft, marginTop + taggingHeight,
				canvasWidth - marginRight, canvasHeight - marginBottom);
		horizontalNum = (int) (coordinateRect.width() / dip2px(60));
		updateTagging();
		updateCutoffwidth();
	}

設置最大和最小值:

private boolean initMaxAndMin(int start, int stop) {
		if (stop <= 0) {
			return true;
		}
		if (stop > this.xRawDatas.size()) {
			stop = this.xRawDatas.size();
		}
		List<Float> yRawDataNews = new ArrayList<>();
		for (int i = 0; i < this.yRawData.size(); i++) {
			yRawDataNews.addAll(yRawData.get(i).subList(start, stop));
		}
		float maxValueNews = getMaxArray(yRawDataNews);
		float minValueNews = getMinArray(yRawDataNews);
		if (isInteger) {
			if (maxValueNews >= 0 && minValueNews >= 0) {
				minValueNews = 0;
				maxValueNews = ((int) ((maxValueNews - 1) / 5 + 1)) * 5;
				yIndex = 5;
			} else if (maxValueNews <= 0 && minValueNews < 0) {
				maxValueNews = 0;
				minValueNews = ((int) ((minValueNews) / 5)) * 5;
				yIndex = 0;
			} else {
				float proportion = Math.abs(maxValueNews / minValueNews); // 比例
				if (proportion >= 4) {
					maxValueNews = ((int) ((maxValueNews - 1) / 4 + 1)) * 4;
					minValueNews = -maxValueNews / 4;
					yIndex = 4;
				} else if (proportion >= 1) {
					maxValueNews = ((int) ((maxValueNews - 1) / 3 + 1)) * 3;
					minValueNews = -maxValueNews * 2 / 3;
					yIndex = 3;
				} else if (proportion >= 1 / 4) {
					minValueNews = ((int) ((minValueNews) / 3)) * 3;
					maxValueNews = -minValueNews * 2 / 3;
					yIndex = 2;
				} else {
					minValueNews = ((int) ((minValueNews) / 4)) * 4;
					maxValueNews = -minValueNews / 4;
					yIndex = 1;
				}
			}
		} else {
			if (minValueNews < 0 && maxValueNews > 0) {
				float proportion = Math.abs(maxValueNews / minValueNews); // 比例
				if (proportion >= 4) {
					minValueNews = -maxValueNews / 4;
					yIndex = 4;
				} else if (proportion >= 1) {
					minValueNews = -maxValueNews * 2 / 3;
					yIndex = 3;
				} else if (proportion >= 1 / 4) {
					maxValueNews = -minValueNews * 2 / 3;
					yIndex = 2;
				} else {
					maxValueNews = -minValueNews / 4;
					yIndex = 1;
				}
			} else if (minValueNews >= 0 && maxValueNews > 0) {
				yIndex = 5;
				minValueNews = 0;
			} else if (minValueNews < 0 && maxValueNews <= 0) {
				yIndex = 0;
				maxValueNews = 0;
			}
		}

		if (maxValueNews == minValueNews) {
			minValueNews = +1;
		}
		if (maxValueNews == maxValue && minValue == minValueNews) {
			return true;
		}
		maxValue = maxValueNews;
		minValue = minValueNews;
		if (isCompanyUpdate) {
			if (maxValue > 300000000 || minValue < -300000000) {
				companyNews = "億" + company;
				priceWeight = 100000000;
			} else if (maxValue > 3000000 || minValue < -3000000) {
				companyNews = "百萬" + company;
				priceWeight = 1000000;
			} else if (maxValue > 30000 || minValue < -30000) {
				companyNews = "萬" + company;
				priceWeight = 10000;
			} else if (maxValue > 300 || minValue < -300) {
				companyNews = "百" + company;
				priceWeight = 100;
			} else {
				companyNews = company;
			}
		} else {
			companyNews = company;
		}
		if (minValue < 0) {
			marginBottom = dip2px(30);
		}
		coordinateRect = new RectF(marginLeft, marginTop + taggingHeight,
				canvasWidth - marginRight, canvasHeight - marginBottom);
		return false;
	}
start和stop是可視區域的開始和結束位置的下標,返回值判斷是否最大和最小值是否改變,如果改變則刷新頁面

最後我們上圖:


代碼下載地址:http://download.csdn.net/detail/yulu5216/9686662

最後我們上圖:

發佈了46 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章