2024年安卓最新Android-高级-UI-进阶之路(四)-Paint-渲染-滤镜-xfermode-使用,2024年最新面试培训机构

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

下图是我进阶学习所积累的历年腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节

整理不易,望各位看官老爷点个关注转发,谢谢!祝大家都能得到自己心仪工作。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

}
/**线性渲染

  • x0, y0, 起始点
  • x1, y1, 结束点
  • int[] mColors, 中间依次要出现的几个颜色
  • float[] positions 位置数组,position的取值范围[0,1],作用是指定几个颜色分别放置在那个位置上,
  • 如果传null,渐变就线性变化。
  • tile 用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法
    */
    //从左边 size 开始,左边看不见的地方开始,以滚动扫描的形式过来
    mLinearGradient = LinearGradient(-size.toFloat(),0f,0f,0f, intArrayOf(0x33ffffff, -0x1, 0x33ffffff),
    floatArrayOf(0f, 0.2f, 1f), Shader.TileMode.CLAMP)
    //将线性渐变添加到 paint 中
    mPaint!!.setShader(mLinearGradient)
    //定义一个矩阵
    mGradientatrix = Matrix()
    }
    }
    }

/**

  • 开始绘制
    /
    override fun draw(canvas: Canvas?) {
    super.draw(canvas)
    val measureWindth = paint.measureText(text.toString())
    mTranslate += delta
    /
    *
  • 如果位置已经移动到了边界,那么文字就开始往回滚动
  • 但是如果小于 1 那么又开始递增,执行另一个逻辑
    */
    if (mTranslate > measureWindth + 1 || mTranslate < 1){
    delta = -delta
    }

//将矩阵平移
mGradientatrix!!.setTranslate(mTranslate.toFloat(),0f)
mLinearGradient!!.setLocalMatrix(mGradientatrix)
//paint是textview的所以只需要不断色控制画笔的shader 然后利用矩阵控制位移即可
postInvalidateDelayed(30)

}
}

10. 雷达扫描 SweepGradient

/**

  • author  : devyk on 2019-11-30 18:50
    
  • blog    : https://juejin.im/user/578259398ac2470061f3a3fb/posts
    
  • github  : https://github.com/yangkun19921001
    
  • mailbox : yang1001yk@gmail.com
    
  • desc    : This is RadarGradientView 渐变渲染/梯度渲染
    

*/
class RadarGradientView : View {

private var mWidth: Int = 0
private var mHeight: Int = 0

private val TAG = javaClass.simpleName

//五个圆
private val pots = floatArrayOf(0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f)

private var scanShader: Shader? = null // 扫描渲染shader
private val scanSpeed = 10 // 扫描速度
private var scanAngle: Int = 0 // 扫描旋转的角度

private lateinit var mMatrix: Matrix // 旋转需要的矩阵

private var mPaintCircle = Paint() // 画圆用到的paint
private var mPaintRadar = Paint() // 扫描用到的paint

private val run = object : Runnable {
override fun run() {
scanAngle = (scanAngle + scanSpeed) % 125 //
Log.d(TAG,“scanAngle:$scanAngle”)
mMatrix.postRotate(scanSpeed.toFloat(), (mWidth / 2).toFloat(), (mHeight / 2).toFloat()) // 旋转矩阵
invalidate() // 通知view重绘
postDelayed(this, 50) // 调用自身 重复绘制
}
}

constructor(context: Context) : super(context) {
init()
}

private fun init() {
mMatrix = Matrix()
// 画圆用到的paint
mPaintCircle = Paint()
mPaintCircle.style = Paint.Style.STROKE // 描边
mPaintCircle.strokeWidth = 1f // 宽度
mPaintCircle.alpha = 100 // 透明度
mPaintCircle.isAntiAlias = true // 抗锯齿
mPaintCircle.color = Color.parseColor(“#B0C4DE”) // 设置颜色 亮钢兰色

// 扫描用到的paint
mPaintRadar = Paint()
mPaintRadar.style = Paint.Style.FILL_AND_STROKE // 填充
mPaintRadar.isAntiAlias = true // 抗锯齿

post(run)
}

constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init()
}

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

Log.d(TAG,“onDraw()”)
for (i in pots.indices) {
canvas.drawCircle((mWidth / 2).toFloat(), (mHeight / 2).toFloat(), mWidth * pots[i], mPaintCircle)
}

// 画布的旋转变换 需要调用save() 和 restore()
canvas.save()

scanShader = SweepGradient(
(mWidth / 2).toFloat(), (mHeight / 2).toFloat(),
intArrayOf(Color.TRANSPARENT, Color.parseColor(“#84B5CA”)), null
)
mPaintRadar.shader = scanShader // 设置着色器
canvas.concat(mMatrix)
canvas.drawCircle((mWidth / 2).toFloat(), (mHeight / 2).toFloat(), mWidth * pots[6], mPaintRadar)

canvas.restore()
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
Log.d(TAG,“onMeasure()”)
// 取屏幕的宽高是为了把雷达放在屏幕的中间
mWidth = measuredWidth
mHeight = measuredHeight
mHeight = Math.min(mWidth, mHeight)
mWidth = mHeight
}

}

11. 放大镜 BitmapShader

class ZoomImageView : View {
// 原图
private val mBitmap: Bitmap
// 放大后的图
private var mBitmapScale: Bitmap? = null
// 制作的圆形的图片(放大的局部),盖在Canvas上面
private val mShapeDrawable: ShapeDrawable

private val mMatrix: Matrix

constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

init {

mBitmap = BitmapFactory.decodeResource(resources, R.mipmap.gild_3)
mBitmapScale = mBitmap
//放大后的整个图片
mBitmapScale = Bitmap.createScaledBitmap(
mBitmapScale!!, mBitmapScale!!.width * FACTOR,
mBitmapScale!!.height * FACTOR, true
)
val bitmapShader = BitmapShader(
mBitmapScale!!, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP
)

mShapeDrawable = ShapeDrawable(OvalShape())
mShapeDrawable.paint.shader = bitmapShader
// 切出矩形区域,用来画圆(内切圆)
mShapeDrawable.setBounds(0, 0, RADIUS * 2, RADIUS * 2)

mMatrix = Matrix()
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

// 1、画原图
canvas.drawBitmap(mBitmap, 0f, 0f, null)

// 2、画放大镜的图
mShapeDrawable.draw(canvas)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
val x = event.x.toInt()
val y = event.y.toInt() - RADIUS

Log.d(“onTouchEvent”, “x:” + x + “y:” + y)

// 将放大的图片往相反的方向挪动
mMatrix.setTranslate((RADIUS - x * FACTOR).toFloat(), (RADIUS - y * FACTOR).toFloat())
mShapeDrawable.paint.shader.setLocalMatrix(mMatrix)
// 切出手势区域点位置的圆
mShapeDrawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS)
// invalidate()
postInvalidate()
return true
}

companion object {

//放大倍数
private val FACTOR = 3
//放大镜的半径
private val RADIUS = 300
}
}

滤镜

//平移运算—加法
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1, 0,0,0,0,
0,1,0,0,100,
0,0,1,0,0,
0,0,0,1,0,
});

//反相效果 – 底片效果
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
-1, 0,0,0,255,
0,-1,0,0,255,
0,0,-1,0,255,
0,0,0,1,0,
});
//缩放运算—乘法 – 颜色增强
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1.2f, 0,0,0,0,
0,1.2f,0,0,0,
0,0,1.2f,0,0,
0,0,0,1.2f,0,
});
/** 黑白照片

  • 是将我们的三通道变为单通道的灰度模式
  • 去色原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色,
  • 同时为了保证图像亮度不变,同一个通道里的R+G+B =1
    */
    ColorMatrix colorMartrix = new ColorMatrix(new float[]{
    0.213f, 0.715f,0.072f,0,0,
    0.213f, 0.715f,0.072f,0,0,
    0.213f, 0.715f,0.072f,0,0,
    0,0,0,1,0,
    });

//发色效果—(比如红色和绿色交换)
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1,0,0,0,0,
0, 0,1,0,0,
0,1,0,0,0,
0,0,0,0.5F,0,
});
//复古效果
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1/2f,1/2f,1/2f,0,0,
1/3f, 1/3f,1/3f,0,0,
1/4f,1/4f,1/4f,0,0,
0,0,0,1,0,
});
复制代码

class FilterView : View {

private var paint = Paint()

lateinit var bitmap: Bitmap

constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

/**

  • 显示的高
    */
    var showHeight = 0

init {

init()
}

private fun init() {

paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = Color.RED

bitmap = BitmapFactory.decodeResource(resources, R.mipmap.gild_3)
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// //关闭单个View的硬件加速功
// // setLayerType(View.LAYER_TYPE_SOFTWARE,null);

//1. 缩放运算—乘法 – 颜色增强
val colorMartrix = ColorMatrix(
floatArrayOf(
1.2f, 0f, 0f, 0f, 0f,
0f, 1.2f, 0f, 0f, 0f,
0f, 0f, 1.2f, 0f, 0f,
0f, 0f, 0f, 1.2f, 0f
)
)
val rectF = RectF(
0f,
showHeight.toFloat() ,
(bitmap.width / 2).toFloat(),
(bitmap.height / 4).toFloat()
)
drawFilterBitmap(colorMartrix, canvas,rectF)

showHeight += bitmap.height / 4

//2 平移运算—加法
var colorMartrix2 = ColorMatrix(floatArrayOf(
1f, 0f,0f,0f,0f,
0f,1f,0f,0f,100f,
0f,0f,1f,0f,0f,
0f,0f,0f,1f,0f
))

val rectF2 = RectF(
0f,
showHeight.toFloat(),
(bitmap.width / 2).toFloat(),
(bitmap.height /4) * 2.toFloat()
)
drawFilterBitmap(colorMartrix2, canvas,rectF2)

showHeight += bitmap.height / 4

//3. 反相效果 – 底片效果
var colorMartrix3 = ColorMatrix(floatArrayOf(
-1f, 0f,0f,0f,255f,
0f,-1f,0f,0f,255f,
0f,0f,-1f,0f,255f,
0f,0f,0f,1f,0f
));

val rectF3 = RectF(
0f,
showHeight.toFloat(),
(bitmap.width / 2).toFloat(),
(bitmap.height /4) * 3.toFloat()
)
drawFilterBitmap(colorMartrix3, canvas,rectF3)

/**

  • 4.黑白照片
  • 是将我们的三通道变为单通道的灰度模式
  • 去色原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色,
  • 同时为了保证图像亮度不变,同一个通道里的R+G+B =1
    */
    var colorMartrix4 = ColorMatrix(floatArrayOf(
    0.213f, 0.715f,0.072f,0f,0f,
    0.213f, 0.715f,0.072f,0f,0f,
    0.213f, 0.715f,0.072f,0f,0f,
    0f,0f,0f,1f,0f
    ));

showHeight += bitmap.height / 4
val rectF4 = RectF(
bitmap.width/2f,
bitmap.height /2f,
(bitmap.width).toFloat(),
(bitmap.height /4) * 3.toFloat()
)
drawFilterBitmap(colorMartrix4, canvas,rectF4)

//5.发色效果—(比如红色和绿色交换)
var colorMartrix5 = ColorMatrix(floatArrayOf(
1f,0f,0f,0f,0f,
0f, 0f,1f,0f,0f,
0f,1f,0f,0f,0f,
0f,0f,0f,0.5F,0f
));

val rectF5 = RectF(
bitmap.width / 2f,
0f,
(bitmap.width / 2 * 2).toFloat(),
(bitmap.height /4) .toFloat()
)
drawFilterBitmap(colorMartrix5, canvas,rectF5)

//6.复古效果
var colorMartrix6= ColorMatrix(floatArrayOf(
1/2f,1/2f,1/2f,0f,0f,
1/3f, 1/3f,1/3f,0f,0f,
1/4f,1/4f,1/4f,0f,0f,
0f,0f,0f,1f,0f
));

val rectF6 = RectF(
bitmap.width / 2f,
bitmap.height /4f,
(bitmap.width / 2 * 2).toFloat(),
(bitmap.height /4 * 2) .toFloat()
)
drawFilterBitmap(colorMartrix6, canvas,rectF6)

}

private fun drawFilterBitmap(colorMartrix: ColorMatrix, canvas: Canvas,rectF: RectF) {

paint.colorFilter = ColorMatrixColorFilter(colorMartrix)
canvas.drawBitmap(bitmap, null, rectF, paint)
}

}

xfermode

private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR), // 清空所有,要闭硬件加速,否则无效
new PorterDuffXfermode(PorterDuff.Mode.SRC), // 显示前都图像,不显示后者
new PorterDuffXfermode(PorterDuff.Mode.DST), // 显示后者图像,不显示前者
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER), // 后者叠于前者
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER), // 前者叠于后者
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN), // 显示相交的区域,但图像为后者
new PorterDuffXfermode(PorterDuff.Mode.DST_IN), // 显示相交的区域,但图像为前者
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT), // 显示后者不重叠的图像
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT), // 显示前者不重叠的图像
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP), // 显示前者图像,与后者重合的图像
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP), // 显示后者图像,与前者重合的图像
new PorterDuffXfermode(PorterDuff.Mode.XOR), // 显示持有不重合的图像
new PorterDuffXfermode(PorterDuff.Mode.DARKEN), // 后者叠于前者上,后者与前者重叠的部份透明。要闭硬件加速,否则无效
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN), // 前者叠于前者,前者与后者重叠部份透明。要闭硬件加速,否则无效
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY), // 显示重合的图像,且颜色会合拼
new PorterDuffXfermode(PorterDuff.Mode.SCREEN) // 显示持有图像,重合的会变白
};

class XfermodeView : View {

lateinit var mPaint: Paint
internal var mItemSize = 0f
internal var mItemHorizontalOffset = 0f
internal var mItemVerticalOffset = 0f
internal var mCircleRadius = 0f
internal var mRectSize = 0f
internal var mCircleColor = -0x33bc//黄色
internal var mRectColor = -0x995501//蓝色
internal var mTextSize = 25f

constructor(context: Context) : super(context) {
init(null, 0)
}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(attrs, 0)
}

constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
init(attrs, defStyle)
}

private fun init(attrs: AttributeSet?, defStyle: Int) {
if (Build.VERSION.SDK_INT >= 11) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
mPaint.textSize = mTextSize
mPaint.textAlign = Paint.Align.CENTER
mPaint.strokeWidth = 2f
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//设置背景色
// canvas.drawARGB(255, 139, 197, 186);

val canvasWidth = canvas.width
val canvasHeight = canvas.height

for (row in 0…3) {
for (column in 0…3) {
canvas.save()
val layer =
canvas.saveLayer(0f, 0f, canvasWidth.toFloat(), canvasHeight.toFloat(), null, Canvas.ALL_SAVE_FLAG)
mPaint.xfermode = null
val index = row * 4 + column
val translateX = (mItemSize + mItemHorizontalOffset) * column
val translateY = (mItemSize + mItemVerticalOffset) * row
canvas.translate(translateX, translateY)
//画文字
val text = sLabels[index]
mPaint.color = Color.BLACK
val textXOffset = mItemSize / 2
val textYOffset = mTextSize + (mItemVerticalOffset - mTextSize) / 2
canvas.drawText(text, textXOffset, textYOffset, mPaint)
canvas.translate(0f, mItemVerticalOffset)
//画边框
mPaint.style = Paint.Style.STROKE
mPaint.color = -0x1000000
canvas.drawRect(2f, 2f, mItemSize - 2, mItemSize - 2, mPaint)
mPaint.style = Paint.Style.FILL
//画圆
mPaint.color = mCircleColor
val left = mCircleRadius + 3
val top = mCircleRadius + 3
canvas.drawCircle(left, top, mCircleRadius, mPaint)
mPaint.xfermode = sModes[index]
//画矩形
mPaint.color = mRectColor
val rectRight = mCircleRadius + mRectSize
val rectBottom = mCircleRadius + mRectSize
canvas.drawRect(left, top, rectRight, rectBottom, mPaint)
mPaint.xfermode = null
//canvas.restore();
canvas.restoreToCount(layer)
}
}
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mItemSize = w / 4.5f
mItemHorizontalOffset = mItemSize / 6
mItemVerticalOffset = mItemSize * 0.426f
mCircleRadius = mItemSize / 3
mRectSize = mItemSize * 0.6f
}

companion object {

private val sModes = arrayOf(
PorterDuffXfermode(PorterDuff.Mode.CLEAR),
PorterDuffXfermode(PorterDuff.Mode.SRC),
PorterDuffXfermode(
PorterDuff.Mode.DST
),
PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
PorterDuffXfermode(
PorterDuff.Mode.SRC_IN
),
PorterDuffXfermode(PorterDuff.Mode.DST_IN),
PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
PorterDuffXfermode(
PorterDuff.Mode.DST_OUT
),
PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
PorterDuffXfermode(
PorterDuff.Mode.XOR
),
PorterDuffXfermode(PorterDuff.Mode.DARKEN),
PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
PorterDuffXfermode(
PorterDuff.Mode.MULTIPLY
),
PorterDuffXfermode(PorterDuff.Mode.SCREEN)
)

private val sLabels = arrayOf(
“Clear”,
“Src”,
“Dst”,
“SrcOver”,
“DstOver”,
“SrcIn”,
“DstIn”,
“SrcOut”,
“DstOut”,
“SrcATop”,
“DstATop”,
“Xor”,
“Darken”,
“Lighten”,
“Multiply”,
“Screen”
)
}
}
复制代码

1. 画圆角 PorterDuff.Mode.DST_IN

public class RoudImageView : View {

constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
var mBitPaint = Paint()
private lateinit var mBmpDST: Bitmap
private lateinit var mBmpSRC: Bitmap
private fun init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
mBmpDST = changeBitmapSize(R.mipmap.gild_3)
mBmpSRC = changeBitmapSize(R.drawable.shade)
}

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

override fun draw(canvas: Canvas) {
super.draw(canvas)
val saveLayer = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
canvas.drawBitmap(mBmpDST,0f,0f,mBitPaint)
mBitPaint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_IN))
canvas.drawBitmap(mBmpSRC,0f,0f,mBitPaint)

mBitPaint.setXfermode(null)
canvas.restoreToCount(saveLayer)
}
private fun changeBitmapSize(res: Int) : Bitmap {
var bitmap = BitmapFactory.decodeResource(getResources(), res);
var width = bitmap.getWidth();
var height = bitmap.getHeight();
Log.e(“width”,“width:”+width);
Log.e(“height”,“height:”+height);
//设置想要的大小
var newWidth=500;
var newHeight=500;

//计算压缩的比率
var scaleWidth=(newWidth)/width .toFloat()
var scaleHeight=(newHeight)/height.toFloat();

//获取想要缩放的matrix
var matrix = Matrix();
matrix.postScale(scaleWidth,scaleHeight.toFloat());

//获取新的bitmap
bitmap=Bitmap.createBitmap(bitmap,0,0,width,height,matrix,true);
bitmap.getWidth();
bitmap.getHeight();
Log.e(“newWidth”,“newWidth”+bitmap.getWidth());
Log.e(“newHeight”,“newHeight”+bitmap.getHeight());
return bitmap;
}

}

2. 倒影 PorterDuff.Mode.DST_IN

class InvertedImageView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val mBitPaint: Paint
private val BmpDST: Bitmap
private val BmpSRC: Bitmap
private val BmpRevert: Bitmap

init {
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
mBitPaint = Paint()
BmpDST = BitmapUtis.changeBitmapSize(context,R.mipmap.gild_3)
BmpSRC = BitmapUtis.changeBitmapSize(context,R.drawable.invert_shade)

val matrix = Matrix()
matrix.setScale(1f, -1f)
// 生成倒影图
BmpRevert = Bitmap.createBitmap(BmpDST, 0, 0, BmpDST.width, BmpDST.height, matrix, true)
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

canvas.drawColor(Color.BLACK)

//先画出原始图片
canvas.drawBitmap(BmpDST, 0f, 0f, mBitPaint)

//再画出倒影
val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
canvas.translate(0f, BmpSRC.height.toFloat())

canvas.drawBitmap(BmpRevert, 0f, 0f, mBitPaint)
mBitPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(BmpSRC, 0f, 0f, mBitPaint)

mBitPaint.xfermode = null

canvas.restoreToCount(layerId)
}
}

4. 不规则波浪 PorterDuff.Mode.DST_IN

class IrregularWaveView(context: Context, attrs: AttributeSet) : View(context, attrs) {

private val mPaint: Paint
private var mItemWaveLength = 0
private var dx = 0

private val BmpSRC: Bitmap
private val BmpDST: Bitmap

init {
mPaint = Paint()

BmpDST = BitmapFactory.decodeResource(resources, R.drawable.wav, null)
BmpSRC = BitmapFactory.decodeResource(resources, R.drawable.circle_shape, null)
//不要让它超出边界
mItemWaveLength = BmpDST.width - BmpSRC.width

startAnim()
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

//先画上圆形
canvas.drawBitmap(BmpSRC, 0f, 0f, mPaint)

//再画上结果
val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
canvas.drawBitmap(
BmpDST,
Rect(dx, 0, dx + BmpSRC.width, BmpSRC.height),
Rect(0, 0, BmpSRC.width, BmpSRC.height),
mPaint
)
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawBitmap(BmpSRC, 0f, 0f, mPaint)
mPaint.xfermode = null
canvas.restoreToCount(layerId)

}

fun startAnim() {
val animator = ValueAnimator.ofInt(0, mItemWaveLength)
animator.duration = 2000
animator.repeatCount = ValueAnimator.INFINITE
animator.interpolator = LinearInterpolator()
animator.addUpdateListener { animation ->
dx = animation.animatedValue as Int
postInvalidate()
}
animator.start()
}
}

5.心电图 PorterDuff.Mode.DST_IN

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

04282228)]

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-qTgxexTs-1715804282228)]

【算法合集】

[外链图片转存中…(img-AOxORlxw-1715804282228)]

【延伸Android必备知识点】

[外链图片转存中…(img-WPIL90a6-1715804282228)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值