1. 程式人生 > >自定義View—使用clipPath或者BitmapShader實現圓角圖片

自定義View—使用clipPath或者BitmapShader實現圓角圖片

實現圓角圖片的方式有三種,上篇文章中是使用了Xfermode,這篇文章則將總結剩下的兩種clipPath、BitmapShader。這裡我們跟上一篇一樣繼承自ImageView。

公共部分

無論是使用哪種方法,都需要自定義的屬性和在構造器中獲得相應的屬性,因此將這部分放到一起。

自定義屬性

我們這個圓角圖片可以定義圖片的圓角度數,因此需要自定義這個屬性如些:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="RoundImageView"
>
<attr name="radius" format="dimension" /> </declare-styleable> </resources>

在構造器中獲得自定義屬性

private static final String TAG = "RoundImageView";

    private Paint mPaint;

    private Xfermode xfermode;
    /** 圖片縮放的比例 */
    private float scale = 1.0f;
    /** 圓角半徑 */
private float mRadius; /** 預設的圓角半徑 */ private static final int DEFAULT_RADIUS = 10; public RoundImageView(Context context) { this(context, null); } public RoundImageView(Context context, AttributeSet attr) { super(context, attr); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); TypedArray ta = context.getTheme().obtainStyledAttributes(attr, R.styleable.RoundImageView, 0
, 0); mRadius = ta.getDimensionPixelSize(R.styleable.RoundImageView_radius, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_RADIUS, getResources() .getDisplayMetrics())); Log.e(TAG, "mRadius:" + mRadius); ta.recycle(); }

獲得Bitmap

我們繪製時,需要用到Bitmap,因此需要獲得資源src對應的Bitmap。

/**
     * 獲得Bitmap
     */
    private Bitmap getBitmap() {
        Bitmap bm = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Config.ARGB_8888);
        Drawable drawable = getDrawable();
        Canvas drawableCanvas = new Canvas(bm);
        // 計算縮放
        int drawablewidth = drawable.getIntrinsicWidth();
        int drawableheight = drawable.getIntrinsicHeight();
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        scale = Math.min(width * 1.0f / drawablewidth, height * 1.0f / drawableheight);
        Log.e(TAG, scale + "");
        // 縮放
        drawable.setBounds(0, 0, (int) (drawablewidth * scale), (int) (drawableheight * scale));
        drawable.draw(drawableCanvas);
        return bm;
    }

clipPath實現圓角圖片

首先需要說的一點是clipPath在api18之前都不支援硬體加速,因此在構造器中需要關閉硬體加速,如下

setLayerType(LAYER_TYPE_SOFTWARE, mPaint);

Canvas類中提供對畫布的裁剪的方法,有clipPath對具體路徑進行裁剪,有clipRect對具體矩形進行裁剪,有clipRegion對具體區域進行裁剪。我們先看clipPath的兩個方法:

  • clipPath(Path path)
    Intersect the current clip with the specified path.
  • clipPath(Path path, Region.Op op)
    Modify the current clip with the specified path.

我們注意到第二個方法裡有個引數Region.Op op,這是一個列舉值:

public enum Op {
         DIFFERENCE(0),
         INTERSECT(1),
         UNION(2),
         XOR(3),
         REVERSE_DIFFERENCE(4),
         REPLACE(5);
}

我們把之前的區域設為A,裁剪的區域為B,則以上列舉值的意義如下:

  • Region.Op DIFFERENCE
    顯示A-B
  • Region.Op INTERSECT
    顯示 A與B的交集
  • Region.Op REPLACE
    顯示B
  • Region.Op REVERSE_DIFFERENCE
    顯示B-A
  • Region.Op UNION
    顯示A與B的並集
  • Region.Op XOR
    顯示 A與B的並集-A與B的交集

不帶Op引數的clipPath(Path path)則預設為使用INTERSECT。

瞭解這些後,我們就可以使用clipPath、重寫onDraw來實現圓角圖片了。首先需要對canvas進行裁剪,如何裁剪呢?我們需要的是圓角的圖片,因此需要將canvas裁剪成圓角矩形的canvas,使得draw出來的是圓角的矩形。

@Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        Path path = new Path();
        path.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), mRadius,
                mRadius, Path.Direction.CW);
        // 先對canvas進行裁剪
        canvas.clipPath(path, Region.Op.INTERSECT);

        Bitmap bm = getBitmap();
        // 繪製bitmap
        canvas.drawBitmap(bm, 0, 0, mPaint);

        canvas.restore();
    }

分析:程式碼中的canvas.clipPath(path, Region.Op.INTERSECT);
之前的區域是整個canvas,而裁剪後的區域的圓角矩形,我們的目的是圓角矩形,因此傳入的Op引數可以REPLACE,也可以是INTERSECT,或者不傳引數。

使用BitmapShader實現圓角圖片

我們這時可能會發現之前的兩種方法都有其缺點,Xfermode需要我們繪製兩次,而clipPath不支援硬體加速,因此我們將希望寄予BitmapShader。

圓角圖片有很多開源的程式碼,例如

這個提到:

  • does not use a clipPath which is not hardware accelerated and not anti-aliased.
  • does not use setXfermode to clip the bitmap and draw twice to the canvas.

而檢視其原始碼,我們可以發現它是用BitmapShader實現的,因此我們可以說BitmapShader真是不負眾望。
BitmapShader是Shader的一個子類,Shader,我們可以理解為著色器,也就是說在設定完畫筆的屬性之後,再去繪製形狀,會以找個著色器來著色。BitmapShader則允許這個著色器為Bitmap。
Shader是Paint的一個屬性,可以通過paint.setShader()來設定。
我們先看看BitmapShader物件是如何建立的:

bitmapShader = new BitmapShader(bm, TileMode.CLAMP, TileMode.CLAMP);

第一個引數是Bitmap,說明著色器的Bitmap來源,後面的兩個引數為TileMode,取值有三種:

  • CLAMP 拉伸
  • REPEAT 重複
  • MIRROR 映象

也就說,如果圖片的長寬不足,可以選擇以重複、拉伸、映象的方式來填充。
接下來的事情就簡單了,再設定完paint的bitmapshader,只需要繪製一個圓角矩形就好,paint就會以bitmap的方式來對這個矩形進行著色。

    @Override
    protected void onDraw(Canvas canvas) {
        Bitmap bm = getBitmap();
        bitmapShader = new BitmapShader(bm, TileMode.CLAMP, TileMode.CLAMP);
        mPaint.setShader(bitmapShader);

        canvas.drawRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), mRadius,
                mRadius, mPaint);
    }

相關推薦

定義View—使用clipPath或者BitmapShader實現圓角圖片

實現圓角圖片的方式有三種,上篇文章中是使用了Xfermode,這篇文章則將總結剩下的兩種clipPath、BitmapShader。這裡我們跟上一篇一樣繼承自ImageView。 公共部分 無論是使用哪種方法,都需要自定義的屬性和在構造器中獲得相應的

Android 定義View(繼承原生元件)實現拖動移位效果

自定義View實現拖拽移位效果 通過繼承GridLayout實現的拖拽移位效果 首先建立Class類繼承GridLayout並重寫前三個構造方法 public class MyGridlayout extends GridLayout implement

Android 定義View(繼承原生元件)實現拖動移位效果

自定義View實現拖拽移位效果 通過繼承GridLayout實現的拖拽移位效果 首先建立Class類繼承GridLayout並重寫前三個構造方法 public class MyGridlayout extends GridLayout implements

Android定義View——從零開始實現水波浪進度框

前言:相信同行們都知道,我們程式設計師有一種痛,叫做別人的程式碼。讀懂別人的程式碼很重要的一點就是要抓住作者的思路,有了思路才能將過程推匯出來,否則腦闊會疼。為己為人,本系列教程部落格,我都會將自己實現的思路寫下來,帶大家一步步從零開始實現我們想要的效果

Android定義View——從零開始實現圓形進度條

前言:以前老是用別人造的輪子,知其然不知其所以然,有時看懂了別人寫的過多幾個月又忘了,遂來開個坑把一步步實現和思路寫下來,弄成一個系列。由於上班時間不多,爭取一週擼個一到兩篇出來 本篇只著重於思路和實現步驟,裡面用到的一些知識原理不會非常細地拿來講,如

Android進階之定義View(1)實現可換行的TextView

         今天來一起學習一下最簡單的自定義view,自己動手寫一個MyTextView,當然不會像系統的TextView那麼複雜,只是實現一下TextView的簡單功能,包括分行顯示及自定義屬性的處理,主要目的是介紹自定義view的實現的基本思路和需要掌握的一些基礎知

Android定義View——從零開始實現雪花飄落效果

前言:轉眼已是十一月下旬了,天氣慢慢轉冷,不知道北方是不是已經開始下雪了呢?本期教程我們就順應季節主題,一起來實現 雪花飄落的效果吧。本篇效果思路參考自國外大神的Android實現雪花飛舞效果,並在此基礎上實現進一步的封裝和功能擴充套件 本篇只著重於思路和

android 定義View 儀表盤 DashboardView 的實現

有天上班,老闆突然扔給我一張圖, 說:這個東西能不能做一下。 我說應該可以。然後老闆那就沒有下文了,我想既然問了,那我就抽空做一下。 當我做出來的時候去找老闆,我說上次你給我發的那個圖,我已經做出來了,您要不要看一下。 老闆說,不用了,不需要了。

Android_定義View的三種實現方式

自定義控制元件的實現有三種方式,分別是:組合控制元件、繼承控制元件和自繪控制元件 一:組合控制元件 1:新建一個Android專案,建立自定義的佈局檔案add_delete.xml <?xml version="1.0" encoding="utf-8"?>

Android定義View初體驗,實現圓形TextView的三種方式

自定義view對我來說一直是比較恐懼的,但是萬事開頭難,今天總結一下自己實現圓形TextView的三種方式。 首先來說一下自定義view的三種方式: 一,自繪控制元件: 自繪控制元件就是說介面展示的內容就是我們在ondraw()方法中繪製出來的,繼承Vie

【朝花夕拾】Android定義View篇之(四)定義View的三種實現方式及定義屬性詳解

前言        儘管Android系統提供了不少控制元件,但是有很多酷炫效果仍然是系統原生控制元件無法實現的。好在Android允許自定義控制元件,來彌補原生控制元件的不足。但是在很多初學者看來,自定義View似乎很難掌握。其中有很大一部分原因是我們平時看到的自定

android高仿抖音、點餐介面、天氣專案、定義view指示、爬取美女圖片等原始碼

Android精選原始碼 Android優質部落格 簡介最近東西寫的挺多的,這不又要弄一個類似於京東的地址選擇器,然後剛開

定義View起步:Canvas之繪製圖片

          在前面的章節中,我們已經介紹了Canvas的一些基本操作,今天我們繼續講解,Canvas類的用法,可見這個類在自定義View中是多麼的重要。今天我們來著重介紹一下如何繪製圖片和文字。在這一章節學習之後我們基本上對於一些簡單的自定義View就可以搞定了。但

android ImageView實現上面圓角下面直角(定義view實現)

主要思想: 重寫imageview,在imageview上畫一個四個圓角的方框,並把畫布下一一個圓角半徑長度,這樣就能不顯示下面對的四個角 重寫ImageView: import androi

定義View(1)--圓形圖片圓角圖片實現

之前說過會將專案中運用的東西抽離出來做一個總結,今天我主要想總結一下圓角和圓形頭像問題。由於我們的應用涉及到很多使用者頭像,如果所有的影象都是方方正正的話,那顯得不是很美觀,所以設計溼強行要我將頭像圓角化處理。好吧,so easy。專案截圖我就不想貼了,還是一貫貼demo截

Android圓形圖片不求人,定義View實現BitmapShader使用)

在很多APP當中,圓形的圖片是必不可少的元素,美觀大方。本文將帶領讀者去實現一個圓形圖片自定View,力求只用一個Java類來完成這件事情。 一、先上效果圖 二、實現思路 在定義View 的onMeasure()方法裡設定View的寬高相等,應該取寬高中的最小值。

Android -- 定義view實現keep歡迎頁倒計時效果

super onfinish -m use new getc awt ttr alt 1,最近打開keep的app的時候,發現它的歡迎頁面的倒計時效果還不錯,所以打算自己來寫寫,然後就有了這篇文章。 2,還是老規矩,先看一下我們今天實現的效果   相較於我們常見的倒計時

Android定義View——實現水波紋效果類似剩余流量球

string 三個點 pre ber block span 初始化 move 理解 最近突然手癢就想搞個貝塞爾曲線做個水波紋效果玩玩,終於功夫不負有心人最後實現了想要的效果,一起來看下吧: 效果圖鎮樓 一:先一步一步來分解一下實現的過程 需要繪制一個正弦曲線(sin

Android從零擼美團(三) - Android多標籤tab滑動切換 - 定義View快速實現高度定製封裝

這是【從零擼美團】系列文章第三篇 【從零擼美團】是一個高仿美團的開源專案,旨在鞏固 Android 相關知識的同時,幫助到有需要的小夥伴。 GitHub 原始碼地址:github.com/cachecats/L… Android從零擼美團(一) - 統一管理 Gradle 依賴 提取到單獨檔案中 Andr

定義View之指南針(反編譯別人的程式碼實現

一、說明        偶爾點開魅族手機內建的工具箱應用,發現其指南針做的還不錯,就想模擬做一個類似的效果,在這裡我們不準備自己從頭開始編寫程式碼,而是採用一點黑科技,首先,我們從魅族系統中匯出工具箱應用的apk,然後反編譯apk,結合