Android實(shí)現(xiàn)圓形頭像效果

效果截圖


image.png

實(shí)現(xiàn)原理

對(duì)于圓形頭像的實(shí)現(xiàn)撑帖,其實(shí)就是對(duì)方形圖像做某些處理,以達(dá)到圓形頭像的效果。一般我們會(huì)通過(guò)Canvas和Paint結(jié)合來(lái)實(shí)現(xiàn)這種效果镶苞。

自定義View來(lái)實(shí)現(xiàn)

因?yàn)閳A形頭像是視覺(jué)方面的需求陕靠,一般我們會(huì)考慮能否從自定義View的角度來(lái)解決問(wèn)題迂尝。自定義的核心有兩點(diǎn):視覺(jué)和交互。視覺(jué)由onMeasure剪芥、onLayout垄开、onDraw這三個(gè)方法來(lái)完成,而交互則是由dispatchTouchEvent税肪、onInterceptTouchEvent溉躲、onTouchEvent等這幾個(gè)方法來(lái)控制榜田,只要處理好這幾個(gè)方法,就能實(shí)現(xiàn)形態(tài)各異的自定義View了锻梳。

方式一 BitmapShader

package com.yds.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatImageView;

/**
 * Created by yds
 * on 2020/3/10.
 */
public class CircleImageView extends AppCompatImageView {

    private int mSize;
    private Paint mPaint;

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

    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CircleImageView(Context context,AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        mSize = Math.min(width,height);  //取寬高的最小值
        setMeasuredDimension(mSize,mSize);    //設(shè)置CircleImageView為等寬高
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //獲取sourceBitmap箭券,即通過(guò)xml或者java設(shè)置進(jìn)來(lái)的圖片
        Drawable drawable = getDrawable();
        if (drawable == null) return;

        Bitmap sourceBitmap = ((BitmapDrawable)getDrawable()).getBitmap();
        if (sourceBitmap != null){
            //對(duì)圖片進(jìn)行縮放,以適應(yīng)控件的大小
            Bitmap bitmap = resizeBitmap(sourceBitmap,getWidth(),getHeight());
            drawCircleBitmapByShader(canvas,bitmap);    //利用BitmapShader實(shí)現(xiàn)
        }
    }

    private Bitmap resizeBitmap(Bitmap sourceBitmap,int dstWidth,int dstHeight){
        int width = sourceBitmap.getWidth();
        int height = sourceBitmap.getHeight();

        float widthScale = ((float)dstWidth) / width;
        float heightScale = ((float)dstHeight) / height;

        //取最大縮放比
        float scale = Math.max(widthScale,heightScale);
        Matrix matrix = new Matrix();
        matrix.postScale(scale,scale);

        return Bitmap.createBitmap(sourceBitmap,0,0,width,height,matrix,true);
    }

    private void drawCircleBitmapByShader(Canvas canvas,Bitmap bitmap){
        BitmapShader shader = new BitmapShader(bitmap,BitmapShader.TileMode.CLAMP,BitmapShader.TileMode.CLAMP);
        mPaint.setShader(shader);
        canvas.drawCircle(mSize / 2,mSize /2 ,mSize / 2,mPaint);
    }
}

方式二 PorterDuffXfermode

package com.yds.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatImageView;

/**
 * Created by yds
 * on 2020/3/10.
 */
public class CircleImageViewPD extends AppCompatImageView {

    private int mWidth;
    private int mHeight;
    private Paint mPaint;
    private Bitmap CircleBitmap;

    public CircleImageViewPD(Context context) {
        super(context);
    }

    public CircleImageViewPD(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CircleImageViewPD(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void ImgCircle(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setColor(Color.GRAY);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.FILL);
        CircleBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(CircleBitmap);
        canvas.drawCircle(mWidth/2,mHeight/2,mWidth/2,paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        ImgCircle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int count = canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 250, Canvas.ALL_SAVE_FLAG);
        super.onDraw(canvas);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(CircleBitmap,0,0, mPaint);
        canvas.restoreToCount(count);
    }
}

方式三 繼承自Drawable

package com.yds.myapplication;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;

/**
 * Created by yds
 * on 2020/3/10.
 */
public class CircleImageViewDrawable extends Drawable {
    private Paint mPaint;
    private BitmapShader mBitmapShader;
    private int mSize;
    private int mRadius;
    public CircleImageViewDrawable(Bitmap bitmap){
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
        mPaint.setShader(mBitmapShader);
        mSize = Math.min(bitmap.getWidth(),bitmap.getHeight());
        mRadius = mSize/2;
    }
    @Override
    public void draw(Canvas canvas) {
        canvas.drawCircle(mRadius,mRadius,mRadius,mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public int getIntrinsicHeight() {
        return mSize;
    }

    @Override
    public int getIntrinsicWidth() {
        return mSize;
    }
}

使用

public class MainActivity extends AppCompatActivity {
    private ImageView mImageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = findViewById(R.id.img);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.img);
        Drawable drawable = new CircleImageViewDrawable(bitmap);
        mImageView.setImageDrawable(drawable);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <ImageView
        android:id="@+id/img"
        android:layout_width="120dp"
        android:layout_height="120dp" />

    <com.yds.myapplication.CircleImageView
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/img"/>
    <com.yds.myapplication.CircleImageViewPD
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/img"/>
</LinearLayout>

地址:https://github.com/ydslib/CircleImageView.git

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疑枯,一起剝皮案震驚了整個(gè)濱河市辩块,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荆永,老刑警劉巖废亭,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異具钥,居然都是意外死亡豆村,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門骂删,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)你画,“玉大人,你說(shuō)我怎么就攤上這事桃漾』捣耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵撬统,是天一觀的道長(zhǎng)适滓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)恋追,這世上最難降的妖魔是什么凭迹? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮苦囱,結(jié)果婚禮上嗅绸,老公的妹妹穿的比我還像新娘。我一直安慰自己撕彤,他們只是感情好鱼鸠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著羹铅,像睡著了一般蚀狰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上职员,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天麻蹋,我揣著相機(jī)與錄音,去河邊找鬼焊切。 笑死扮授,一個(gè)胖子當(dāng)著我的面吹牛芳室,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刹勃,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渤愁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了深夯?” 一聲冷哼從身側(cè)響起抖格,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咕晋,沒(méi)想到半個(gè)月后雹拄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掌呜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年滓玖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片质蕉。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡势篡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出模暗,到底是詐尸還是另有隱情禁悠,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布兑宇,位于F島的核電站碍侦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏隶糕。R本人自食惡果不足惜瓷产,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枚驻。 院中可真熱鬧濒旦,春花似錦、人聲如沸再登。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)霎冯。三九已至铃拇,卻和暖如春钞瀑,著一層夾襖步出監(jiān)牢的瞬間沈撞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工雕什, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缠俺,地道東北人显晶。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像壹士,于是被迫代替她去往敵國(guó)和親磷雇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容