1. 程式人生 > >巧用 Drawable 之實現一個最簡單的自定義電池圖示

巧用 Drawable 之實現一個最簡單的自定義電池圖示

在 Android 中自定義一個電池圖示,一般是採用自定義 View,在 onDraw 中採用 Canvas 去繪製 Bitmap 或者各種幾何圖形。但是自定義 View 對初學者來說可能會有一點難度,那麼有沒有更簡單的辦法來實現自定義電池圖示呢?

實現電池圖示 Drawable

我們來分析下繪製一個電池圖示我們需要做些什麼?

電池圖示

如圖所示,電池圖示可看成有三種狀態,空的,滿的,介於空和滿的。那我們就可以這麼做,先繪製一個空的,再根據電量繪製一個半滿的。我們知道 Drawable 有很多子類,LayerDrawable 可以用來繪製多個圖層,正好是我們需要的,另外繪製一半的可以採用 ClipDrawable。

ClipDrawable 在 draw 方法中會通過 Canvas#clipRect(Rect rect)方法去裁剪畫布。ClipDrawable 可以通過 clipOrientation 來指定裁剪的方法為 horizontal 還是 vertical,而且還可以通過 gravity 屬性來指定從哪邊開始裁剪。

瞭解這些之後我們就可以著手寫 drawable 了,在 /res/drawable 下新建一個 battery.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
> <!-- 空的電池 --> <item> <bitmap android:src="@drawable/battery_empty" /> <!-- 裁剪滿的電池圖示 --> <item android:id="@+id/clip_drawable"> <clip android:clipOrientation="horizontal" android:drawable="@drawable/battery_full"
android:gravity="left" /> </item> </layer-list>

上面兩個圖層最終組成我們想要的電池圖示。當然,我們還需要將 battery.xml 應用到我們的 View 中 。這裡我採用的是 ImageView,直接為 ImageView 指定一個android:src="@drawable/battery"即可,也可以作為 View 的 background使用。

接下來還需要在 java 程式碼中指定 ClipDrawable 要裁剪的區域。程式碼如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageBattery = (ImageView) findViewById(R.id.image_battery);
        LayerDrawable layerDrawable = (LayerDrawable) imageBattery.getDrawable();
        clipDrawable = (ClipDrawable) layerDrawable.findDrawableByLayerId(R.id.clip_drawable);

        clipDrawable.setLevel(level);
    }

LayerDrawable 中有個 findDrawableByLayerId(int id) 方法,可以找到指定 id 的 Drawable。有點類似於 View 中的 findViewById(int id)方法。另外 ClipDrawable#setLevel(int level),引數 level 的取值範圍是 0-10000,表示要裁剪 Drawable 多大的範圍。

到這裡我們已經基本實現了自定義電池圖示。不過我們還沒有給定真實的電量值。

獲取手機電量

獲取手機電量只需註冊一個廣播即可,當手機電量改變時,我們就能在廣播中收到訊息。直接看程式碼:

public class MainActivity extends Activity {
    @Override
    protected void onResume() {
        super.onResume();
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        registerReceiver(batteryChangedReceiver, intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(batteryChangedReceiver);
    }
    // 電量變化廣播
    private BroadcastReceiver batteryChangedReceiver = new BroadcastReceiver() {

        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
                int level = intent.getIntExtra("level", 0);
                int scale = intent.getIntExtra("scale", 100);
                // 0 - 100
                int power = level * 100 / scale;
                // setLevel(int level): level 的範圍是 0 -10000
                clipDrawable.setLevel(power * 100);
            }
        }
    }
}

至此自定義電池圖示就完成的差不多了。不過細心的童鞋可能會發現,電量還沒滿,比如 90% 的時候電池圖示已經顯示是滿的了。這是因為電量顯示的區域只有中間某一塊,而不是整個圖示,如果要求比較高,我們還需要寫給方法去精確的計算裁剪的區域。

    // 根據自己的電池圖示精確計算裁剪區域
    private int calculateLevel(int progress) {
        int leftOffest = Utils.dip2px(this, 2);
        int powerLength = Utils.dip2px(this, 26.5f);// 40 px in hdpi
        int totalLength = Utils.dip2px(this, 32.5f);// 49 px in hdpi
        int level = (leftOffest + powerLength * progress / 100) * 10000 / totalLength;
        return level;
    }

結合圖片看程式碼:
考慮電池邊界值

這些值我們可以通過ps的標尺或者其他軟體來獲取。同時還要注意圖片放在哪個資料夾,轉換成 mdpi 下畫素值,作為 dp 單位。比如我把圖片放在 hdpi,圖片的實際寬度(totalLength)為 49px,那麼在 mdpi 大約為 32.5px,32.5 即為我們的 dp值。

dp 轉 px 的方法:

public class Utils {

    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}

修改 onReceive 中的clipDrawable.setLevel(power * 100)clipDrawable.setLevel(calculateLevel(power))

到這裡整個自定義電池圖示就全部完成了,如果需要顯示充電狀態,可以通過 Handler 配合屬性動畫去完成。這不是本節的重點,詳見原始碼。

相關推薦

Drawable 實現一個簡單定義電池圖示

在 Android 中自定義一個電池圖示,一般是採用自定義 View,在 onDraw 中採用 Canvas 去繪製 Bitmap 或者各種幾何圖形。但是自定義 View 對初學者來說可能會有一點難度,那麼有沒有更簡單的辦法來實現自定義電池圖示呢? 實現電

Android——史上簡單定義開關按鈕的實現

很多時候,我們在很多無論是Android還是IOS的APP中都會遇到這樣的一種效果,有一個按鈕,我們點選一下,便會滑動一下,一會顯示“開”,一會顯示“關”,這便是開關按鈕了,比如:很多Android手機的設定功能裡,就有很多功能是用開關按鈕實現的,那麼這些開關按鈕時如何實

javaWeb一個簡單的servlet

tran oid w3c write 分享 瀏覽器 servle code mapping 1. 創建一個類servletTest2 繼承HttpServlet類。 public class servletTest2 extends HttpServlet {

300行ABAP代碼實現一個簡單的區塊鏈原型

指向 repo 方法調用 輸入參數 transacti ui控件 挖礦 太多的 work 不知從什麽時候起,區塊鏈在網上一下子就火了。 這裏Jerry就不班門弄斧了,網上有太多的區塊鏈介紹文章。我的這篇文章沒有任何高大上的術語,就是300行ABAP代碼,實現一個最簡單的區

java實現一個簡單的tomcat服務

連接數 accep print tex soc ins udp web服務 reply 1.如何啟動? main方法是程序的入口,tomcat也不例外,查看tomcat源碼,發現main是在Bootstrap 類中的; 2.如何建立連接? 要通訊,必須要建議so

python只使用Queue和Thread自己實現一個簡單的執行緒池

        我的思路就是就是寫一個TifCutting類繼承自Thread,這個類裡有個屬性Queue;有一個addTask新增任務的方法,這個方法是把需要執行的函式放到Queue裡;因為繼承自Thread類,一定有一個重寫的run方法,這個方法是從自己的Queue屬性裡

Python實現一個簡單的MapReduce程式設計模型WordCount

MapReduce程式設計模型: Map:對映過程 Reduce:合併過程 import operator from functools import reduce # 需要處理的資料 lst = [ "Tom", "Jack",

實現一個簡單的flask應用程式

首先先將flask包匯入,建立flask物件,然後使用flask路由器指定網址和控制器。最後使用程式入口將flask應用啟動,port引數用來調整埠號,flask模組預設埠號為5000 程式碼如下: from flask import Flask app = Flask(_

live555 實現一個簡單的RTSP伺服器

用live555中的庫寫了一個最簡單的RTSPServer程式,僅用於學習目的。從下例的程式碼中,可以清析的明白RTSPServer的函式呼叫流程。[cpp] view plaincopyprint?#include <BasicUsageEnvironment.h

java去解析一個簡單的XML檔案

此處僅僅是去解析最基本的XML檔案,XML檔案如下: <Books> <Book> <Name>Java入門</Name> <Price>30.00</Price>

使用Java實現一個簡單的Web Server

Hello Web Server Web Server沒有你想象的那麼難實現(當然要實現一個好的Java Web Server還是很有難度的)。你只要明白基本的HTTP協議,TCP程式設計和IO知識。當然,你也要會最簡單的HTML程式碼。 我們先來一個屌絲版

HTML5audio標籤做一個簡單的音訊播放器

在做系統的時候,要求做一個音訊播放器,就在網上查找了一些資料,發現這樣的資料還是很千篇一律的,EasyUI框架並沒有給我們一個音訊播放器的功能,在bootstrap上有,但是也是結合html5來寫的,因此,我們在這裡就用純的html5血一個音訊播放器,如何播放本地的音訊。

spring boot搭建一個簡單的使用者登入介面,其艱難歷程!

   目前的Java專案中,最基本的包括:controller層、service層、dao層,當然還用entity實體層。 controller層,顧名思義,就是控制器,控制各個層面的動作。 service層,其中寫了具體的方法,具體怎樣的實現,就在其中寫著。 dao層

C語言實現一個簡單的佇列

1、佇列.h #include<stdio.h> #include<stdlib.h> #define N 100 //定義佇列最大多少個 #define datatype char //定義佇列的資料型別 struct

struct模組實現python socket收發定義TCP包

最近跳槽到西安一家機器人公司,我們的產品屬於教育機器人的範疇,為了增強客戶吸引力,引進了一個智慧家居公司的產品API介面,讓機器人來操作智慧家居 該公司的智慧家居API是自定義TCP包,即直接在TCP頭後面寫自定義資料結構: 客戶端請求下載 傢俱資料庫 的格式 命令字(4

【Android進階】如何寫一個很屌的動畫(1)---先實現一個簡易的定義動畫框架

class MyView extends View { public void onDraw(Canvas canvas) { super.onDraw(canvas); invalidate(); } } 這樣一來,View每次繪製都是觸發下一次繪製,不過

Drawable 實現Android UI 元素間距效果

purple 固定 展示 .com otto 技巧 log contain dev 源文地址: 巧用Drawable 實現Android UI 元素間距效果 在大部分的移動UI或者Web UI都是基於網格概念而設計的。這種網格一般都是有一些對其的方塊組成,然後

Behavior Tree Lua 實現一個簡行為樹

urn ret pri end put true for 行為樹 一個 1 local SELECTOR = 1 2 local SEQUENCE = 2 3 local CONDITION = 3 4 local ACTION = 4 5 6 loca

一個Python通過select實現簡單的web框架

127.0.0.1 log put lec func select odi block pos 1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import socket 5 import selec

2018.3.29學習總結如何運行一個簡單的Servlet程序

ati get png aid 父類 eclips 網上 自己 nco 1,我編寫了我的第一個Servlet程序。HelloServlet 繼承自HttpServlet。因此需要導入javax.servlet開頭的一系列包,那麽這些包來自哪裏呢?答案是Tomcat安裝目錄下