2024年Android最新效果炸了··Android自定义View实现一个炫酷的时钟,android 基础面试

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

image

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

image

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

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

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

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

*/

private double randomAnger = 0;

}

粒子的初始位置位于随机角度的圆周,且一个粒子具有随机的半径,透明度,速度等,通过init()方法,实现初始化粒子如下

public void init(Random random, float viewRadius) {

anger = Math.toRadians(random.nextInt(360));

velocity = random.nextFloat() * 2F;

radius = random.nextInt(6) + 5;

mX = (float) (viewRadius * Math.cos(anger));

mY = (float) (viewRadius * Math.sin(anger));

//随机偏移角度-30°~30°

randomAnger = Math.toRadians(30 - random.nextInt(60));

alpha = 153 + random.nextInt(102);

}

想让粒子运动起来,使用update更新粒子的这些坐标属性就能实现,比如粒子现在坐标在(5,5)``,通过update()改变粒子的坐标到(6,6),结合属性动画不停地调用update()则就能不停的改变x,y的坐标,实现粒子运动,然后当粒子移动超过一定距离,或者调用update超过一定次数,再重新调用init()`让粒子重新从圆周上开始下一个生命周期运动

public void updatePoint(Random random, float viewRadius) {

//每一帧偏移的像素大小

float distance = 1F;

double moveAnger = anger + randomAnger;

mX = (float) (mX - distance * Math.cos(moveAnger) * velocity);

mY = (float) (mY - distance * Math.sin(moveAnger) * velocity);

//模拟半径逐渐变小

radius = radius - 0.02F * velocity;

num++;

//如果到了最大值 则重新给运动粒子一个轨迹属性

int maxDistance = 180;

int maxNum = 400;

if (velocity * num > maxDistance || num > maxNum) {

num = 0;

init(random, viewRadius);

}

}

在View中大致实现如下

/**

* 初始化动画

*/

private void initAnim() {

//绘制运动的粒子

AnimPoint mAnimPoint = new AnimPoint();

for (int i = 0; i < pointCount; i++) {

//通过clone创建对象,避免重复创建

AnimPoint cloneAnimPoint = mAnimPoint.clone();

//先给每个粒子初始化各类属性

cloneAnimPoint.init(mRandom, mRadius - mOutCircleStrokeWidth / 2F);

mPointList.add(cloneAnimPoint);

}

//画运动粒子

mPointsAnimator = ValueAnimator.ofFloat(0F, 1F);

mPointsAnimator.setDuration(Integer.MAX_VALUE);

mPointsAnimator.setRepeatMode(ValueAnimator.RESTART);

mPointsAnimator.setRepeatCount(ValueAnimator.INFINITE);

mPointsAnimator.addUpdateListener(animation -> {

for (AnimPoint point : mPointList) {

//通过属性动画不停的计算下粒子的下一个坐标

point.updatePoint(mRandom, mRadius);

}

invalidate();

});

mPointsAnimator.start();

}

@Override

protected void onDraw(final Canvas canvas) {

super.onDraw(canvas);

canvas.save();

canvas.translate(mCenterX, mCenterY);

//画运动粒子

for (AnimPoint animPoint : mPointList) {

mPointPaint.setAlpha(animPoint.getAlpha());

canvas.drawCircle(animPoint.getmX(), animPoint.getmY(),

animPoint.getRadius(), mPointPaint);

}

}

2.2、实现渐变色圆

实现圆从内到外渐变使用RadialGradient 大致实现方式如下

float[] mRadialGradientStops = {0F, 0.69F, 0.86F, 0.94F, 0.98F, 1F};

mRadialGradientColors[0] = transparentColor;

mRadialGradientColors[1] = transparentColor;

mRadialGradientColors[2] = parameter.getInsideColor();

mRadialGradientColors[3] = parameter.getOutsizeColor();

mRadialGradientColors[4] = transparentColor;

mRadialGradientColors[5] = transparentColor;

mRadialGradient = new RadialGradient(

0,

0,

mCenterX,

mRadialGradientColors,

mRadialGradientStops,

Shader.TileMode.CLAMP);

mSweptPaint.setShader(mRadialGradient);

//onDraw()绘制

canvas.drawCircle(0, 0, mCenterX, mSweptPaint);

2.3、展示背景圆的扇形区域

原本想通过DrawArc实现这个效果,但是DrawArc无法实现到圆心的区域

那么如何实现这么一个不规则的形状呢,可以使用canvas.clipPath()实现裁剪不规则的形状,所以只要得到扇形的Path就能实现,通过圆点+弧形再闭合path就能实现

/**

* 绘制扇形path

* @param r 半径

* @param startAngle 开始角度

* @param sweepAngle 扫过的角度

*/

private void getSectorClip(float r, float startAngle, float sweepAngle) {

mArcPath.reset();

mArcPath.addArc(-r, -r, r, r, startAngle, sweepAngle);

mArcPath.lineTo(0, 0);

mArcPath.close();

}

//然后再onDraw()中,裁剪画布

canvas.clipPath(mArcPath);

2.4、实现指针变色

指针是不规则形状,无法通过绘制几何图形实现,所以选用drawBitmap实现

至于如何实现bitmap指针图片的颜色变化呢,原本的方案是使用AvoidXfermode改变指定像素通道范围内的颜色,但是AvoidXfermode在API 24已经被移除,所以这方案无效

最终采用图层混合模式实现指针图片变色

通过PorterDuff.Mode.MULTIPLY模式可以实现bitmap颜色,源图像为要修改的指针颜色,目标图像为白色指针,通过获取两个图像的重叠部分实现变色 大致实现如下

/**

* 初始化指针图片的Bitmap

*/

private void initBitmap() {

float f = 130F / 656F;

mBitmapDST = BitmapFactory.decodeResource(getResources(), R.drawable.indicator);

float mBitmapDstHeight = width * f;

float mBitmapDstWidth = mBitmapDstHeight * mBitmapDST.getWidth() / mBitmapDST.getHeight();

//初始化指针的图层混合模式

mXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);

mPointerRectF = new RectF(0, 0, mBitmapDstWidth, mBitmapDstHeight);

mBitmapSRT = Bitmap.createBitmap((int) mBitmapDstWidth, (int) mBitmapDstHeight, Bitmap.Config.ARGB_8888);

mBitmapSRT.eraseColor(mIndicatorColor);

}

@Override

protected void onDraw(final Canvas canvas) {

super.onDraw(canvas);

//画指针

canvas.translate(mCenterX, mCenterY);

canvas.rotate(mCurrentAngle / 10F);

canvas.translate(-mPointerRectF.width() / 2, -mCenterY);

mPointerLayoutId = canvas.saveLayer(mPointerRectF, mBmpPaint);

mBitmapSRT.eraseColor(mIndicatorColor);

canvas.drawBitmap(mBitmapDST, null, mPointerRectF, mBmpPaint);

mBmpPaint.setXfermode(mXfermode);

canvas.drawBitmap(mBitmapSRT, null, mPointerRectF, mBmpPaint);

mBmpPaint.setXfermode(null);

canvas.restoreToCount(mPointerLayoutId);

}

2.5、实现背景圆颜色随扇形角度变化

把圆形控件拆成3600°,每一个角度对应控件一种具体颜色值,那么如何计算特定角度他具体的颜色值呢?

参考属性动画中的变色动画android.animation.ArgbEvaluator实现方式,计算两个颜色中具体某一个点的颜色值方式如下

public Object evaluate(float fraction, Object startValue, Object endValue) {

int startInt = (Integer) startValue;

float startA = ((startInt >> 24) & 0xff) / 255.0f;

float startR = ((startInt >> 16) & 0xff) / 255.0f;

float startG = ((startInt >>  8) & 0xff) / 255.0f;

float startB = ( startInt        & 0xff) / 255.0f;

int endInt = (Integer) endValue;

float endA = ((endInt >> 24) & 0xff) / 255.0f;

float endR = ((endInt >> 16) & 0xff) / 255.0f;

float endG = ((endInt >>  8) & 0xff) / 255.0f;

float endB = ( endInt        & 0xff) / 255.0f;

// convert from sRGB to linear

startR = (float) Math.pow(startR, 2.2);

startG = (float) Math.pow(startG, 2.2);

startB = (float) Math.pow(startB, 2.2);

endR = (float) Math.pow(endR, 2.2);

endG = (float) Math.pow(endG, 2.2);

endB = (float) Math.pow(endB, 2.2);

// compute the interpolated color in linear space

float a = startA + fraction * (endA - startA);

float r = startR + fraction * (endR - startR);

float g = startG + fraction * (endG - startG);

float b = startB + fraction * (endB - startB);

// convert back to sRGB in the [0…255] range

a = a * 255.0f;

r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;

g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;

b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

return Math.round(a) << 24 | Math.round® << 16 | Math.round(g) << 8 | Math.round(b);

}

控件中总共有四个颜色段,3600/4=900,所以 fraction = progressValue % 900 / 900;然后判断当前的角度位于第几段颜色值中,通过android.animation.ArgbEvaluator.evaluate(float fraction, Object startValue, Object endValue)就能回去具体的颜色值

大致实现过程如下

private ProgressParameter getProgressParameter(float progressValue) {

float fraction = progressValue % 900 / 900;

if (progressValue < 900) {

//第一个颜色段

mParameter.setInsideColor(evaluate(fraction, insideColor1, insideColor2));

mParameter.setOutsizeColor(evaluate(fraction, outsizeColor1, outsizeColor2));

mParameter.setProgressColor(evaluate(fraction, progressColor1, progressColor2));

mParameter.setPointColor(evaluate(fraction, pointColor1, pointColor2));

mParameter.setBgCircleColor(evaluate(fraction, bgCircleColor1, bgCircleColor2));

mParameter.setIndicatorColor(evaluate(fraction, indicatorColor1, indicatorColor2));

} else if (progressValue < 1800) {

尾声

面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Android核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、热修复设计、插件化框架解读、组件化框架设计、图片加载框架、网络、设计模式、设计思想与代码质量优化、程序性能优化、开发效率优化、设计模式、负载均衡、算法、数据结构、高级UI晋升、Framework内核解析、Android组件内核等。

不仅有学习文档,视频+笔记提高学习效率,还能稳固你的知识,形成良好的系统的知识体系。这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

Android进阶学习资料库

一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!

image

大厂面试真题

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《2017-2021字节跳动Android面试历年真题解析》

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

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

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

[外链图片转存中…(img-Stxl2oXl-1715677608798)]

大厂面试真题

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-j0VQNOYP-1715677608798)]

《2017-2021字节跳动Android面试历年真题解析》

[外链图片转存中…(img-Et8bjhkH-1715677608798)]

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

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

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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值