1. 程式人生 > >android自定義View:純canvas繪製的體重刻度尺

android自定義View:純canvas繪製的體重刻度尺

一個體重刻度尺
廢話不多說,先上效果圖

  • 在此特別感謝扔物線的HenCoder系列,目前已更新了8章內容,讓我從一個純canvas小白變成了canvas小菜。
  • 刻度尺效果是仿寫HenCoder公眾號中《仿寫酷介面》中的薄荷健康的滑動捲尺效果,效果圖如下:
  • 這裡寫圖片描述
  • 網上已經有很多大神都寫過類似的刻度尺,我是抱著學習canvas的態度來寫的,做得不好還請指教

需求分析

  1. 繪製刻度
  2. 可滑動,滑動停止後自動移動到最近刻度
  3. 增加了兩邊透明度變化

涉及知識點

  1. canvas繪製
  2. 屬性動畫
  3. 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;
  1. 例項化畫筆,在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);
    
  2. 繪製刻度線

    //畫刻度線
    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;
    }
    
  3. 繪製遮罩層,用來做兩邊透明度的效果

    //繪製遮罩層,用來進行兩端透明度的變化
    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));
    }
    
  4. 例項化手勢識別器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定義Viewcanvas繪製體重刻度尺

廢話不多說,先上效果圖 在此特別感謝扔物線的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定義ViewCanvas

https://www.jianshu.com/p/fb18c28d6627 用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得咱自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們來了解下Canvas。 Canvas Canvas我們可

Android 定義ViewCanvas詳解

自定義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設定圖片時,如果圖片不夠大,而在大解析度的手機上時會出現無法填充滿,所以自己寫一個,既修復該問題,又可方便拓展,所以拋磚引玉 功能介紹:橫向進度條,分為背景與封面兩層,封面與背景均支援圓角矩形、漸變、圖片。 效果圖: (上面仿微博