最近公司產(chǎn)品需求,有一個類似QQ等級顯示的UI效果,用太陽、月亮和星星這三種圖標(biāo)表示用戶的等級,有點類似RatingBar效果,但又有很多不一樣的地方,Github上搜了一大圈,沒有找到滿意的;仔細(xì)想想,實現(xiàn)起來也比較簡單,最后還是決定自己寫一個算了,也練習(xí)下自定義View。
要達(dá)到的效果
截了一張TIM的效果圖,其實效果差不多:
QQ等級
最終實現(xiàn)的效果
實現(xiàn)效果
我只添加了三級顯示,并沒有添加皇冠。因為我的QQ等級離皇冠還差的很遠(yuǎn)...
≡(▔﹏▔)≡,不過實現(xiàn)原理上并沒有任何的區(qū)別。
實現(xiàn)原理簡介
本Demo主要用到的知識就是自定義View中基本的View測量(onMeasure)和繪制(onDraw),加上簡單的QQ等級步進(jìn)算法。大致思路就是根據(jù)用戶設(shè)置的等級(level字段),通過等級步進(jìn)算法,計算出該等級可以用xx個太陽,xx個月亮和xx個星星表示出來;用一個list存儲所有的等級圖標(biāo),然后把這些小圖標(biāo)連續(xù)的繪制出來。
實現(xiàn)代碼
代碼僅200多行,我已經(jīng)添加了詳細(xì)的注釋:
package com.mewlxy.qqlevelbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 類描述:
* 創(chuàng)建人:luoxingyuan
* 創(chuàng)建時間:2017/8/20 21:45
* 修改人:luoxingyuan
* 修改時間:2017/8/20 21:45
* 修改備注:
*/
public class QQLevelBar extends View
{
private Context context;
private int viewWidth;
private int viewHeight;
private int margin;
private Bitmap sunBitmap;
private Bitmap moonBitmap;
private Bitmap starBitmap;
private Paint bitmapPaint;
private int level;
private int step = 5;//默認(rèn)步進(jìn),就是五個星星==一個月亮,QQ是4
private int drawableResId1;
private int drawableResId2;
private int drawableResId3;
private List<String> list; //這里面的每個元素就代表一個需要繪制的小圖標(biāo)
public int getLevel()
{
return level;
}
public void setLevel(int level)
{
this.level = level;
}
public int getStep()
{
return step;
}
public void setStep(int step)
{
this.step = step;
}
public int getDrawableResId1()
{
return drawableResId1;
}
public void setDrawableResId1(int drawableResId1)
{
this.drawableResId1 = drawableResId1;
}
public int getDrawableResId2()
{
return drawableResId2;
}
public void setDrawableResId2(int drawableResId2)
{
this.drawableResId2 = drawableResId2;
}
public int getDrawableResId3()
{
return drawableResId3;
}
public void setDrawableResId3(int drawableResId3)
{
this.drawableResId3 = drawableResId3;
}
//重寫構(gòu)造方法,讓所有構(gòu)造方法都指向三個參數(shù)的方法。
public QQLevelBar(Context context)
{
this(context, null, 0);
}
public QQLevelBar(Context context, @Nullable AttributeSet attrs)
{
this(context, attrs, 0);
}
public QQLevelBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
this.context = context;
//獲取自定義屬性樣式列表
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.qq_level_view);
level = typedArray.getInt(R.styleable.qq_level_view_level, 0);
step = typedArray.getInt(R.styleable.qq_level_view_step, 5);
drawableResId1 = typedArray.getResourceId(R.styleable.qq_level_view_drawable_1, R.drawable.icon_sun_checked);
drawableResId2 = typedArray.getResourceId(R.styleable.qq_level_view_drawable_2, R.drawable.icon_moon_checked);
drawableResId3 = typedArray.getResourceId(R.styleable.qq_level_view_drawable_3, R.drawable.icon_star_checked);
//獲取完了之后別忘了回收
typedArray.recycle();
init();
}
//初始化一些資源
private void init()
{
sunBitmap = BitmapFactory.decodeResource(getResources(), drawableResId1);
moonBitmap = BitmapFactory.decodeResource(getResources(), drawableResId2);
starBitmap = BitmapFactory.decodeResource(getResources(), drawableResId3);
bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
list = calculateLevel(level);
}
@Override//View測量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
//獲取寬度和高度的測量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
switch (widthMode)
{
//如果使用者沒有明確指定View的尺寸,那么我們就給它設(shè)置一個默認(rèn)值
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED://這里我的默認(rèn)寬度是根據(jù)圖片大小和圖片數(shù)量計算出來的,具體的含義后面會說
viewWidth = (sunBitmap.getWidth() + sunBitmap.getWidth() / 3) * list.size() + sunBitmap.getWidth() / 2;
break;
case MeasureSpec.EXACTLY://如果用戶明確指定了尺寸,就按照用戶指定的來
viewWidth = MeasureSpec.getSize(widthMeasureSpec);
break;
}
switch (heightMode)
{
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED://View高度默認(rèn)設(shè)置為圖片高度的兩倍,如果涉及到換行,這里還需要做動態(tài)計算,我就先偷個懶
viewHeight = sunBitmap.getHeight() * 2;
break;
case MeasureSpec.EXACTLY:
viewHeight = MeasureSpec.getSize(widthMeasureSpec);
break;
}
setMeasuredDimension(viewWidth, viewHeight);//把測量出來的寬高尺寸設(shè)置到view中去,這個很重要
margin = sunBitmap.getHeight() / 2;//上下左右的margin值
}
@Override
protected void onDraw(Canvas canvas)//在這里繪制想要的效果
{
int bitmapMargin = sunBitmap.getWidth() / 3;//圖片之間的橫向間隔
for (int i = 0; i < list.size(); i++)
{
//從list中循環(huán)取出元素,根據(jù)元素的值來判斷該繪制哪種圖標(biāo)
if (list.get(i).equals("日"))
{
//繪制太陽圖標(biāo),(橫坐標(biāo)為圖片寬度+相鄰兩張圖片的間隔)*i+整體左邊的margin值,縱坐標(biāo)為view的高度/2-整體的margin值,下面的繪制同理
canvas.drawBitmap(sunBitmap, (sunBitmap.getWidth() + bitmapMargin) * i + sunBitmap.getWidth() / 2,
viewHeight / 2 - margin, bitmapPaint);
} else if (list.get(i).equals("月"))
{
canvas.drawBitmap(moonBitmap, (moonBitmap.getWidth() + bitmapMargin) * i + moonBitmap.getWidth() / 2,
viewHeight / 2 - margin, bitmapPaint);
} else
{
canvas.drawBitmap(starBitmap, (starBitmap.getWidth() + bitmapMargin) * i + starBitmap.getWidth() / 2,
viewHeight / 2 - margin, bitmapPaint);
}
}
}
/**
* 輸入要表示的等級,計算所需要的各種圖標(biāo)的個數(shù),并返回list
* @param level
* @return
*/
private List<String> calculateLevel(int level)
{
List<String> list = new ArrayList<>();
int sunNum = level / (step * step);//太陽圖標(biāo)的個數(shù)為:等級/步進(jìn)的平方(一個太陽為25級)
int moonNum = (level - (step * step) * sunNum) / step;//月亮的個數(shù):總等級先減去太陽表示的等級/步進(jìn)值
int starNum = level - sunNum * (step * step) - moonNum * step;//同理,計算出星星的個數(shù)
//根據(jù)不同圖標(biāo)的個數(shù)添加不同的元素
for (int i = 0; i < sunNum; i++)
{
list.add("日");
}
for (int i = 0; i < moonNum; i++)
{
list.add("月");
}
for (int i = 0; i < starNum; i++)
{
list.add("星");
}
return list;
}
}
自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="qq_level_view">
<attr name="level" format="integer"/><!--設(shè)置等級-->
<attr name="step" format="integer"/><!--設(shè)置步進(jìn)-->
<attr name="drawable_1" format="reference"/><!--一級圖片-->
<attr name="drawable_2" format="reference"/><!--二級圖片-->
<attr name="drawable_3" format="reference"/><!--三級圖片-->
</declare-styleable>
</resources>
可根據(jù)自己的實際需求添加更多的自定義屬性,這里就簡單的添加了幾個。
源碼
https://github.com/lxygithub/QQLevelBar
可下載源碼自行改造出自己想要的效果,如果能給個star那就更好了。
(~ ̄▽ ̄)~
使用
這里是最簡單的用法,當(dāng)然你可以設(shè)置自己的想要的步進(jìn)值和圖標(biāo)。
<com.mewlxy.qqlevelbar.QQLevelBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:level="111"/>