前言
打算自己维护一个日常开发会需要用到的UI锦集,方便快(tou)速(lan)完成产品的需求,每一个UI网上很可能都有轮子,这里只是记录笔者自己的实现思路,细节暂时不予深度优化(^__^),例如自定义控件手机旋转的onSaveInstanceState
,内存的回收等等,争取每个UI都写一篇博客作为记录,应该不会烂尾吧
SimpleUI_GitHub地址
开篇_自定义ImageView
关于圆角ImageView的开源框架已经很多了,有基于BitmapShader实现的,也有基于ProterDuff实现的。
BitmapShader:通过ScaleType设置不同的Matrix,进行的图片的位移缩放策略,然后将Matrix设置进入BitmapShader,然后在Paint设置BitmapShader,然后在Canvas上用这只加了特技的画笔绘制一个圆,DUANG,圆角ImageView就出来了。
ProterDuff:如果说BitmapShader是在绘制时加特技,那么ProterDuff就是在原生ImageView绘制完毕后在上面再次绘制,如果还是要绘制圆角的话,在上面再次绘制一个矩形,比如PorterDuff.Mode.SRC_IN,但是由于是属于过度绘制,一般这种做法实现圆角比较少。
在开发时偶尔会遇到一些需求,局部圆角的ImageView,可能有同学说,哎我用CardView不就好了,然而不幸的是在5.0以下CardView不支持超出部分自动裁剪。CardView的用法
笔者整理了下除去PorterDuff还大约有3种ImageView裁剪方案
1.Android L的ViewOutlineProvider
if (Build.VERSION.SDK_INT >= LOLLIPOP) {
imageview.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (Build.VERSION.SDK_INT >= LOLLIPOP) {
outline.setOval(0, 0, circleL.getMeasuredWidth(), circleL.getMeasuredHeight());
}
}
});
imageview.setClipToOutline(true);
}
因为是5.0+的限定方法,跳进去一看,发现底层是借助RenderNode完成硬件渲染╮(╯▽╰)╭至此第一个方案是没办法用了
2.在ImageView调用onDraw的时候把Canvas裁掉一部分
甚至还能裁掉任意的Path,canvas.clipPath(Path path)
细心的同学会发现边缘出现了明显的颗粒化,然而这是底层的锯齿问题,笔者在测试的时候加上了canvas.setDrawFilter(new PaintFlagsDrawFilter(0,Paint.FILTER_BITMAP_FLAG|Paint.ANTI_ALIAS_FLAG));
也没有解决问题
3.BitmapShader绘制Path
上面也提到了,我们吧要绘制的Drawable通过BitmapShader载入画笔中,在绘制一个我们想要的图形就可以了,至于ImageView显示图片的ScaleType我们可以直接从源码中把CENTER_CROP的Matrix操作copy下来,有兴趣的同学可以了解下ImageView的源码,无非就是一些缩放,位移操作
private void configureBounds() {
if (mDrawable == null || !mHaveFrame) {
return;
}
int dwidth = mDrawableWidth;
int dheight = mDrawableHeight;
int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
int vheight = getHeight() - mPaddingTop - mPaddingBottom;
boolean fits = (dwidth < 0 || vwidth == dwidth) &&
(dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} else {
// We need to do the scaling ourself, so have the drawable
// use its native size.
mDrawable.setBounds(0, 0, dwidth, dheight);
if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
} else if (fits) {
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
} else if (ScaleType.CENTER == mScaleType) {
// Center bitmap in view, no scaling.
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
Math.round((vheight - dheight) * 0.5f));
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
dx = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 0.5f);
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
} else {
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}
简单来用的话我们可以抽象一个View出来,让子类实现Path,父类只要负责绘制就行了,笔者以前见过局部圆角矩形ImageView的一个实现方法:通过Layout布局遮罩和设置偏移。。。当我们采用方案3的实现思路甚至能够支持svg转换的Path,笔者这里就不尝试了。
自定义ImageView的代码
我感谢那给我力量的我们主基督耶稣,因他以我有忠心,派我服侍他。我从前是亵渎 神的,逼迫人的,侮慢人的;然而我还蒙了怜悯,因我是不信不明白的时候而做的。 (提摩太前书 1:12-13 和合本)