4.自定义View

自定义View的流程?

  1. 自定义属性(在attrs.xml中定义),然后在布局文件(如:activity_main.xml)中使用
    • attrs.xml中通过declare-styleable标签定义自定义控件的名称,然后再定义一个个自定义控件的属性,代码举例:
      //在attrs.xml中定义属性
      <declare-styleable name="CustomTextView">
          <attr name="customColor1" format="color" />
          <attr name="customColor2" format="color" />
      </declare-styleable>
      
      //在自定义控件中使用我们的属性,如:CustomTextView.java
      //在三参构造函数中定义我们的属性
      public CustomTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
          //获取属性集合
          TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustommTextView);
          //获取属性集合中具体的属性
          int customColor1 = typedArray.getColor(R.styleable.CustommTextView_customColor1, getTextColors().getDefaultColor());
          int customColor2 = typedArray.getColor(R.styleable.CustommTextView_customColor2, getTextColors().getDefaultColor());
          //回收TypedArray
          typedArray.recycle();
      }
      
      //在xml布局文件中使用
      <com.tangkun.customView.CustomTextView
          android:id="@+id/tv"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="自定义控件属性举例"
          android:textSize="20sp"
          app:customColor1="@color/black"
          app:customColor2="@color/white" />
      
  2. 测量 重写onMeasure方法,只需要测量当前自定义的控件
    • 测量当前自定义控件的宽高
  3. 绘制 重写onDraw方法,绘制当前控件
    • 布局确定自定义控件在屏幕的坐标,由于我们的自定义控件一般都是嵌套在xml布局文件中使用,在xml布局文件中可以确定自定义控件的坐标,所以我们在自定义View的时候可以不用关心布局onLayout(),只需要关心绘制onDraw()
  4. 交互

自定义ViewGroup的流程?

利用流式布局FlowLayout来举例说明

  1. 自定义属性,然后在xml中使用

  2. 测量onMeasure

    • 根据测量的子View宽高,来计算父容器的宽高,然后通过setMeasureDimension()保存宽高,给自定义ViewGrouponLayout()使用,通过getMeasuredWidth()getMeasuredHeight()
    • 具体步骤:
      • 确定ViewGroup的测量模式和测量大小;测量模式分为宽和高的测量模式,测量大小分为宽和高的测量大小
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //扩展:我们可以通过下面的方式更改ViewGroup的测量模式,达到我们控件的展示效果
        //heightMode = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY)
        
      • 获取到每一个子View;
        • 实现方式:child = getChildAt(i);
        • 通过getChildCount获取到ViewGroup中子View的数量,然后根据索引得到每一个子View,通过getChildAt(i)得到具体的子View
      • 测量每个子View的宽高;
        • 通过measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec)方法;
      • 根据子View的宽高计算自定义ViewGroup的宽高
      • 保存测量的自定义ViewGroup宽和高;
        • 通过这个方法来保存:
          setMeasuredDimension(measuredWidth, measuredHeight);

    总结一:
    如果自定义ViewGroup宽和高的测量模式是MeasureSpec.EXACTLY,那么无需根据子View测量的宽高来计算自定义ViewGroup的宽高,直接使用自定义ViewGroup的宽和高的测量大小保存即可;
    如果自定义ViewGroup宽和高的测量模式是MeasureSpec.AT_MOSTMeasureSpec.UNSPECIFIED,根据子View测量的宽高来计算自定义ViewGroup的宽高,然后保存给onLayout()使用.

    总结二:
    自定义ViewGroup
    在测量时候,ViewGroup在测量时需要注意padding,而里面的子View在测量的时需要注意margin,否则会导致测量的控件大小不对;
    在布局时候,在布局子View时,仍然需要注意marginpadding的问题

  3. 布局onLayout

    • 根据计算规则确定子View的位置
    • 具体步骤:
      • 获取到每一个子View,实现方式:child = getChildAt(i);
      • 确定每一个子View的坐标;
        • 确定子View的坐标时,就会用到上面onMeasure方法中测量的子View宽和高,来确定下一个子View的坐标
      • 依次确定将每一个子View布局的位置,实现方式:child.layout(left, top, right, bottom)

      总结:
      依次将每一个子View布局在屏幕的具体坐标上;
      所谓换行,就是将子View布局的坐标更该,即可达到换行的效果

  4. 绘制 一般情况下都是不会使用绘制方法

    • onDraw正常情况下是不会调用的,而是调用dispatchDraw方法,所以要绘制的话可以重写dispatchDraw方法;
  5. 交互

总结:

  1. 自定义View中需要处理自己的padding,而由父容器处理自定义View设置的margin;
  2. ViewGroup中重写generateLayoutParams(LayoutInflater.inflat()方法中会调用到该方法)方法并返回创建的MarginLayoutParams,然后通过子View(MarginLayoutParams)child.getLayoutParams()可以获取到上下左右间距(leftMargin…).
    注意:
    ViewGrouponMeasure()测量子View需要用到measureChildWithMargins方法,而不是measureChild方法.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值