android 自定义View

一、view的测量

1.测量的模式:

EXACTLY :精确值模式,当把空间的layout_width属性或者layout_height的属性设置为具体的数值时,或者指定为match_parent时候,系统就是使用的EXACTLY 模式;

AT_MOST: 最大值模式,当空间的layout_width属性或者layout_height属性指定为wrap_content时候,控件大小跟着控件的内容变化而变化,此时控件的尺寸只要不超过父控件允许的最大尺寸即可。

UNSPECIFIED: 这个属性比较奇怪,不指定其测量大小模式,view想多大就多大,通常情况下只有在绘制自定义view的时候才会使用

2.简单演示如何进行view的测量

1.自定义一个类继承自View

publicclassoneextendsView{publicone(Contextcontext){super(context);}publicone(Contextcontext,@NullableAttributeSetattrs){super(context,attrs);}publicone(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);}//重写onMeasure方法来进行测量@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));}//测量高度privateintmeasureHeight(intheightMeasureSpec){//第一步,从MeasureSpec对象中提取出具体的测量模式和大小intspecMode=MeasureSpec.getMode(heightMeasureSpec);intspecSize=MeasureSpec.getSize(heightMeasureSpec);intresult=0;//检测是否是精确值模式if(specMode==MeasureSpec.EXACTLY){result=specSize;}else{//如果不是就自己设定result=200;if(specMode==MeasureSpec.AT_MOST){result=Math.min(result,specSize);}}returnresult;}privateintmeasureWidth(intwidthMeasureSpec){intspecMode=MeasureSpec.getMode(widthMeasureSpec);intspecSize=MeasureSpec.getSize(widthMeasureSpec);intresult=0;if(specMode==MeasureSpec.EXACTLY){result=specSize;}else{result=200;if(specMode==MeasureSpec.AT_MOST){result=Math.min(result,specSize);}}returnresult;}}

2.新建一个xml文件来作为自定义view的载体

3.在main活动的xml文件下添加自定义的控件

主要是进行测量,测量的主要步骤就是重写onMeasure方法,在代码中有很详细的注释

二、view的绘制

简单来说就是继承view之后再重写onDraw方法,并在Canvas对象上来绘制需要的图形

以绘制一个闪光的文字作为例子

1.新建一个类及其xml文件

publicclassMyTextViewextendsTextView{privateintmViewWidth;privatePaintmPaint;privateLinearGradientmLinearGradient;privateMatrixmGradientMatrix;privateintmTranslate;publicMyTextView(Contextcontext){super(context);}publicMyTextView(Contextcontext,@NullableAttributeSetattrs){super(context,attrs);}@OverrideprotectedvoidonSizeChanged(intw,inth,intoldw,intoldh){super.onSizeChanged(w,h,oldw,oldh);mPaint=newPaint();if(mViewWidth==0){mViewWidth=getMeasuredWidth();if(mViewWidth>0){mPaint=getPaint();/**

                * LinearGradient构造方法中的参数int[] color:

                * 第一个元素:发光字体闪过后所显示的字体颜色,这里给定与第三个元素一样

                * 第二个元素:字体发光的颜色

                * 第三个元素:原字体显示的颜色

                *

                * mViewWidth:设置发光的宽度

                * */mLinearGradient=newLinearGradient(0,0,mViewWidth,0,newint[]{0x22ffffff,0xffffffff,0x22ffffff},null,Shader.TileMode.CLAMP);mPaint.setShader(mLinearGradient);//创建矩形mGradientMatrix=newMatrix();}}}@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);if(mGradientMatrix!=null){mTranslate+=mViewWidth/5;if(mTranslate>mViewWidth*2){mTranslate=-mViewWidth;}mGradientMatrix.setTranslate(mTranslate,0);mLinearGradient.setLocalMatrix(mGradientMatrix);//控制闪过的时间postInvalidateDelayed(80);}}}

2.将自定义控件添加到主活动的xml文件下面

三、组合自定义控件

1.先建一个类,因为是组合控件,使其继承自RelativeLayout

2建一个xml文件

android 自定义View_第1张图片

3.设置按钮按下和松开时候不同的效果

(1)设置左右两边按钮的自定义背景(按下时)

左边

右边

(2)设置不触碰时左右两边按钮的自定义背景

左边

>

右边

(3)设置一个选择器,实现按下和松开展示不同的背景

左边

右边

4.把自定义控件添加到主活动里边去

5.在建的那个类中填写代码

packagecom.example.customize_learning.customview;importandroid.content.Context;importandroid.os.Build;importandroid.util.AttributeSet;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.widget.EditText;importandroid.widget.RelativeLayout;importandroid.widget.TextView;importandroidx.annotation.RequiresApi;importcom.example.customize_learning.R;publicclassInputNumberViewextendsRelativeLayout{privateintmCurrentNumber=0;privateViewaddBtn;privateEditTextvalueEdt;privateViewminBtn;privateOnNumberChangemOnNumberChange=null;publicInputNumberView(Contextcontext){this(context,null);}publicInputNumberView(Contextcontext,AttributeSetattrs){this(context,attrs,0);}publicInputNumberView(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);//绑定布局initView(context);//处理事件setEvent();}privatevoidsetEvent(){//实现值的增加addBtn.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){mCurrentNumber++;updateValue();}});//实现值的减少minBtn.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){mCurrentNumber--;updateValue();}});}//数据的更新privatevoidupdateValue(){valueEdt.setText(String.valueOf(mCurrentNumber));}privatevoidinitView(Contextcontext){//添加布局Viewview=LayoutInflater.from(context).inflate(R.layout.input_value_layout,this,false);addView(view);addBtn=this.findViewById(R.id.plus_btn);valueEdt=this.findViewById(R.id.values_edt);minBtn=this.findViewById(R.id.minus_btn);}publicintgetNumber(){returnmCurrentNumber;}publicvoidsetNumber(intmCurrentNumber){this.mCurrentNumber=mCurrentNumber;this.updateValue();}//暴露接口publicvoidSetOnNumberChange(OnNumberChangeonNumberChange){this.mOnNumberChange=onNumberChange;}publicinterfaceOnNumberChange{voidonNumberChange(intvalue);}}

里边代码很详尽,包含了暴露接口、处理事件的过程

6.在主活动java文件下处理事件

publicclassMainActivityextendsAppCompatActivityimplementsInputNumberView.OnNumberChange{publicstaticfinalStringTAG="MainActivity";@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);InputNumberViewinputNumberView=this.findViewById(R.id.input_number);inputNumberView.SetOnNumberChange(this);}//调用接口处理事件@OverridepublicvoidonNumberChange(intvalue){Log.d(TAG,"current value is == > "+value);}}

四、获取自定义view的属性

1.在values文件下面新建一个attrs的文件

Integer,比如说行数,TextView的maxLine,就是Integer类型

enum,枚举类型,比如说gravity,left,top,bottom,center,right这些是枚举类型

boolean,布尔类型,比如说layout_alignParentRight

dimension,尺寸比如说size,margin_left这些,单位为px,dp,sp这些

color,这个大家都清楚了,颜色嘛,比如说background,比如说textColor

flags,标记,比如说我们学习activity声明周期时的configChanges

float,浮点数,也就是小数,比如说,透明度alpha

fraction,百分数,比如说动画的开始位置,fromDx

refrence,引用,比如说background,src,有同学可能有疑问了,background可以是color又可以是refrence,怎么整呢? 其实是可以多个的哈,比如说:name=“switch_time” format=“integer|float”,可以是Integer类型,或者float类型

string,这个最简单了,比如说text

2.获取属性

publicInputNumberView(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);//获取相关属性initAttrs(context,attrs);//绑定布局initView(context);//处理事件setEvent();}//获取相关属性privatevoidinitAttrs(Contextcontext,AttributeSetattrs){typedArray=context.obtainStyledAttributes(attrs,R.styleable.InputNumberView);mMax=typedArray.getInt(R.styleable.InputNumberView_max,0);mMin=typedArray.getInt(R.styleable.InputNumberView_min,0);mStep=typedArray.getInt(R.styleable.InputNumberView_step,0);mValue=typedArray.getInt(R.styleable.InputNumberView_valueSize,0);mDisable=typedArray.getBoolean(R.styleable.InputNumberView_disable,false);typedArray.getResourceId(R.styleable.InputNumberView_btnColor,-1);Log.d(TAG,"mMax === >: "+mMax);Log.d(TAG,"mMin === >: "+mMin);Log.d(TAG,"mStep === >: "+mStep);Log.d(TAG,"mValue === >: "+mValue);Log.d(TAG,"mDisable === >: "+mDisable);typedArray.recycle();}

3.在引入该自定义控件的xml文件下使用属性

五、自定义viewgroup

1.首先创建一个类继承自viewgroup,实现方法

publicclassFlowLayoutextendsViewGroup{publicFlowLayout(Contextcontext){this(context,null);}publicFlowLayout(Contextcontext,AttributeSetattrs){this(context,attrs,0);}publicFlowLayout(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);}@OverrideprotectedvoidonLayout(booleanchanged,intl,intt,intr,intb){}}

2.获取相关属性

在values文件夹下面新建一个attrs文件

在该类下面获取相关属性

publicclassFlowLayoutextendsViewGroup{publicstaticfinalintDEFAULT_LINE=3;publicstaticfinalintDEFAULT_HORZONTAL_MARGIN=5;publicstaticfinalintDEFAULT_VERTIAL_MARGIN=5;publicstaticfinalintDEFAULT_TEXT_MAX_LENGTH=20;publicstaticfinalintDEFAULT_BORDER_RADIUS=20;privateTypedArraytypedArray;privateintanInt;privatefloathorizontalMargin;privatefloatverticalMargin;privateintmMaxLine;privateinttextcolor;privateintborderradius;privateListmList=null;publicFlowLayout(Contextcontext){this(context,null);}publicFlowLayout(Contextcontext,AttributeSetattrs){this(context,attrs,0);}publicFlowLayout(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);//获取属性typedArray=context.obtainStyledAttributes(attrs,R.styleable.Flowlayout);//maxLineanInt=typedArray.getInt(R.styleable.Flowlayout_InputNumberView_max,DEFAULT_LINE);//itemHorizontalMarginhorizontalMargin=typedArray.getDimension(R.styleable.FloeLayout_itemHorizontalMargin,DEFAULT_HORZONTAL_MARGIN);//itemVerticalMarginverticalMargin=typedArray.getDimension(R.styleable.FloeLayout_itemHorizontalMargin,DEFAULT_VERTIAL_MARGIN);//textMaxLinemMaxLine=typedArray.getInt(R.styleable.FloeLayout_maxLine,DEFAULT_TEXT_MAX_LENGTH);//textColortextcolor=typedArray.getColor(R.styleable.Flowlayout_textColor,getResources().getColor(R.color.black));//borderColortypedArray.getColor(R.styleable.Flowlayout_borderColor,getResources().getColor(R.color.black));//borderRadiusborderradius=typedArray.getInt(R.styleable.Flowlayout_borderRadius,DEFAULT_BORDER_RADIUS);typedArray.recycle();}publicTypedArraygetTypedArray(){returntypedArray;}publicvoidsetTypedArray(TypedArraytypedArray){this.typedArray=typedArray;}publicintgetAnInt(){returnanInt;}publicvoidsetAnInt(intanInt){this.anInt=anInt;}publicfloatgetHorizontalMargin(){returnhorizontalMargin;}publicvoidsetHorizontalMargin(floathorizontalMargin){this.horizontalMargin=horizontalMargin;}publicfloatgetVerticalMargin(){returnverticalMargin;}publicvoidsetVerticalMargin(floatverticalMargin){this.verticalMargin=verticalMargin;}publicintgetmMaxLine(){returnmMaxLine;}publicvoidsetmMaxLine(intmMaxLine){this.mMaxLine=mMaxLine;}publicintgetTextcolor(){returntextcolor;}publicvoidsetTextcolor(inttextcolor){this.textcolor=textcolor;}publicintgetBorderradius(){returnborderradius;}publicvoidsetBorderradius(intborderradius){this.borderradius=borderradius;}@OverrideprotectedvoidonLayout(booleanchanged,intl,intt,intr,intb){}}

把子view添加进来

有两种方法,第一种是直接在xml布局中进行添加,第二种就是想listview一样使用适配器来进行添加

我们使用第二种

publicvoidsettextList(Listdata){this.mList.clear();this.mList.addAll(data);setUpText();}privatevoidsetUpText(){//先清空原来的内容removeAllViews();//添加子view进来for(Strings:mList){TextViewtextView=newTextView(getContext());textView.setText(s);//设置相关属性addView(textView);}}

在主活动中进行添加

publicclassTrueActiivtyextendsAppCompatActivity{privateListdata;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_true_actiivty);FlowLayoutflowLayout=findViewById(R.id.flow_layout);data=newArrayList<>();data.add("键盘");data.add("显示器");data.add("鼠标");data.add("iPad");data.add("air pot");data.add("女装");data.add("男鞋");data.add("男装");flowLayout.settextList(data);}}

六、自定义view绘制几何图形

1.直接在java文件中进行操作

importandroidx.appcompat.app.AppCompatActivity;importandroid.content.Context;importandroid.graphics.Canvas;importandroid.graphics.Color;importandroid.graphics.Paint;importandroid.graphics.Path;importandroid.os.Bundle;importandroid.view.View;publicclassMainActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(newTestView(this));}privateclassTestViewextendsView{publicTestView(Contextcontext){super(context);}@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);//设置一个背景图片canvas.drawColor(Color.YELLOW);//设置画笔Paintpaint=newPaint();paint.setStrokeWidth(30);paint.setStyle(Paint.Style.STROKE);//去掉锯齿paint.setAntiAlias(true);//绘制一个矩形canvas.drawRect(100,100,500,500,paint);//再绘制一个矩形paint.setStyle(Paint.Style.FILL);canvas.drawRect(600,100,1000,500,paint);//更改颜色paint.setColor(Color.BLUE);//画一个圆canvas.drawCircle(550,700,300,paint);//再画一个圆paint.setColor(Color.WHITE);canvas.drawCircle(510,720,100,paint);//画一个三角形paint.setColor(Color.RED);//定义一个路径Pathpath=newPath();path.moveTo(200,1000);path.lineTo(900,1000);path.lineTo(5500,1100);path.close();canvas.drawPath(path,paint);//绘制文字paint.setTextSize(100);paint.setColor(Color.BLUE);canvas.drawText("hello",300,1200,paint);}}}

定义一个内部类,继承自view,覆写onDraw()方法,利用画笔来绘制图形

2.运行效果:

android 自定义View_第2张图片

七、MVC小实例及自定义view小实例

1.布局文件下添加控件

2.设置一个类继承自view

packagecom.example.customviewactivity;importandroid.content.Context;importandroid.graphics.Canvas;importandroid.graphics.Color;importandroid.graphics.Paint;importandroid.util.AttributeSet;importandroid.view.View;importandroidx.annotation.Nullable;publicclassTestViewextendsView{intx,y;publicTestView(Contextcontext,@NullableAttributeSetattrs){super(context,attrs);}protectedvoidsetXY(int_x,int_y){x=_x;y=_y;}@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);canvas.drawColor(Color.CYAN);Paintpaint=newPaint();paint.setAntiAlias(true);paint.setColor(Color.BLACK);canvas.drawCircle(x,y,40,paint);paint.setColor(Color.WHITE);canvas.drawCircle(x-8,y-8,8,paint);}}

重写onDraw()方法,进行绘制

3.主活动java文件下编辑代码

packagecom.example.customviewactivity;importandroidx.appcompat.app.AppCompatActivity;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;importandroid.widget.EditText;publicclassMainActivity2extendsAppCompatActivityimplementsView.OnClickListener{EditTextedit_x,edit_y;Buttonbtn;TestViewtestView;intx1=150,y1=50;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main2);edit_x=findViewById(R.id.editText1);edit_y=findViewById(R.id.editText2);btn=findViewById(R.id.button1);testView=findViewById(R.id.testView);testView.setXY(x1,y1);btn.setOnClickListener(this);}@OverridepublicvoidonClick(Viewv){x1=Integer.parseInt(edit_x.getText().toString());y1=Integer.parseInt(edit_y.getText().toString());testView.setXY(x1,y1);//每次给到一个新的xy值,必须进行刷新testView.invalidate();}}

4.运行效果

android 自定义View_第3张图片

实现效果:小球跟随输入的坐标位置移动

5.MVC思想:

主活动的xml文件就是 VIEW 层

主活动java文件 就是 Contral 层

TestView 就是 Model



作者:刂彐
链接:https://www.jianshu.com/p/8b7ea3a45f77
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(Android,gitee)