Days28 自定义Veiw(一)
来源:互联网 发布:vb有什么用 编辑:程序博客网 时间:2024/06/05 07:56
注意1:
1、关键部分:
Drawing the layout is a two pass process: a measure pass and a layout pass.
所以一个view执行OnDraw时最关键的是measure和layout。其实这很好理解的,一个view需要绘制出来,那么必须知道他要占多大的空间也就是measure,还得知道在哪里绘制,也就是把view放在哪里即layout。把这两部分掌握好也就可以随意自定义view了。至于viewGroup中如何绘制就参考官方文档,其实就是一个分发绘制,直到child是一个view自己进行绘制。
2、重写一个view一般情况下只需要重写onDraw()方法。那么什么时候需要重写onMeasure()、onLayout()、onDraw() 方法呢,这个问题只要把这几个方法的功能弄清楚就应该知道怎么做了。
①、如果需要绘制View的图像,那么需要重写onDraw()方法。(这也是最常用的重写方式。)
②、如果需要改变view的大小,那么需要重写onMeasure()方法。
③、如果需要改变View的(在父控件的)位置,那么需要重写onLayout()方法。
④、根据上面三种不同的需要你可以组合出多种重写方案。
3、按类型划分,自定义View的实现方式可分为三种:自绘控件、组合控件、以及继承控件。
注意2:
View的三种测量模式
ViewGroup会为childView指定测量模式,下面简单介绍下三种测量模式:
1、EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
2、AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
3、UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
【备注】:
上面的每一行都有一个一般,意思上述不是绝对的,对于childView的mode的设置还会和ViewGroup的测量mode有一定的关系;当然了,这是第一篇自定义ViewGroup,而且绝大部分情况都是上面的规则,所以为了通俗易懂,暂不深入讨论其他内容。
注意3:
三种类型的自定义View
(1)自绘控件:
1、概念:自绘控件的意思就是,这个View上所展现的内容全部都是自己绘制出来的。
2、自绘控件的步骤:
1、绘制View :
绘制View主要是onDraw()方法中完成。通过参数Canvas来处理,相关的绘制主要有drawRect、drawLine、drawPath等等。
Canvas绘制的常用方法:
drawColor() 填充颜色
drawLine() 绘制线
drawLines() 绘制线条
drawOval() 绘制圆
drawPaint()
drawPath() 绘制路径
drawPicture() 绘制图片
drawPoint() 绘制点
drawPoints() 绘制点
drawRGB() 填充颜色
drawRect() 绘制矩形
drawText() 绘制文本
drawTextOnPath() 在路径上绘制文本
2、刷新View :(刷新view的方法这里主要有:)
invalidate(int l,int t,int r,int b)
刷新局部,四个参数分别为左、上、右、下
invalidate()
整个view刷新。执行invalidate类的方法将会设置view为无效,最终重新调用onDraw()方法。
invalidate()是用来刷新View的,必须是在UI线程中进行工作。在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。
invalidate(Rect dirty)
刷新一个矩形区域
3、控制事件:(例如处理以下几个事件)
onSaveInstanceState() 处理屏幕切换的现场保存事件
onRestoreInstanceState() 屏幕切换的现场还原事件
onKeyDown() 处理按键事件
onMeasure() 当控件的父元素正要放置该控件时调用
(2)组合控件:
概念:
组合控件的意思就是,不需要自己去绘制视图上显示的内容,而只是用系统原生的控件就好了,但可以将几个系统原生的控件组合到一起,这样创建出的控件就被称为组合控件。
(3)继承控件
概念:
继承控件的意思就是,我们并不需要自己重头去实现一个控件,只需要去继承一个现有的控件,然后在这个控件上增加一些新的功能,就可以形成一个自定义的控件了。
案例一:
自定义ImageView控件:自定义ImageView控件继承ImageView
备注:
短句:使用短句生成构造方法 ViewConstructor
自定义ImageView控件类
public class SmartImageView extends ImageView { private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: //自定义ImageView类对象调用此方法 setImageBitmap((Bitmap) msg.obj); break; case 2: setImageResource(msg.arg1); break; } } }; public SmartImageView(Context context) { this(context, null); } public SmartImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SmartImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setSmartImageView(final String url, final int resId){ new Thread(){ @Override public void run() { super.run(); Message msg = Message.obtain(); byte[] data = OkHttpUtils.getByteArrayByUrl(url); if(data != null){ Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length); msg.what = 1; msg.obj = bitmap; }else{ msg.what = 2; msg.arg1 = resId; } handler.sendMessage(msg); } }.start(); }}
xml:
<com.sign.days28selfview01.SmartImageView android:id="@+id/ivPic" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@mipmap/ic_launcher" android:contentDescription="@string/app_name"/>
MainActivity:
public void download(View view) { //调用自定义ImageView控件类的设置图片方法 ivPic.setSmartImageView(url,R.mipmap.ic_launcher); }
案例二:自定义TextView控件
自定义TextView控件类
public class Rular extends TextView { public Rular(Context context) { this(context, null); } public Rular(Context context, AttributeSet attrs) { this(context, attrs, 0); } public Rular(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);// 设置Ruler中的文字位置。相当于属性中的android:gravity setGravity(Gravity.BOTTOM); } /** * 重写该方法,实现自己“绘制”控件的样式 * 该方法无须手动调用, 系统在绘制该控件时会自动调用该方法绘制该控件 * @param canvas */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);// Paint(画笔 Paint p = new Paint();// 设置画笔颜色 p.setColor(Color.YELLOW);// getWidth()为获取控件的宽度 int w = getWidth()/10; int top = 1; for (int i = 0;i<10;i++){// canvas的drawRect(float left,float top,float right,float bottom,Paint paint)// 参数分别为左上右下(顺时针)(左上右下离控件的x,y轴的距离),画笔对象// Canvas(画布) canvas.drawRect(i,top,w+i*w,top+w,p); }// for循环语句相当于// canvas.drawRect(0,top,getWidth(),top+w,p); }}
布局文件:
MainActivity添加控件时会自动找到控件对应的类,并自动调用其中方法对控件进行相应设置
<com.sign.days28selfview02ruler.Rular android:layout_width="40dp" android:layout_height="40dp" android:background="#0cf" android:text="0cm" /> <com.sign.days28selfview02ruler.Rular android:layout_width="40dp" android:layout_height="40dp" android:background="#acc" android:text="1cm" /> <com.sign.days28selfview02ruler.Rular android:layout_width="40dp" android:layout_height="40dp" android:background="#f0d" android:text="2cm" /> <com.sign.days28selfview02ruler.Rular android:layout_width="40dp" android:layout_height="40dp" android:background="#ae0" android:text="3cm" /> <com.sign.days28selfview02ruler.Rular android:layout_width="40dp" android:layout_height="40dp" android:background="#0d0" android:text="4cm" />
案例三:
组合控件:
将progressbar与button组合在一起,点击button按钮后,发送一个消息,在主线程中更新progressBar进度并判断是否到100%若不是继续发送消息并根据进度设置Button上文字,若是则不再发送消息并设置Button文字为下载完成
(实际上是ProgressBar在下面,Button在上面,看到的ProgressBar进度实际上是ProgressBar的边框在更新)
备注:在Android的layout样式定义中,可以使用xml文件方便的实现,有时候为了模块的复用,使用include标签可以达到此目的。
在activity_main中:
<include layout="@layout/my_layout"></include>
引入my_layout布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="70dp" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.sign.days28selfview03progressbutton.MainActivity"> <ProgressBar android:id="@+id/pbShow" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="25dp" android:max="100" android:progress="0" android:progressDrawable="@drawable/my_back"/> <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#0fc" android:textSize="16sp" android:onClick="start" android:text="start"/></LinearLayout>
此处注意:
progressbar的progressDrawable属性为一个xml文件
还不太理解??????
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <!--<clip android:drawable="@drawable/btn_back_pre" android:gravity="left"> </clip>--> <clip android:clipOrientation="horizontal" android:gravity="left" android:drawable="@drawable/pb_back"> </clip> </item></layer-list>
此处再注意:
第二个clip节点的drawable为名为pb_back的xml文件
**备注:**Android中常常使用shape来定义控件的一些显示属性
参考自:
http://blog.csdn.net/by317966834/article/details/8773518
http://blog.csdn.net/harvic880925/article/details/41850723
pd_back.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <!--设置渐变 startColor centerColor endColor 起始、中间、结束颜色 type 渐变模式 linear 线性渐变 radial 径向渐变 需要指定半径--> <gradient android:centerColor="#0cd" android:centerX="0" android:centerY="0" android:endColor="#f00" android:gradientRadius="50" android:startColor="#0f0" android:type="radial" /> <!--设置填充--> <solid android:color="@android:color/holo_blue_bright" /> <!--设置dashGap和dashWidth则边框显示为虚线,dashGap为破折线空隙,dashWidth为破折线长度 dashGap为0dp时为实线 dashWidth需要指定--> <!--width为边框的宽度--> <stroke android:width="5dp" android:color="@android:color/holo_orange_light" android:dashGap="2dp" android:dashWidth="0dp" /> <!--设置矩形的圆角半径--> <corners android:radius="5dp" /></shape>
案例四:
自定义ProgressView继承View
效果:一个逐渐转动变大的扇形
在values目录下添加attrs.xml文件
<resources> <!--attrs.xml文件 该文件是定义属性名和格式的地方, declare-styleable是给自定义控件添加自定义属性用的 需要用<declare-styleable name="ToolBar"></declare-styleable>包围所有属性。 其中name为该属性集的名字,主要用途是标识该属性集--> <declare-styleable name="MyProgressView"> <attr name="sweepStep" format="integer"/> <attr name="padding" format="integer"/> <attr name="startAngle" format="integer"/> <attr name="circleColor" format="color|reference"/> <attr name="sweepColor" format="color|reference"/> </declare-styleable></resources>
自定义ProgressView控件
public class MyProgressView extends View { private static final int DEFAULT_WIDTH = 100; private static final int DEFAULT_HEIGHT = 100; private int sweepAngle = 0;//扇形扫过的角度 private int sweepStep = 5;//扇形添加的角度 private int padding = 5;//设置扇形与圆之间距离 private int startAngle = -90;//设置扇形开始的角度 private int circleColor = Color.GREEN;//圆的背景颜色 private int sweepColor = Color.BLUE;//扇形的颜色 public MyProgressView(Context context) { this(context, null); } public MyProgressView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyProgressView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);/** * TypedArray实例是个属性的容器,context.obtainStyledAttributes()方法返回得到 * context.obtainStyledAttributes(AttributeSet set,@StyleableRes int[] attrs) * set :节点的属性集合 * attrs :包含attr节点的StyleableRes节点name标识 */ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView); if(array != null){// 分别获取MyProgress节点中的各个属性 sweepStep = array.getInteger(R.styleable.MyProgressView_sweepStep,sweepStep); startAngle = array.getInteger(R.styleable.MyProgressView_startAngle,startAngle); padding = array.getInteger(R.styleable.MyProgressView_padding,padding); sweepColor = array.getInteger(R.styleable.MyProgressView_sweepColor,sweepColor); circleColor = array.getInteger(R.styleable.MyProgressView_circleColor,circleColor);// array复用 作用:在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了 array.recycle(); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint p = new Paint();// 消除锯齿 p.setAntiAlias(true);// 画圆// 设置圆的颜色 p.setColor(circleColor);// 前两个参数分别确定圆心x、y坐标,第三个参数为圆的半径,第四个参数为画笔 canvas.drawCircle(getWidth()/2,getWidth()/2,getWidth()/2,p);// 画扇形// 重新设置画笔颜色为扇形颜色 p.setColor(sweepColor); /** * public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) oval :指定圆弧的外轮廓矩形区域。 startAngle: 圆弧起始角度,单位为度。0度为水平向左 sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。 useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。 paint: 画笔 */ canvas.drawArc(new RectF(padding,padding,getWidth()-padding,getWidth()-padding),startAngle,sweepAngle,true,p);// 每画完一个扇形累加已扫过的角度 sweepAngle += sweepStep;// 当已扫过的角度为360时,将已扫过的角度置空 sweepAngle= sweepAngle >= 360 ? 0:sweepAngle;// 刷新整个界面(每次画完一个扇形就刷新整个界面,看起来就像扇形在更新进度) invalidate(); } /** * 设置控件自身的宽和高 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); switch (widthMode){ case MeasureSpec.AT_MOST: widthSize = DEFAULT_WIDTH; heightSize = DEFAULT_HEIGHT; break; case MeasureSpec.EXACTLY: widthSize = heightSize = Math.min(widthSize,heightSize); break; case MeasureSpec.UNSPECIFIED: break; }// 这个方法决定了当前View的大小 setMeasuredDimension(widthSize,heightSize); }}
activity_main文件:
备注: xmlns:app=”http://schemas.android.com/apk/res-auto”
android中xml中有些控件的属性里面有 “app:..” ,此处的app:是什么意思?和一般的android:有什么区别?
答:这两个是声明的不同的命名空间,android的是系统的,app是自定义的。Android自定义控件的属性,在xml中使用自己自定义的attr的时候,其中有一步就是要自定义一个xml的命名空间后然后再给自定义属性赋值。
参考自:http://zhidao.baidu.com/link?url=628WYzFtOkZXw2aAau3vV8yZhBWN6OApXGFxn5IBTmy7OUUekmOeaQxpWpc9RLr-ecsYG-QzpA1Tz7ncbrEApC5RyYwRIFRvBzXOteg0aky
<!--注意:此处使用的为wrap_content,在MyProgressView中会走到case MeasureSpec.AT_MOST 设置宽和高为100,但其为100px,所以与下一个控件的大小100dp并不相等--> <com.sign.days28selfviw04progress.MyProgressView android:layout_width="wrap_content" android:layout_height="wrap_content"/> <com.sign.days28selfviw04progress.MyProgressView android:layout_width="100dp" android:layout_height="100dp" app:circleColor="#0f0" app:sweepColor="#0cc" app:padding="26" app:startAngle="0" app:sweepStep="5"/>
- Days28 自定义Veiw(一)
- 自定义View(一):初认识自定义Veiw
- 自定义Veiw的实践(一)---一个简易侧滑菜单的实现
- 笔记—自定义Veiw之Paint详解
- 开源(veiw)
- 自定义控件(一)
- 自定义控件(一)
- 自定义tableviewcell(一)
- 自定义控件(一)
- 自定义SimpleAdapter(一)
- 自定义注解(一)
- 自定义控件(一)
- 自定义View(一)
- 自定义view(一)
- 自定义控件(一)
- 自定义View(一)
- 自定义ViewGroup(一)
- 自定义View(一)
- hdu5902 GCD is Funny
- 如何学习Linux
- js对象字面量
- docker 安装gitlab-ce下ssh无法使用原因
- 初学mysql(二)-数据库之表内容单表查询
- Days28 自定义Veiw(一)
- CentOS 安装 JDK
- vector所存储对象地址在vector操作过程中发生变化
- emacs 插件列表
- 忙里偷闲 教你如何通过修改android内核实现root
- crt添加循环发送命令
- img标签在jsp页面无法显示时该怎么办
- 一行python能做什么!
- 重新理解自增表达式