2024年Android最全项目实战 - 仿 bilibili 刷新按钮的实现,2024年最新Android岗大厂面试官常问的那些问题

资源分享

  • 最新大厂面试专题

这个题库内容是比较多的,除了一些流行的热门技术面试题,如Kotlin,数据库,Java虚拟机面试题,数组,Framework ,混合跨平台开发,等

  • 对应导图的Android高级工程师进阶系统学习视频
    最近热门的,NDK,热修复,MVVM,源码等一系列系统学习视频都有!

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

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

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

mPaint.setColor(borderColor);
mPaint.setStrokeWidth(borderWidth);
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), borderRadius, borderRadius, mPaint);

// 2、画字
mPaint.setTextSize(textSize);
mPaint.setColor(textColor);
mPaint.setStyle(Paint.Style.FILL);
float measureText = mPaint.measureText(text);
float measureAndIcon = measureText + space4TextAndIcon + iconSize;
float textStartX = getWidth() / 2 - measureAndIcon / 2;
float textBaseY = getHeight() / 2 + (Math.abs(mPaint.ascent()) - mPaint.descent()) / 2;
canvas.drawText(text, textStartX, textBaseY, mPaint);

// 3、画刷新图标
float iconStartX = textStartX + measureText + space4TextAndIcon;
canvas.drawBitmap(iconBitmap, iconStartX, getHeight() / 2 - iconSize / 2, mPaint);
}

先来看看效果:

我给该控件设置了宽为200dp,高为100dp。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到效果还不错,但还是有一点点问题的,下面就分别说说这3部分是怎么画的,及存在的小问题。

1)画圆角矩形

其实画圆角矩形很简单,设置好画笔的样式、颜色、线粗,再调用canvas的drawRoundRect()方法即可实现。

  • 因为我们要画的圆角矩形只需要画线,所以画笔的样式便设置为Paint.Style.STROKE。
  • canvas的drawRoundRect()方法中,第一个参数是绘制范围,这里就直接按该控件的大小来设置即可。第二、三个参数是x轴和y轴的圆角半径,第三个参数是画笔(要画东西当然需要画笔~)。

但你有没有发现,此时的 线粗为0(borderWidth=0),矩形线怎么还有?这是因为画笔的样式为Paint.Style.STROKE,当线粗为0时,还要画出1px的线,因为对画笔来说,最小的线粗就是1px。所以,上面的代码需要做如下改动:

// 1、画圆角矩形
if (borderWidth > 0) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(borderColor);
mPaint.setStrokeWidth(borderWidth);
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), borderRadius, borderRadius, mPaint);
}

2)画字

画字的一般步骤是设置文字大小、文字颜色、画笔样式,绘制起点。其中后2个最为重要。

  • 画笔样式对画出的字是有影响的,当画笔样式为Paint.Style.STROKE时,画出来的字是镂空的(不信你可以试试),我们需要的是实心的字,所以需要修改画笔的样式为Paint.Style.FILL。
  • 在安卓中,文字的绘制跟其它绘制是不同的,例如,圆角矩形和旋转图标的绘制起点是左上角,而文字则是按文字左下字为起点,也就是按基线(Baseline)来绘制,故需要得到基线起点的坐标。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如上图中,现在要获得的就是文字左下角的点,这要怎么求呢?

先说x,一般需要让文字居中显示(跟文字的对齐方式也有关系,这里以默认的左对齐为例),所以计算公式一般为: x = 控件宽度/2 - 文字长度/2。但我们这个控件有点不同,它还需要考虑到旋转图标的位置问题,所以x应该这么求: x = 控件宽度/2 - (文字长度+空隙+旋转图标宽度)/2

// 得到文字长度
float measureText = mPaint.measureText(text);
// 得到 文字长度+空隙+旋转图标宽度
float measureAndIcon = measureText + space4TextAndIcon + iconSize;
// 得到文字绘制起点
float textStartX = getWidth() / 2 - measureAndIcon / 2;

再说y,如图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果直接用控件的高度的一半作为文字绘制的基线,那么绘制出来的文字肯定偏上,这是因为Ascent的高度比Descent的高度要高的多,我们在计算Baseline时,需要在Ascent中减去Descent的高度得到两者高度差,再让控件中心y坐标加上(下降)这个高度差的一半。故:

float textBaseY = getHeight() / 2 + (Math.abs(mPaint.ascent()) - mPaint.descent()) / 2;

3)画刷新图标

最后就是画刷新图标了,它是以左上角为起点的,通过canvas的drawBitmap()方法进行绘制即可。

但是,有一点需要注意,iconSize是我自己定的一个大小,并不是图标的实际大小,所以在往后做旋转动画时获取到的旋转中心会有误差,将导致图标旋转时不是按中心进行旋转。所以,这里需要对图标大小进行调整:

public class LQRRefreshButton extends View {

public LQRRefreshButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

// icon
iconBitmap = BitmapFactory.decodeResource(getResources(), iconSrc);
iconBitmap = zoomImg(iconBitmap, iconSize, iconSize);
}

public Bitmap zoomImg(Bitmap bm, float newWidth, float newHeight) {
// 获得图片的宽高
int width = bm.getWidth();
int height = bm.getHeight();
// 计算缩放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
// 得到新的图片
Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
return newbm;
}

}

3、动画

现在,要实现旋转图标的旋转功能了。原理就是在canvas绘制图标时,将canvas进行旋转,canvas旋转着绘制图标也很简单,只需要4步:

canvas.save();
canvas.rotate(degress, centerX, centerY);
canvas.drawBitmap(iconBitmap, iconStartX, getHeight() / 2 - iconSize / 2, mPaint);
canvas.restore();

接下来要做的,就是计算出旋转中心,旋转角度,并不停止的去调用onDraw()编制图标,可以使用ValueAnimator或ObjectAnimator实现这个功能,这里选用ObjectAnimator。实现如下:

public class LQRRefreshButton extends View {

private float degress = 0;
private ObjectAnimator mAnimator;

public LQRRefreshButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 旋转动画
mAnimator = ObjectAnimator.ofObject(this, “degress”, new FloatEvaluator(), 360, 0);
mAnimator.setDuration(2000);
mAnimator.setRepeatMode(ObjectAnimator.RESTART);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 3、画刷新图标
float iconStartX = textStartX + measureText + space4TextAndIcon;
canvas.save();
float centerX = iconStartX + iconSize / 2;
int centerY = getHeight() / 2;
canvas.rotate(degress, centerX, centerY);
canvas.drawBitmap(iconBitmap, iconStartX, getHeight() / 2 - iconSize / 2, mPaint);
canvas.restore();
}

public void start() {
mAnimator.start();
}

public void stop() {
mAnimator.cancel();
setDegress(0);
}

public float getDegress() {
return degress;
}

public void setDegress(float degress) {
this.degress = degress;
invalidate();
}
}

使用ObjectAnimator可以对任意属性值进行修改,所以需要在该控件中声明一个旋转角度变量(degress),并编写getter和setter方法,还需要在setter方法中调用invalidate(),这样才能在角度值发生变换时,让控件回调onDraw()进行图标的旋转绘制。ObjectAnimator的使用也不复杂,这里就不详细介绍了。来看下动画效果吧:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4、自定义属性

一个自定义控件,是不能把属性值写死在控件里的,所以我们需要自定义属性,从外界获取这些属性值。

1)属性文件编写

在attrs.xml中编写如下代码:

<?xml version="1.0" encoding="utf-8"?>

2)属性值获取

在控件的第三个构造函数中获取这些属性值:

public class LQRRefreshButton extends View {

public LQRRefreshButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

Android进阶资料

以下的资料是近年来,我和一些朋友面试收集整理了很多大厂的面试真题和资料,还有来自如阿里、小米、爱奇艺等一线大厂的大牛整理的架构进阶资料。希望可以帮助到大家。

Android进阶核心笔记

百万年薪必刷面试题

最全Android进阶学习视频

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

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

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

最全Android进阶学习视频

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值