美高梅平台下载-美高梅娱乐平台登录

热门关键词: 美高梅平台下载,美高梅娱乐平台登录

View 的测量是在 onMeasure() 方法中进行,继承子V

日期:2019-09-30编辑作者:美高梅平台下载
  • 1 一个高度满屏也就是match_parent,宽度自定义的一个VIew,不是系统组合控件,需要全部自己绘制
  • 2 需要向外抛出自定义属性 文字颜色选中的文字颜色文字大小
  • 3 测量--->onMeasure()
  • 4 画出来! --->onDraw()
  • 5 onTouch事件处理
  • 6 对外接口回掉分解成若干步骤就可以开始尝试了!
美高梅娱乐平台登录,view实现会在XML布局文件中使用,有3中情况

1: android:layout_width="wrap_content"

布局文件,debug后,执行代码

美高梅娱乐平台登录 1

美高梅平台下载,图片.png

发现widthSpecMode 会对应到MeasureSpec.AT_MOST的模式,
发现widthSpecSize = 720px

所以当android:layout_width="wrap_content"时,对应MeasureSpec.AT_MOST的模式,而且系统自己计算的宽度是相当于match_parent的宽度,而我们自定义的view肯定是只要自己的宽度

自己的宽度 等于基本控件宽度+paddingLeft+paddingRight的宽度

美高梅娱乐平台登录 2

图片.png

2: android:layout_width="match_parent", 等同于wrap_content的情况

3: android:layout_width="100dp"准确值

美高梅娱乐平台登录 3

图片.png

当准确值的模式下,debug发现
发现测量模式MeasureSpec.EXACTLY
发现测量宽度widthSpecSize = 200px(布局文件100dp->200px)

所以当这种模式,自定的view的宽度,处理是直接获取自己的宽度,也可以自由点可以准确的值, 这里是取两个当中大的值,防止宽度太小导致view显示不全,这样保证了至少是view完整的大小

美高梅娱乐平台登录 4

图片.png

MeasureSpec 的三种模式

其中测量的模式分为以下三种:

  1. EXACTLY
    精确值模式,在手动指定了控件的 layout_width 或 layout_heigth 属性为具体数值的时候,或者指定为 match_parent 时,系统使用的是 EXACTLY 模式。

  2. AT_MOST
    最大值模式,在手动指定了控件的 layout_width 或 layout_heigth 属性为 wrap_content 的时候,控件大小一般随着控件的子控件或内容变化而变化,只要不超过父控件允许的最大尺寸即可。

  3. UNSPECIFIED
    表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。

View 类模式的 onMeasure() 方法只支持 EXACTLY 模式,所以如果自定义控件的时候不重写 onMeasure() 方法的话,就只能使用 EXACTLY 模式。控件可以响应你指定的具体宽高值或者是 match_parent 属性。但是如果需要 View 支持 wrap_content 属性,就必须重写 onMeasure() 方法来指定 wrap_content 模式时的大小。

概念:继承子View或者系统控件如Textview,Imageview,然后自己画点东西,实现一些动画 这种称之为纯粹的自定义View(其实说法很多自己喜欢就好)

为什么要知道测试方法,测试模式呢?

因为自定义view在一下模式下面,需要自己对应的宽度和高度,或者需要自由的定义。

绘制


View 的绘制过程是在 onDraw() 方法中,如果你去看源代码,会发现这个方法是空的,但是子类可以继承。想来也正常,每个控件都有自己的表现方式,绘制方法,自然要自己来写这部分绘制的代码。接下来我们继续使用上面的 MyView 自定义绘制部分的代码。

在 onDraw() 方法中,传进来了一个 Canvas 对象,这个对象等于一块画布,我们可以在上面作画,那现在有了画布,我们还需要一支笔,那就是 Paint 对象。

public class MyView extends View {
  //创建一个画笔对象
  private Paint mPaint;
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化画笔对象
        mPaint = new Paint();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        //设置画笔的颜色为蓝色
        mPaint.setColor(Color.BLUE);
        //使用画笔画一个矩形
        canvas.drawRect(0,0,50,50,mPaint);
        //设置画笔的颜色的黄色
        mPaint.setColor(Color.YELLOW);
        //设置画笔的字体大小为40
        mPaint.setTextSize(40);
        //使用画笔写出一行字
        canvas.drawText("我可是用笔写的", 0, 80, mPaint);
    }
}

最后的效果

美高梅娱乐平台登录 5

QQ截图20160128161413.png

这只是简单的一个例子,一般来说一个控件的绘制过程是相当复杂的,这个就要根据自己的情况来自定义了。

 if (mOnTouchMyListener != null) { mOnTouchMyListener.SidebarLetterTouchListener(现在选择的字母, 现在选择的下标, 手指是否离开了); }
View类中的方法onMeasure() 是用来测量当前view的宽度和高度的, 3种模式说明

测量方法 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
测量模式int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);

MeasureSpec.UNSPECIFIED 不确定值
MeasureSpec.AT_MOST 最大值
MeasureSpec.EXACTLY 完全准确值

以一个列子来说明?
自定义view 通过测量控制view的宽度和高度,并draw图片的一个view

public class ThumbView extends View {

    private static final String TAG = "ThumbView";
    private Bitmap thumbUp;
    private Paint mBitmapPaint;
    private int startX;
    private int startY;

    public ThumbView(Context context) {
        this(context, null);
    }

    public ThumbView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ThumbView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        thumbUp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_messages_like_selected);
        mBitmapPaint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);//如果是继承的viewgroup比如linearlayout时,可以先计算
        int widthResult = 0;
        //view根据xml中layout_width和layout_height测量出对应的宽度和高度值,
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        switch (widthSpecMode){
            case MeasureSpec.UNSPECIFIED:
                widthResult = widthSpecSize;
                break;
            case MeasureSpec.AT_MOST://wrap_content时候
                widthResult = getContentWidth();
                break;
            case MeasureSpec.EXACTLY:
                //当xml布局中是准确的值,比如200dp是,判断一下当前view的宽度和准确值,取两个中大的,这样的好处是当view的宽度本事超过准确值不会出界
                //其实可以直接使用准确值
                widthResult = Math.max(getContentWidth(), widthSpecSize);
                break;
        }

        int heightResult = 0;
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (heightSpecMode){
            case MeasureSpec.UNSPECIFIED:
                heightResult = heightSpecSize;
                break;
            case MeasureSpec.AT_MOST://wrap_content时候
                heightResult = getContentHeight();
                break;
            case MeasureSpec.EXACTLY:
                heightResult = Math.max(getContentHeight(), heightSpecSize);
                break;
        }
        setMeasuredDimension(widthResult, heightResult);
    }

    /**
     * view中大拇指图片和padding的宽度, 就是自己本身包括的内容总的宽度
     */
    private int getContentWidth(){
        float contentWidth = thumbUp.getWidth()+getPaddingLeft()+getPaddingRight();
        Log.d(TAG, "getContentWidth: contentWidth="+contentWidth);
        return (int)contentWidth;
    }

    int getContentHeight(){
        float contentHeight = thumbUp.getHeight()+getPaddingTop()+getPaddingBottom();
        Log.d(TAG, "getContentWidth: contentHeight="+contentHeight);
        return (int)contentHeight;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
//        startX = w/2 - thumbUp.getWidth()/2 - getPaddingLeft()/2 -  getPaddingRight()/2;
//        startY = h/2 - thumbUp.getHeight()/2 - getPaddingTop()/2 -  getPaddingBottom()/2;
        Log.d(TAG, "onSizeChanged: w="+w+",h="+h+",startX="+startX+",startY="+startY);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制图片
        canvas.drawBitmap(thumbUp, startX+getPaddingLeft(), startY+getPaddingTop(), mBitmapPaint);
    }

MeasureSpec

View 的测量是在 onMeasure() 方法中进行。其中 Android 设计了一个类用来进行测量 ---- MeasureSpec 类,这个类的值是一个 32 位的 int 值,高 2 位表示测量的模式,低 30 位表示测量的大小。

手机不会弄动图,2333 后期我会加一些炫酷的效果

View 的显示过程


一个 View 经过三步重点流程,最终才能显示到屏幕上。分别是:测量,布局,绘制。

其实很容易理解,一个图形要想显示在界面上,首先要进行测量决定大小。然后要进行布局,决定摆放的位置。最后就是进行绘制,用线条和图形描述出来。

如果要进行自定义 View 的学习,那么了解这些流程是必须的。

在 Android 中,View 同样要经过以上三个步骤,只是其中的布局通常是由父布局,一个 ViewGroup 来决定,我们主要先来了解一下测量以及绘制的过程。

重写 onMeasure

下面我们自定义一个 View 来试试吧,我们先新建一个空的自定义 View 。

package com.shire.myapplication;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

public class MyView extends View {
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

我们并没有修改任何东西。接下来看看 XML 文件,我们添加了一个 myView 并设置了宽高为 wrap_content 背景是绿色便于观察控件大小,那么在这个情况下的显示效果会是怎么样?

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.shire.myapplication.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f2f2" />
</LinearLayout>

美高梅娱乐平台登录 6

QQ截图20160128142829.png

如图,虽然我们设置了 wrap_content 属性,当时控件依然充满了父控件,这就是我们上面说的,View 在默认的情况下,是不支持 wrap_content 的,而且在不设置指定的宽高的情况下会把父控件的宽高传过来,所以必须要重写 onMeasure() 方法。

接下来看看应该如何重写 onMeasure() 方法 。根据源码的方案,是由 getDefaultSize() 方法来进行测量,然后将结果给 setMeasuredDimension() 所以我们主要就是自定义一个 “getDefaultSize()” 方法。我们看下具体的代码。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       setMeasuredDimension(getSize(widthMeasureSpec),getSize(heightMeasureSpec));
    }

我们重写了 onMeasure() 方法,并自定义了一个测量方法 getSize() ,接下来就是看看 getSize() 中是如何写的。

private int getSize(int  MeasureSpec) {
    //初始化一个返回值变量
    int result;
    //获得测量模式
    int specMode = View.MeasureSpec.getMode(MeasureSpec);
    //获得测量大小
    int specSize = View.MeasureSpec.getSize(MeasureSpec);
    //判断模式是否是 EXACTLY
    if (specMode == View.MeasureSpec.EXACTLY) {
        //如果模式是 EXACTLY 则直接使用specSize的测量大小
        result  = specSize;
    }else{
        //如果是其他两个模式,先设置一个默认大小值 200
        result = 200;
        //如果是 AT_MOST 也就是 wrap_content 的话,就取默认值 200 和 specSize 中小的一个为准。
        if (specMode == View.MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }
    }
    return result;
}

在上面的代码中,我们对 AT_MOST 模式时的测量方式进行了处理,接下来看看效果如何。还是同样的 XML 文件。

美高梅娱乐平台登录 7

QQ截图20160128150118.png

可以看到,现在 AT_MOST 模式已经生效了,当我们在设置 wrap_content 的时候不会再填充父布局,而是根据我们自定义的测量代码进行测量,用了 200 这个默认值。

至此,对 View 的测量算是简单的讲解完成了,其实总结一句话,如果的你自定义 View 不需要使用 wrap_content,就不用管 onMeasure() 方法。不然的话,就需要重写。

MeasureSpec 是怎么来的

关于这个 MeasureSpec 是由父布局传递给子布局的布局要求,我通过代码调试得到一些信息,我们来看一下。

在一个 1080 x 1920 的手机上,最外层使用一个 LinearLayout , width 和 height 都使用 match_parent,然后包裹了一个自定义的 View 。

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.shire.myapplication.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#f2f2" />

</LinearLayout>

此时在 MyView 的 onMeasure() 中我获取了 widthMeasureSpec 和 heightMeasureSpec ,提取的结果为:
widthMeasureSpec 的 size 为:1080
heightMeasureSpec 的 size 为:1557

这里的 1080 就是顶层 LinearLayout 充满屏幕的宽度,而 1557 就是顶层的 LinearLayout 除去状态栏高度之后充满屏幕的高度,由此可以得到 MeasureSpec 是传过来的是父布局的大小。

但是!如果你对 View 进行了自定义的大小,传过来的就是你定义的大小。比如上面的 MyView 的 layout_width 更改为 100dp ,那么获取到的结果就是:
widthMeasureSpec 的 size 为:300
heightMeasureSpec 的 size 为:1557

至于为什么 100dp 变成 300 这是 dp 转 px 的一个过程导致的,详细的可以看我另一篇文章: Android开发中dip,dpi,density,px等详解

 for (int i = 0; i < 字母数量; i++) { canvas.drawText(字母数组[i], 宽度, 字母块的高度 * i + mBaseline, mPaintTouch); }

分析 onMeasure

通过 MeasureSpec 我们可以获得测量模式以及大小,我们来看看部分源码是如何进行测量的。
我们先看 View 中的 onMeasure() 方法:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

在这里通过 getDefaultSize() 来从 MeasureSpec 中获取相应的大小以及模式,最后转换为一个 int 类型给 setMeasuredDimension() 作为参数进行最后测量的结果,我们看看这个 getDefaultSize()。

   public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

可以看到,首先通过 MeasureSpec.getMode() 和 MeasureSpec.getSize() 取出模式和大小,然后判断模式。
可以看到在 AT_MOST 或 EXACTLY 模式下都是同样的处理方式,这也说明了上面所说的,View 在默认情况下只支持 EXACTLY 模式,但是如果需要 View 支持 wrap_content 属性,也就是 AT_MOST,就必须重写 onMeasure() 方法来指定 AT_MOST 模式时的大小。

美高梅娱乐平台登录 8image.png

测量


这些都是很基础很简单的东西 我就不多说了

这里就要涉及一个简单的想法 文字每次绘制的高度怎们递增,或者说按照什么标准来递增,我们看第一个文字 它画的时候是不是 仅仅考虑了他基线的位置(不知道什么是基线的我也帮不了你)

测量这里有必要说一下widthMeasureSpec heightMeasureSpec 这两个值大家都知道测量模式把,有三种 1 UNSPECIFIED || 2 EXACTLY || 3 AT_MOST分别代表什么意思呢- -很简单那

这东西你得触摸吧,触摸有几种啊??无非就是 按下 移动 抬起来所以基础代码就是 算了 基础代码不想写了 switch case写写吧- -一点一点分析 - -你触摸上了怎么知道,你当前位置选择得是那个 字母event.getY() 可以获取你当前触摸得位置我们还知道每一个字母块的高度 所以啊- - 一个除法就出来了啊index = (event.getY() / 每一个字母块); index 就是你触摸的26个字母的哪一个

本文由美高梅平台下载发布于美高梅平台下载,转载请注明出处:View 的测量是在 onMeasure() 方法中进行,继承子V

关键词:

注解处理器,接下我们将学习使用APT

主目录见:Android高级进阶知识我们在开发的时候为了提高效率往往会选择一个基于注解的框架,但是有时使用反射通...

详细>>

任何一个包含n个节点完全二叉树(满足从根节点开

一直以来,我都很少使用也避免使用到树和图,总觉得它们神秘而又复杂,但是树在一些运算和查找中也不可避免的要...

详细>>

知乎著作权归作者所有,2017 Android暑期实习生面

一个多月没有更新博客了,一直在忙着找工作。现在已经尘埃落定,最终选择了网易。这篇文章主要总结一下自己秋...

详细>>

实现方法,真正调用的构造

参考:Android中定时器的3种实现方法 在Android开发中,定时器一般有以下3种实现方法: 总结 Handler类的主要作用是发送...

详细>>