=================================================================
- measure()
作用:测量View的宽高
相关函数:measure(),setMeasuredDimension(),onMeasure()
- layout()
作用:计算当前View和子View的位置
相关函数:layout(),onLayout(),setFrame()
- draw()
作用:绘制View图形
相关函数:draw(),onDraw()
用一种很形象的说法来说明View的绘制:
meaure决定用多大的盘子装菜,layout决定怎么摆盘好看,而draw就是服务员,把上面的东西给客人摆好。
============================================================
借用网络图片说明这个问题,对我们绘制UI理解有帮助:
- Android坐标系:
- View坐标系:
======================================================================
我们用实例说话,继承TextView控件:
public class MyTextView extends TextView {
private Paint mPaint;
private String mText;
private int mTextColor;
private int mTextSize;
public MyTextView(Context context) {
this(context, null);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyView, 0, 0);
mText = a.getString(R.styleable.MyView_iText);
mTextColor = a.getColor(R.styleable.MyView_iTextColor, Color.BLUE);
mTextSize = (int) a.getDimension(R.styleable.MyView_iTextSize, 16);
a.recycle(); //回收
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mTextColor);
mPaint.setTextSize(mTextSize);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//1. 获取 自定义 View 的宽度,高度 的模式
int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if (MeasureSpec.AT_MOST == heigthMode) {
Rect bounds = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
height = bounds.height() + getPaddingBottom() + getPaddingTop();
}
if (MeasureSpec.AT_MOST == widthMode) {
Rect bounds = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
width = bounds.width() + getPaddingLeft() + getPaddingRight();
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算基线
Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
int x = getPaddingLeft();
// x: 开始的位置 y:基线
canvas.drawText(mText, x, baseLine, mPaint);
}
}
我们重写了OnMeasure和OnDraw两个方法,前面也说到了,这两个方法是View绘制过程中用到的,可以按需重写,关于OnMeasure和OnLayout具体的内容这里就暂不详述了。
说到自定义View我们不得不说View的属性,上面代码里面TypedArray就是我们自己给View定义的属性,在attrs中设置:
<?xml version="1.0" encoding="utf-8"?>================================================================
public class MyLinearLayout extends LinearLayout implements View.OnClickListener {
private Button mBtnBack;
private ClickCallBack mClickCallBack;
public MyLinearLayout(Context context) {
this(context, null);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_my, this);
mBtnBack = view.findViewById(R.id.btn_left);
mBtnBack.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_left) {
if (mClickCallBack != null) {
mClickCallBack.onBack();
}
}
}
public void setClickCallBack(ClickCallBack clickCallBack) {
mClickCallBack = clickCallBack;
}
public interface ClickCallBack {
void onBack();
}
}
我们可以在layout_my布局文件里面将我们的控件组合完成,然后inflate进来即可。
在MyLinearLayout里面,我们还可以跟普通类一样,定义一些外部调用的接口,比如这里的ClickCallBack。
==================================================================
这里我们直接继承View,从最基层开始:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
总结
学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!
最后如何才能让我们在面试中对答如流呢?
答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:
这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。
最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!
这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
(img-9fQnNcyA-1712218834996)]
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。
最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!
这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下