最近做项目的时候,加入优惠券模块,效果图如下:
下面简单的说下实现过程。
- 首先是自定义了一个控件CouponView,该控件继承了LinearLayout,该控件有如下几个自定义属性:
<declare-styleable name="CouponView">
<attr name="radius" format="dimension"/>
<attr name="sawtooth_color" format="color"/>
<attr name="background_color" format="color"/>
<attr name="sawtooth_width_gap" format="dimension"/>
<attr name="sawtooth_weight" format="float"/>
</declare-styleable>
其中radius表示的是右边那列小锯齿的半径大小,sawtooth_color表示左边的锯齿部分的背景颜色,background_color表示整体的背景颜色,sawtooth_width_gap表示锯齿之间的间隙大小,sawtooth_weight表示左边含有锯齿部分的宽度占整个布局的宽度比例。
- 接下来在CouponView中获取到这些属性:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CouponView);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CouponView_radius:
mRRadius = a.getDimension(attr, 10);
break;
case R.styleable.CouponView_sawtooth_color:
mSawtoothColor = a.getColor(attr, 0);
break;
case R.styleable.CouponView_sawtooth_width_gap:
mRCircleGap = a.getDimension(attr, 8);
break;
case R.styleable.CouponView_sawtooth_weight:
mSawtoothWeight = a.getFloat(attr, 0.65f);
break;
case R.styleable.CouponView_background_color:
mBackgroundColor = a.getColor(attr, 0);
}
}
a.recycle();
获取参数这个没什么好说的,获取完参数之后别忘了初始化Paint,还有千万别漏了setWillNotDraw(false),如果不设置该属性为false,onDraw有可能不会被调用到:
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
//别忘了将该属性设置为false
setWillNotDraw(false);
- 接着根据既有数值算出锯齿的个数及剩余空间:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(changed) {
//计算剩余空间
if (mRRemain == 0) {
mRRemain = (int) (b - t - mRCircleGap - getPaddingTop() - getPaddingBottom()) % (2 * mRRadius + mRCircleGap);
}
//计算锯齿半圆个数
mRCircleNum = (int) ((b - t - mRCircleGap - getPaddingTop() - getPaddingBottom()) / (2 * mRRadius + mRCircleGap));
}
}
这里的剩余空间是指根据指定的半径大小画了mRCircleNum个 半圆后还剩余的高度,用于指定画半圆的开始的位置。
- 接下来就是draw的过程了:
float width = getWidth() - getPaddingLeft() - getPaddingRight();
float height = getHeight() - getPaddingTop() - getPaddingBottom();
mPaint.setColor(mSawtoothColor);
//画锯齿背景
canvas.drawRect(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + (width * mSawtoothWeight), getPaddingTop() + height, mPaint);
mPaint.setColor(mBackgroundColor);
//画锯齿部分
for (int i = 0; i < mRCircleNum; i++) {
float y = mRCircleGap + mRRadius + mRRemain / 2 + ((mRCircleGap + mRRadius * 2) * i) + getPaddingTop();
canvas.drawCircle(getPaddingLeft() + width * mSawtoothWeight, y, mRRadius, mPaint);
}
//画右边剩余部分
canvas.drawRect(getPaddingLeft() + width * mSawtoothWeight, getPaddingTop(), width + getPaddingLeft(), height + getPaddingTop(), mPaint);
mPaint);
//画左边的大的半圆
mOval.set(getPaddingLeft() - mLRadius,
getPaddingTop() + (height - 2 * mLRadius) / 2,
getPaddingLeft() + mLRadius,
getPaddingTop() + (height - 2 * mLRadius) / 2 + 2 * mLRadius);
canvas.drawArc(mOval, -90, 180, true, mPaint);
这里需要说明的是画左边的半圆用的是drawArc而不是drawCircle,因为drawCircle会画出整个圆,所以这里通过计算弧度来画半圆。其余的都比较简单,不用多说。
- 最后在布局文件中使用就行了:
<com.example.coupondemo.view.CouponView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:coupon="http://schemas.android.com/apk/res-auto"
coupon:background_color="@android:color/white"
coupon:sawtooth_color="@color/coupon_sawtooth_bg_color"
coupon:sawtooth_weight="0.7"
android:layout_width="match_parent"
android:layout_height="120dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:orientation="horizontal">
代码就这么多,基本实现了该效果。
附上代码:
CouponDemo