android mask,Android,自定义MaskImage

卫阳曜
2023-12-01

public class MaskableFrameLayout extends FrameLayout {

//Constants

private static final String TAG = "MaskableFrameLayout";

private static final int MODE_ADD = 0;

private static final int MODE_CLEAR = 1;

private static final int MODE_DARKEN = 2;

private static final int MODE_DST = 3;

private static final int MODE_DST_ATOP = 4;

private static final int MODE_DST_IN = 5;

private static final int MODE_DST_OUT = 6;

private static final int MODE_DST_OVER = 7;

private static final int MODE_LIGHTEN = 8;

private static final int MODE_MULTIPLY = 9;

private static final int MODE_OVERLAY = 10;

private static final int MODE_SCREEN = 11;

private static final int MODE_SRC = 12;

private static final int MODE_SRC_ATOP = 13;

private static final int MODE_SRC_IN = 14;

private static final int MODE_SRC_OUT = 15;

private static final int MODE_SRC_OVER = 16;

private static final int MODE_XOR = 17;

private Handler mHandler;

//Mask props

@Nullable

private Drawable mDrawableMask = null;

@Nullable

private Bitmap mFinalMask = null;

//Drawing props

private Paint mPaint = null;

private PorterDuffXfermode mPorterDuffXferMode = null;

public MaskableFrameLayout(Context context) {

super(context);

}

public MaskableFrameLayout(Context context, AttributeSet attrs) {

super(context, attrs);

construct(context, attrs);

}

public MaskableFrameLayout(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

construct(context, attrs);

}

private void construct(Context context, AttributeSet attrs) {

mHandler = new Handler();

setDrawingCacheEnabled(true);

setLayerType(LAYER_TYPE_SOFTWARE, null); //Only works for software layers

mPaint = createPaint(false);

Resources.Theme theme = context.getTheme();

if (theme != null) {

TypedArray a = theme.obtainStyledAttributes(

attrs,

R.styleable.MaskableLayout,

0, 0);

try {

//Load the mask if specified in xml

initMask(loadMask(a));

//Load the mode if specified in xml

mPorterDuffXferMode = getModeFromInteger(

a.getInteger(R.styleable.MaskableLayout_porterduffxfermode, 0));

initMask(mDrawableMask);

//Check antiAlias

if (a.getBoolean(R.styleable.MaskableLayout_anti_aliasing, false)) {

//Recreate paint with anti aliasing enabled

//This can take a performance hit.

mPaint = createPaint(true);

}

} finally {

if (a != null) {

a.recycle();

}

}

} else {

log("Couldn't load theme, mask in xml won't be loaded.");

}

registerMeasure();

}

@NonNull

private Paint createPaint(boolean antiAliasing) {

Paint output = new Paint(Paint.ANTI_ALIAS_FLAG);

output.setAntiAlias(antiAliasing);

output.setXfermode(mPorterDuffXferMode);

return output;

}

//Mask functions

@Nullable

private Drawable loadMask(@NonNull TypedArray a) {

final int drawableResId = a.getResourceId(R.styleable.MaskableLayout_mask, -1);

if (drawableResId == -1) {

return null;

}

return AppCompatResources.getDrawable(getContext(), drawableResId);

}

private void initMask(@Nullable Drawable input) {

if (input != null) {

mDrawableMask = input;

if (mDrawableMask instanceof AnimationDrawable) {

mDrawableMask.setCallback(this);

}

} else {

log("Are you sure you don't want to provide a mask ?");

}

}

@Nullable

public Drawable getDrawableMask() {

return mDrawableMask;

}

@Nullable

private Bitmap makeBitmapMask(@Nullable Drawable drawable) {

if (drawable != null) {

if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {

Bitmap mask = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),

Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(mask);

drawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());

drawable.draw(canvas);

return mask;

} else {

log("Can't create a mask with height 0 or width 0. Or the layout has no children and is wrap content");

return null;

}

} else {

log("No bitmap mask loaded, view will NOT be masked !");

}

return null;

}

public void setMask(int drawableRes) {

Resources res = getResources();

if (res != null) {

setMask(res.getDrawable(drawableRes));

} else {

log("Unable to load resources, mask will not be loaded as drawable");

}

}

public void setMask(@Nullable Drawable input) {

initMask(input);

swapBitmapMask(makeBitmapMask(mDrawableMask));

invalidate();

}

public void setPorterDuffXferMode(PorterDuff.Mode mode) {

this.mPorterDuffXferMode = new PorterDuffXfermode(mode);

}

//Once the size has changed we need to remake the mask.

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

setSize(w, h);

}

private void setSize(int width, int height) {

if (width > 0 && height > 0) {

if (mDrawableMask != null) {

//Remake the 9patch

swapBitmapMask(makeBitmapMask(mDrawableMask));

}

} else {

log("Width and height must be higher than 0");

}

}

//Drawing

@Override

protected void dispatchDraw(Canvas canvas) {

super.dispatchDraw(canvas);

if (mFinalMask != null && mPaint != null) {

mPaint.setXfermode(mPorterDuffXferMode);

canvas.drawBitmap(mFinalMask, 0.0f, 0.0f, mPaint);

mPaint.setXfermode(null);

} else {

log("Mask or paint is null ...");

}

}

//Once inflated we have no height or width for the mask. Wait for the layout.

private void registerMeasure() {

final ViewTreeObserver treeObserver = MaskableFrameLayout.this.getViewTreeObserver();

if (treeObserver != null && treeObserver.isAlive()) {

treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

ViewTreeObserver aliveObserver = treeObserver;

if (!aliveObserver.isAlive()) {

aliveObserver = MaskableFrameLayout.this.getViewTreeObserver();

}

if (aliveObserver != null) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

aliveObserver.removeOnGlobalLayoutListener(this);

} else {

aliveObserver.removeGlobalOnLayoutListener(this);

}

} else {

log("GlobalLayoutListener not removed as ViewTreeObserver is not valid");

}

swapBitmapMask(makeBitmapMask(mDrawableMask));

}

});

}

}

//Logging

private void log(@NonNull String message) {

Log.d(TAG, message);

}

//Animation

@Override

public void invalidateDrawable(Drawable dr) {

if (dr != null) {

initMask(dr);

swapBitmapMask(makeBitmapMask(dr));

invalidate();

}

}

@Override

public void scheduleDrawable(Drawable who, Runnable what, long when) {

if (who != null && what != null) {

mHandler.postAtTime(what, when);

}

}

@Override

public void unscheduleDrawable(Drawable who, Runnable what) {

if (who != null && what != null) {

mHandler.removeCallbacks(what);

}

}

private void swapBitmapMask(@Nullable Bitmap newMask) {

if (newMask != null) {

if (mFinalMask != null && !mFinalMask.isRecycled()) {

mFinalMask.recycle();

}

mFinalMask = newMask;

}

}

//Utils

private PorterDuffXfermode getModeFromInteger(int index) {

PorterDuff.Mode mode = null;

switch (index) {

case MODE_ADD:

if (Build.VERSION.SDK_INT >= 11) {

mode = PorterDuff.Mode.ADD;

} else {

log("MODE_ADD is not supported on api lvl " + Build.VERSION.SDK_INT);

}

case MODE_CLEAR:

mode = PorterDuff.Mode.CLEAR;

break;

case MODE_DARKEN:

mode = PorterDuff.Mode.DARKEN;

break;

case MODE_DST:

mode = PorterDuff.Mode.DST;

break;

case MODE_DST_ATOP:

mode = PorterDuff.Mode.DST_ATOP;

break;

case MODE_DST_IN:

mode = PorterDuff.Mode.DST_IN;

break;

case MODE_DST_OUT:

mode = PorterDuff.Mode.DST_OUT;

break;

case MODE_DST_OVER:

mode = PorterDuff.Mode.DST_OVER;

break;

case MODE_LIGHTEN:

mode = PorterDuff.Mode.LIGHTEN;

break;

case MODE_MULTIPLY:

mode = PorterDuff.Mode.MULTIPLY;

break;

case MODE_OVERLAY:

if (Build.VERSION.SDK_INT >= 11) {

mode = PorterDuff.Mode.OVERLAY;

} else {

log("MODE_OVERLAY is not supported on api lvl " + Build.VERSION.SDK_INT);

}

case MODE_SCREEN:

mode = PorterDuff.Mode.SCREEN;

break;

case MODE_SRC:

mode = PorterDuff.Mode.SRC;

break;

case MODE_SRC_ATOP:

mode = PorterDuff.Mode.SRC_ATOP;

break;

case MODE_SRC_IN:

mode = PorterDuff.Mode.SRC_IN;

break;

case MODE_SRC_OUT:

mode = PorterDuff.Mode.SRC_OUT;

break;

case MODE_SRC_OVER:

mode = PorterDuff.Mode.SRC_OVER;

break;

case MODE_XOR:

mode = PorterDuff.Mode.XOR;

break;

default:

mode = PorterDuff.Mode.DST_IN;

}

log("Mode is " + mode.toString());

return new PorterDuffXfermode(mode);

}

}

 类似资料: