自定义view样色色差问题
- 自定义view在动态设置颜色的时候可能UI显示的效果和自己设置的值不一样,这个就是你的view中颜色层叠方式不太对,
- 使用 Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));给画笔这个Xfermode,各种效果如下图所示

完整自定义view
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import androidx.annotation.FloatRange;
import com.beans.base.R;
import com.beans.base.top.GlobalVariableKt;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.List;
import static com.beans.base.top.GlobalVariableKt.FloatViewComplete;
import static com.beans.base.top.GlobalVariableKt.getFloatViewHeight;
import static com.beans.base.top.GlobalVariableKt.getFloatViewWidth;
import static com.beans.base.top.GlobalVariableKt.setFloatViewHeight;
import static com.beans.base.top.GlobalVariableKt.setFloatViewWidth;
public class FloatView extends View{
private static final String TAG = "FloatView";
private static final float DEFAULT_AMPLI_RATIO = 0.45f;
private static final float DEFAULT_HEIGHT_RATIO = 0.5f;
private static final float DEFAULT_LENGTH_RATIO = 1.0f;
private static final float DEFAULT_DISTANCE_RATIO = 0.125f;
private static final float DEFAULT_MIN_VALUE = 0.00000001f;
private static final float DEFAULT_MAX_AMPLI_VALUE = 0.5f;
private float mDefaultHeight;
private float mShiftRatio = 1.0f;
private float mLastAmplitudeRatio = 0.1f;
private float amplitudeRatio = 0.1f;
private float heightRatio = DEFAULT_HEIGHT_RATIO;
private float distanceRatio = DEFAULT_DISTANCE_RATIO;
private float frequency = 1.0f;
private int heightTime = 1000;
private int shiftTime = 2000;
private int borderWidth = 0;
private int borderColor = Color.parseColor("#44FFFFFF");
private int behindColor = Color.parseColor("#28FFFFFF");
private int frontColor ;
private int backgroundColor = Color.parseColor("#00FFFFFF");
private Shape shape = Shape.CIRCLE;
private BitmapShader mWaveShader;
private BitmapShader mWaveShader2;
private Matrix mShaderMatrix;
private Matrix mShaderMatrix2;
private Paint mViewPaint;
private Paint mViewPaint2;
private Paint mBorderPaint;
private AnimatorSet mAnimatorSet;
public FloatView(Context context) {
this(context, null, 0);
}
public FloatView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
setLayerType(LAYER_TYPE_HARDWARE, null);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAnimatorSet != null) {
mAnimatorSet.end();
mAnimatorSet.cancel();
}
}
@Override
public void onWindowFocusChanged(boolean hasFoucus) {
super.onWindowFocusChanged(hasFoucus);
if (mAnimatorSet != null) {
if (hasFoucus) {
if (mAnimatorSet.isStarted()) {
mAnimatorSet.resume();
} else {
mAnimatorSet.start();
}
} else {
mAnimatorSet.pause();
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
createShader(-1);
if (mAnimatorSet != null) mAnimatorSet.start();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
if (mWaveShader != null && mWaveShader2 != null && getWidth() > 0 && getHeight() > 0) {
if (mViewPaint.getShader() == null) {
mViewPaint.setShader(mWaveShader);
}
if (mViewPaint2.getShader() == null) {
mViewPaint2.setShader(mWaveShader2);
}
mShaderMatrix.setScale(frequency, amplitudeRatio / DEFAULT_AMPLI_RATIO, 0, mDefaultHeight);
mShaderMatrix.postTranslate(mShiftRatio * getWidth() * 1f, (DEFAULT_HEIGHT_RATIO - heightRatio) * getHeight());
mShaderMatrix2.setScale(frequency, amplitudeRatio / DEFAULT_AMPLI_RATIO, 0, mDefaultHeight);
mShaderMatrix2.postTranslate(mShiftRatio * getWidth() * 2f, (DEFAULT_HEIGHT_RATIO - heightRatio) * getHeight());
mWaveShader.setLocalMatrix(mShaderMatrix);
mWaveShader2.setLocalMatrix(mShaderMatrix2);
float borderWidth = mBorderPaint == null ? 0f : mBorderPaint.getStrokeWidth();
switch (shape) {
case CIRCLE:
//绘制外边界圆
if (borderWidth > 0) {
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, (getWidth() - borderWidth) / 2f - 1f, mBorderPaint);
}
float radius = getWidth() / 2f - borderWidth;
Paint paint = new Paint();
paint.setColor(backgroundColor);
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, paint);
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mViewPaint);
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mViewPaint2);
break;
case SQUARE:
setBackgroundColor(backgroundColor);
if (borderWidth > 0) {
canvas.drawRect(borderWidth / 2f, borderWidth / 2f, getWidth() - borderWidth / 2f - 0.5f, getHeight() - borderWidth / 2f - 0.5f, mBorderPaint);
}
canvas.drawRect(borderWidth, borderWidth, getWidth() - borderWidth, getHeight() - borderWidth, mViewPaint);
canvas.drawRect(borderWidth, borderWidth, getWidth() - borderWidth, getHeight() - borderWidth, mViewPaint2);
break;
}
} else {
mViewPaint.setShader(null);
mViewPaint2.setShader(null);
}
}
private void createShader(int newFrontColor) {
Log.e(TAG, "createShader: 尺寸:" +getWidth() + getHeight() );
int width,height;
if (getWidth() != 0 && getHeight() != 0){
setFloatViewWidth(getWidth());
setFloatViewHeight(getHeight());
width = getWidth();
height = getHeight();
EventBus.getDefault().post(FloatViewComplete);
}else {
width = getFloatViewWidth();
height = getFloatViewHeight();
}
double mDefaultAngularFrequency = 2.0f * Math.PI / DEFAULT_LENGTH_RATIO / width;
float mDefaultAmplitude = height * DEFAULT_AMPLI_RATIO;
mDefaultHeight = height * DEFAULT_HEIGHT_RATIO;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Bitmap bitmap2 = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas2 = new Canvas(bitmap2);
Paint wavePaint = new Paint();
wavePaint.setStrokeWidth(2);
wavePaint.setAntiAlias(true);
wavePaint.setFilterBitmap(true);
final int endX = width + 1;
final int endY = height + 1;
float[] waveY = new float[endX];
if (newFrontColor != -1){
Log.e(TAG, "createShader: 自定义颜色" );
wavePaint.setColor(newFrontColor);
}else {
Log.e(TAG, "createShader: 初始化颜色" );
wavePaint.setColor(frontColor);
}
for (int beginX = 0; beginX < endX; beginX++) {
double wx = beginX * mDefaultAngularFrequency;
float beginY = (float) (mDefaultHeight + mDefaultAmplitude * Math.sin(wx));
canvas.drawLine(beginX, beginY, beginX, endY, wavePaint);
waveY[beginX] = beginY;
}
if (newFrontColor != -1){
Log.e(TAG, "createShader: 自定义正弦颜色" );
wavePaint.setColor(newFrontColor);
}else {
Log.e(TAG, "createShader: 默认正弦颜色" );
wavePaint.setColor(frontColor);
}
Log.e(TAG, "createShader: frontColor == "+frontColor );
wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
final int wave2Shift = (int) (width * DEFAULT_DISTANCE_RATIO);
for (int beginX = 0; beginX < endX; beginX++) {
canvas2.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, endY, wavePaint);
}
mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
mViewPaint.setShader(mWaveShader);
mWaveShader2 = new BitmapShader(bitmap2, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
mViewPaint2.setShader(mWaveShader2);
}
private void init(AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.WaveView, 0, 0);
float heightRatio = typedArray.getFloat(R.styleable.WaveView_wa_heightRatio, this.heightRatio);
if (heightRatio >= 0 && heightRatio <= 1.0f) {
this.heightRatio = heightRatio;
} else {
Log.e(TAG, "invalid heightRatio");
}
float amplitudeRatio = typedArray.getFloat(R.styleable.WaveView_wa_amplitudeRatio, this.amplitudeRatio);
if (amplitudeRatio > DEFAULT_MIN_VALUE && amplitudeRatio <= DEFAULT_MAX_AMPLI_VALUE) {
this.amplitudeRatio = amplitudeRatio;
this.mLastAmplitudeRatio = this.amplitudeRatio;
} else {
Log.e(TAG, "invalid amplitudeRatio");
}
this.amplitudeRatio = adjustAmplitudeRatio(this.heightRatio, this.amplitudeRatio, this.mLastAmplitudeRatio);
int frequency = typedArray.getInteger(R.styleable.WaveView_wa_frequency, 1);
if (frequency > 0) {
this.frequency = 2.0f / ((int) Math.pow(2, frequency - 1));
} else {
Log.e(TAG, "invalid frequency");
}
int borderWidth = typedArray.getColor(R.styleable.WaveView_wa_borderWidth, this.borderWidth);
if (borderWidth >= 0) {
this.borderWidth = borderWidth;
} else {
Log.e(TAG, "invalid borderWidth");
}
heightTime = typedArray.getInteger(R.styleable.WaveView_wa_heightTime, heightTime);
shiftTime = typedArray.getInteger(R.styleable.WaveView_wa_shiftTime, shiftTime);
frontColor = typedArray.getColor(R.styleable.WaveView_wa_frontColor, frontColor);
behindColor = typedArray.getColor(R.styleable.WaveView_wa_behindColor, behindColor);
shape = typedArray.getInt(R.styleable.WaveView_wa_shape, 0) == 0 ? shape : Shape.SQUARE;
borderColor = typedArray.getColor(R.styleable.WaveView_wa_borderColor, borderColor);
backgroundColor = typedArray.getColor(R.styleable.WaveView_wa_background, backgroundColor);
typedArray.recycle();
}
mShaderMatrix = new Matrix();
mShaderMatrix2 = new Matrix();
mViewPaint = new Paint();
mViewPaint.setDither(true);
mViewPaint.setAntiAlias(true);
mViewPaint.setFilterBitmap(true);
mViewPaint2 = new Paint();
mViewPaint2.setDither(true);
mViewPaint2.setAntiAlias(true);
mViewPaint2.setFilterBitmap(true);
mAnimatorSet = new AnimatorSet();
mBorderPaint = new Paint();
mBorderPaint.setAntiAlias(true);
mBorderPaint.setDither(true);
mBorderPaint.setFilterBitmap(true);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setColor(borderColor);
mBorderPaint.setStrokeWidth(borderWidth);
List<Animator> animators = new ArrayList<>();
animators.add(getShiftAnimator(this.shiftTime));
animators.add(getHeightAnimator(this.heightTime));
}
private DisplayMetrics getScreenMetrics() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics;
}
private float getShiftRatio() {
return mShiftRatio;
}
private void setShiftRatio(@FloatRange(from = 0.0f, to = 1.0f) float waveShiftRatio) {
if (mShiftRatio != waveShiftRatio) {
mShiftRatio = waveShiftRatio;
invalidate();
}
}
private ObjectAnimator getShiftAnimator(int shiftTime) {
ObjectAnimator waveShiftAnim = ObjectAnimator.ofFloat(this, "shiftRatio", 0f, 2f);
waveShiftAnim.setRepeatCount(ValueAnimator.INFINITE);
waveShiftAnim.setDuration(shiftTime);
waveShiftAnim.setRepeatMode(ValueAnimator.RESTART);
waveShiftAnim.setInterpolator(new LinearInterpolator());
return waveShiftAnim;
}
private ObjectAnimator getHeightAnimator(int heightTime) {
ObjectAnimator waveLevelAnim = ObjectAnimator.ofFloat(this, "heightRatio", 0f, getHeightRatio());
waveLevelAnim.setDuration(heightTime);
waveLevelAnim.setInterpolator(new DecelerateInterpolator());
return waveLevelAnim;
}
private float getHeightRatio() {
return heightRatio;
}
public void setHeightRatio(@FloatRange(from = 0.0f, to = 1.0f) float heightRatio) {
if (heightRatio > 1f) {
heightRatio = 1f;
}
if (this.heightRatio != heightRatio && heightRatio >= 0) {
this.heightRatio = heightRatio;
this.amplitudeRatio = adjustAmplitudeRatio(this.heightRatio, this.amplitudeRatio, this.mLastAmplitudeRatio);
}
}
private float adjustAmplitudeRatio(float heightRatio, float amplitudeRatio, float lastAmplitudeRatio) {
float ratio = amplitudeRatio;
if (ratio != lastAmplitudeRatio) {
ratio = lastAmplitudeRatio;
}
if ((ratio + heightRatio > 1)) {
ratio = 1f - heightRatio + DEFAULT_MIN_VALUE;
} else {
if (heightRatio < lastAmplitudeRatio) {
ratio = heightRatio;
}
}
return ratio;
}
private void setDistanceRatio(@FloatRange(from = 0.0f, to = 1.0f) float distanceRatio) {
if (this.distanceRatio != distanceRatio && getWidth() > 0 && getHeight() > 0) {
this.distanceRatio = distanceRatio;
mWaveShader = null;
createShader(-1);
}
}
public void setShape(Shape Shape) {
if (this.shape != Shape) {
this.shape = Shape;
}
}
public void setAmplitudeRatio(@FloatRange(from = DEFAULT_MIN_VALUE, to = DEFAULT_MAX_AMPLI_VALUE) float amplitudeRatio) {
if (this.amplitudeRatio != amplitudeRatio && amplitudeRatio <= DEFAULT_MAX_AMPLI_VALUE && amplitudeRatio >= DEFAULT_MIN_VALUE) {
this.mLastAmplitudeRatio = amplitudeRatio;
this.amplitudeRatio = adjustAmplitudeRatio(heightRatio, amplitudeRatio, mLastAmplitudeRatio);
invalidate();
}
}
public void setFrequency(int frequency) {
if (frequency <= 0) {
frequency = 1;
}
int powNumber = (int) Math.pow(2, frequency - 1);
float backNumber = 2.0f / powNumber;
if (backNumber != frequency) {
this.frequency = backNumber;
}
}
public void setFrontColor(int frontColor) {
if (getFloatViewWidth() > 0 && getFloatViewHeight() > 0 && this.frontColor != frontColor) {
this.frontColor = frontColor;
mWaveShader = null;
createShader(frontColor);
}
}
public void setBehindColor(int behindColor) {
if (getWidth() > 0 && getHeight() > 0 && this.behindColor != behindColor) {
this.behindColor = behindColor;
mWaveShader = null;
createShader(-1);
}
}
public void setShiftTime(int shiftTime) {
this.shiftTime = shiftTime;
if (mAnimatorSet != null) {
mAnimatorSet.end();
}
mAnimatorSet = new AnimatorSet();
mAnimatorSet.play(getShiftAnimator(this.shiftTime));
mAnimatorSet.start();
}
public void setBorderWidth(int width) {
mBorderPaint.setStrokeWidth(width);
}
public void setBorderColor(int color) {
mBorderPaint.setColor(color);
}
public enum Shape {
CIRCLE, SQUARE
}
}