android自定義View:純canvas繪製的體重刻度尺
廢話不多說,先上效果圖
- 在此特別感謝扔物線的HenCoder系列,目前已更新了8章內容,讓我從一個純canvas小白變成了canvas小菜。
- 刻度尺效果是仿寫HenCoder公眾號中《仿寫酷介面》中的薄荷健康的滑動捲尺效果,效果圖如下:
- 網上已經有很多大神都寫過類似的刻度尺,我是抱著學習canvas的態度來寫的,做得不好還請指教
需求分析
- 繪製刻度
- 可滑動,滑動停止後自動移動到最近刻度
- 增加了兩邊透明度變化
涉及知識點
- canvas繪製
- 屬性動畫
- GestureDetector手勢識別器
大概的實現過程
首先放上所有成員變數
/** * 最大的滑動速度 */ private static final int MAX_FLING_SPEED = 6000; private static final String KG = "kg"; private static final int ONE_KG = 1000; /** * 最大的可滑動過去的體重數 預設:3公斤 */ private static final int MAX_FLING_WEIGHT = 3000; /** * 最大的滑動動畫持續時間 */ private static final int MAX_FLING_WEIGHT_DURATION = 1000; private LinearOutSlowInInterpolator mLinearOutSlowInInterpolator = new LinearOutSlowInInterpolator(); /** * 最小體重:30kg */ private int minWeight = 30000; /** * 最大體重:200.0kg */ private int maxWeight = 200000; /** * 預設體重為60.0kg * 數字單位為g */ private int bodyWeight = 60000; /** * 體重的文字大小 */ private int textSizeWeight = sp2pix(22); /** * kg字的文字大小 */ private int textSizeKg = sp2pix(16); /** * 刻度基線的寬度 */ private int lineWidthBase = dp2pix(1); /** * 刻度線g的寬度 */ private int lineWidthG = dp2pix(2); /** * 刻度線g的高度 */ private int lineHeightG = dp2pix(20); /** * 刻度線kg的寬度 */ private int lineWidthKg = dp2pix(4); /** * 刻度線kg的高度 */ private int lineHeightKg = dp2pix(40); /** * 每公斤中的刻度數量 */ private int scaleTableGNum = 10; /** * 每格的寬度 */ private int scaleTableGWidth = dp2pix(10); private Paint mWeightTextPain; private Paint mScaleLinePain; private Paint mScaleWeightTextPain; /** * 遮罩的畫筆 */ private Paint mForegroundPaint; /** * 手勢識別器 */ private GestureDetector mGesture; /** * 平滑動畫 */ private ObjectAnimator mFlingAnim; private FlingAnimUpdateListener mFlingAnimUpdateListener = new FlingAnimUpdateListener(); private BodyWeightUpdateListener mBodyWeightUpdateListener;
例項化畫筆,在onDraw中繪製最基本的元素
//畫出當前公斤數 int textLine = centerY - dp2pix(50); mWeightTextPain.setTextSize(textSizeWeight); mWeightTextPain.setTextAlign(Paint.Align.CENTER); canvas.drawText(weightStr, centerX, textLine, mWeightTextPain); mWeightTextPain.setTextSize(textSizeKg); canvas.drawText(KG, centerX + mWeightTextPain.measureText(weightStr), textLine - mWeightTextPain.getFontSpacing() / 2, mWeightTextPain); //畫刻度基準線 mScaleLinePain.setStrokeWidth(lineWidthBase); canvas.drawLine(0, centerY, canvasWidth, centerY, mScaleLinePain);
繪製刻度線
//畫刻度線 int everyScaleG = ONE_KG / scaleTableGNum; //這個求餘,是為了得到當前體重數和最小刻度的偏差,用於接下來繪製的時候進行位置修正 float offset = bodyWeight % everyScaleG; // float handOffset = offset / everyScaleG * scaleTableGWidth; float currentLeftHandWeight; float currentRightHandWeight; float leftLineX; float rightLineX; if (offset == 0) { float lineX = centerX; float currentHandWeight = bodyWeight; drawScaleTable(canvas, centerY, lineX, currentHandWeight); currentLeftHandWeight = bodyWeight - everyScaleG; currentRightHandWeight = bodyWeight + everyScaleG; leftLineX = lineX - scaleTableGWidth; rightLineX = lineX + scaleTableGWidth; } else { currentLeftHandWeight = bodyWeight - offset % everyScaleG; currentRightHandWeight = bodyWeight + everyScaleG - offset % everyScaleG; leftLineX = centerX - handOffset; rightLineX = centerX + scaleTableGWidth - handOffset; } //這是為了不同寬度的螢幕適配,所以從中間往兩邊繪製 while (rightLineX < canvasWidth + 2 * scaleTableGWidth) { //從中開始向左畫指標 if (currentLeftHandWeight >= minWeight) { drawScaleTable(canvas, centerY, leftLineX, currentLeftHandWeight); } //從中開始向右畫指標 if (currentRightHandWeight <= maxWeight) { drawScaleTable(canvas, centerY, rightLineX, currentRightHandWeight); } currentLeftHandWeight -= everyScaleG; currentRightHandWeight += everyScaleG; leftLineX -= scaleTableGWidth; rightLineX += scaleTableGWidth; }
繪製遮罩層,用來做兩邊透明度的效果
//繪製遮罩層,用來進行兩端透明度的變化 if (mForegroundPaint == null) { initForegroundPaint(canvasWidth); } canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mForegroundPaint); //初始化遮罩的畫筆 private void initForegroundPaint(int w) { mForegroundPaint = new Paint(); mForegroundPaint.setShader(new LinearGradient(0, 0, w / 2, 0, 0X00FFFFFF, 0XFFFFFFFF, Shader.TileMode.MIRROR)); mForegroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); }
例項化手勢識別器GestureDetector,用它來接管View的onTouchEvent,並在onScroll和onFling中作出對應動作
@Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (mFlingAnim != null) { mFlingAnim.cancel(); } //每次滾動的刻度 bodyWeight += distanceX * scaleTableGWidth; if (bodyWeight >= maxWeight) { bodyWeight = maxWeight; } else if (bodyWeight <= minWeight) { bodyWeight = minWeight; } invalidate(); return true; @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int target; if (velocityX > 0) { target = bodyWeight - getTargetWeightByVelocityX(velocityX); } else { target = bodyWeight + getTargetWeightByVelocityX(velocityX); } startSmoothAnim(target, getDurationByVelocityX(velocityX)); return true; }
以上用到的方法
//通過滑動速率來得到滑動動畫的目標體重值 private int getTargetWeightByVelocityX(float velocityX) { return (int) (MAX_FLING_WEIGHT * mLinearOutSlowInInterpolator.getInterpolation(Math.abs(velocityX / MAX_FLING_SPEED))); } //通過滑動速率來得到滑動動畫持續時間 private int getDurationByVelocityX(float velocityX) { return (int) (MAX_FLING_WEIGHT_DURATION * mLinearOutSlowInInterpolator.getInterpolation(Math.abs(velocityX / MAX_FLING_SPEED))); } //開始一個平滑動畫 private void startSmoothAnim(int targetWeight, int duration) { if (targetWeight >= maxWeight) { targetWeight = maxWeight; } else if (targetWeight <= minWeight) { targetWeight = minWeight; } else { targetWeight = revisedTarget(targetWeight); } if (mFlingAnim != null) { mFlingAnim.cancel(); mFlingAnim = null; } mFlingAnim = ObjectAnimator.ofInt(BodyWeightScaleTableView.this, "bodyWeight", this.bodyWeight, targetWeight); mFlingAnim.setInterpolator(mLinearOutSlowInInterpolator); mFlingAnim.setDuration(duration); mFlingAnim.addUpdateListener(mFlingAnimUpdateListener); mFlingAnim.start();
}
實現思想
本Demo是使用Canvas進行繪製刻度,為了適配不同寬度的螢幕,所以小的刻度使用從中心指標處向兩邊繪製到螢幕邊界的方法,使用GestureDetector接管View的觸控事件,在onScroll中修改體重值並不斷進行重繪,達到刻度尺滾動的效果。並在onFling中接收拋動的事件,開啟一個屬性動畫達到平滑的效果。由於我沒有在GestureDetector中找到能響應不fling的up事件,所以要自己手動在onTouchEvent中寫ACIONT_UP時的動作
@Override
public boolean onTouchEvent(MotionEvent event) {
mGesture.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
if (mFlingAnim != null && !mFlingAnim.isRunning()) {
startSmoothAnim(revisedTarget(bodyWeight), 100);
}
}
return true;
}
遮罩層的實現方式,是drawRec 一個整個畫布大小的白色矩形,使用線性漸變,兩邊不透明到中間透明。注意,由於使用了PorterDuff.Mode.MULTIPLY來合成,所以需要使用到離屏緩衝,關於離屏緩衝可以看看HenCoder
最後附上github:https://github.com/Pro47x/BodyWeightScaleTableView
寫的很簡陋,輕噴
相關推薦
android自定義View:純canvas繪製的體重刻度尺
廢話不多說,先上效果圖 在此特別感謝扔物線的HenCoder系列,目前已更新了8章內容,讓我從一個純canvas小白變成了canvas小菜。 刻度尺效果是仿寫HenCoder公眾號中《仿寫酷介面》中的薄荷健康的滑動捲尺效果,效果圖如下: 網上已經有很多
Android 自定義View小例項-實現繪製打折標籤
前言 許多商城APP都會有商品打折的需求,而為文字新增下劃線直接設定style就可以完成,我們在這裡說的如下圖,也就是我們demo實現的效果圖。 1. 選取自定義View的方法 我們都知道自定義View有多種方式,比如繼承自View、ViewGroup或者繼承自現有的View子
Android自定義View:你需要一個簡單好用、含歷史搜尋記錄的搜尋框嗎?
前言 Android開發中,類似下圖的搜尋功能非常常見 今天,我將帶來一款 封裝了 歷史搜尋記錄功能 & 樣式 的Android 自定義搜尋框 開源庫,希望你們會喜歡。
Android自定義View之使用Path繪製手勢軌跡和水波效果
先看下效果圖: 繪製軌跡 繪製手指的軌跡主要是攔截View的onTouchEvent()方法,並根據手指的軌跡繪製path。path中有兩種可以實現的方法 1、Path.lineTo(x,y)方法 public class MoveP
android自定義View學習3--文字繪製
介紹一大堆繪製文字相關的函式… 主要分canvas繪製文字、Paint輔助繪製文字、Paint測量文字相關值。 1. canvas繪製文字 1) drawText(String text, float x, float y, Paint paint
Android自定義View:帶背景顏色的TextView和條形圖--(1)
初始: 最近在看《Android群英傳》一書,程式碼自己敲了一遍,想想之前敲了又忘記的慘痛經歷,決定在部落格上記錄自己敲的程式碼,有幾個寫幾篇,放在一個系列裡邊,就這樣,以後看就能一下子找到了。 自定義View 自定義View我們大致可以從是三個方面著手:
Android自定義View:水平帶數字百分比的進度條
public class NumberProgressView extends View { /** * 進度條畫筆的寬度(dp) */ private int paintProgressWidth = 3; /** * 文字百分比的字型大小(sp)
Android自定義View——canvas 繪製一個會動的時鐘
文章目錄 ####1、功能例項 用canvas 繪製一個 會動的 指標式 時鐘 ####2、程式碼架構 ####3、主要功能程式碼 activity_main.xml 檔案 <?xml version="1.0" encodin
Android自定義View【實戰教程】5⃣️---Canvas詳解及程式碼繪製安卓機器人
友情連結: 神馬是Canvas 基本概念 Canvas:可以理解為是一個為我們提供了各種工具的畫布,我們可以在上面盡情的繪製(旋轉,平移,縮放等等)。可以理解為系統分配給我們一個一個記憶體空間,然後提供了一些對這個記憶體空間操作的方法(AP
Android 自定義View練習:雷達圖(比重)繪製
code: package com.louisgeek.louiscustomviewstudy; import android.content.Context; import android.content.res.Resources; import
【朝花夕拾】Android自定義View篇之(四)Canvas繪製文字教程
前言 前面的文章中在介紹Canvas的時候,提到過後續單獨講Canvas繪製文字,因為這一節內容比較細緻,內容很多。這裡先宣告一下,本文的內容的來源於騰訊課堂中“仍物線學堂”中課件,因為該課件對常用的繪製文字基本技巧做了比較詳細的講解
Android 自定義View 系列文章目錄 Canvas篇
一、基礎 Android 畫筆 Paint 基本操作API:https://blog.csdn.net/huangliniqng/article/details/82588824 Android 畫布 Canvas 基本操作API:https://blog.csdn.net/
Android自定義View之Canvas
https://www.jianshu.com/p/fb18c28d6627 用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得咱自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們來了解下Canvas。 Canvas Canvas我們可
Android 自定義View之Canvas詳解
自定義View的相關文章: Android 實現一個簡單的自定義View Android 自定義View步驟 Android Paint詳解 Android 自定義View之Canvas相關方法說明 Android 自定義View例項之 “京東跑”
Android自定義View系列:標籤LabelView實戰篇
前言部分 本文主要介紹如何自定義一個常見的labels標籤,功能上主要支援,單選、多選、點選三種模式。因為這個使用率很高,並且這個是比較典型學習自定義ViewGroup的例子,所以特意動手實踐,加深對Android的認識。這個專案主要是為了自己學習使用,所以並不是很完善,先上一個效果
Android開發:Android自定義View
本文為博主- 山水相逢-z 原創文章,地址: https://blog.csdn.net/weixin_38251977/article/details/82286127 上週遇到一個需求,用一個圓形進度條的形式來展示某項操作所佔的比例,雖然類似的輪子已經有很多了,但是
Android 自定義View,繪製一個帶比例的環形進度條
最近專案有一個需求,要在首頁顯示三個環形餅狀圖,要求可以顯示比例大小,中間顯示文字部分,並且需要可以自定義顏色。設計圖如下: 思路: 繪製一個帶百分比的圓環,一共分了四個部分: 1.背景圓(就是底圖圓) 2.預設圓環 3.繪製的圓環(就是比例圓環) 4.中心文字 下面我們開始進行繪製,先準
HenCoder Android 自定義 View 1-7:屬性動畫 Property Animation(進階篇)
這期是 HenCoder 自定義繪製的第 1-7 期:屬性動畫(進階篇) 簡介 上期的內容,對於大多數簡單的屬性動畫場景已經夠用了。這期的內容主要針對兩個方面: 針對特殊型別的屬性來做屬性動畫; 針對複雜的屬性關係來做屬性動畫。 TypeEvaluator
Android自定義View的三種方式:繼承佈局,繼承原生控制元件,繼承View
自定義View非常的常用,也是Android開發的一項基本技能,自定義View有三種方式:繼承佈局,繼承原生控制元件,繼承View。一、繼承佈局先看效果圖:程式碼實現:1.在layout資料夾中建立佈局title_view.xml,這一步根據自己需要寫,本例中的佈局如下:佈
Android 自定義View ProgressBarHorizontal:橫向進度條、支援圓角、漸變、圖片
背景:因系統的ProgressBar設定圖片時,如果圖片不夠大,而在大解析度的手機上時會出現無法填充滿,所以自己寫一個,既修復該問題,又可方便拓展,所以拋磚引玉 功能介紹:橫向進度條,分為背景與封面兩層,封面與背景均支援圓角矩形、漸變、圖片。 效果圖: (上面仿微博