編輯:關於Android編程
1.簡介
其實這個效果幾天之前就寫了,但是一直沒有更新博客,本來想著把芝麻分雷達圖也做好再發博客的,然後今天看到鴻洋的微信公眾號有朋友發了芝麻分的雷達圖,所以就算了,算是一個互補吧。平時文章也寫的比較少,所以可能有點雜亂,有什麼需要改進的地方歡迎給出建議,不勝感激。
效果圖:
2.步驟:
初始化View的屬性
初始化畫筆
繪制代表最高分和最低分的兩根虛線
繪制文字
繪制代表月份的屬性
繪制芝麻分折線
繪制代表芝麻分的圓點
繪制選中分數的懸浮文字以及背景
處理點擊事件
3.編碼:
初始化View屬性
/** * 初始化布局配置 * * @param context * @param attrs */ private void initConfig(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ScoreTrend); maxScore=a.getInt(R.styleable.ScoreTrend_max_score,700); minScore=a.getInt(R.styleable.ScoreTrend_min_score,650); brokenLineColor=a.getColor(R.styleable.ScoreTrend_broken_line_color,brokenLineColor); a.recycle(); }
初始化畫筆:
private void init() { brokenPath = new Path(); brokenPaint = new Paint(); brokenPaint.setAntiAlias(true); brokenPaint.setStyle(Paint.Style.STROKE); brokenPaint.setStrokeWidth(dipToPx(brokenLineWith)); brokenPaint.setStrokeCap(Paint.Cap.ROUND); straightPaint = new Paint(); straightPaint.setAntiAlias(true); straightPaint.setStyle(Paint.Style.STROKE); straightPaint.setStrokeWidth(brokenLineWith); straightPaint.setColor((straightLineColor)); straightPaint.setStrokeCap(Paint.Cap.ROUND); dottedPaint = new Paint(); dottedPaint.setAntiAlias(true); dottedPaint.setStyle(Paint.Style.STROKE); dottedPaint.setStrokeWidth(brokenLineWith); dottedPaint.setColor((straightLineColor)); dottedPaint.setStrokeCap(Paint.Cap.ROUND); textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setTextAlign(Paint.Align.CENTER); textPaint.setStyle(Paint.Style.FILL); textPaint.setColor((textNormalColor)); textPaint.setTextSize(dipToPx(15)); }
繪制代表最高分和最低分虛線
//繪制虛線 private void drawDottedLine(Canvas canvas, float startX, float startY, float stopX, float stopY) { dottedPaint.setPathEffect(new DashPathEffect(new float[]{20, 10}, 4)); dottedPaint.setStrokeWidth(1); // 實例化路徑 Path mPath = new Path(); mPath.reset(); // 定義路徑的起點 mPath.moveTo(startX, startY); mPath.lineTo(stopX, stopY); canvas.drawPath(mPath, dottedPaint); }
繪制文本
//繪制文本 private void drawText(Canvas canvas) { textPaint.setTextSize(dipToPx(12)); textPaint.setColor(textNormalColor); canvas.drawText(String.valueOf(maxScore), viewWith * 0.1f - dipToPx(10), viewHeight * 0.15f + textSize * 0.25f, textPaint); canvas.drawText(String.valueOf(minScore), viewWith * 0.1f - dipToPx(10), viewHeight * 0.4f + textSize * 0.25f, textPaint); textPaint.setColor(0xff7c7c7c); float newWith = viewWith - (viewWith * 0.15f) * 2;//分隔線距離最左邊和最右邊的距離是0.15倍的viewWith float coordinateX;//分隔線X坐標 textPaint.setTextSize(dipToPx(12)); textPaint.setStyle(Paint.Style.FILL); textPaint.setColor(textNormalColor); textSize = (int) textPaint.getTextSize(); for(int i = 0; i < monthText.length; i++) { coordinateX = newWith * ((float) (i) / (monthCount - 1)) + (viewWith * 0.15f); if(i == selectMonth - 1) { textPaint.setStyle(Paint.Style.STROKE); textPaint.setColor(brokenLineColor); RectF r2 = new RectF(); r2.left = coordinateX - textSize - dipToPx(4); r2.top = viewHeight * 0.7f + dipToPx(4) + textSize / 2; r2.right = coordinateX + textSize + dipToPx(4); r2.bottom = viewHeight * 0.7f + dipToPx(4) + textSize + dipToPx(8); canvas.drawRoundRect(r2, 10, 10, textPaint); } //繪制月份 canvas.drawText(monthText[i], coordinateX, viewHeight * 0.7f + dipToPx(4) + textSize + dipToPx(5), textPaint); textPaint.setColor(textNormalColor); } }
繪制代表月份的屬性
//繪制月份的直線(包括刻度) private void drawMonthLine(Canvas canvas) { straightPaint.setStrokeWidth(dipToPx(1)); canvas.drawLine(0, viewHeight * 0.7f, viewWith, viewHeight * 0.7f, straightPaint); float newWith = viewWith - (viewWith * 0.15f) * 2;//分隔線距離最左邊和最右邊的距離是0.15倍的viewWith float coordinateX;//分隔線X坐標 for(int i = 0; i < monthCount; i++) { coordinateX = newWith * ((float) (i) / (monthCount - 1)) + (viewWith * 0.15f); canvas.drawLine(coordinateX, viewHeight * 0.7f, coordinateX, viewHeight * 0.7f + dipToPx(4), straightPaint); } }
繪制芝麻分折線
//繪制折線 private void drawBrokenLine(Canvas canvas) { brokenPath.reset(); brokenPaint.setColor(brokenLineColor); brokenPaint.setStyle(Paint.Style.STROKE); if(score.length == 0) { return; } Log.v("ScoreTrend", "drawBrokenLine: " + scorePoints.get(0)); brokenPath.moveTo(scorePoints.get(0).x, scorePoints.get(0).y); for(int i = 0; i < scorePoints.size(); i++) { brokenPath.lineTo(scorePoints.get(i).x, scorePoints.get(i).y); } canvas.drawPath(brokenPath, brokenPaint); }
繪制代表芝麻分的圓點
//繪制折線穿過的點 private void drawPoint(Canvas canvas) { if(scorePoints == null) { return; } brokenPaint.setStrokeWidth(dipToPx(1)); for(int i = 0; i < scorePoints.size(); i++) { brokenPaint.setColor(brokenLineColor); brokenPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(3), brokenPaint); brokenPaint.setColor(Color.WHITE); brokenPaint.setStyle(Paint.Style.FILL); if(i == selectMonth - 1) { brokenPaint.setColor(0xffd0f3f2); canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(8f), brokenPaint); brokenPaint.setColor(0xff81dddb); canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(5f), brokenPaint); //繪制浮動文本背景框 drawFloatTextBackground(canvas, scorePoints.get(i).x, scorePoints.get(i).y - dipToPx(8f)); textPaint.setColor(0xffffffff); //繪制浮動文字 canvas.drawText(String.valueOf(score[i]), scorePoints.get(i).x, scorePoints.get(i).y - dipToPx(5f) - textSize, textPaint); } brokenPaint.setColor(0xffffffff); canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(1.5f), brokenPaint); brokenPaint.setStyle(Paint.Style.STROKE); brokenPaint.setColor(brokenLineColor); canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(2.5f), brokenPaint); } }
繪制選中分數的懸浮文字以及背景
//繪制顯示浮動文字的背景 private void drawFloatTextBackground(Canvas canvas, int x, int y) { brokenPath.reset(); brokenPaint.setColor(brokenLineColor); brokenPaint.setStyle(Paint.Style.FILL); //P1 Point point = new Point(x, y); brokenPath.moveTo(point.x, point.y); //P2 point.x = point.x + dipToPx(5); point.y = point.y - dipToPx(5); brokenPath.lineTo(point.x, point.y); //P3 point.x = point.x + dipToPx(12); brokenPath.lineTo(point.x, point.y); //P4 point.y = point.y - dipToPx(17); brokenPath.lineTo(point.x, point.y); //P5 point.x = point.x - dipToPx(34); brokenPath.lineTo(point.x, point.y); //P6 point.y = point.y + dipToPx(17); brokenPath.lineTo(point.x, point.y); //P7 point.x = point.x + dipToPx(12); brokenPath.lineTo(point.x, point.y); //最後一個點連接到第一個點 brokenPath.lineTo(x, y); canvas.drawPath(brokenPath, brokenPaint); }
處理點擊事件
@Override public boolean onTouchEvent(MotionEvent event) { this.getParent().requestDisallowInterceptTouchEvent(true);//一旦底層View收到touch的action後調用這個方法那麼父層View就不會再調用onInterceptTouchEvent了,也無法截獲以後的action switch(event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: onActionUpEvent(event); this.getParent().requestDisallowInterceptTouchEvent(false); break; case MotionEvent.ACTION_CANCEL: this.getParent().requestDisallowInterceptTouchEvent(false); break; } return true; } private void onActionUpEvent(MotionEvent event) { boolean isValidTouch = validateTouch(event.getX(), event.getY()); if(isValidTouch) { invalidate(); } } //是否是有效的觸摸范圍 private boolean validateTouch(float x, float y) { //曲線觸摸區域 for(int i = 0; i < scorePoints.size(); i++) { // dipToPx(8)乘以2為了適當增大觸摸面積 if(x > (scorePoints.get(i).x - dipToPx(8) * 2) && x < (scorePoints.get(i).x + dipToPx(8) * 2)) { if(y > (scorePoints.get(i).y - dipToPx(8) * 2) && y < (scorePoints.get(i).y + dipToPx(8) * 2)) { selectMonth = i + 1; return true; } } } //月份觸摸區域 //計算每個月份X坐標的中心點 float monthTouchY = viewHeight * 0.7f - dipToPx(3);//減去dipToPx(3)增大觸摸面積 float newWith = viewWith - (viewWith * 0.15f) * 2;//分隔線距離最左邊和最右邊的距離是0.15倍的viewWith float validTouchX[] = new float[monthText.length]; for(int i = 0; i < monthText.length; i++) { validTouchX[i] = newWith * ((float) (i) / (monthCount - 1)) + (viewWith * 0.15f); } if(y > monthTouchY) { for(int i = 0; i < validTouchX.length; i++) { Log.v("ScoreTrend", "validateTouch: validTouchX:" + validTouchX[i]); if(x < validTouchX[i] + dipToPx(8) && x > validTouchX[i] - dipToPx(8)) { Log.v("ScoreTrend", "validateTouch: " + (i + 1)); selectMonth = i + 1; return true; } } } return false; }
獲取控件的寬高
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); viewWith = w; viewHeight = h; initData(); }
4.總結
還有一些比較不夠完善的地方需要處理,比如說可以通過XML調節的屬性太少了。平時寫的東西還是太少了,希望以後多總結完善寫作功底吧。需要的屬性後面需要再完善吧
GitHub地址:https://github.com/FelixLee0527/ZhiMaScoreCurve
以上所述是小編給大家介紹的Android 自定義View實現芝麻分曲線圖效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!
我們已經了解了android觸摸事件傳遞機制,接著我們再來研究一下與觸摸事件傳遞相關的幾個比較重要的類,比如MotionEvent。我們今天就來詳細說明一下這個類的各方面
先來看看要實現的效果圖:對於安卓用戶來說,手機應用市場說滿天飛可是一點都不誇張,比如小米,魅族,百度,360,機鋒,應用寶等等,當我們想上線一款新版本APP時,先不說渠道
准備需要下載ntfs-3g驅動包,並做相應修改,這個網上已經可以下載到修改好的包,本文最後也會附加。為什麼要移植在Android原生代碼中,只支持了FAT格式的掛載,並未
一、 進程概念介紹四大組件都是運行在主線程Service是在一段不定的時間運行在後台,不和用戶交互應用組件。每個Service必須在manifest中 通過來聲明。可以通