当前位置:   article > 正文

自定义view

自定义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

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

view_my.xml 

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content"
  4. android:orientation="horizontal">
  5. <Button
  6. android:id="@+id/add_button"
  7. android:layout_width="0dp"
  8. android:layout_height="match_parent"
  9. android:layout_weight="2"
  10. android:text="+"
  11. android:textStyle="bold"
  12. android:gravity="center"
  13. android:textSize="14sp"
  14. android:textColor="#ffffff"
  15. />
  16. <EditText
  17. android:id="@+id/content_edit"
  18. android:layout_width="0dp"
  19. android:layout_height="match_parent"
  20. android:layout_weight="1"
  21. android:enabled="false"
  22. android:background="@null"
  23. android:layout_marginLeft="10dp"
  24. android:text="0"
  25. android:gravity="center"/>
  26. <Button
  27. android:id="@+id/del_button"
  28. android:layout_width="0dp"
  29. android:layout_height="match_parent"
  30. android:layout_weight="2"
  31. android:text="-"
  32. android:textStyle="bold"
  33. android:gravity="center"
  34. android:textSize="14sp"
  35. android:layout_marginLeft="10dp"
  36. android:textColor="#ffffff"
  37. />
  38. </LinearLayout>

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

  1. LayoutInflater.from(context).inflate(R.layout.view_my,this);
  2. mAddBtn=findViewById(R.id.add_button);
  3. mAddBtn.setOnClickListener(this);
  4. mDelBtn=findViewById(R.id.del_button);
  5. mDelBtn.setOnClickListener(this);
  6. mContentET=findViewById(R.id.content_edit);
  7. TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attrs, R.styleable.MyLayout);
  8. int bgColor=obtainStyledAttributes.getColor(R.styleable.MyLayout_bgButton,0xffff00);
  9. int fontSize=obtainStyledAttributes.getDimensionPixelSize(R.styleable.MyLayout_sizeEdit,12);
  10. mAddBtn.setBackgroundColor(bgColor);
  11. mDelBtn.setBackgroundColor(bgColor);
  12. mContentET.setTextSize(fontSize);
  13. obtainStyledAttributes.recycle();

四、继承view或者viewGroup

1、view和viewGroup的区别

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

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

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

自定义属性 style.xml

  1. <declare-styleable name="MyView">
  2. <!-- 被选中圆点的颜色 -->
  3. <attr name="fillColor" format="color" />
  4. <!-- 未选中圆点的颜色 -->
  5. <attr name="strokeColor" format="color" />
  6. <!-- 圆点的大小 -->
  7. <attr name="radius" format="dimension" />
  8. <!-- 圆点间间距的大小 -->
  9. <attr name="circleInterval" format="dimension" />
  10. </declare-styleable>

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

  1. public class MyView extends View {
  2. private float radius=2;//圆点半径
  3. private Paint mFillPaint;//当前圆点的画笔
  4. private Paint mStokePaint;//其他圆点的画笔
  5. private int fillColor;//当前圆点的颜色值
  6. private int stokeColor;//其他圆点的颜色值
  7. private float circleMargin;//圆点直接的间隔
  8. private int count;//圆点总个数
  9. private int current=0;//当前圆点下标值
  10. public MyView(Context context) {
  11. super(context);
  12. }
  13. public MyView(Context context, @Nullable AttributeSet attrs) {
  14. super(context, attrs);
  15. init(context,attrs);
  16. }
  17. public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  18. super(context, attrs, defStyleAttr);
  19. init(context,attrs);
  20. }
  21. public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  22. super(context, attrs, defStyleAttr, defStyleRes);
  23. init(context,attrs);
  24. }
  25. private void init(Context context, @Nullable AttributeSet attrs){
  26. TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyView);
  27. fillColor=typedArray.getColor(R.styleable.MyView_fillColor,0x000000);
  28. stokeColor=typedArray.getColor(R.styleable.MyView_strokeColor,0xffffff);
  29. circleMargin=typedArray.getDimension(R.styleable.MyView_circleInterval,2);
  30. radius=typedArray.getDimension(R.styleable.MyView_radius,2);
  31. mFillPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
  32. mFillPaint.setColor(fillColor);
  33. mFillPaint.setStyle(Paint.Style.FILL);
  34. mStokePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
  35. mStokePaint.setColor(stokeColor);
  36. mStokePaint.setStyle(Paint.Style.STROKE);
  37. typedArray.recycle();
  38. }
  39. public void setCount(int count){
  40. this.count=count;
  41. requestLayout();
  42. }
  43. public void setCurrent(int current){
  44. this.current=current;
  45. invalidate();
  46. }
  47. @Override
  48. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  49. int widthMode=MeasureSpec.getMode(widthMeasureSpec);
  50. int widthSize=MeasureSpec.getSize(widthMeasureSpec);
  51. int heightMode=MeasureSpec.getMode(heightMeasureSpec);
  52. int heightSize=MeasureSpec.getSize(heightMeasureSpec);
  53. int width=0;
  54. int height=0;
  55. //计算宽度
  56. if (widthMode==MeasureSpec.EXACTLY){//确切模式
  57. width=widthSize;
  58. }else {//wrap_content
  59. float result=getPaddingLeft()+getPaddingRight()+count*2*radius+(count-1)*circleMargin;
  60. width=Math.min((int)result,widthSize);
  61. }
  62. //计算高度
  63. if (heightMode==MeasureSpec.EXACTLY){//确切模式
  64. height=heightSize;
  65. }else {//wrap_content
  66. float result=getPaddingTop()+getPaddingBottom()+2*radius;
  67. height=Math.min((int)result,heightSize);
  68. }
  69. setMeasuredDimension(width,height);
  70. }
  71. @Override
  72. protected void onDraw(Canvas canvas) {
  73. super.onDraw(canvas);
  74. int with=getMeasuredWidth();
  75. float result=getPaddingLeft()+getPaddingRight()+count*2*radius+(count-1)*circleMargin;
  76. if (with>result) {
  77. float startX = (canvas.getWidth() - result) / 2;
  78. if (startX > 0) {
  79. canvas.translate(startX, 0);
  80. }
  81. }
  82. for (int i=0;i<count;i++){
  83. if (i==current){//画当前点
  84. float cx=(radius+getPaddingLeft()+(2*radius+circleMargin)*i);
  85. canvas.drawCircle(cx,getPaddingTop()+radius,radius,mFillPaint);
  86. }else {
  87. float cx=(radius+getPaddingLeft()+(2*radius+circleMargin)*i);
  88. canvas.drawCircle(cx,getPaddingTop()+radius,radius,mStokePaint);
  89. }
  90. }
  91. }
  92. }

使用自定义view

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

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号