目录
效果展示
实现原理
这里的实现原理很简单,就是添加多个矩形路径,并不断的延长各个矩形路径的宽度(通过onDraw方法的递归实现),然后在矩形路径中绘制Bitmap即可。
实现步骤
1. 构建用于展示的Bitmap
这里我们选择在onSizeChanged方法中初始化Bitmap,因为当控件大小改变时方便我们重新计算所需展示Bitmap的大小。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mBitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.timg), w, h, false);//根据控件大小创建一个与控件宽高相同的Bitmap对象
postInvalidate();//创建完Bitmap后对控件进行刷新
super.onSizeChanged(w, h, oldw, oldh);
}
2. 构建矩形裁剪区域并添加到Path中
由上图可以看出我们需要添加的矩形路径分为左半边部分和右半边部分,我们以左半部分第一个矩形和右半部分第一个矩形为例:
左边矩形的构建参数:left = 0,top = 0,right = clipwidth,bottom = SINGLERECT_HEIGHT。
右边矩形的构建参数:left = View_Width - clipwidth,top = SINGLERECT_HEIGHT,right = View_Width,bottom = 2 * SINGLERECT_HEIGHT。
根据如上构建参数的规律我们总结出如下公式(其中i代表由上到下第几个矩形):
左边矩形构建公式:RectF rectleft = new RectF(0,i * SINGLEREGION_HEIGHT,cilpWidth,(i + 1) * SINGLEREGION_HEIGHT)
右边矩形构建公式:RectF rectright = new RectF(View_Width - cilpWidth,i * SINGLEREGION_HEIGHT,getWidth(),(i + 1) * SINGLEREGION_HEIGHT)
根据如上公式我们在代码中添加路径:
//根据控件的高度来添加矩形路径
for(int i=0;i*SINGLEREGION_HEIGHT<getHeight();i++){
if(i%2==0){
mPath.addRect(new RectF(0,i*SINGLEREGION_HEIGHT,cilpWidth,(i+1)*SINGLEREGION_HEIGHT), Path.Direction.CCW);
}else {
mPath.addRect(new RectF(getWidth()-cilpWidth,i*SINGLEREGION_HEIGHT,getWidth(),(i+1)*SINGLEREGION_HEIGHT), Path.Direction.CCW);
}
}
3. 在对应的路径中绘制出Bitmap
这里使用Canvas的clipPath方法将画布裁切成路径的形状,然后在裁切后的画布上绘制图片。
canvas.clipPath(mPath);//根据路径裁切画布
canvas.drawBitmap(mBitmap,0,0,mPaint);//在裁切后的画布上绘制图片
4. 利用递归实现动画效果
if(cilpWidth>getWidth()){
//当矩形的宽度等于控件宽度时停止重绘
return;
}
cilpWidth+=5;//每次绘制完需要增加clipWidth的宽度
invalidate();//重绘(运用递归)
5. 当图片完全显示时替换图片
图片完全显示也是cilpWidth>控件宽度的时候。
if(cilpWidth>getWidth()){
//当图片完全展示时替换图片
mBitmap=Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.baozou), getWidth(), getHeight(), false);
canvas.drawBitmap(mBitmap,0,0,mPaint);
return;
}
完整代码展示
public class View_ClipAnim extends View {
private Paint mPaint;
private Path mPath;
private final float SINGLEREGION_HEIGHT=30;//每个长条的高度
private Bitmap mBitmap;
float cilpWidth=0;//矩形宽度
public View_ClipAnim(Context context) {
this(context,null);
}
public View_ClipAnim(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public View_ClipAnim(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 初始化画笔等
*/
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mBitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.timg), w, h, false);
cilpWidth=0;
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();//每次绘制之前先将Path重置
for(int i=0;i*SINGLEREGION_HEIGHT<getHeight();i++){
if(i%2==0){
mPath.addRect(new RectF(0,i*SINGLEREGION_HEIGHT,cilpWidth,(i+1)*SINGLEREGION_HEIGHT), Path.Direction.CCW);
}else {
mPath.addRect(new RectF(getWidth()-cilpWidth,i*SINGLEREGION_HEIGHT,getWidth(),(i+1)*SINGLEREGION_HEIGHT), Path.Direction.CCW);
}
}
canvas.clipPath(mPath);//根据路径裁切画布
canvas.drawBitmap(mBitmap,0,0,mPaint);//在裁切后的画布上绘制图片
if(cilpWidth>getWidth()){
//当图片完全展示时替换图片
mBitmap=Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.baozou), getWidth(), getHeight(), false);
canvas.drawBitmap(mBitmap,0,0,mPaint);
return;
}
cilpWidth+=5;//每次绘制完需要增加clipWidth的宽度
invalidate();//重绘(运用递归)
}
}
扩展
扫描式图片展示
这里主要是使用弧形路径来展示图片的,通过不断增加弧形角度来扩大显示区域(这里的弧形半径是通过勾股定理得出的)。
public class View_ClipCircleAnim extends View {
private Paint mPaint;
private float mRadius;//圆形的半径
private Path mPath;
private Bitmap mBitmap;
private int mAngle=0;//圆形角度
public View_ClipCircleAnim(Context context) {
this(context,null);
}
public View_ClipCircleAnim(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public View_ClipCircleAnim(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
float a = w/2;
float b = h/2;
mRadius = (float) Math.sqrt(a*a+b*b);//根据勾股定理算出圆形的半径
mBitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.timg), w, h, false);
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth()/2,getHeight()/2);//将(0,0)点移动到画布中心
if(mAngle>=360){
canvas.drawBitmap(Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.baozou), getWidth(), getHeight(), false),-getWidth()/2,-getHeight()/2,mPaint);
return;
}
mPath.reset();//清空路径
mPath.moveTo(0,0);
mPath.arcTo(new RectF(-mRadius,-mRadius,mRadius,mRadius),0,mAngle,false);//添加闭合的弧形
canvas.clipPath(mPath);//裁剪画布为路径的形状
canvas.drawBitmap(mBitmap,-getWidth()/2,-getHeight()/2,mPaint);
mAngle++;
postInvalidate();
}
}