最近需要實現(xiàn)一個凹凸效果的擬物化優(yōu)惠券效果,我一看沛鸵,本來想用.9圖片做背景實現(xiàn)的括勺,雖說圖片做背景實現(xiàn)省事兒方便缆八,但是能用代碼實現(xiàn)最好不過了,最終我還是選擇了用代碼來實現(xiàn)疾捍,于是有了下文奈辰。
1.完整代碼
先看完整的代碼,后面我們再對代碼逐一的解釋
public class CouponDisplayView extends RelativeLayout {
private Paint mPaint;
private Paint mPaint2;
// 圓間距
private float gap = 0;
// 半徑
private float radius = 20;
// 圓數(shù)量
private int circleNum;
private float remain;
private int color;
public CouponDisplayView(Context context) {
super(context);
}
public CouponDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setDither(true);
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (remain == 0) {
remain = (int) (w - gap) % (2 * radius + gap);
}
circleNum = (int) ((w - gap) / (2 * radius + gap));
}
public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < circleNum; i++) {
float x = gap + radius + remain / 2 + ((gap + radius * 2) * i);
canvas.drawCircle(x, 0, radius, mPaint);
}
mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint2.setDither(true);
mPaint2.setColor(getResources().getColor(R.color.divider_color_car));
mPaint2.setStyle(Paint.Style.FILL);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.DKGRAY);
Path path = new Path();
path.moveTo(0, getHeight() / 2 + 60);
path.lineTo(getWidth(), getHeight() / 2 + 60);
PathEffect effects = new DashPathEffect(new float[]{15, 15, 15, 15}, 2);
paint.setPathEffect(effects);
canvas.drawPath(path, paint);
canvas.drawCircle(0, getHeight() / 2 + 60, radius, mPaint2);
canvas.drawCircle(getWidth(), getHeight() / 2 + 60, radius, mPaint2);
}
public void setColor(int color) {
this.color = color;
}
}
2.方法解釋
1乱豆、CouponDisplayView繼承自RelativeLayout奖恰,通過打印日志測試已知View的執(zhí)行順序如下:
CouponDisplayView(context,attrs,defStyleAttr)
CouponDisplayView(context,attrs)
onSizeChanged()
onDraw()
onSizeChanged(int w, int h, int oldw, int oldh)
當view的大小發(fā)生變化時觸發(fā)
onDraw(Canvas canvas)
負責將View繪制在屏幕上
public CouponDisplayView(Context context)
Java代碼直接new一個CouponDisplayView實例的時候,會調(diào)用這個只有一個參數(shù)的構(gòu)造函數(shù)
public CouponDisplayView(Context context, AttributeSet attrs)
在默認的XML布局文件中創(chuàng)建的時候調(diào)用這個有兩個參數(shù)的構(gòu)造函數(shù)宛裕。AttributeSet類型的參數(shù)負責把XML布局文件中所自定義的屬性通過AttributeSet帶入到View內(nèi)瑟啃;
public CouponDisplayView(Context context,AttributeSet attrs, int defStyleAttr)
構(gòu)造函數(shù)中第三個參數(shù)是默認的Style,這里的默認的Style是指它在當前Application或者Activity所用的Theme中的默認Style揩尸,且只有在明確調(diào)用的時候才會調(diào)用
3.代碼實現(xiàn)思路
從上面的效果圖來看蛹屿,這個自定義View和普通的Linearlayout,RelativeLayout一樣岩榆,只是上下兩邊多了類似于半圓鋸齒的形狀错负,我們需要在上下兩條線上畫一個個白色的小圓來實現(xiàn)這種效果。
假如我們上下線的半圓以及半圓與半圓之間的間距是固定的勇边,那么不同尺寸的屏幕肯定會畫出不同數(shù)量的半圓犹撒,那么我們只需要根據(jù)控件的寬度來獲取能畫的半圓數(shù)。
我們觀察效果圖會發(fā)現(xiàn)粥诫,圓的數(shù)量總是圓間距數(shù)量-1油航,
也就是說,假設(shè)圓的數(shù)量是circleNum,那么圓間距就是circleNum+1怀浆,所以我們可以根據(jù)這個計算出circleNum: 這里gap就是圓間距谊囚,radius是圓半徑,w是view的寬
circleNum = (int) ((w-gap)/(2*radius+gap));
1 执赡、重寫onSizeChanged()方法镰踏,根據(jù)上面的圓的半徑和圓間距來計算需要畫的圓數(shù)量circleNum
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (remain == 0) {
remain = (int) (w - gap) % (2 * radius + gap);
}
circleNum = (int) ((w - gap) / (2 * radius + gap));
}
2.接下來只需要重寫onDraw()方法,簡單的根據(jù)circleNum的數(shù)量將一個一個的圓繪制在屏幕上
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < circleNum; i++) {
float x = gap + radius + remain / 2 + ((gap + radius * 2) * i);
canvas.drawCircle(x, 0, radius, mPaint);
}
}
3.畫中間的黑色虛線
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.DKGRAY);
Path path = new Path();
path.moveTo(0, getHeight() / 2 + 60);
path.lineTo(getWidth(), getHeight() / 2 + 60);
PathEffect effects = new DashPathEffect(new float[]{15, 15, 15, 15}, 2);
paint.setPathEffect(effects);
canvas.drawPath(path, paint);
4.畫兩邊居中的半圓
mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint2.setDither(true);
mPaint2.setColor(getResources().getColor(R.color.divider_color_car));
mPaint2.setStyle(Paint.Style.FILL);
canvas.drawCircle(0, getHeight() / 2 + 60, radius, mPaint2);
canvas.drawCircle(getWidth(), getHeight() / 2 + 60, radius, mPaint2);
代碼分析完畢
3.設(shè)置自定義樣式屬性
考慮到復(fù)用地方不是很多沙合,所以上面的代碼沒有寫自定義樣式屬性奠伪,而是用了
public void setColor(int color) {this.color = color;}
有需要設(shè)置自定義屬性的我在這里寫一下哈,嘻嘻
1首懈、在res/values/ 下建立一個attr.xml 绊率, 在里面定義我們的需要用到的屬性以及聲明相對應(yīng)屬性的取值類型
<?xml version="1.0" encoding="utf-8"?>
<resources>
//半圓顏色
<attr name="radiusColor" format="color" />
<declare-styleable name="CouponDisplayView">
<attr name="radiusColor" />
</declare-styleable>
</resources>
上面定義的半圓顏色的屬性,format屬性的取值類型總共有10種,包括:string
究履,color
滤否,demension
,integer
最仑,enum
藐俺,reference
炊甲,float
,boolean
欲芹,fraction
卿啡,flag
。
2菱父、然后在XML布局中聲明我們的自定義View
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<--注意:一定要引入xmlns:custom="http://schemas.android.com/apk/res-auto"
custom名字可以自定義-->
<com.xxx.xxx.CouponDisplayView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FBB039"
android:orientation="horizontal"
android:padding="16dp"
custom:radiusColor="@Color/red">
............
</com.xxx.xxx.CouponDisplayView>
</LinearLayout>
3颈娜、在View的構(gòu)造方法中,獲得我們的xml布局文件中定義的顏色
public CouponDisplayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Log.d("mDebug", "CouponDisplayView context,attrs,defStyleAttr");
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CouponDisplayView, defStyleAttr, 0);
for (int i = 0; i < a.getIndexCount(); i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CouponDisplayView_radiusColor:
radius = a.getDimensionPixelSize(R.styleable.CouponDisplayView_radiusColor, 10);
break;
}
}
a.recycle();
}
OK滞伟,設(shè)置自定義樣式屬性到此就寫完了揭鳞。