赞
踩
在实际的开发过程中,Android系统自带的控件往往满足不了我们的需求,这就需要我们具备自定义控件的能力。一般来说,我们通常会有以下几种方法来实现:
1、继承原生控件进行扩展
2、组合原生几种控件
3、继承view或者viewGroup
在学习自定义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();
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"
- />
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。