首页 > 其他分享 >Andorid view 绘制原理

Andorid view 绘制原理

时间:2022-11-04 21:01:14浏览次数:94  
标签:canvas layout int draw Andorid android 绘制 view


承接上一篇:​​Android View 绘制原理​

blog新地址: 进入 ​​newbie’s home​

1.Onlayout()

对于自定义View,分为两种:

1.是自定义控件(继承View类).
2.是自定义布局容器(继承ViewGroup)。

自定义View时候注意几个点

如果是自定义控件(View),则一般需要重载两个方法,一个是onMeasure(),
用来测量控件尺寸,另一个是onDraw(),用来绘制控件的UI。

而自定义布局容器,则一般需要实现/重载三个方法,一个是onMeasure(),
也是用来测量尺寸;一个是onLayout(),用来布局子控件;
还有一个是dispatchDraw(),用来绘制UI。

相对于(控件)View自定义 布局容器(viewgroup)有点难度,这里拿Viewgroup进行举例。

1.ViewGroup类的onLayout()函数是abstract型,继承者必须实现,由于ViewGroup的定位就是一个容器,
用来盛放子控件的,所以就必须定义要以什么的方式来盛放,
比如LinearLayout就是以横向或者纵向顺序存放,而RelativeLayout则以相对位置来摆放子控件,
同样,我们的自定义ViewGroup也必须给出我们期望的布局方式,而这个定义就通过onLayout()函数来实现。

2.自定义Viewgroup 派生类

第一步,则是自定ViewGroup的派生类,继承默认的构造函数。

public class MesureViewgroup extends ViewGroup {
public MesureViewgroup(Context context) {
super(context, null);
}

public MesureViewgroup(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}

public MesureViewgroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

}

3.重载OnMesure()

这里需要注意的是,自定义ViewGroup的onMeasure()方法中,除了计算自身的尺寸外,
还需要调用measureChildren()函数来计算子控件的尺寸。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//要求子类进行
measureChildren(widthMeasureSpec, heightMeasureSpec);
}
3.  实现onLayout()方法

//abstrct class,子类必须重写
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//进行布局排列
}
1)参数changed表示view有新的尺寸或位置搜索;
2)参数l表示相对于父view的Left位置;
3)参数t表示相对于父view的Top位置;
4)参数r表示相对于父view的Right位置;
5)参数b表示相对于父view的Bottom位置。


到这一步已经完成自定义的前期工作,接下来需要设置viweproup的排列方式,这里介绍水平排列
方式加深理解。

需求:水平排列,当超过一屏自动换行到下一行进行绘制

分析:

1,需要知道自定义的ViewGroup 宽度 getMeasuredWidth()
2.需要知道子View的宽度 子view .getMeasuredWidth()

代码:

package com.acmenxd.mvp.utils.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
* Created by weichyang on 2017/5/18.
*/

public class MesureViewgroup extends ViewGroup {
public MesureViewgroup(Context context) {
super(context, null);
}

public MesureViewgroup(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}

public MesureViewgroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//要求子类进行
measureChildren(widthMeasureSpec, heightMeasureSpec);
}

//子类必须重写
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//进行布局排列

int viewGroupWidth = getMeasuredWidth(); //parent content width

int xpoint = l; //对于父view的Left位置
int yPoint = t; //对于父view的top位置

for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
if (xpoint + childWidth > viewGroupWidth) {
//进行换行
xpoint = l;
yPoint = yPoint + childHeight; //更新相对父容器的垂直位置
}
childView.layout(xpoint, yPoint, xpoint + childWidth, yPoint + childHeight); //子类的位置由子类来实现
xpoint += childWidth; // 更新相对父容器的水平位置
}


}


}
layout.xml

<com.acmenxd.mvp.utils.widget.MesureViewgroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">

<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black" />

<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black" />

<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black" />

<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
android:background="@android:color/black" />

</com.acmenxd.mvp.utils.widget.MesureViewgroup>
看到上面的布局xml ,你会说,添加了margin为什么没有体现?因为还没有对margin 进行兼容处理

下面我们来研究下如何添加margin效果

怎么研究,总不能凭空臆想,当然需要参照,这里参照水平布局的处理
/**
* Per-child layout information associated with ViewLinearLayout.
* 每个孩子与ViewLinearLayout相关的布局信息(存储子view的配置信息的内容类)
* @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
* @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
*/
public static class LayoutParams extends ViewGroup.MarginLayoutParams {


}
其实,如果要自定义ViewGroup支持子控件的layout_margin参数,则自定义的ViewGroup类必须重载
generateLayoutParams()函数,并且在该函数中返回一个ViewGroup.MarginLayoutParams派生类对象,
这样才能使用margin参数。
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MesureViewgroup.LayoutParams(getContext(), attrs);
}


// 继承自margin,支持子视图android:layout_margin属性
public static class LayoutParams extends MarginLayoutParams {


public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}


public LayoutParams(int width, int height) {
super(width, height);
}


public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}


public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
}

效果图片

Andorid view 绘制原理_子类

总结:

1.viewgroup的 onlayout() 流程,
2.参数代表的含义
3.重写 generateLayoutParams() 继承 MarginLayoutParams
MarginLayoutParams
4.onmesure()中对子类view进行mesure()----查看onmesure()分析篇: 链接

demo下载地址:​​http://pan.baidu.com/s/1c2EKQis​

Android ondraw( ) 分析

View 绘制的三个步骤,mesure,layout ,draw,前两种已经简单梳理了,下面是对draw的梳理。

/** 
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
* called. When implementing a view, implement
* {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
* If you do need to override this method, call the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/
@CallSuper
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/

// Step 1, draw the background, if needed
int saveCount;

if (!dirtyOpaque) {
drawBackground(canvas);
}

// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);

// Step 4, draw the children
dispatchDraw(canvas);

// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}

// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);

// we're done...
return;
}

/*
* Here we do the full fledged routine...
* (this is an uncommon case where speed matters less,
* this is why we repeat some of the tests that have been
* done above)
*/

boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;

float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;

// Step 2, save the canvas' layers
忽略

// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);

// Step 4, draw the children
dispatchDraw(canvas);

// Step 5, draw the fade effect and restore layers
忽略
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}ew源码,6个步骤清晰// Step 3, draw the content

if (!dirtyOpaque) onDraw(canvas);ondraw()方法是子类中进行定义

// Step 4, draw the children
dispatchDraw(canvas);

/**
* Called by draw to draw the child views. This may be overridden
* by derived classes to gain control just before its children are drawn
* (but after its own view has been drawn).
* @param canvas the canvas on which to draw the view
*/
protected void dispatchDraw(Canvas canvas) {

}

Viewgroup 中dispatchDraw() 主要做 view动画处理,子view绘制工作,欲了解自行前往Viewgroup中进行源码阅读

以上都执行完后就会进入到第六步,也是最后一步,这一步的作用是对视图的滚动条进行绘制。那么你可能会奇怪,当前的视图又不一定是ListView或者ScrollView,为什么要绘制滚动条呢?其实不管是Button也好,TextView也好,任何一个视图都是有滚动条的,只是一般情况下我们都没有让它显示出来而已。绘制滚动条的代码逻辑也比较复杂,这里就不再贴出来了,因为我们的重点是第三步过程。

通过以上流程分析,相信大家已经知道,View是不会帮我们绘制内容部分的,因此需要每个视图根据想要展示的内容来自行绘制。
如果你去观察TextView、ImageView等类的源码,你会发现它们都有重写onDraw()这个方法,并且在里面执行了相当不少的绘制逻辑。
绘制的方式主要是借助Canvas这个类,它会作为参数传入到onDraw()方法中,供给每个视图使用。
Canvas这个类的用法非常丰富,基本可以把它当成一块画布,在上面绘制任意的东西。

canvas + paint (画笔)=android 界面任何可见元素

这里作为一个梳理 关于Canvas api,需要通过百度学习。


标签:canvas,layout,int,draw,Andorid,android,绘制,view
From: https://blog.51cto.com/u_15861646/5824764

相关文章

  • Matplotlib.plot 绘制饼图
    Python代码:#导入第三方包importmatplotlibimportmatplotlib.pyplotasplt#matplotlib其实是不支持显示中文的显示中文需要额外设置#设置字体类型,宋体:SimSun......
  • Matplotlib.plot 绘制散点图
    Python代码:#导入第三方包importmatplotlibimportnumpyasnpimportmatplotlib.pyplotasplt#matplotlib其实是不支持显示中文的显示中文需要额外设置#设置......
  • Matplotlib.plot 绘制条形图
    Python代码:#导入第三方包importmatplotlibimportnumpyasnpimportmatplotlib.pyplotasplt#matplotlib其实是不支持显示中文的显示中文需要额外设置#设置......
  • 使用LabVIEW实现基于pytorch的DeepLabv3图像语义分割
     前言今天我们一起来看一下如何使用LabVIEW实现语义分割。一、什么是语义分割图像语义分割(semanticsegmentation),从字面意思上理解就是让计算机根据图像的语义来进......
  • PR曲线的绘制
    什么是PR曲线?PR曲线是由模型的查准率和查全率为坐标轴形成的曲线,查准率P为纵坐标查全率R为横坐标把正例正确分类为正例,表示为TP(truepositive),把正例错误分类为负例,表示......
  • MATLAB使用手记(一):绘制余弦信号图形
    前言记录下matlab使用过程的一些应用笔记基本余弦信号\[f(x)=A\cos(\omegat+\phi)=A\cos(2\pift+\phi)\]余弦信号基本公式如上,其中\(A\)是振幅、\(f\)是频率,\(\ome......
  • Andorid图片等比例缩放解决方案示例
    前言图片等比例缩放平时经常用到,网上也提供了很多种方式来解决。这里记录自己开发过程中用到的一种等比例缩放场景。AndroidimageViewadjustViewBounds属性设置,可以支持等......
  • Android TextView 设置内容可滚动
    前言开发中scrollBar用的最多的地方就是在内容超过显示区域后,可以手动上下左右滑动来查看解决方案比较多。方案一使用一个可滑动的组件ScroolView包裹用于在内容超过显示......
  • RecycleView根据内容自适应高度,最大高度限制
    前言recylceView日常开发经常使用到,默认高度自适应,如何增加最大高度限制如下:publicclassMeasureLinearLayoutManagerextendsLinearLayoutManager{privateintmax......
  • 使用Winform绘制仿造微信的客户
    由于工作需要,开发一个聊天工具,安卓,IOS,Web都开发好了,还需要一个客户端形式的聊天工具,要求是简介大方好用,呵呵哒,好看我不知道,但是模仿一个到时可以的,说干就干,经过几天的时......