拿聊天气泡举例,点击气泡时气泡视图出现点击态给人反馈是正常操作,如果没有设计师,自己重写View的setPressed方法直接改下View的alpha值就可以实现“点击变淡”的效果。
然鹅这种操作,对于要求高的设计师一般是不可能让你这样的。
变淡参数不好设置也就算了,变深怎么操作?
一般简单的办法就是让设计师切一个点击态视图,与原图构成selector。
有没有别的通用的方法?
气泡时不规则的,直接蒙个蒙层周围也变灰了,怎么办?
熟悉canvas的同学可能已经想出来了:
自己绘制一张蒙层Bitmap,与View绘制的背景Bitmap做混合,设置PorterDuff模式为SRC_IN,即可生成一个气泡形蒙层,然后再onDraw方法里加上这个蒙层就成功了。
可是如何拿到View的背景呢?
沙雕的我发现,即使onDraw方法什么也不执行,View照样会显示背景,那么背景是在哪里绘制的呢?
看View#draw方法,发现原来背景是在draw方法里面绘制的。
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
那如果拿到背景的bitmap呢?
看到了View的这个方法,可惜已经废弃了。
@Deprecated
public Bitmap getDrawingCache() {
return getDrawingCache(false);
}
看来只能自己动手了。
既然我们只是想用背景和我们的蒙层做mulitiply,那么我们需要的只是背景的轮廓而已。
draw方法绘制了背景以及内容,那么draw出来的图形形状是与背景相同的,我们用它就好了。用下面代码生成蒙层,生成之后进行一次重绘:
private void updateMaskBitmap(){
Log.d(TAG, "updateMaskBitmap");
maskBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas1 = new Canvas(maskBitmap);
draw(canvas1);
canvas1.drawColor(colorMask, PorterDuff.Mode.SRC_IN);
invalidate();
}
在draw中进行蒙层的生成,注意生成蒙层会触发draw,要防止死循环:
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
int w = getWidth();
int h = getHeight();
int lastW = drawSize.x;
int lastH = drawSize.y;
drawSize = new Point(getWidth(), getHeight());
Log.d(TAG, "w = " + w + " h = " + h);
if (w > 0 && h > 0){
if (w != lastW || h != lastH){
updateMaskBitmap();
}
}
}
在onDraw中如果生成了蒙层并且是点击状态,则绘制蒙层:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isPressed()){
if (maskBitmap != null){
canvas.drawBitmap(maskBitmap, 0, 0, paint);
}
}
}
最后在点击状态改变时也要刷新:
@Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
invalidate();
}
最后,如果要使用的是TextView,那就继承TextView,如果是别的View呢,抽象出一个delegate吧:
public class MaskTextView extends AppCompatTextView {
private static final String TAG = MaskTextView.class.getSimpleName();
private IrregularMaskDelegate delegate = new IrregularMaskDelegate(this);
public MaskTextView(Context context) {
super(context);
init();
}
public MaskTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MaskTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
delegate.draw(canvas);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
delegate.onDraw(canvas);
}
@Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
delegate.setPressed(pressed);
}
private static class IrregularMaskDelegate{
private Paint paint = new Paint();
private Bitmap maskBitmap;
private Point drawSize = new Point(0, 0);
private int colorMask = Color.parseColor("#22000000");
private View host;
public IrregularMaskDelegate(View v){
host = v;
}
public void draw(Canvas canvas) {
int w = host.getWidth();
int h = host.getHeight();
int lastW = drawSize.x;
int lastH = drawSize.y;
drawSize = new Point(w, h);
Log.d(TAG, "w = " + w + " h = " + h);
if (w > 0 && h > 0){
if (w != lastW || h != lastH){
updateMaskBitmap();
}
}
}
private void updateMaskBitmap(){
Log.d(TAG, "updateMaskBitmap");
maskBitmap = Bitmap.createBitmap(host.getWidth(), host.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas1 = new Canvas(maskBitmap);
host.draw(canvas1);
canvas1.drawColor(colorMask, PorterDuff.Mode.MULTIPLY);
host.invalidate();
}
public void onDraw(Canvas canvas) {
if (host.isPressed()){
if (maskBitmap != null){
canvas.drawBitmap(maskBitmap, 0, 0, paint);
}
}
}
public void setPressed(boolean pressed) {
host.invalidate();
}
}
}
本文介绍了一种自定义View点击态的实现方法,通过在View的onDraw方法中生成和应用蒙层Bitmap,实现不规则形状View的点击态效果,如变暗或变深,适用于高要求的设计需求。
1万+

被折叠的 条评论
为什么被折叠?



