自定义view

在实际的开发过程中,Android系统自带的控件往往满足不了我们的需求,这就需要我们具备自定义控件的能力。一般来说,我们通常会有以下几种方法来实现:

1、继承原生控件进行扩展

2、组合原生几种控件

3、继承view或者viewGroup

一、view的绘制流程

在学习自定义view的时候,首先我们要搞清楚的就是Android view的绘制大致流程,了解相关函数的作用

View的绘制基本由measure()、layout()、draw()这个三个函数完成

第一步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。

第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。

第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;

二、继承原生控件

这是最简单的实现方式,只需要继承Android原生控件,根据逻辑重写相关方法,就能得到一个全新的自定义控件

比如我们要实现一个圆角的imageview,只需要继承imageview,重写onDraw方法,在绘制的时候增加圆角处理即可。

三、组合原生控件

这种方式就是将Android原生的几个控件组合在一起,成为一个全新的控件

比如我们将两个button和一个editText组合在一起,通过button控制editText里面的数字增减。

1、首先自定义属性和自定义布局

style.xml

<declare-styleable name="MyLayout">
    <!-- button的背景色 -->
    <attr name="bgButton" format="color"></attr>
    <!-- 输入框的文字字号 -->
    <attr name="sizeEdit" format="dimension"></attr>
</declare-styleable>

view_my.xml 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <Button
        android:id="@+id/add_button"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:text="+"
        android:textStyle="bold"
        android:gravity="center"
        android:textSize="14sp"
        android:textColor="#ffffff"
        />

    <EditText
        android:id="@+id/content_edit"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:enabled="false"
        android:background="@null"
        android:layout_marginLeft="10dp"
        android:text="0"
        android:gravity="center"/>


    <Button
        android:id="@+id/del_button"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:text="-"
        android:textStyle="bold"
        android:gravity="center"
        android:textSize="14sp"
        android:layout_marginLeft="10dp"
        android:textColor="#ffffff"
        />

</LinearLayout>

2、加载自定义布局和定义的属性

LayoutInflater.from(context).inflate(R.layout.view_my,this);
mAddBtn=findViewById(R.id.add_button);
mAddBtn.setOnClickListener(this);
mDelBtn=findViewById(R.id.del_button);
mDelBtn.setOnClickListener(this);
mContentET=findViewById(R.id.content_edit);
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attrs, R.styleable.MyLayout);
int bgColor=obtainStyledAttributes.getColor(R.styleable.MyLayout_bgButton,0xffff00);
int fontSize=obtainStyledAttributes.getDimensionPixelSize(R.styleable.MyLayout_sizeEdit,12);
mAddBtn.setBackgroundColor(bgColor);
mDelBtn.setBackgroundColor(bgColor);
mContentET.setTextSize(fontSize);
obtainStyledAttributes.recycle();

四、继承view或者viewGroup

1、view和viewGroup的区别

可能有些人还不知道他们之间的区别吧,我们平时开发不会直接使用到这两个类,但是我们的UI界面其实都是由这两个类构成的,只是使用的都是他们派生出来的,比如TextView继承的view,RelativeLayout继承的viewGroup

viewGroup是容纳view的容器,本身也是继承的view,所以viewgroup里面容纳了很多的child也就是view,所以在viewGroup绘制的时候也就是绘制他的child

2、我们来实现一个简单的自定义view,实现一个圆点指示器的效果

自定义属性 style.xml

<declare-styleable name="MyView">
    <!-- 被选中圆点的颜色 -->
    <attr name="fillColor" format="color" />
    <!-- 未选中圆点的颜色 -->
    <attr name="strokeColor" format="color" />
    <!-- 圆点的大小 -->
    <attr name="radius" format="dimension" />
    <!-- 圆点间间距的大小 -->
    <attr name="circleInterval" format="dimension" />
</declare-styleable>

 继承view,重写onMeasure、onDraw方法,计算宽高和绘制相关效果

public class MyView extends View {

    private float radius=2;//圆点半径
    private Paint mFillPaint;//当前圆点的画笔
    private Paint mStokePaint;//其他圆点的画笔
    private int fillColor;//当前圆点的颜色值
    private int stokeColor;//其他圆点的颜色值
    private float circleMargin;//圆点直接的间隔
    private int count;//圆点总个数
    private int current=0;//当前圆点下标值

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context,attrs);
    }

    private void init(Context context, @Nullable AttributeSet attrs){
        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyView);
        fillColor=typedArray.getColor(R.styleable.MyView_fillColor,0x000000);
        stokeColor=typedArray.getColor(R.styleable.MyView_strokeColor,0xffffff);
        circleMargin=typedArray.getDimension(R.styleable.MyView_circleInterval,2);
        radius=typedArray.getDimension(R.styleable.MyView_radius,2);
        mFillPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mFillPaint.setColor(fillColor);
        mFillPaint.setStyle(Paint.Style.FILL);
        mStokePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mStokePaint.setColor(stokeColor);
        mStokePaint.setStyle(Paint.Style.STROKE);
        typedArray.recycle();
    }

    public void setCount(int count){
        this.count=count;
        requestLayout();
    }

    public void setCurrent(int current){
        this.current=current;
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);
        int width=0;
        int height=0;
        //计算宽度
        if (widthMode==MeasureSpec.EXACTLY){//确切模式
            width=widthSize;
        }else {//wrap_content
            float result=getPaddingLeft()+getPaddingRight()+count*2*radius+(count-1)*circleMargin;
            width=Math.min((int)result,widthSize);
        }

        //计算高度
        if (heightMode==MeasureSpec.EXACTLY){//确切模式
            height=heightSize;
        }else {//wrap_content
            float result=getPaddingTop()+getPaddingBottom()+2*radius;
            height=Math.min((int)result,heightSize);
        }
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int with=getMeasuredWidth();
        float result=getPaddingLeft()+getPaddingRight()+count*2*radius+(count-1)*circleMargin;
        if (with>result) {
            float startX = (canvas.getWidth() - result) / 2;
            if (startX > 0) {
                canvas.translate(startX, 0);
            }
        }
        for (int i=0;i<count;i++){
            if (i==current){//画当前点
                float cx=(radius+getPaddingLeft()+(2*radius+circleMargin)*i);
                canvas.drawCircle(cx,getPaddingTop()+radius,radius,mFillPaint);
            }else {
                float cx=(radius+getPaddingLeft()+(2*radius+circleMargin)*i);
                canvas.drawCircle(cx,getPaddingTop()+radius,radius,mStokePaint);
            }
        }
    }
}

使用自定义view

<com.test.find.MyView
    android:id="@+id/myView2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/myView"
    android:layout_marginTop="10dp"
    app:fillColor="#ff0000"
    app:strokeColor="#000000"
    app:radius="4dp"
    app:circleInterval="5dp"
    />

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值