android实现自定义view
来源:互联网 发布:java锁的种类 编辑:程序博客网 时间:2024/05/06 04:11
很多朋友看到这简直就是噩梦,因为自定Vewi的实现可以很复杂,也可以很简单,我们就从简单的开始,毕竟自己也是初学者
先在value文件家下面新建attrs.xml文件,内容如下
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="MyView"> <attr name="textSize" format="integer"></attr> <attr name="textColor" format="reference|color"></attr> </declare-styleable></resources>
在上面我们自定义了属性,一个size,格式为整形,一Color为引用型,以及color,具体就不解释了,大家可以百度,然后我们新建class对象,继承View,具体如下
package com.example.fang.myapplication;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by fang on 2017/11/10. * 自定义View的实现 */public class MyView extends View { String TAG = "main"; int textColor; int textSize; Paint paint; RectF rectf; public MyView(Context context) { this(context, null); } public MyView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray type = getResources().obtainAttributes(attrs, R.styleable.MyView); int count = type.getIndexCount(); for (int i=0;i<count;i++){ int index=type.getIndex(i); switch(index){ case R.styleable.MyView_textColor:{ textColor=type.getColor(index,-1); break; } case R.styleable.MyView_textSize:{ textSize=type.getInteger(index,-1); } } } intiialData(); } private void intiialData() { paint=new Paint(); paint.setColor(textColor); paint.setTextSize(textSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int min= (int) Math.min(getWidth(),getHeight()); canvas.drawCircle(min/2,min/2,min/2,paint); }}
然后在将我们自定义的View加到主布局中,内容如下
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:user="http://schemas.android.com/apk/res/com.example.fang.myapplication" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.fang.myapplication.MainActivity"> <com.example.fang.myapplication.MyView android:layout_width="200dp" android:layout_height="200dp" user:textColor="@color/colorAccent" user:textSize="20" /></LinearLayout>
需要注意的是上面的命名空间 xmlns:user=”http://schemas.android.com/apk/res/com.example.fang.myapplication”,这里res后面的是我的程序的完整路径名,你们在书写时,也要写上自己的完整路径名,why?
because我们自定义了属性,我们当然要通过命名空间加载,这里我们的命名空间为user,就像android都是android一样,之后我们运行
那么现在有两个问题,上面我们myView的布局宽高使用的是实际值
ps:android:layout_width=”200dp , android:layout_height=”200dp” 那么有时我们考虑到程序的兼容性,会将值设为wrap_content,那么我们将值设为wrap_content看会出现什么情况,下面是运行结果:
我去变大了,注意这里我们在绘制园的时候使用的是getWidth和getheight,刚才在测试发现,如果我们设实际值,园就会按实际值显示,而用wrap_content就会显示为整个屏幕的大小,具体原因为什么,这就涉及到底层的实现了,我们就不讨论了(其实是没那水平),大家可以百度一下,下面我们来看看应对方法,我们改下代码
package com.example.fang.myapplication;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by fang on 2017/11/10. * 自定义View的实现 */public class MyView extends View { String TAG = "main"; int textColor; int textSize; Paint paint; RectF rectf; int width; int height;//View的宽高 int radio=100;//园的半径 //我们将圆的半径设为100,如果使用Wrap_content就为200大小 public MyView(Context context) { this(context, null); } public MyView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray type = getResources().obtainAttributes(attrs, R.styleable.MyView); int count = type.getIndexCount(); for (int i = 0; i < count; i++) { int index = type.getIndex(i); switch (index) { case R.styleable.MyView_textColor: { textColor = type.getColor(index, -1); break; } case R.styleable.MyView_textSize: { textSize = type.getInteger(index, -1); } } } intiialData(); } private void intiialData() { paint = new Paint(); paint.setColor(textColor); paint.setTextSize(textSize); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int min = (int) Math.min(width, height); Log.d(TAG, "width="+width); canvas.drawCircle(min / 2, min / 2, min / 2, paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int mode1 = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); int mode2= MeasureSpec.getMode(heightMeasureSpec); int size1 =MeasureSpec.getSize(heightMeasureSpec); if(mode1==MeasureSpec.EXACTLY){//这个代表的是match_parent width=size;//当我们在定义布局是采用的是match_parent时,我们就采用采用实际大小 }else{ width=radio*2; } if(mode2==MeasureSpec.EXACTLY){//这个代表的是match_parent height=size;//当我们在定义布局是采用的是match_parent时,我们就采用采用实际大小 }else{ height=radio*2; } setMeasuredDimension(width,height); }}
我们重写了Onmeasure方法,在这里调用了MeasureSpec对Mode和Size进行判断,如果是EXACTLY(代表额是match_parent),反之对应的是 MeasureSpec.AT_MOST(对应wrap_content)和MeasureSpec.UNSPECIFIED(对应于不对View有任何限制),在里面我们定义了园的半径大小,如果是内容填充,就是Radio*2了;这个就不要问我为什么了吧,半径=半径*半径(开玩笑的),继续看啊
另外,还有个问题就是这鬼东西不居中,我们首先想到是通过gravity进行控制
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:user="http://schemas.android.com/apk/res/com.example.fang.myapplication" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.fang.myapplication.MainActivity"> <com.example.fang.myapplication.MyView android:layout_width="wrap_content" android:layout_gravity="center_horizontal" android:layout_height="wrap_content" user:textColor="@color/colorAccent" user:textSize="20" /></LinearLayout>
这样进行控制也可以,其实我们也可以通过修改代码View的代码进行实现,我们来实现一下吧
修改主布局代码
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:user="http://schemas.android.com/apk/res/com.example.fang.myapplication" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.fang.myapplication.MainActivity"> <com.example.fang.myapplication.MyView android:layout_width="match_parent" android:layout_height="wrap_content" user:textColor="@color/colorAccent" user:textSize="20" /></LinearLayout>
在上面我们取消了通过gravity的设置,然后我们把width改为了match_parent,然后我们看下View的实现代码
package com.example.fang.myapplication;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.RectF;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by fang on 2017/11/10. * 自定义View的实现 */public class MyView extends View { String TAG = "main"; int textColor; int textSize; Paint paint; RectF rectf; int width; int height;//View的宽高 int radio=100;//园的半径 //我们将圆的半径设为100,如果使用Wrap_content就为200大小 Path path; public MyView(Context context) { this(context, null); } public MyView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray type = getResources().obtainAttributes(attrs, R.styleable.MyView); int count = type.getIndexCount(); for (int i = 0; i < count; i++) { int index = type.getIndex(i); switch (index) { case R.styleable.MyView_textColor: { textColor = type.getColor(index, -1); break; } case R.styleable.MyView_textSize: { textSize = type.getInteger(index, -1); } } } intiialData(); } private void intiialData() { paint = new Paint(); paint.setColor(textColor); paint.setTextSize(textSize); path=new Path(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); invaliallayout(); int x= (int) ((rectf.left+rectf.right)/2); int min = (int) Math.min(width, height); Log.d(TAG, "min="+min+"width="+x); canvas.drawCircle(x, min / 2, min / 2, paint); } private void invaliallayout() { int size=radio*2; int left=(width-size)/2; int top=(height-size)/2; int right=left+size; int bottom=top+size; rectf=new RectF(left,top,right,bottom); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int mode1 = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); int mode2= MeasureSpec.getMode(heightMeasureSpec); int size1 =MeasureSpec.getSize(heightMeasureSpec); // MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED if(mode1==MeasureSpec.EXACTLY){//这个代表的是match_parent width=size;//当我们在定义布局是采用的是match_parent时,我们就采用采用实际大小 }else{ width=radio*2; } if(mode2==MeasureSpec.EXACTLY){//这个代表的是match_parent height=size;//当我们在定义布局是采用的是match_parent时,我们就采用采用实际大小 }else{ height=radio*2; } setMeasuredDimension(width,height); }}
我们发现代码增加了一个实现RectF的方法,由于我们已经通过width和height拿到了view布局中的宽高,然后通过运算将Rectf放在View的中间, int size=radio*2;
int left=(width-size)/2;
int top=(height-size)/2;
int right=left+size;
int bottom=top+size;
是实现的关键,通过实际(View的大小-直径)/2,我们可以求出左边的边距,同样上面也一样,right和bottom通过加法就能实现,当然你如果想通过 int left=(width-size)/2;这种方法实现也可以,多计算了一次,没这必要,既然Rectf居中了,我们在draw中通过计算得出Rectf的中心坐标,将值传给绘制圆的x坐标,就能实现圆的居中了。
有图有真相:
到此为止!!!
- Android自定义View实现
- Android 实现自定义View
- Android实现自定义View
- Android实现自定义View
- android实现自定义view
- Android自定义View的实现
- Android自定义View的实现
- android自定义view的实现
- Android 自定义View-实现手写板
- Android 自定义view的实现
- android自定义View实现步骤
- android自定义View的实现
- Android 自定义View实现SlideSwitchView
- Android实现自定义倒计时view
- Android:自定义view实现动画
- Android自定义View实现ColorProgressbar
- Android自定义view实现扇形
- android 自定义view实现时钟
- python之r.raise_for_status()
- Linux的文件权限与目录配置
- SVN提交文件失败:系统找不到指定路径
- imax6q项目:使用psplash制作开机动画
- 剑指Offer每日一刷 -2017年11月11日
- android实现自定义view
- 反射机制(Reflection)详细解析(1)
- c语言实现求第n个斐波那契数。(递归和非递归)
- 《HTML之路径问题》
- Android学习之隐藏虚拟按键的实现
- Git 安装和本地服务器构建
- Collection接口的使用
- java 文件上传到ftp
- 欢迎使用CSDN-markdown编辑器