因为技术原理比较简单,所以就不详细赘述实现的细节了。
效果需求
一个具有圆形背景的等宽等高的视图上,上下来回滚动一个渐变的矩形,矩形的两边不能超出圆,也不能比圆小。
遇到的问题
如果只看上面这句话,大家都会觉得很简单,在视图上绘制一个圆,再绘制一个渐变的矩形,控制坐标来移动矩形就好了。但当大家实际操作的时候可能就会遇到这样一个问题:矩形绘制的时候总感觉很不协调,达不到预期效果。为什么呢?因为android默认的视图轮廓都是矩形的,即使背景是个圆,但是轮廓还是矩形,拟绘制的矩形宽度不变的话,就像一根固定的棍子在圆内滚动,还会超出圆的边界;如果动态设置矩形的宽度,矩形的两端还是不能平滑的和圆重叠。
解决的一些思路
1.如果是Android5.0及以上的系统,这个问题很好解决,有一个方法:View.setClipToOutline(boolean clip)或者在xml里android:clipToOutline =boolean,设置成true后,给view设置一个任何形状的背景,画矩形时把矩形的宽度设得比view个宽度大些或者相等,那么绘制的矩形两端就会很平滑的和圆相切,像是被圆的边盖住了一样;
2.如果是Android5.0以下的系统,也不要灰心,我们可以从裁剪Canvas入手。说到底,View的绘制还是在Canvas上面进行的,只要我们能把Canvas裁剪成一个以view的中心为圆心,以view的宽度的一半或高度的一半为半径的圆,那么不管滚动的矩形多宽,也只能绘制在这个圆形的画板上,多月部分被平滑的截掉了,达到了预期效果。
作为一个自定义view新手,可能这些方法对大牛们而言就是小儿科,但是我却花了一天多时间才折腾出来,大家随意看看就好。
效果图
代码
/**
* com.ykb.json.customview
* 描述 :带扫描线的ImageView
* 作者 : ykb
* 时间 : 15/11/4.
*/
public class ScanningImageView extends ImageView {
private static final int CHANGE_BOUNDS = 50;
private Paint mPaint;
private int mHeight = 0;
private Path mPath;
public ScanningImageView(Context context) {
this(context, null);
}
public ScanningImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScanningImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setClipToOutline(true);//设置绘制的覆盖物不能超出背景的轮廓
}
mPath = new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setAlpha(255);
}
@Override
protected void onDraw(Canvas canvas) {
mHeight += 10;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
mPath.reset();
canvas.clipPath(mPath);
mPath.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, Path.Direction.CCW);
canvas.clipPath(mPath, Region.Op.REPLACE);
}
LinearGradient linearGradient = new LinearGradient(0, mHeight - CHANGE_BOUNDS, 0, mHeight, new int[]{Color.TRANSPARENT, Color.WHITE}, null, Shader.TileMode.CLAMP);
mPaint.setShader(linearGradient);
canvas.drawRect(0, mHeight - CHANGE_BOUNDS, getWidth(), mHeight, mPaint);
if (mHeight >= getHeight()) {
mHeight = 0;
}
postInvalidateDelayed(40);
super.onDraw(canvas);
}
}
======================我是华丽的分割线==========================
因为当时没有适配机型测试,后来发现在三星等手机5.0以下的系统版本上会出现无法裁剪的bug,现在来修正一下以前的做法o(╯□╰)o
1.首先,在5.0以下的手机上,必须把这个视图关闭硬件加速——解决不能正常裁剪的问题;
2.把构造方法里的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setClipToOutline(true);//设置绘制的覆盖物不能超出背景的轮廓
}
去掉;
3.把onDraw方法里的
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
mPath.reset();
canvas.clipPath(mPath);
mPath.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, Path.Direction.CCW);
canvas.clipPath(mPath, Region.Op.REPLACE);
}
if判断去掉
@Override
protected void onDraw(Canvas canvas) {
mHeight += 10;
mPath.reset();
canvas.clipPath(mPath);
mPath.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, Path.Direction.CCW);
canvas.clipPath(mPath, Region.Op.REPLACE);
LinearGradient linearGradient = new LinearGradient(0, mHeight - CHANGE_BOUNDS, 0, mHeight, new int[]{Color.TRANSPARENT, Color.WHITE}, null, Shader.TileMode.CLAMP);
mPaint.setShader(linearGradient);
canvas.drawRect(0, mHeight - CHANGE_BOUNDS, getWidth(), mHeight, mPaint);
if (mHeight >= getHeight()) {
mHeight = 0;
}
postInvalidateDelayed(