------------------------------------------------------------------>>> 簡單版 ---- Android自定義控件之折線圖android
效果圖:canvas
佈局代碼:ide
<!-- 自定義折線圖 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myswitch="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".upgrade.MainActivity"> <custom.view.upgrade.my_line_chart.MyLineChart android:id="@+id/my_line_chart" android:layout_width="400dp" android:layout_height="300dp" android:background="@color/ring_test3"/> </LinearLayout>
設置參數相關的代碼:佈局
/** * 自定義折線圖 */ MyLineChart myLineChart = findViewById(R.id.my_line_chart); myLineChart.setYItemText(new String[]{"1級", "2級", "3級", "4級", "5級"}); myLineChart.setXItemText(new String[]{"2001", "2002", "2003", "2004", "2005", "2006", "2007"});
自定義折線圖View:ui
package custom.view.upgrade.my_line_chart; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; public class MyLineChart extends View { private final String TAG = MyLineChart.class.getSimpleName(); public MyLineChart(Context context, AttributeSet attrs) { super(context, attrs); initAction(); } private Paint paintYItem; // 專門用於畫Y軸的畫筆 private Paint paintXItem; // 專門用於話X軸的畫筆 private Paint paintLine; // 線與點共用一個畫筆 private Rect rectX = new Rect(); private Rect rectY = new Rect(); private int w; private int h; private String[] xItemText = {}; // X軸的文字 private String[] yItemText = {}; // Y軸的文字 /** * 給使用者設置X軸的文字集合 */ public void setXItemText(String[] xItemText) { this.xItemText = xItemText; } /** * 給使用者設置Y軸的文字集合 */ public void setYItemText(String[] yItemText) { this.yItemText = yItemText; saveYHeightList = new int[yItemText.length]; } private void initAction() { paintYItem = new Paint(); paintYItem.setAntiAlias(true); // 抗鋸齒 paintYItem.setColor(Color.RED); paintYItem.setTextSize(40); paintXItem = new Paint(); paintXItem.setAntiAlias(true); paintXItem.setColor(Color.BLUE); paintXItem.setTextSize(40); paintLine = new Paint(); paintLine.setAntiAlias(true); paintLine.setColor(Color.BLACK); paintLine.setTextSize(40); } /** * 測量自身View控件的寬和高,寬和高獲得父控件給子控件測量的高寬等 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 精確模式 // MeasureSpec.EXACTLY 當控件設置尺寸的值是多少,控件的寬高就是多少 // 最大模式 // MeasureSpec.AT_MOST 由父控件給出了最大控件,子控件最大隻能這麼大,固然也能夠比父控件小 if (widthMode == MeasureSpec.EXACTLY) { w = widthSize; } if (heightMode == MeasureSpec.EXACTLY) { h = heightSize; } if (heightMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.AT_MOST) { throw new IllegalArgumentException("參數異常,請輸入指定大小,例如:200dp"); } setMeasuredDimension(w, h); } private int countYHeight; // 計算記錄Y的高度 private int[] saveYHeightList; // 保存Y軸 五級的高度 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (yItemText.length == 0 || xItemText.length == 0) { throw new IllegalArgumentException("參數異常,設置x軸/y軸集合數據值爲空"); } // 畫Y軸集合數據 for (int y = 0; y< yItemText.length; y++) { countYHeight = (y + 1) * 118; canvas.drawText(yItemText[y], 40, countYHeight, paintYItem); saveYHeightList[y] = countYHeight; } paintLine.getTextBounds(xItemText[0], 0, xItemText[0].length(), rectX); paintLine.getTextBounds(yItemText[0], 0, yItemText[0].length(), rectY); // 畫X軸集合的數據 for (int x = 0; x <xItemText.length; x++) { int xLocation = (x + 1) * 122; canvas.drawText(xItemText[x], xLocation, (countYHeight + 80), paintXItem); // 定義線的 開始 到 結束 int tempStartY = 0; int tempStopY = 0; switch (x) { case 0: tempStartY = saveYHeightList[0] - (rectY.height() / 3); tempStopY = saveYHeightList[1] - (rectY.height() / 3); break; case 1: tempStartY = saveYHeightList[1] - (rectY.height() / 3); tempStopY = saveYHeightList[2] - (rectY.height() / 3); break; case 2: tempStartY = saveYHeightList[2] - (rectY.height() / 3); tempStopY = saveYHeightList[3] - (rectY.height() / 3); break; case 3: tempStartY = saveYHeightList[3] - (rectY.height() / 3); tempStopY = saveYHeightList[4] - (rectY.height() / 3); break; case 4: tempStartY = saveYHeightList[4] - (rectY.height() / 3); tempStopY = saveYHeightList[3] - (rectY.height() / 3); break; case 5: tempStartY = saveYHeightList[3] - (rectY.height() / 3); tempStopY = saveYHeightList[1] - (rectY.height() / 3); break; case 6: tempStartY = saveYHeightList[1] - (rectY.height() / 3); break; default: break; } // 畫點,注意:X軸須要與點保持一致的位置,只變化Y軸值的位置 canvas.drawCircle(xLocation + (rectX.width() / 2), tempStartY, 20, paintLine); // 畫線 if (x < xItemText.length-1) { int xLocationLine = ((x + 1) + 1) * 122; canvas.drawLine(xLocation + (rectX.width() / 2), tempStartY, xLocationLine + (rectX.width() / 2), tempStopY, paintLine); } } } }
------------------------------------------------------------------>>> 升級版 ---- Android自定義控件之折線圖this
效果圖:spa
背景顏色:3d
佈局代碼:code
<!-- 自定義折線圖 升級版 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myswitch="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".upgrade.MainActivity"> <custom.view.upgrade.my_line_chart2.MyLineChart2 android:id="@+id/my_line_chart" android:layout_width="match_parent" android:layout_height="300dp" android:background="@color/ring_test3"/> </LinearLayout>
自定義折線圖繪製類:xml
package custom.view.upgrade.my_line_chart2; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.View; import custom.view.R; public class MyLineChart2 extends View { private final String TAG = MyLineChart2.class.getSimpleName(); public MyLineChart2(Context context, AttributeSet attrs) { super(context, attrs); initView(); } private Paint xyLinePaint; // X軸與Y軸的灰色線的畫筆 private final int XY_LINE_SPACE = 120; // X軸Y軸距離左邊底邊距離間隙值 private String[] yListData = {"7.0", "6.0", "5.0", "4.0", "3.0", "2.0", "1.0", "0.0"}; private String[] xListData = {"活期", "三個月", "六個月", "一年", "兩年", "三年", "四年", "五年"}; // 基準利率的畫筆 private Paint standardRatePaint; // 真惠存利率 private Paint trueHuicunReatPaint; // 記錄X軸文字的位置 private int[] xTextLocation; // 記錄Y軸頂部越界值 與底部越界值 private int topDemarcation; private int bottomeDemarcation; // 基準利率動態變化的Y軸值 private int changeY; private int changStopY; // 真惠存利率動態變化的Y軸值 private int changeYHuicun; private int changeStopYHuicun; // 基準利率動態變化的值 private int[] changYList = {200, 400, 300, 350, 500, 450, 300}; // 真惠存動態變化的值 private int[] changYHuicunList = {540, 300, 350, 600, 550, 480, 200}; private Rect rect; // 專門用於畫解釋信息文字的畫筆 private Paint textInfoPaint; private void initView() { xyLinePaint = new Paint(); xyLinePaint.setAntiAlias(true); // 抗鋸齒 xyLinePaint.setColor(getResources().getColor(R.color.xyline)); xyLinePaint.setTextSize(22); standardRatePaint = new Paint(); standardRatePaint.setAntiAlias(true); standardRatePaint.setColor(getResources().getColor(R.color.standard_rate)); standardRatePaint.setStyle(Paint.Style.STROKE); standardRatePaint.setStrokeWidth(6); trueHuicunReatPaint = new Paint(); trueHuicunReatPaint.setAntiAlias(true); trueHuicunReatPaint.setColor(getResources().getColor(R.color.true_huicun)); trueHuicunReatPaint.setStyle(Paint.Style.STROKE); trueHuicunReatPaint.setStrokeWidth(6); xTextLocation = new int[xListData.length]; rect = new Rect(); textInfoPaint = new Paint(); textInfoPaint.setAntiAlias(true); textInfoPaint.setColor(getResources().getColor(R.color.xyline)); textInfoPaint.setTextSize(32); } /** * 基準利率七個值 */ public void setStandardData(int[] changYList) { this.changYList = changYList; } /** * 真惠存利率七個值 */ public void setTureHuicunData(int[] changYHuicunList) { this.changYHuicunList = changYHuicunList; } private int viewWidth; // 當前控件的寬度 private int viewHeight; // 當前控件的高度 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // MeasureSpec.EXACTLY 精確模式,指定大小,例如:200dp // MeasureSpec.AT_MOST 最大模式,由父控件控制最大,子控件不能比父控件大,固然能夠比父控件小 if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { viewHeight = MeasureSpec.getSize(heightMeasureSpec); } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { throw new IllegalArgumentException("高度指定參數異常,請按照,例如:200dp"); } viewWidth = MeasureSpec.getSize(widthMeasureSpec); // 這兩個值是根據手機屏幕而變化的 Log.d(TAG, "viewHeight:" + viewHeight + " viewWidth:" + viewWidth); setMeasuredDimension(viewWidth, viewHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 畫Y軸的箭頭符號 canvas.drawLine(XY_LINE_SPACE, 40, XY_LINE_SPACE + 16, 80, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, 40, XY_LINE_SPACE - 16, 80, xyLinePaint); // 畫Y軸線 canvas.drawLine(XY_LINE_SPACE, 40, XY_LINE_SPACE, viewHeight - XY_LINE_SPACE, xyLinePaint); // 畫Y軸線中的短線 15根 int startStopY = 0; for (int y=0; y<15; y++) { if (y == 0) { startStopY = 80; } startStopY += 35; canvas.drawLine(XY_LINE_SPACE, startStopY, XY_LINE_SPACE + 20, startStopY, xyLinePaint); } /*canvas.drawLine(XY_LINE_SPACE, startStopY = 100, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint); canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);*/ // 畫Y軸線中的信息 字符串 int textY = 0; for (int y2 = 0; y2 < yListData.length; y2 ++) { if (y2 == 0) { textY = 80; topDemarcation = textY; topDemarcation += 60; } textY += 60; bottomeDemarcation = textY; canvas.drawText(yListData[y2], XY_LINE_SPACE - 60, textY, xyLinePaint); } // 畫X的箭頭符號 canvas.drawLine(viewWidth - (viewWidth / 8), viewHeight - XY_LINE_SPACE, (viewWidth - (viewWidth / 8)) - 40, (viewHeight - XY_LINE_SPACE) - 16, xyLinePaint); canvas.drawLine(viewWidth - (viewWidth / 8), viewHeight - XY_LINE_SPACE, (viewWidth - (viewWidth / 8)) - 40, (viewHeight - XY_LINE_SPACE) + 16, xyLinePaint); // 畫X軸線 canvas.drawLine(XY_LINE_SPACE, viewHeight - XY_LINE_SPACE, viewWidth - (viewWidth / 8), viewHeight - XY_LINE_SPACE, xyLinePaint); // 畫15根X軸線 int startStopX = 0; for (int x = 0; x<14; x ++) { if (x == 0) { startStopX = XY_LINE_SPACE + 20; } startStopX += 50; canvas.drawLine(startStopX, viewHeight - XY_LINE_SPACE, startStopX, viewHeight - XY_LINE_SPACE - 20, xyLinePaint); } // 畫X軸字符串信息 int testX = 0; for (int x2 = 0; x2 < xListData.length; x2 ++) { if (testX == 0) { testX = 110; } testX += 100; xTextLocation[x2] = testX; // 記錄X軸方向的文字位置 canvas.drawText(xListData[x2], testX,viewHeight - XY_LINE_SPACE + 40, xyLinePaint); } // 畫七個點,X軸方向位置一直不變,Y軸方向的位置是變化的 for (int dianXY = 0; dianXY < xListData.length - 1; dianXY ++) { standardRatePaint.getTextBounds(xListData[dianXY], 0, xListData[dianXY].length(), rect); changeY = changYList[dianXY]; // 若是不等於最後一個,就能夠+1 Log.d(TAG, "dianXY:" + dianXY + " changYList.length:" + changYList.length); if (dianXY < changYList.length - 1) { changStopY = changYList[dianXY + 1]; } if (changeY > bottomeDemarcation) { changeY = bottomeDemarcation; } else if (changeY < topDemarcation) { changeY = topDemarcation; } if (changStopY > bottomeDemarcation) { changStopY = bottomeDemarcation; } else if (changStopY < topDemarcation) { changStopY = topDemarcation; } Log.d(TAG, "rect.width():" + rect.width() + " bottomeDemarcation:" + bottomeDemarcation + " topDemarcation:" + topDemarcation + " changeY:" + changeY); standardRatePaint.getTextBounds(yListData[0], 0, yListData[0].length(), rect); canvas.drawCircle(xTextLocation[dianXY] + (rect.width()) ,changeY - rect.height(),10, standardRatePaint); // 畫線 開始的X軸與Y軸要增長,結束的X軸與Y軸要減小 if (dianXY < xListData.length - 2) { changeY -= rect.height(); // changStopY += rect.height(); changStopY -= rect.height(); /*changeY += 10; changStopY -= 10;*/ // 畫線,線的走向是根據點走的 canvas.drawLine(xTextLocation[dianXY] + (rect.width() + 8), changeY, xTextLocation[dianXY > xListData.length ? xListData.length : dianXY + 1] + (rect.width() - 8), changStopY, standardRatePaint); } /** * 真惠存利率區域 */ changeYHuicun = changYHuicunList[dianXY]; // 若是不等於最後一個,就能夠+1 Log.d(TAG, "changYHuicunList.length:" + changYHuicunList.length); if (dianXY < changYHuicunList.length - 1) { changeStopYHuicun = changYHuicunList[dianXY + 1]; } if (changeYHuicun > bottomeDemarcation) { changeYHuicun = bottomeDemarcation; } else if (changeYHuicun < topDemarcation) { changeYHuicun = topDemarcation; } if (changeStopYHuicun > bottomeDemarcation) { changeStopYHuicun = bottomeDemarcation; } else if (changeStopYHuicun < topDemarcation) { changeStopYHuicun = topDemarcation; } trueHuicunReatPaint.getTextBounds(yListData[0], 0, yListData[0].length(), rect); canvas.drawCircle(xTextLocation[dianXY] + (rect.width()) ,changeYHuicun - rect.height(),10, trueHuicunReatPaint); // 畫線 開始的X軸與Y軸要增長,結束的X軸與Y軸要減小 if (dianXY < xListData.length - 2) { changeYHuicun -= rect.height(); changeStopYHuicun -= rect.height(); /*changeY += 10; changStopY -= 10;*/ // 畫線,線的走向是根據點走的 canvas.drawLine(xTextLocation[dianXY] + (rect.width() + 8), changeYHuicun, xTextLocation[dianXY > xListData.length ? xListData.length : dianXY + 1] + (rect.width() - 8), changeStopYHuicun, trueHuicunReatPaint); } } // 畫兩個描述:基準利率(%) 真惠存利率(%) int heightLineCircle = XY_LINE_SPACE - (XY_LINE_SPACE / 2); int radius = 10; // 先畫一個點 canvas.drawCircle(xTextLocation[2], heightLineCircle, radius, standardRatePaint); // 畫點的左邊橫線 canvas.drawLine(xTextLocation[2] - 6, heightLineCircle, xTextLocation[2] - 70, heightLineCircle, standardRatePaint); // 畫點的右邊橫線 canvas.drawLine(xTextLocation[2] + 6,heightLineCircle,xTextLocation[2] + 70, heightLineCircle, standardRatePaint); // 畫文字 canvas.drawText("基準利率(%)", xTextLocation[2] - 90, heightLineCircle + 50, textInfoPaint); // 先畫一個點 canvas.drawCircle(xTextLocation[5], heightLineCircle, radius,trueHuicunReatPaint); canvas.drawLine(xTextLocation[5] - 6, heightLineCircle, xTextLocation[5] - 70, heightLineCircle, trueHuicunReatPaint); canvas.drawLine(xTextLocation[5] + 6, heightLineCircle, xTextLocation[5] + 70, heightLineCircle, trueHuicunReatPaint); canvas.drawText("真惠存利率(%)", xTextLocation[5] - 90, heightLineCircle + 50, textInfoPaint); } /** * dp轉px * @param dp * @return */ /* private int dip2px(int dp) { float density = getContext().getResources().getDisplayMetrics().density; return (int) (dp * density + 0.5); }*/ }