1. 程式人生 > >Android 正 N 邊形圓角頭像的實現

Android 正 N 邊形圓角頭像的實現

賣一下廣告,歡迎大家關注我的微信公眾號,掃一掃下方二維碼或搜尋微訊號 stormjun94(徐公碼字),即可關注。 目前專注於 Android 開發,主要分享 Android開發相關知識和一些相關的優秀文章,包括個人總結,職場經驗等。

前言

在上一篇部落格 Android 圓形頭像的兩種實現方式 中,我們塔倫了實現圓形頭像的兩種實現方式。

  • 第一種: 使用 Paint 的 Xfermode 實戰
  • 第二種: 使用 BitmapShader 實現

今天,讓我們一起來看一下怎樣實現正 N 變形圓角頭像的實現。

在講解之前,讓我們先來看一下怎樣使用我們的控制元件

老規矩,在講解怎樣實現以前,我們先一起來看一下怎樣使用我們的自定義控制元件。

自定義屬性說明

<attr name="type">
    <enum name="circle" value="0" />
    <enum name="round" value="1" />
    <enum name="polygon" value="2"/>
</attr>

<declare-styleable name="MultiImageView">
    <attr name="type"/>
    <attr name="miv_border_width" format="dimension" />
    <attr name="miv_border_color" format="color" />
    <attr name="miv_border_overlay" format="boolean" />
    <attr name="miv_fill_color" format="color" />
    <attr name="miv_corner_radius" format="dimension"/>
    <attr name="miv_sides" format="integer"/>
    <attr name="miv_rotate_angle" format="float"/>
</declare-styleable>
引數 說明
type 相應的值有 circle,round,polygon
miv_border_width 表示邊界 Path 的寬度 (預設值是 0 )
miv_border_color 表示邊界 Path 的 Color
miv_border_overlay 表示邊界 Path 是否要覆蓋在圖片上面
miv_fill_color 表示填充圓的顏色,預設是 Translate,即不可見
miv_corner_radius 只有當 type round 或者 polygon 的時候才生效,表示邊界 Path 圓角半徑的大小,
miv_sides 正 N 邊形的變數,只有 type 為 polygon 的時候,該屬性才生效
miv_rotate_angle 旋轉的角度,只有 type 為 polygon 的時候,該屬性才生效

指定圓形頭像

<com.xj.shapeview.MultiImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:src="@mipmap/tanyan"
    app:type="circle"
/>

指定圓角矩形

<com.xj.shapeview.MultiImageView
    android:layout_marginLeft="15dp"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:src="@mipmap/tanyan"
    app:type="round"
    app:miv_corner_radius="15dp"/>
    

指定正 N 邊形

正五邊形

<com.xj.shapeview.MultiImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:src="@mipmap/tanyan"
    app:type="polygon"
    app:miv_sides="5"
    app:miv_corner_radius="25dp"/>

如果需要其旋轉相應的角度,我們只需指定 app:miv_rotate_angle="180" 即可,這裡以 180 度為列子講解說明

如果需要正六邊形,只需要更改為 app:miv_sides="6"

效果圖

相應的佈局檔案實現

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

<ScrollView 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">


    <GridLayout
        android:columnCount="3"
        android:rowCount="3"

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >


        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:type="circle"
            app:miv_sides="6"
            app:miv_corner_radius="15dp"/>

        <com.xj.shapeview.MultiImageView
            android:layout_marginLeft="15dp"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:miv_sides="5"
            app:type="round"
            app:miv_corner_radius="15dp"/>

        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:type="polygon"
            app:miv_sides="5"
            app:miv_corner_radius="25dp"/>

        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:type="polygon"
            app:miv_sides="5"

            app:miv_corner_radius="25dp"
            app:miv_rotate_angle="180"/>

        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:miv_sides="7"
            app:type="polygon"
            app:miv_corner_radius="0dp"
            app:miv_rotate_angle="0"/>


        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:miv_sides="6"
            app:type="polygon"
            app:miv_corner_radius="0dp"
            app:miv_border_overlay="true"
             app:miv_fill_color="@color/colorAccent"/>

        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:miv_sides="6"
            app:type="polygon"
            app:miv_corner_radius="0dp"
            app:miv_rotate_angle="0"
            app:miv_border_overlay="true"
            app:miv_border_width="1dp"
            app:miv_border_color="@android:color/darker_gray"
          />


        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:miv_sides="6"
            app:type="polygon"
            app:miv_corner_radius="0dp"
            app:miv_rotate_angle="0"
            app:miv_border_overlay="false"
            app:miv_border_width="1dp"
            app:miv_border_color="@android:color/black"
        />



        <com.xj.shapeview.MultiImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@mipmap/tanyan"
            app:miv_sides="7"
            app:type="polygon"
            app:miv_corner_radius="10dp"
            app:miv_rotate_angle="0"

        />


    </GridLayout>
</ScrollView>

正 N 邊形圓角頭像的實現原理分析

要實現正 N 變形主要有幾個難點

  • 怎樣讓我們的頭像變成正 N 邊形
  • 怎樣繪製正 N 邊形
  • 怎樣繪製帶圓角的正 N 邊形

怎樣讓我們的頭像變成正 N 邊形?

其實這個問題在上篇部落格已經講到,有兩種實現方式。

  • 第一種: 使用 Paint 的 Xfermode 實戰
  • 第二種: 使用 BitmapShader 實現

今天,這邊部落格主要以 BitmapShader 為例子實現。

核心程式碼實現

mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);

@Override
protected void onDraw(Canvas canvas) {

       -----

    Path path = getPath(canvas,mType,(int)mDrawableRadius*2,(int)mDrawableRadius*2,mDrawableRadius,mSides,mCornerRadius);

    canvas.drawPath(path,mBitmapPaint);
}

核心思路分析:

  • 拿到 Bitmap,並使用 BitmapShader 進行包裝
  • 將 mBitmapShader 設定給畫筆 Paint
  • 第三步,在 onDraw 方法,將其繪製出來

怎樣繪製正 N 邊形

這裡的思想主要來自該部落格 如何用Canvas畫一個正多邊形

數學原理分析

首先,我們先來看一張圖片

從圖中可以看一看到,我們若想繪製出一個正 N 邊形,那麼我們只需要計算出各個點的座標,然後使用 Path 連線起來即可。

那我們要怎樣計算出各個點的座標呢

  • 從圖中不難得出,圓心角 a 的度數為 360/n,弧度計算為 2π/n
  • 如果把圓心的座標為(0,0),那麼頂點P1的座標為[X1=cos(a),Y1=sin(a)]。
  • 以此類推,頂點Pn座標為[Xn=cos(an),Yn=sin(an)]。
    圓心的實際座標是外接矩形的中心:[Ox=(rect.right+rect.left)/2 , Oy=(rect.top+rect.bottom)/2]。
    所以Pn的實際座標是[Xn+Ox,Yn+Oy]。

最後我們把把 P0-P1…Pn 連起來,就是我們要的結果了。

核心虛擬碼實現

float a = 2π / n ; // 角度
Path path = new Path();
for( int i = 0; i < = n; i++ ){
    float x = R * cos(a * i); 
    float y = R * sin(a * i);
    if (i = 0){
        path.moveTo(x,y); // 移動到第一個頂點   
    }else{
        path.lineTo(x,y); //    
    }
}
drawPath(path);

實際程式碼實現

在上面的例子中,我們假設我們的圓形座標是 (0,0), 但實際上並不是,實際上在 Android 中我們的圓心座標是 (width/2,height/2)。因此,我們在計算座標的時候需要加上

圓心座標
float mX = (rect.right + rect.left) / 2;
float my = (rect.top + rect.bottom) / 2;

// PN點的 x,y 座標
float nextX = mX + Double.valueOf(r * Math.cos(alpha)).floatValue();
float nextY = my + Double.valueOf(r * Math.sin(alpha)).floatValue();

當然我們這裡以可以用 canvas 的 translate 方法來移動。

public static void drawPolygon (RectF rect, Canvas canvas, Paint paintByLevel, int number) {
    if(number < 3) {
        return;
    }
    float r = (rect.right - rect.left) / 2;
    float mX = (rect.right + rect.left) / 2;
    float my = (rect.top + rect.bottom) / 2;
    Path path = new Path();
    for (int i = 0; i <= number; i++) {
        // - 0.5 : Turn 90 ° counterclockwise
        float alpha = Double.valueOf(((2f / number) * i - 0.5) * Math.PI).floatValue();
        float nextX = mX + Double.valueOf(r * Math.cos(alpha)).floatValue();
        float nextY = my + Double.valueOf(r * Math.sin(alpha)).floatValue();
        if (i == 0) {
            path.moveTo(nextX, nextY);
        } else {
            path.lineTo(nextX, nextY);
        }
    }
    canvas.drawPath(path, paintByLevel);
}

怎樣繪製帶有圓角的正 N 邊形

這個問題我一開始的思路是根據圓形的半徑,然後計算出各個點的座標,接著使用 path 中的 addArc() 方法來繪製。但是在計算各個點的座標的時候,遇到很多難度,最後無法得出。

後面查閱了 Android 官方的文件,發現了有這樣一個方法

PathEffect setPathEffect (PathEffect effect)

從字面意思很容易理解,就是設定 PathEffect,可以對 Path 產生相應的影響。

那這個 PathEffect 又是什麼東東呢?

public class PathEffect extends Object

Known Direct Subclasses
ComposePathEffect,CornerPathEffect,DashPathEffect,DiscretePathEffect,PathDashPathEffect,SumPathEffect

從官方文件可以瞭解到是繼承於 Object 的,實現的子類有 ComposePathEffect, CornerPathEffect, DashPathEffect 等。

看到這裡的時候你有沒有突然有一種醍醐灌頂的感覺? 這個 CornerPathEffect 是不是就可以實現呢?沒錯,確實可以實現,而且賊簡單。

核心程式碼只有這幾句,就可以讓我們繪製出的正 N 邊形具有圓角

CornerPathEffect cornerPathEffect = new CornerPathEffect(mCornerRadius);
mBitmapPaint.setPathEffect(cornerPathEffect);

程式碼實現細節注意事項

當空間的寬度和高度不一致的時候,半徑怎樣取值?

這裡我們選擇寬度和高度值較小的一個,然後除以2

mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);

當圖片較大的時候,會不會發生 OOM

當圖片較大的時候,我們會對其進行相應的縮放,採用的是矩陣的方法

private void updateShaderMatrix() {
    float scale;
    float dx = 0;
    float dy = 0;

    mShaderMatrix.set(null);

    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
        scale = mDrawableRect.height() / (float) mBitmapHeight;
        dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
    } else {
        scale = mDrawableRect.width() / (float) mBitmapWidth;
        dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
    }

    mShaderMatrix.setScale(scale, scale);
    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);


    mBitmapShader.setLocalMatrix(mShaderMatrix);
}

自定義控制元件怎樣支援 padding 屬性

在繪製圖片的時候,我們對其進行相應的處理,確保我們的座標是正確的。

float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
float top = getPaddingTop() + (availableHeight - sideLength) / 2f;
case CIRCLY:
    ClipHelper.setCirclePath(path,width,height);
    break;
case RECTAHGE:
    ClipHelper.setRectangle(path,calculateBounds(),cornerRadius);
    break;
case POLYGON:
    ClipHelper.setPolygon(path,calculateBounds(),sides,mRotateAngles);
    break;



private RectF calculateBounds() {
    int availableWidth  = getWidth() - getPaddingLeft() - getPaddingRight();
    int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();

    int sideLength = Math.min(availableWidth, availableHeight);

    float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
    float top = getPaddingTop() + (availableHeight - sideLength) / 2f;

    return new RectF(left, top, left + sideLength, top + sideLength);
}

正 N 邊形的角度旋轉是怎樣實現的。

其實,這裡,我們採用的是矩陣的方式進行旋轉的,呼叫 path.transform 方法

Matrix matrix = new Matrix();
matrix.postRotate(rotateAngle,mX,my);
path.transform(matrix);

題外話

在開發的時候,一剛開始說要實現圓角六邊形的時候,查閱了相關的資料,知道有兩種方法

  • 第一種方法,讓 UI 設計師直接給圖, 使用 Paint 的 Xfermode 實現
  • 第二種方法:直接繪製 Path;

那時候專案比較趕,採用的是第一種方式實現。不過作為一名程式猿,感覺採用第一種方法實現,總感覺有點 low。後面晚上下班的時候,查閱了相關的資料,最終終於實現了上述的效果。

這種正 N 邊形圓角頭像的效果,說難也不難,說容易也不容易。因為裡面綜合了很多知識點,需要一步步去處理。(比如怎樣繪製正 N 邊形,怎樣支援圓角,怎樣處理 Padding 等等)。

最後,給大家推薦 github 上面的一個開源庫。ShapeOfView,裡面實現了很多常見的圖片(心形,五角星。六角形等)


參考部落格:如何用Canvas畫一個正多邊形

Android 圓形頭像的兩種實現方式
Android 正 N 邊形圓角頭像的實現

如果,你覺得效果還不錯,請到我的 github 上面 star,謝謝。

MultiImageView

相關推薦

Android N 圓角頭像實現

賣一下廣告,歡迎大家關注我的微信公眾號,掃一掃下方二維碼或搜尋微訊號 stormjun94(徐公碼字),即可關注。 目前專注於 Android 開發,主要分享 Android開發相關知識和一些相關的優秀文章,包括個人總結,職場經驗等。 前言 在上一篇部落格 Android 圓形頭像的兩種實現方式 中,我們

HNCMU1387: 簡單的幾何學解題報告---判斷n個整點能否構成n

                                       1387: 簡單的

動態規劃 凸 n 三角剖分最小周長

題目輸入凸 n 邊形 p 1 ,p 2 ,··· ,p n , 其中頂點按凸多邊形邊界的逆時針序給出,多邊形中不相鄰頂點間的連線稱為弦。試設計一個動態規劃演算法,用若干條弦將凸邊形 p 1 ,p 2 ,··· ,p n 剖分成一些無公共區域的三角形,使得所有三角形的周長之和最

遞推之凸n的不同劃分方式

描述卡特蘭數又稱卡塔蘭數,英文名Catalan number,是組合數學中一個常出現在各種計數問題中出現的數列。以比利時的數學家歐仁·查理·卡塔蘭 (1814–1894)的名字來命名。最初,給卡塔蘭數建立的數學模型是:一個凸n邊形,通過不相交於n邊形內部的對角線,把n邊形拆分

PCB 板圓角實現方法(基本演算法一)

當PCB外形是直角時,通常工程製作外形(鑼帶)時,會將直角或尖角的地方倒成圓角,主要是為了防止板邊容易劃傷板且容易扎傷人 所以當客戶沒有特殊要求時,PCB外形是直角一般會預設倒角0.5mm圓角(如下圖所示)  一.PCB板邊倒圓角點分析    原PCB外形  

android TextView裏實現圖文混配效果

分享 str images for pan override 設置 styles @override 用TextView實現這種效果,圖片文字混排,文字不同顏色字體。打電話和吊起瀏覽器等等 代碼例如以下: @Override public v

Android安卓手機能不能實現BT文件下邊播?

P2P技術 BT邊下邊播 P2P安卓 BT下載 Android安卓手機能不能實現BT文件邊下邊播?Android安卓手機實現類似pc端的bt邊下載邊播放技術的可行性分析 PC端實現對BT文件的邊下載邊播放功能,已經有很長時間,也在很多領域得到了很好的應用,尤其是在視頻文件的分發傳輸中。但是隨著

android shap圓角邊框實現

在drawable資料夾下建立一個shape檔案,在佈局中要設定的控制元件下引入background此檔案就OK <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android=“http://schemas.an

android自定義View之仿通訊錄側欄滑動,實現A-Z字母檢索

我們的手機通訊錄一般都有這樣的效果,如下圖: OK,這種效果大家都見得多了,基本上所有的android手機通訊錄都有這樣的效果。那我們今天就來看看這個效果該怎麼實現。 一.概述 1.頁面功能分析 整體上來說,左邊是一個ListView,右邊是一個自定義View,但

Android studio 修改 app的 build.gradle實現同一APP不同版本同時存在(同一APP安裝N次)

//執行lint檢查,有任何的錯誤或者警告提示,都會終止構建,我們可以將其關掉。 lintOptions { abortOnError false checkReleaseBuilds false // 防止在釋出的時候出現因MissingTranslat

Android利用glide載入圓形圖片,頭像實現

主要是用到了RoundedBitmapDrawable這個類是Drawable的一個子抽象類 可以實現從檔案路徑,輸入流或bitmap 的物件都可以轉換成圓形,或圓角,就不用使用第三方了,很方便 如

android實現圖片圓角實現三種方法

方法一 自定義ImageView /** * 自定義的圓角矩形ImageView,可以直接當元件在佈局中使用。 * @author caizhiming * */ public class

Android基礎學習筆記之-ListView進階用法(item圓角效果實現

今天簡單用快取優化方式實現了listview的功能,下面讓我們實現一下上篇文章留下來的改進方案:     1).實現item佈局的圓角效果     2).對listview的item進行監聽

android DrawerLayout 側實現

現在實現側邊欄比較簡單了,官方提供的DrawerLayout可以很方便實現。 主要實現方法是:用DrawerLayout 作為介面根控制元件。在DrawerLayout裡面第一個View為當前介面主內容;第二個和第三個View為抽屜選單內容。如果當前介面只需要

android progressbar 使用自定義圖片時的左右兩端圓角效果實現

              前幾天一直在折騰progressbar的圓角進度條動畫,各種爬貼摸索,幾經折騰找到一種比較方便的方法實現,這裡做下筆記,避免下次折騰。原生的progressbar的條形進度條的進度左右是直角的,沒有圓角效果的。首先我們來認識一下progressb

Android雙向側滑動實現

Android雙向滑動實現 記得在很早之前,我寫了一篇關於Android滑動選單的文章,其中有一個朋友在評論中留言,希望我可以幫他將這個滑動選單改成雙向滑動的方式。當時也沒想花太多時間,簡單修改了一下就發給了他,結果沒想到後來卻有一大批的朋友都來問我要這份雙

Android自定義ImageView,方角,圓角頭像(可根據自己的需求來設定頭像圓角或者方角的大小)

直接上程式碼,只要改程式碼的一處地方即可根據自己的需求來設定圖片的圓角的大小 在xml中引用自定義的ImageView即可 import android.annotation.SuppressLint; import android.content.Context; im

android縮放動畫的兩種實現方法

get odi omx rac tor Coding eight rpo odin 在android開發。我們會常常使用到縮放動畫,普通情況下縮放動畫有兩種實現方式。一種是直接通過java代碼去實現,第二種是通過配置文件實現動畫,以下是兩種動畫的基本是用法: Ja

IDA 調試 Android 方法及簡單的脫殼實現

all fun cati chm std function sso .apk 代碼位置 IDA 調試 Android 方法及簡單的脫殼實現 標簽: android原創逆向調試dalvik 2016-05-24 14:24 9286人閱讀 評論(3) 收藏

Android中Xposed框架篇-微信實現本地視頻發布到朋友圈功能

快速定位 adb 本地 ref jad jadx mps 頁面 視頻 微信非常龐大,還好有一些強大的工具,下面就來總結收獲的知識。 一、使用adb shell dumpsys activity top命令快速定位頁面 二、使用Jadx進行方法跟蹤時候如果發現沒有結