Android View和ViewGroup的关系

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

Android View和ViewGroup的关系

是不是经常在自定义View中看到View 和ViewGroup,如果你对自定义View很熟悉,则可以跳过这篇文章了, 如果你只是初步了解,这篇文章或许能让你理解的更透传一些。

关系

View 是单个的组件,类似于文字组件、图片组件、输入框组件;
ViewGroup 是一个容器,类似于线性布局、相对布局等
所以在自定义View的时候,取决于你想自定义一组组件,还是单个组件。

但是要注意的是,ViewGroup的父类是View,所以它也具有View的特征,可以实现View的方法,但它主要用来充当View的容器。

自定义View

其实我们平时的用的组件,都算是自定义View,只是系统帮我们实现好了而已。来看看TextView的实现方法,它也是继承了VIew


public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {

...

}

复制代码

我们来模仿者实现一个自适应的自定义VIew文字组件吧。

在撸代码前,先来了解一下基本概念和View中常用的方法。

我们想一下,我们自己在涂鸦之前,是不是要知道在哪里画、画多大、画成什么样,然后带着这些理解下面的方法。

  • onLayout: 决定组件的位置。当此视图应为其每个子项分配大小和位置时,从布局调用。带有子级的派生类应该覆盖此方法并在其每个子级上调用布局。

  • onMeasure:决定组件的大小。测量视图及其内容以确定测量的宽度和测量的高度。此方法measure(int, int)由子类调用并应由子类覆盖,以提供对其内容的准确有效测量。

  • onDraw: 组件画成什么样。 执行此操作以进行绘图。

准备工作都齐全了,现在就准备画图。

  1. 自定义属性定义。在value文件夹下创建attrs.xml文件

<resources>

<declare-styleable name="MyTextView">

<attr name="myText" format="string" />

<attr name="myTextSize" format="dimension" />

</declare-styleable>

</resources>

复制代码
  1. 在layout文件中写入自定义的View

<com.demo.demo.MyTextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

app:myText="测试"

app:myTextSize="30sp"

android:background="@color/purple_200"

/>

复制代码
  1. 自定义View的类

public class MyTextView extends AppCompatTextView {

private String text; // 文字

private int textSize; // 文字大小

private Rect bound;

private Paint paint;

public MyTextView(Context context) {

super(context);

}

public MyTextView(Context context, AttributeSet attrs) {

super(context, attrs);

//获取自定义属性的值

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);

text = a.getString(R.styleable.MyTextView_myText);

textSize = (int) a.getDimension(R.styleable.MyTextView_myTextSize, 100);

a.recycle();

paint = new Paint();

paint.setTextSize(textSize);

//获得绘制文本的宽和高

bound = new Rect();

Log.d("text:", "text:" + text);

paint.getTextBounds(text, 0, text.length(), bound);

}

public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//指定控件的宽高,需要测量

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

int heightMode = MeasureSpec.getMode(heightMeasureSpec);

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSize = MeasureSpec.getSize(heightMeasureSpec);

if (widthMode == MeasureSpec.AT_MOST) {

//在布局中指定了wrap_content

Rect bounds = new Rect();

paint.getTextBounds(text, 0, text.length(), bounds);

widthSize = bounds.width() + getPaddingLeft() + getPaddingRight();

} else if (widthMode == MeasureSpec.EXACTLY) {

} else if (widthMode == MeasureSpec.UNSPECIFIED) {//尽可能的大,很少能用到

}

if (heightMode == MeasureSpec.AT_MOST) {

//在布局中指定了wrap_content

Rect bounds = new Rect();

paint.getTextBounds(text, 0, text.length(), bounds);

heightSize = bounds.height() + getPaddingTop() + getPaddingBottom();

} else if (heightMode == MeasureSpec.EXACTLY) {//在布局中指定了确定的值 比如:100dp / match_parent

} else if (heightMode == MeasureSpec.UNSPECIFIED) {//尽可能的大,很少能用到

}

setMeasuredDimension(widthSize, heightSize);

}

@Override

protected void onDraw(Canvas canvas) {

Paint.FontMetrics fontMetrics = paint.getFontMetrics();

int dy = (int) ((fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent);

//得到基线(BaseLine)

int baseLine = getHeight() / 2 + dy;

int x = getPaddingLeft();

canvas.drawText(text, x, baseLine, paint);

}

}

复制代码

Guess you like

Origin juejin.im/post/7032489712497459208