一、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文件
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;privateList
把子view添加进来
有两种方法,第一种是直接在xml布局中进行添加,第二种就是想listview一样使用适配器来进行添加
我们使用第二种
publicvoidsettextList(List
在主活动中进行添加
publicclassTrueActiivtyextendsAppCompatActivity{privateList
六、自定义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.运行效果:
七、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.运行效果
实现效果:小球跟随输入的坐标位置移动
5.MVC思想:
主活动的xml文件就是 VIEW 层
主活动java文件 就是 Contral 层
TestView 就是 Model
作者:刂彐
链接:https://www.jianshu.com/p/8b7ea3a45f77
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。