天天看點

安卓開發:仿微網誌自定義帶進度條和vip辨別功能的圓形頭像IdentityImageView

*本篇文章已授權微信公衆号 guolin_blog(郭霖)獨家釋出

最近産品增加了兩個小功能,一個是頭像加一個進度條,用于更新提示,一個是身份辨別功能,也就是辨別Vip的功能,如圖:

安卓開發:仿微網誌自定義帶進度條和vip辨別功能的圓形頭像IdentityImageView

很多朋友看見這個小功能,肯定覺得特簡單,就是兩張圖檔疊在一起嘛,用個RelaiveLayout或者其他布局一下就搞定了 , 沒錯 , 是很簡單,但是如果需要動态設定這個頭像的大小,而且很多地方用到的話,在每個地方都去羅列的話,難免不開心并且出現大小錯位等問題,找了好久沒找到開源此控件的,隻能自己動手了并分享給大家,有任何問題可加QQ群詢問:661614986,效果圖如下:

安卓開發:仿微網誌自定義帶進度條和vip辨別功能的圓形頭像IdentityImageView

具體控件特性為:

  • 有進度條,進度條顔色、寬度可随意設定
  • 有辨別身份,辨別位置可随意改變、可隐藏
  • 辨別身份的可以是圖檔,也可以隐藏圖檔,設定文字

下面來具體說一下實作思路:

一、圓形圖檔的實作:

首先要解決的就是要把圖檔裁剪成圓形,這種控件很多,谷歌v4包下有個自帶的

CircleImageView,不過沒用過,用的是hdodenhof的CircleImageView ,源碼也非常簡單易上手

其實這種圖檔可以這樣了解,就是一個正方形裡面有個大圓,還有個小圓,小圓和大圓的和為長方形邊長,如圖:

安卓開發:仿微網誌自定義帶進度條和vip辨別功能的圓形頭像IdentityImageView

那麼這個時候就可以自定義一個View繼承ViewGroup,命名為:IdentityImageView;由于小圓和大圓半徑是有關系的,那麼重寫onMeasure方法可為:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int viewWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int viewHeightMode = MeasureSpec.getMode(heightMeasureSpec);
        int viewWidht = MeasureSpec.getSize(widthMeasureSpec);
        int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
        switch (viewWidthMode) {
            case MeasureSpec.EXACTLY:   //說明在布局檔案中使用的是具體值:100dp或者match_parent
                //為了友善,讓半徑等于寬高中小的那個,再設定寬高至半徑大小
                totalwidth = viewWidht < viewHeight ? viewWidht : viewHeight;
                float scale =  + radiusScale;
                int radius2 = totalwidth / ;
                radius = (int) (radius2 / scale);
                break;
            case MeasureSpec.AT_MOST:  //說明在布局檔案中使用的是wrap_content:
                //這時我們也寫死寬高
                radius = ;
                totalwidth = (int) ((radius + radius * radiusScale) * );
                break;
            default:
                radius = ;
                totalwidth = (int) ((radius + radius * radiusScale) * );
                break;
        }
        setMeasuredDimension(totalwidth, totalwidth);
        adjustThreeView();
    }
           

神之憤怒

安卓開發:仿微網誌自定義帶進度條和vip辨別功能的圓形頭像IdentityImageView

二、初始化IdentityImageView:

public class IdentityImageView extends ViewGroup {
    private Context mContext;
    private CircleImageView bigImageView;//大圓
    private CircleImageView smallImageView;//小圓
    private float radiusScale = f;//小圖檔與大圖檔的比例,預設0.4
    int radius;//大圖檔半徑
    private int smallRadius;//小圖檔半徑
    private double angle = ; //辨別角度大小
    private boolean isprogress;//是否可以加載進度條,必須設定為true才能開啟
    private int progressCollor;//進度條顔色
    private int borderColor;//邊框顔色
    private int borderWidth;//邊框、進度條寬度
    private TextView textView;//辨別符為文字,用的地方比較少
    private boolean hintSmallView;//辨別符是否隐藏
    private Paint mBorderPaint;//邊框畫筆
    private Paint mProgressPaint;//進度條畫筆
    private float progresss;
    private Drawable bigImage;//大圖檔
    private Drawable smallimage;//小圖檔
    private int setprogressColor = ;//動态設定進度條顔色值

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

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

    public IdentityImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        setWillNotDraw(false);//是的ondraw方法被執行


        addThreeView();

        initAttrs(attrs);
    }

           

其中addThreeView()方法就是執行個體化出我們需要的兩個圓形圖檔和一個TextView;但是執行個體化出來的大小不是我們想要的,是以在onMeasure方法的結尾處,我們重新調整了一下各個控件的大小。

private void addThreeView() {

        bigImageView = new CircleImageView(mContext);//大圓

        smallImageView = new CircleImageView(mContext);//小圓

        textView = new TextView(mContext);//文本
        textView.setGravity(Gravity.CENTER);
        addView(bigImageView, , new LayoutParams(radius, radius));
        smallRadius = (int) (radius * radiusScale);
        addView(smallImageView, , new LayoutParams(smallRadius, smallRadius));
        addView(textView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        smallImageView.bringToFront();//使小圖檔位于最上層
    }




//調整圖檔的大小
    private void adjustThreeView() {
        bigImageView.setLayoutParams(new LayoutParams(radius, radius));
        smallRadius = (int) (radius * radiusScale);
        smallImageView.setLayoutParams(new LayoutParams(smallRadius, smallRadius));
        textView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
    }

           

這些步驟大家都很熟悉,需要注意的是setWillNotDraw(false)這個方法,因為這裡沒有加載布局,如果沒有此方法,onDraw方法走不到,initAttrs(attrs)就是擷取自定義的屬性,屬性有:

<declare-styleable name="IdentityImageView">
        <attr name="iciv_bigimage" format="reference"></attr><!--大圖檔-->
        <attr name="iciv_smallimage" format="reference"></attr><!--小圖檔-->
        <attr name="iciv_angle" format="float"></attr><!--辨別角度-->
        <attr name="iciv_radiusscale" format="float"></attr><!--大小圖檔比例-->
        <attr name="iciv_isprogress" format="boolean"></attr><!--是否有進度條-->
        <attr name="iciv_progress_collor" format="color|reference"></attr><!--進度條顔色-->
        <attr name="iciv_border_color" format="color|reference"></attr><!--邊框顔色-->
        <attr name="iciv_border_width" format="integer"></attr><!--邊框寬度-->
        <attr name="iciv_hint_smallimageview" format="boolean"></attr><!--是否隐藏小圖檔-->
    </declare-styleable>
           

三、辨別圖檔的位置

屬性中smallRadius的值為:

要放入小圖檔,肯定是在重寫onLayout方法,大圖檔也是如此,重寫onLayout方法,大圖檔容易實作,left和top為smallRadius,right和bottom都為控件寬減去totalwidth-smallRadius,關鍵是小圖檔的坐标如何确定,說肯定說不清楚,還是借一張西川地理位置圖比較容易知道情況,如下:

安卓開發:仿微網誌自定義帶進度條和vip辨別功能的圓形頭像IdentityImageView

隻要得到圖中所标的下x,y坐标,那麼就可以得到小圓左上角坐标的具體值了,仔細看圖就能明白,這是個幾何問題,用到正弦餘弦,也就是三角函數的sin,cos,具體代碼如下:

double cos = Math.cos(angle * Math.PI / );//調用三角函數,這裡的angle為圖中的角a
        double sin = Math.sin(angle * Math.PI / );
        double left = totalwidth/ + (radius * cos - smallRadius);
//圖中x的值
            double top = totalwidth/ + (radius * sin - smallRadius);//圖中y的值
           

right和bottom加上小圓的直徑smallRadius*2就可以了;

是以onLayout方法重寫如下:

@Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        //重點在于smallImageView的位置确定,預設為放在右下角,可自行拓展至其他位置


        double cos = Math.cos(angle * Math.PI / );
        double sin = Math.sin(angle * Math.PI / );
        double left = totalwidth/ + (radius * cos - smallRadius);
        double top = totalwidth/ + (radius * sin - smallRadius);
        int right = (int) (left +  * smallRadius);
        int bottom = (int) (top +  * smallRadius);
        bigImageView.layout(smallRadius, smallRadius, totalwidth-smallRadius, totalwidth-smallRadius);
        textView.layout((int) left, (int) top, right, bottom);
        smallImageView.layout((int) left, (int) top, right, bottom);

    }

    }
           

四、增加邊框、進度條

下面就剩下外圓和進度條了,不用想是在onDraw裡面用畫筆畫出來的,隻不過這裡面有幾個坑:

  • Paint的setStrokeWidth方法,并不是往圓内側增加圓環(圓弧)寬度的,而是往外側增加一半,往内側增加一半。
  • add進來的View(比如兩個圖檔View)顯示在畫出來的圓弧上面,時間緊迫就沒去搞明白怎麼回事。

    這兩個坑讓我調整了一下代碼,把大圓的半徑減去了圓弧寬度的一半,這樣剛好,能看見圓弧,小圖又能遮蓋住圓弧,功能實作了就沒想那麼多,以後有時間再琢磨一下圖層關系。

外圓邊框和進度條的代碼如下:

canvas.drawCircle(totalwidth/, totalwidth/, radius - borderWidth / , mBorderPaint);//畫邊框,之是以半徑減半,是因為第一個坑


        RectF rectf = new RectF(smallRadius+borderWidth / , smallRadius+borderWidth / , getWidth() -smallRadius- borderWidth / , getHeight()-smallRadius - borderWidth / );
    //定義的圓弧的形狀和大小的範圍

        canvas.drawArc(rectf, (float) angle, progresss, false, mProgressPaint);
        //畫進度條,angle為起始角度,和上圖的a值一樣,progress為弧度角度,false為不顯示半徑線條

           

五、對外提供一些動态設定參數的方法

這裡沒涉及到點選滑動事件,是以沒有重寫分發事件一系列的方法,主要對外提供的方法有:

getBigCircleImageView();
getSmallCircleImageView();
//獲得大、小圖CircleImageView;拿到以後
//可以調用setImageDrawable、setImageResource()等方法直接設定圖檔進去,也可以加載網絡圖檔設定進去,

 public void setAngle(int angles);//設定辨別的角度
 public void setRadiusScale(float v);//設定辨別的大小
 public void setIsprogress(boolean b) ;//設定是否可以有進度條
 public void setBorderColor(int color) ;//設定填充的顔色
 public void setProgressColor(int color);//設定進度條顔色
 public void setBorderWidth(int width) ;//設定進度條以及邊框寬度
           

這樣,帶進度條和辨別功能的原型圖檔就完成了,不熟悉自定義View的同學可以練一下,坑隻有自己踩了才知道,源碼已上傳github,點選檢視;有問題歡迎大家指正,共同進步,!

另外我的線上項目為空藝術,點選可以下載下傳,朋友可以看下我的 頁面的頭像線上效果。

安卓問題交流群:661614986