QQ運動步數&自定義ProgressBar

来源:https://www.cnblogs.com/chenyangqi/archive/2018/08/16/9489115.html
-Advertisement-
Play Games

效果如下 gif圖展示效果不好,實際體驗無卡頓 1.自定義屬性 早Values目錄下New values resource file,命名為attrs.xml(命名隨意,但規範命名為attrs.xml) 自定義屬性如下,註意format不要與Android自帶的命名重覆。 2.編寫自定義View 3 ...


效果如下

gif圖展示效果不好,實際體驗無卡頓

1.自定義屬性

早Values目錄下New-values resource file,命名為attrs.xml(命名隨意,但規範命名為attrs.xml)
自定義屬性如下,註意format不要與Android自帶的命名重覆。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="QQStepView">
        <attr name="outerColor" format="color" />
        <attr name="innerColor" format="color" />
        <attr name="borderWidth" format="dimension" />
        <attr name="stepTextSize" format="dimension" />
        <attr name="stepTextColor" format="color" />
    </declare-styleable>

    <declare-styleable name="MyProgressBar">
        <attr name="leftColor" format="color" />
        <attr name="rightColor" format="color" />
        <attr name="progressTextColor" format="color" />
        <attr name="progressTextSize" format="dimension" />
        <attr name="progressBounds" format="dimension" />
    </declare-styleable>
</resources>
2.編寫自定義View
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.cyq.customview2.R;
import com.cyq.customview2.utils.MeasureUtils;

@SuppressWarnings("all")
public class QQStepView extends View {
    private int mOuterColor = Color.parseColor("#2196F3");
    private int mInnerColor = Color.parseColor("#F44336");
    private int mStepTextColor = Color.parseColor("#EC407A");
    private int mBorderWidth = 20;//px
    private int mStepTextSize = 18;//px

    private int mSeptMax = 10000;
    private int mSeptCurrent = 0;

    private Paint mOutPaint;
    private Paint mInnerPaint;
    private Paint mTextPaint;

    public QQStepView(Context context) {
        this(context, null);
    }

    public QQStepView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public QQStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepView);
        mOuterColor = array.getColor(R.styleable.QQStepView_outerColor, mOuterColor);
        mInnerColor = array.getColor(R.styleable.QQStepView_innerColor, mInnerColor);
        mStepTextColor = array.getColor(R.styleable.QQStepView_stepTextColor, mStepTextColor);
        mBorderWidth = (int) array.getDimension(R.styleable.QQStepView_borderWidth, MeasureUtils.dp2px(mBorderWidth, this));
        mStepTextSize = array.getDimensionPixelSize(R.styleable.QQStepView_stepTextSize, MeasureUtils.sp2px(mStepTextSize, this));
        array.recycle();

        mOutPaint = new Paint();
        mOutPaint.setAntiAlias(true);
        mOutPaint.setStrokeWidth(mBorderWidth);
        mOutPaint.setColor(mOuterColor);
        mOutPaint.setStyle(Paint.Style.STROKE);
        mOutPaint.setStrokeCap(Paint.Cap.ROUND);//圓角

        mInnerPaint = new Paint();
        mInnerPaint.setAntiAlias(true);
        mInnerPaint.setStrokeWidth(mBorderWidth);
        mInnerPaint.setColor(mInnerColor);
        mInnerPaint.setStyle(Paint.Style.STROKE);//實心
        mInnerPaint.setStrokeCap(Paint.Cap.ROUND);//圓角

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setColor(mStepTextColor);
        mTextPaint.setTextSize(mStepTextSize);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            //用戶設置的是wrap_content,此時設置一個預設寬高100
            width = height = MeasureUtils.dp2px(200, this);
        }
        setMeasuredDimension(width > height ? height : width, width > height ? height : width);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int center = getWidth() / 2;
        int radius = getWidth() / 2 - mBorderWidth;
        RectF rectF = new RectF(mBorderWidth, mBorderWidth, center + radius, center + radius);
        canvas.drawArc(rectF, 135, 270, false, mOutPaint);

        if (mSeptMax == 0) return;
        float sweepAngle = (float) mSeptCurrent / mSeptMax;
        canvas.drawArc(rectF, 135, 270 * sweepAngle, false, mInnerPaint);

        String stepText = mSeptCurrent + "";
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);
        int dx = getWidth() / 2 - textBounds.width() / 2;
        int baseLine = MeasureUtils.measureBaseLine(mTextPaint, stepText, this);
        canvas.drawText(stepText, dx, baseLine, mTextPaint);
    }

    public void setmSeptMax(int mSeptMax) {
        this.mSeptMax = mSeptMax;
    }

    public synchronized void setmSeptCurrent(int mSeptCurrent) {
        this.mSeptCurrent = mSeptCurrent;
        //重繪
        invalidate();
    }
}
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.cyq.customview2.R;
import com.cyq.customview2.utils.MeasureUtils;

@SuppressWarnings("all")
public class MyProgressBar extends View {
    private int mLiftColor = Color.parseColor("#F44336");
    private int mRightColor = Color.parseColor("#E0E0E0");
    private int mProgressTextColor = Color.parseColor("#616161");
    private int mProgressTextSize = 12;//px 後續再考慮需不需要轉換成sp
    private int mProgressBounds = 1;//px

    private int mCurrentProgress, mMaxProgress = 100;//預設最大刻度為100

    private Paint mLeftPaint, mRightPaint, mTextPaint;

    public MyProgressBar(Context context) {
        this(context, null);
    }

    public MyProgressBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyProgressBar);
        mLiftColor = array.getColor(R.styleable.MyProgressBar_leftColor, mLiftColor);
        mRightColor = array.getColor(R.styleable.MyProgressBar_rightColor, mRightColor);
        mProgressTextColor = array.getColor(R.styleable.MyProgressBar_progressTextColor, mProgressTextColor);
        mProgressTextSize = array.getDimensionPixelSize(R.styleable.MyProgressBar_progressTextSize, mProgressTextSize);
        array.recycle();


        mLeftPaint = new Paint();
        mLeftPaint.setAntiAlias(true);
        mLeftPaint.setColor(mLiftColor);

        mRightPaint = new Paint();
        mRightPaint.setAntiAlias(true);
        mRightPaint.setColor(mRightColor);

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setColor(mProgressTextColor);
        mTextPaint.setTextSize(mProgressTextSize);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widht = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(widht, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRightPaint.setStrokeWidth(getHeight());
        RectF rightRect = new RectF(0, 0, getWidth(), getHeight());
        canvas.drawRoundRect(rightRect, getHeight() / 2, getHeight() / 2, mRightPaint);

        mLeftPaint.setStrokeWidth(getHeight());
        float progress = (float) mCurrentProgress / (mMaxProgress * 10);
        int radius = getHeight() / 2;
        RectF rectF = new RectF(0, 0, progress * getWidth(), getHeight());
        canvas.drawRoundRect(rectF, radius, radius, mLeftPaint);

        //畫文字隨著進度條右移
        String text = (float) mCurrentProgress / 10 + "%";
        int dx = getHeight() / 2;
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
        int baseLine = MeasureUtils.measureBaseLine(mTextPaint, text, this);
        canvas.drawText(text, progress * getWidth() + 10, baseLine, mTextPaint);
    }

    public void setProgress(int mCurrentProgress) {
        this.mCurrentProgress = mCurrentProgress;
        //重繪
        invalidate();
    }

    public void setMaxProgress(int mMaxProgress) {
        this.mMaxProgress = mMaxProgress;
    }

    public int getProgress() {
        return mCurrentProgress;
    }
}
3.為自定義View添加動畫

首先在xml中使用我們的自定義佈局和自定義屬性

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="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=".page3.QQSportActivity">

    <com.cyq.customview2.page3.QQStepView
        android:id="@+id/custom_QQ_step"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp"
        app:borderWidth="6dp"
        app:innerColor="@color/innerColor"
        app:outerColor="@color/outerColor"
        app:stepTextSize="30sp"
        android:layout_marginBottom="100dp"/>

    <com.cyq.customview2.page3.MyProgressBar
        android:id="@+id/custom_progressbar"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:layout_margin="50dp"
        app:leftColor="@color/innerColor"
        app:progressTextColor="@color/stepTextColor"
        app:progressTextSize="16sp"
        app:rightColor="@color/greyColor" />
</LinearLayout>

通過屬性動畫動態增加進度

  
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.animation.DecelerateInterpolator;

import com.cyq.customview2.R;

import butterknife.BindView;
import butterknife.ButterKnife;

public class QQSportActivity extends AppCompatActivity {
    @BindView(R.id.custom_QQ_step)
    QQStepView customQQStep;
    @BindView(R.id.custom_progressbar)
    MyProgressBar customProgressbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qqsport);
        ButterKnife.bind(this);

        customQQStep.setmSeptMax(10000);

        //屬性動畫
        ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 8765);
        valueAnimator.setDuration(2000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());//插值器
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentStep = (Float) animation.getAnimatedValue();
                customQQStep.setmSeptCurrent((int) currentStep);
            }
        });
        valueAnimator.start();

        //屬性動畫
        ValueAnimator valueAnimator2 = ObjectAnimator.ofFloat(0, 780);
        valueAnimator2.setDuration(2000);
        valueAnimator2.setInterpolator(new DecelerateInterpolator());//插值器
        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentStep = (Float) animation.getAnimatedValue();
                customProgressbar.setProgress((int) currentStep);
            }
        });
        valueAnimator2.start();
    }
}

獲取文字基線和sp,dp轉xp的工具類如下;

import android.graphics.Paint;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View;

public class MeasureUtils {
    /**
     * drawText獲取基線
     *
     * @param textPaint
     * @param text
     * @param view
     * @return
     */
    public static int measureBaseLine(Paint textPaint, String text, View view) {
        Rect textBounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), textBounds);
        Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        int baseLine = view.getHeight() / 2 + dy;
        return baseLine;
    }

    public static int sp2px(int sp, View view) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, view.getResources().getDisplayMetrics());
    }

    public static int dp2px(int dp, View view) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, view.getResources().getDisplayMetrics());
    }
}

後續待改進

1.sp,dp,xp的轉換
2.進度文字接近100%時不向右邊移動,並且文字和進度重疊部分動態變色


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • select request_session_id spid, OBJECT_NAME(resource_associated_entity_id) tableName from sys.dm_tran_locks where resource_type='OBJECT' 然後kill 裡面的進程 ...
  • 一、創建資料庫1.啟動服務(三種) 開始--cmd--輸入net start mssqlserver 開始--運行--net start mssqlserver 開始--服務--找到SQLSERVER(MSSQLSERVER)--右鍵--啟動 2.打開ssms 3.登錄:windows身份驗證 混合 ...
  • 在鞋廠的第一個任務,拆表。需要把訂單表按照開始日期和結束日期拆分成多條記錄,挺新鮮的~ transform方式,使用到了python。 (1)把hive表的數據傳入,通過python按照日期迴圈處理,返回多條記錄。 (2)生成序列表,然後採用cross join的方式,在hive端生成多條記錄,再根 ...
  • 本章主要介紹了 MapReduce 的開發原理及應用場景,講解如何利用 Combine、Partitioner、WritableComparable、WritableComparator 等組件對數據進行排序篩選聚合分組的功能。利用例子模仿 SQL 關係資料庫進行SELECT、WHERE、GROUP... ...
  • [toc] mysql utilities 是python編寫 MySQL官方版的命令行工具集,涵蓋主從校驗,主從建立,主從切換等等 安裝 rpm安裝 註意 儘量不要通過yum來安裝mysql utilities,mysql utilities 1.6.5和mysql connector pytho ...
  • 用的Oracle版本是12c,在解鎖scott時提示用戶不存在,於是搜解決方法,給出了兩種方法: 一種是載入scott.sql文件(我的scott.sql文件是存在的,在Oracle安裝目錄下搜索一下),然後在命令行裡面輸入:“@”+“你剛剛查找到的scott.sql文件的目錄”例如:SQL>@E: ...
  • 在安裝APK的時候有時候會出現這種錯誤,原因是卸載之前的APK不徹底,有殘餘,手動刪除android目錄下相關的文件。 ...
  • 大家都知道,想給RecycleView設置分割線可以重寫RecyclerView.ItemDecoration 項目過程中,遇到一個需求:RecycleView頂部有一條灰色的間隔,我想到了給RecycleView設置分割線的方法,當然只給第一個Item設置,而且在上方。 在onDrawOver方法 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...