自定义控件<一>

View的工作流主要是指measure、layout、draw,即测量、布局和绘制。

  • measure确定View的测量宽/高
  • layout确定View最终的宽高和四个顶点的位置。
  • draw则将View绘制到屏幕上

案例:做一个圆形的控件

public class CircleView extends View {

    private int mColor = Color.RED;

    //设置画笔
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        initView();
    }

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        mPaint.setColor(mColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingRight = getPaddingRight();
        int paddingLeft = getPaddingLeft();
        int paddingBottom = getPaddingBottom();
        int paddingTop = getPaddingTop();

        //获取控件的宽和高
        int width = getWidth() - paddingRight - paddingLeft;
        int height = getHeight() - paddingBottom - paddingTop;

        //获取控件宽和高中最小值,然后除以2,得到控件的半径
        int radiu = Math.min(width, height) / 2;

        //绘制View
        canvas.drawCircle(width / 2 + paddingLeft, height / 2 + paddingTop, radiu, mPaint);
    }
}

上述代码是自定义的红色的圆形控件。但是有两个问题:

问题一:

<zx.demo.zx_circle_view.view.CircleView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

<zx.demo.zx_circle_view.view.CircleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

两种布局的效果一样,都是第一种的match_parent效果,那么怎样设定一个wrap_content效果?

解决方法:

想要解决这个问题,那么就需要引入MeasureSpec。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    if (widthSpecMode == MeasureSpec.AT_MOST
            && heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(500, 500);
    } else if (widthSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(500, heightSpecSize);
    } else if (heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(widthSpecSize, 500);
    }
}

问题二:
怎样添加自定义属性,比如添加控件选择控件的颜色。

解决方法:

第一步、在values目录下创建自定义属性的XML。

第二步、在View的构造方法中解析自定义属性的值并做相应处理。

public CircleView(Context context, AttributeSet attrs) {
    super(context, attrs);

    //加载自定义属性集合CircleView
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
    //解析CircleView中的circle_color属性,如果默认情况下为红色
    mColor = typedArray.getColor(R.styleable.CircleView_circle_color, Color.RED);
    //释放资源
    typedArray.recycle();
    initView();
}

第三步、在布局文件中使用自定义属性

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">


    <zx.demo.zx_circle_view.view.CircleView
        android:layout_width="300dp"
        android:layout_height="300dp"
        app:circle_color="@color/colorPrimary"
        />
</RelativeLayout>

注意:xmlns:app=”http://schemas.android.com/apk/res-auto”

app:自定义属性的前缀,可以换其他名字,但是CircleView中的自定义属性的前缀必须和它一致。

还有一种声明方式是:xmlns:app=”http://schemas.android.com/apk/res/zx.demo.zx_circle_view”,在apk/res/后面加上包名。

MeasureSpec理解:

第二个问题的解决很好理解,但是第一个问题解决方案中的MeasureSpec是什么东西呢?

MeasureSpec代表一个32位int值,其中最高的2位代表SpecMode,后面的30为代表SpecSize。

一个MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求,一个MeasureSpec由大小和模式组成,它有三种模式:

  1. UNSPECIFIED(未指定):父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量状态。
  2. EXACTLY(完全):父容器已经检测到View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式。
  3. AT_MOST(至多):父容器指定了一个可用大小即SpecSize,View的的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值