這篇文章給大家介紹Android中怎么自定義可左右滑動(dòng)和點(diǎn)擊的折線圖,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
成都一家集口碑和實(shí)力的網(wǎng)站建設(shè)服務(wù)商,擁有專業(yè)的企業(yè)建站團(tuán)隊(duì)和靠譜的建站技術(shù),十多年企業(yè)及個(gè)人網(wǎng)站建設(shè)經(jīng)驗(yàn) ,為成都上千家客戶提供網(wǎng)頁(yè)設(shè)計(jì)制作,網(wǎng)站開發(fā),企業(yè)網(wǎng)站制作建設(shè)等服務(wù),包括成都營(yíng)銷型網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計(jì),同時(shí)也為不同行業(yè)的客戶提供成都網(wǎng)站建設(shè)、網(wǎng)站制作的服務(wù),包括成都電商型網(wǎng)站制作建設(shè),裝修行業(yè)網(wǎng)站制作建設(shè),傳統(tǒng)機(jī)械行業(yè)網(wǎng)站建設(shè),傳統(tǒng)農(nóng)業(yè)行業(yè)網(wǎng)站制作建設(shè)。在成都做網(wǎng)站,選網(wǎng)站制作建設(shè)服務(wù)商就選創(chuàng)新互聯(lián)建站。
1、自定義view所需要的屬性
確定所需要的自定義view的屬性,然后在res/values目錄下,新建一個(gè)attrs.xml文件,代碼如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- xy坐標(biāo)軸顏色 --> <attr name="xylinecolor" format="color" /> <!-- xy坐標(biāo)軸寬度 --> <attr name="xylinewidth" format="dimension" /> <!-- xy坐標(biāo)軸文字顏色 --> <attr name="xytextcolor" format="color" /> <!-- xy坐標(biāo)軸文字大小 --> <attr name="xytextsize" format="dimension" /> <!-- 折線圖中折線的顏色 --> <attr name="linecolor" format="color" /> <!-- x軸各個(gè)坐標(biāo)點(diǎn)水平間距 --> <attr name="interval" format="dimension" /> <!-- 背景顏色 --> <attr name="bgcolor" format="color" /> <!--是否在ACTION_UP時(shí),根據(jù)速度進(jìn)行自滑動(dòng),建議關(guān)閉,過(guò)于占用GPU--> <attr name="isScroll" format="boolean" /> <declare-styleable name="chartView"> <attr name="xylinecolor" /> <attr name="xylinewidth" /> <attr name="xytextcolor" /> <attr name="xytextsize" /> <attr name="linecolor" /> <attr name="interval" /> <attr name="bgcolor" /> <attr name="isScroll" /> </declare-styleable> </resources>
2、在自定義view的構(gòu)造方法中獲取我們的自定義屬性:
public ChartView(Context context) { this(context, null); } public ChartView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ChartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); initPaint(); } /** * 初始化 * * @param context * @param attrs * @param defStyleAttr */ private void init(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.chartView, defStyleAttr, 0); int count = array.getIndexCount(); for (int i = 0; i < count; i++) { int attr = array.getIndex(i); switch (attr) { case R.styleable.chartView_xylinecolor://xy坐標(biāo)軸顏色 xylinecolor = array.getColor(attr, xylinecolor); break; case R.styleable.chartView_xylinewidth://xy坐標(biāo)軸寬度 xylinewidth = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xylinewidth, getResources().getDisplayMetrics())); break; case R.styleable.chartView_xytextcolor://xy坐標(biāo)軸文字顏色 xytextcolor = array.getColor(attr, xytextcolor); break; case R.styleable.chartView_xytextsize://xy坐標(biāo)軸文字大小 xytextsize = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xytextsize, getResources().getDisplayMetrics())); break; case R.styleable.chartView_linecolor://折線圖中折線的顏色 linecolor = array.getColor(attr, linecolor); break; case R.styleable.chartView_interval://x軸各個(gè)坐標(biāo)點(diǎn)水平間距 interval = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, interval, getResources().getDisplayMetrics())); break; case R.styleable.chartView_bgcolor: //背景顏色 bgcolor = array.getColor(attr, bgcolor); break; case R.styleable.chartView_isScroll://是否在ACTION_UP時(shí),根據(jù)速度進(jìn)行自滑動(dòng) isScroll = array.getBoolean(attr, isScroll); break; } } array.recycle(); } /** * 初始化畫筆 */ private void initPaint() { xyPaint = new Paint(); xyPaint.setAntiAlias(true); xyPaint.setStrokeWidth(xylinewidth); xyPaint.setStrokeCap(Paint.Cap.ROUND); xyPaint.setColor(xylinecolor); xyTextPaint = new Paint(); xyTextPaint.setAntiAlias(true); xyTextPaint.setTextSize(xytextsize); xyTextPaint.setStrokeCap(Paint.Cap.ROUND); xyTextPaint.setColor(xytextcolor); xyTextPaint.setStyle(Paint.Style.STROKE); linePaint = new Paint(); linePaint.setAntiAlias(true); linePaint.setStrokeWidth(xylinewidth); linePaint.setStrokeCap(Paint.Cap.ROUND); linePaint.setColor(linecolor); linePaint.setStyle(Paint.Style.STROKE); }
3、獲取一寫基本點(diǎn)
這些基本點(diǎn)包括:xy軸的原點(diǎn)坐標(biāo),第一個(gè)點(diǎn)的x軸的初始化坐標(biāo)值以及其最大值和最小值。這些參數(shù)可以在onLayout()方法里面獲取。
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (changed) { //這里需要確定幾個(gè)基本點(diǎn),只有確定了xy軸原點(diǎn)坐標(biāo),第一個(gè)點(diǎn)的X坐標(biāo)值及其最大最小值 width = getWidth(); height = getHeight(); //Y軸文本最大寬度 float textYWdith = getTextBounds("000", xyTextPaint).width(); for (int i = 0; i < yValue.size(); i++) {//求取y軸文本最大的寬度 float temp = getTextBounds(yValue.get(i) + "", xyTextPaint).width(); if (temp > textYWdith) textYWdith = temp; } int dp2 = dpToPx(2); int dp3 = dpToPx(3); xOri = (int) (dp2 + textYWdith + dp2 + xylinewidth);//dp2是y軸文本距離左邊,以及距離y軸的距離 // //X軸文本最大高度 xValueRect = getTextBounds("000", xyTextPaint); float textXHeight = xValueRect.height(); for (int i = 0; i < xValue.size(); i++) {//求取x軸文本最大的高度 Rect rect = getTextBounds(xValue.get(i) + "", xyTextPaint); if (rect.height() > textXHeight) textXHeight = rect.height(); if (rect.width() > xValueRect.width()) xValueRect = rect; } yOri = (int) (height - dp2 - textXHeight - dp3 - xylinewidth);//dp3是x軸文本距離底邊,dp2是x軸文本距離x軸的距離 xInit = interval + xOri; minXInit = width - (width - xOri) * 0.1f - interval * (xValue.size() - 1);//減去0.1f是因?yàn)樽詈笠粋€(gè)X周刻度距離右邊的長(zhǎng)度為X軸可見長(zhǎng)度的10% maxXInit = xInit; } super.onLayout(changed, left, top, right, bottom); }
4、利用ondraw()方法進(jìn)行繪制
@Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); canvas.drawColor(bgcolor); drawXY(canvas); drawBrokenLineAndPoint(canvas); } /** * 繪制折線和折線交點(diǎn)處對(duì)應(yīng)的點(diǎn) * * @param canvas */ private void drawBrokenLineAndPoint(Canvas canvas) { if (xValue.size() <= 0) return; //重新開一個(gè)圖層 int layerId = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG); drawBrokenLine(canvas); drawBrokenPoint(canvas); // 將折線超出x軸坐標(biāo)的部分截取掉 linePaint.setStyle(Paint.Style.FILL); linePaint.setColor(bgcolor); linePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); RectF rectF = new RectF(0, 0, xOri, height); canvas.drawRect(rectF, linePaint); linePaint.setXfermode(null); //保存圖層 canvas.restoreToCount(layerId); } /** * 繪制折線對(duì)應(yīng)的點(diǎn) * * @param canvas */ private void drawBrokenPoint(Canvas canvas) { float dp2 = dpToPx(2); float dp4 = dpToPx(4); float dp7 = dpToPx(7); //繪制節(jié)點(diǎn)對(duì)應(yīng)的原點(diǎn) for (int i = 0; i < xValue.size(); i++) { float x = xInit + interval * i; float y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(i)) / yValue.get(yValue.size() - 1); //繪制選中的點(diǎn) if (i == selectIndex - 1) { linePaint.setStyle(Paint.Style.FILL); linePaint.setColor(0xffd0f3f2); canvas.drawCircle(x, y, dp7, linePaint); linePaint.setColor(0xff81dddb); canvas.drawCircle(x, y, dp4, linePaint); drawFloatTextBox(canvas, x, y - dp7, value.get(xValue.get(i))); } //繪制普通的節(jié)點(diǎn) linePaint.setStyle(Paint.Style.FILL); linePaint.setColor(Color.WHITE); canvas.drawCircle(x, y, dp2, linePaint); linePaint.setStyle(Paint.Style.STROKE); linePaint.setColor(linecolor); canvas.drawCircle(x, y, dp2, linePaint); } } /** * 繪制顯示Y值的浮動(dòng)框 * * @param canvas * @param x * @param y * @param text */ private void drawFloatTextBox(Canvas canvas, float x, float y, int text) { int dp6 = dpToPx(6); int dp18 = dpToPx(18); //p1 Path path = new Path(); path.moveTo(x, y); //p2 path.lineTo(x - dp6, y - dp6); //p3 path.lineTo(x - dp18, y - dp6); //p4 path.lineTo(x - dp18, y - dp6 - dp18); //p5 path.lineTo(x + dp18, y - dp6 - dp18); //p6 path.lineTo(x + dp18, y - dp6); //p7 path.lineTo(x + dp6, y - dp6); //p1 path.lineTo(x, y); canvas.drawPath(path, linePaint); linePaint.setColor(Color.WHITE); linePaint.setTextSize(spToPx(14)); Rect rect = getTextBounds(text + "", linePaint); canvas.drawText(text + "", x - rect.width() / 2, y - dp6 - (dp18 - rect.height()) / 2, linePaint); } /** * 繪制折線 * * @param canvas */ private void drawBrokenLine(Canvas canvas) { linePaint.setStyle(Paint.Style.STROKE); linePaint.setColor(linecolor); //繪制折線 Path path = new Path(); float x = xInit + interval * 0; float y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(0)) / yValue.get(yValue.size() - 1); path.moveTo(x, y); for (int i = 1; i < xValue.size(); i++) { x = xInit + interval * i; y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(i)) / yValue.get(yValue.size() - 1); path.lineTo(x, y); } canvas.drawPath(path, linePaint); } /** * 繪制XY坐標(biāo) * * @param canvas */ private void drawXY(Canvas canvas) { int length = dpToPx(4);//刻度的長(zhǎng)度 //繪制Y坐標(biāo) canvas.drawLine(xOri - xylinewidth / 2, 0, xOri - xylinewidth / 2, yOri, xyPaint); //繪制y軸箭頭 xyPaint.setStyle(Paint.Style.STROKE); Path path = new Path(); path.moveTo(xOri - xylinewidth / 2 - dpToPx(5), dpToPx(12)); path.lineTo(xOri - xylinewidth / 2, xylinewidth / 2); path.lineTo(xOri - xylinewidth / 2 + dpToPx(5), dpToPx(12)); canvas.drawPath(path, xyPaint); //繪制y軸刻度 int yLength = (int) (yOri * (1 - 0.1f) / (yValue.size() - 1));//y軸上面空出10%,計(jì)算出y軸刻度間距 for (int i = 0; i < yValue.size(); i++) { //繪制Y軸刻度 canvas.drawLine(xOri, yOri - yLength * i + xylinewidth / 2, xOri + length, yOri - yLength * i + xylinewidth / 2, xyPaint); xyTextPaint.setColor(xytextcolor); //繪制Y軸文本 String text = yValue.get(i) + ""; Rect rect = getTextBounds(text, xyTextPaint); canvas.drawText(text, 0, text.length(), xOri - xylinewidth - dpToPx(2) - rect.width(), yOri - yLength * i + rect.height() / 2, xyTextPaint); } //繪制X軸坐標(biāo) canvas.drawLine(xOri, yOri + xylinewidth / 2, width, yOri + xylinewidth / 2, xyPaint); //繪制x軸箭頭 xyPaint.setStyle(Paint.Style.STROKE); path = new Path(); //整個(gè)X軸的長(zhǎng)度 float xLength = xInit + interval * (xValue.size() - 1) + (width - xOri) * 0.1f; if (xLength < width) xLength = width; path.moveTo(xLength - dpToPx(12), yOri + xylinewidth / 2 - dpToPx(5)); path.lineTo(xLength - xylinewidth / 2, yOri + xylinewidth / 2); path.lineTo(xLength - dpToPx(12), yOri + xylinewidth / 2 + dpToPx(5)); canvas.drawPath(path, xyPaint); //繪制x軸刻度 for (int i = 0; i < xValue.size(); i++) { float x = xInit + interval * i; if (x >= xOri) {//只繪制從原點(diǎn)開始的區(qū)域 xyTextPaint.setColor(xytextcolor); canvas.drawLine(x, yOri, x, yOri - length, xyPaint); //繪制X軸文本 String text = xValue.get(i); Rect rect = getTextBounds(text, xyTextPaint); if (i == selectIndex - 1) { xyTextPaint.setColor(linecolor); canvas.drawText(text, 0, text.length(), x - rect.width() / 2, yOri + xylinewidth + dpToPx(2) + rect.height(), xyTextPaint); canvas.drawRoundRect(x - xValueRect.width() / 2 - dpToPx(3), yOri + xylinewidth + dpToPx(1), x + xValueRect.width() / 2 + dpToPx(3), yOri + xylinewidth + dpToPx(2) + xValueRect.height() + dpToPx(2), dpToPx(2), dpToPx(2), xyTextPaint); } else { canvas.drawText(text, 0, text.length(), x - rect.width() / 2, yOri + xylinewidth + dpToPx(2) + rect.height(), xyTextPaint); } } } }
5、點(diǎn)擊的處理以及左右
重寫ontouchEven()方法,來(lái)處理點(diǎn)擊和滑動(dòng)
@Override public boolean onTouchEvent(MotionEvent event) { if (isScrolling) return super.onTouchEvent(event); this.getParent().requestDisallowInterceptTouchEvent(true);//當(dāng)該view獲得點(diǎn)擊事件,就請(qǐng)求父控件不攔截事件 obtainVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = event.getX(); break; case MotionEvent.ACTION_MOVE: if (interval * xValue.size() > width - xOri) {//當(dāng)期的寬度不足以呈現(xiàn)全部數(shù)據(jù) float dis = event.getX() - startX; startX = event.getX(); if (xInit + dis < minXInit) { xInit = minXInit; } else if (xInit + dis > maxXInit) { xInit = maxXInit; } else { xInit = xInit + dis; } invalidate(); } break; case MotionEvent.ACTION_UP: clickAction(event); scrollAfterActionUp(); this.getParent().requestDisallowInterceptTouchEvent(false); recycleVelocityTracker(); break; case MotionEvent.ACTION_CANCEL: this.getParent().requestDisallowInterceptTouchEvent(false); recycleVelocityTracker(); break; } return true; }
點(diǎn)擊的處理是計(jì)算當(dāng)前點(diǎn)擊的X、Y坐標(biāo)范圍進(jìn)行判斷點(diǎn)擊的是那個(gè)點(diǎn)
/** * 點(diǎn)擊X軸坐標(biāo)或者折線節(jié)點(diǎn) * * @param event */ private void clickAction(MotionEvent event) { int dp8 = dpToPx(8); float eventX = event.getX(); float eventY = event.getY(); for (int i = 0; i < xValue.size(); i++) { //節(jié)點(diǎn) float x = xInit + interval * i; float y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(i)) / yValue.get(yValue.size() - 1); if (eventX >= x - dp8 && eventX <= x + dp8 && eventY >= y - dp8 && eventY <= y + dp8 && selectIndex != i + 1) {//每個(gè)節(jié)點(diǎn)周圍8dp都是可點(diǎn)擊區(qū)域 selectIndex = i + 1; invalidate(); return; } //X軸刻度 String text = xValue.get(i); Rect rect = getTextBounds(text, xyTextPaint); x = xInit + interval * i; y = yOri + xylinewidth + dpToPx(2); if (eventX >= x - rect.width() / 2 - dp8 && eventX <= x + rect.width() + dp8 / 2 && eventY >= y - dp8 && eventY <= y + rect.height() + dp8 && selectIndex != i + 1) { selectIndex = i + 1; invalidate(); return; } } }
處理滑動(dòng)的原理,就是通過(guò)改變第一個(gè)點(diǎn)的X坐標(biāo),通過(guò)改變這個(gè)基本點(diǎn),依次改變后面的X軸的點(diǎn)的坐標(biāo)。
最后在布局里面應(yīng)用就可以啦,我就不貼代碼啦!
總結(jié):
項(xiàng)目還是有缺點(diǎn)的:
(1)左右滑動(dòng)時(shí),抬起手指仍然可以快速滑動(dòng);代碼里面給出了一種解決方案,但是太過(guò)于暫用資源,沒(méi)有特殊要求不建議使用,所以給出一個(gè)boolean類型的自定義屬性isScroll,true:?jiǎn)?dòng),反之亦然;還有一種解決方案就是外面再加一層橫向ScrollView,請(qǐng)讀者自行解決,也很簡(jiǎn)單,只需要稍作修改即可。
(2)點(diǎn)擊的時(shí)候忘記添加回調(diào),只有添加了回調(diào)在可以在activity或者fragment里面獲取點(diǎn)擊的內(nèi)容;代碼很簡(jiǎn)單,自行腦補(bǔ)。
關(guān)于Android中怎么自定義可左右滑動(dòng)和點(diǎn)擊的折線圖就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
網(wǎng)站題目:Android中怎么自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
文章地址:http://www.muchs.cn/article46/gehchg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)公司、建站公司、標(biāo)簽優(yōu)化、手機(jī)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)