自定义view之基本流程


基本步骤

  1. 继承View类或其子类 
  2. 复写view中的一些函数

 3.为自定义View类增加属性(两种方式)

 4.绘制控件(导入布局)

 5.响应用户事件

 6.定义回调函数(根据自己需求来选择)

重要方法

  • onDraw()

  view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的绘制。对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的(但必须实现dispatchDraw()函数,告诉子view绘制自己)。

  • onLayout()

  主要是为viewGroup类型布局子视图用的,在View中这个函数为空函数。

  • onMeasure()

  用于计算视图大小(即长和宽)的方式,并通过setMeasuredDimension(width, height)保存计算结果。

  • onTouchEvent

  定义触屏事件来响应用户操作。 

流程图

我们调用requestLayout()的时候,会触发measure 和 layout 过程,调用invalidate,会执行 draw 过程。

自定义控件的三种方式

1. 继承已有的控件

  当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。

2. 继承一个布局文件

  一般用于自定义组合控件,在构造函数中通过inflater和addView()方法加载自定义控件的布局文件形成图形界面(不需要onDraw方法)。

3.继承view

  通过onDraw方法来绘制出组件界面。

自定义属性

在res/values/ 下建立一个attrs.xml 来声明自定义view的属性

可以定义的属性有:

<declare-styleable name = "名称"> 
//参考某一资源ID (name可以随便命名)
<attr name = "background" format = "reference" /> 
//颜色值 
<attr name = "textColor" format = "color" /> 
//布尔值
<attr name = "focusable" format = "boolean" /> 
//尺寸值 
<attr name = "layout_width" format = "dimension" /> 
//浮点值 
<attr name = "fromAlpha" format = "float" /> 
//整型值 
<attr name = "frameDuration" format="integer" /> 
//字符串 
<attr name = "text" format = "string" /> 
//百分数 
<attr name = "pivotX" format = "fraction" /> 
​
//枚举值 
<attr name="orientation"> 
<enum name="horizontal" value="0" /> 
<enum name="vertical" value="1" /> 
</attr> 
​
//位或运算 
<attr name="windowSoftInputMode"> 
<flag name = "stateUnspecified" value = "0" /> 
<flag name = "stateUnchanged" value = "1" /> 
</attr> 
​
//多类型
<attr name = "background" format = "reference|color" /> 
</declare-styleable>

自定义随手指移动的小球

实现上面的效果我们大致需要分成这几步

  • 在res/values/ 下建立一个attrs.xml 来声明自定义view的属性
  • 一个继承View并复写部分函数的自定义view的类
  • 一个展示自定义view 的容器界面

自定义属性attrs.xml:

<resources>
    <declare-styleable name="MyView">
        <attr name="firstColor" format="color"/>
        <attr name="secondColor" format="color"/>
    </declare-styleable>
</resources>

MyView要重写onDraw和ontouch方法,重写onTouch方法表示view自己就会处理触摸事件

获取属性值的写法R.styleable.MyView_secondColor

package com.example.simaxiaochen.makeview;
​
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
​
/**
 *  @Description:   TODO
 *  @author         天意 
 *  @version        V1.0   
 *  @Date           2017/3/3   10:05
 */
​
public class MyView extends View {
​
    private Paint mPaint;
    private float mCurrentX = 100;
    private float mCurrentY = 100;
    private int mFirstColor;
    private int mSecondColor;
​
    public MyView(Context context) {
        super(context);
        init();
    }
​
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
​
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        mFirstColor = typedArray.getColor(R.styleable.MyView_firstColor, Color.BLUE);
        mSecondColor = typedArray.getColor(R.styleable.MyView_secondColor, Color.CYAN);
        typedArray.recycle();
    }
​
    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
​
    private void init() {
        mPaint = new Paint();
    }
​
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(mFirstColor);
        canvas.drawCircle(mCurrentX, mCurrentY, 50, mPaint);
        mPaint.setColor(mSecondColor);
        canvas.drawText("移动的圆形", mCurrentX, mCurrentY + 120, mPaint);
​
    }
​
    @Override
    public boolean onTouchEvent(MotionEvent event) {
​
        mCurrentX = event.getX();
        mCurrentY = event.getY();
        invalidate();
        return true;
    }
}

MainActivity的布局文件,主要注意命名空间xmlns:MyView="http://schemas.android.com/apk/res-auto"

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:MyView="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.simaxiaochen.makeview.MainActivity">
​
<com.example.simaxiaochen.makeview.MyView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    MyView:firstColor="@color/colorAccent"
    MyView:secondColor="@color/colorPrimary"/>
</RelativeLayout>

参考资料

https://github.com/HaoTianYi/ForAndroidInterview/blob/master/android/Android%20%E8%87%AA%E5%AE%9A%E4%B9%89View%E5%85%A5%E9%97%A8.md


It's time for you to begin thinking out of the box