1首先创建项目;
2首先要明白自己想要一个什么功能的view,个人觉得这点很重要。好多人都是都是一味的模仿但是不知道自己想实现什么总是别人实现什么就跟着做什么,其实我开始也是这么做的学了半天不知道自己想要什么样的View。
3定义属性;
在layout目录下创建attrs.xml文件,里边包含自己想要定义的属性,
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="textTitle" format="string"/>
<attr name="textSize" format="dimension"/>
<attr name="textColor" format="color"/>
</declare-styleable>
</resources>
这个不知道大家是不是纯手打我一直都是手打的如果有什么好的方法不防大家分享一下,因为我也是初学者。
简单介绍一下name就是自己定义的属性,format就是他的取值类型,
有十种:
reference 参考某资源id;
color:颜色值;
boolean:布尔值;
dimension:尺寸值;
float:浮点值;
integer:整形值;
string:字符串;
fraction:百分数;
eaum:枚举值;
flag:位或运算;
4 自定义控件:
自定义一个类继承View
public class MyView extends View {
}
添加三个构造方法;
public MyView(Context context) {
this(context, null);
// TODO Auto-generated constructor
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
// TODO Auto-generated constructor
}
一个参数的构造方法是:在java代码创建视图的时候被调用,如果是从xml填充的视图,就不会调用这个
两个参数的构造方法是:在xml创建但是没有指定style的时候被调用
三个参数的构造方法是:自己要实现的构造方法。
我们用this.让所有的构造方法都指向了三个参数的构造方法,前两个构造方法也要添加不然会出错的。
我们的自己写的构造方法具体代码如下:
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray array=context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyView, defStyle, 0);
int n=array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr=array.getIndex(i);
switch (attr) {
case R.styleable.MyView_textColor:
textColor =array.getColor(attr, Color.BLACK);
break;
case R.styleable.MyView_textSize:
textSize=array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
case R.styleable.MyView_textTitle:
textTitle =array.getString(attr);
break;
default:
break;
}
}
array.recycle();//在TypedArray后调用recycle主要是为了缓存
paint=new Paint();
paint.setTextSize(textSize);
rect=new Rect();
paint.getTextBounds(textTitle, 0, textTitle.length(), rect);
setOnClickListener(this);
}
最后给控件添加了监听
重写ondraw()方法,和onMeasure()方法。
@Override
protected void onDraw(Canvas canvas) {
paint.setColor(Color.BLUE);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
paint.setColor(textColor);
canvas.drawText(textTitle, getWidth()/2-rect.width()/2, getHeight()/2+rect.height()/2, paint);
}
在onDraw()方法中首先是给paint设置颜色这个颜色是后边的正方形颜色,用canvas调用drawRect方法绘制正方形,再给paint设置颜色这是给文字设置颜色。
重点说一下drawText()方法,第一个参数是要写的文字的内容,第二个参数和第三个参数是文字从哪开始写的横坐标和纵坐标,第四个参数是所用的画笔paint。
注意:文字的起始位置是在左下角,左下角,左下角,所以getWidth()/2是自定义控件的宽度的一半,而rect.width()/2是文字所在的矩形的宽度的一半如果文字居中的话就应该让getWidth()/2-rect.width()/2。
y轴的坐标向下是正方向所以要用控件的一半加上文字的高度的一半才会是在竖直方向居中的,这个一定要搞懂不然很难理解。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width=0;
int height=0;
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int heightMode =MeasureSpec.getMode(heightMeasureSpec);
int heightsize =MeasureSpec.getSize(heightMeasureSpec);
if (widthMode==MeasureSpec.EXACTLY) {
width=widthSize;
}else {
paint.setTextSize(textSize);
paint.getTextBounds(textTitle, 0, textTitle.length(), rect);
float textWidth=rect.width();
int desired=(int) (textWidth+getPaddingLeft()+getPaddingRight());
width=desired;
}
if (heightMode==MeasureSpec.EXACTLY) {
height=heightsize;
}else {
paint .setTextSize(textSize);
paint.getTextBounds(textTitle, 0, textTitle.length(), rect);
float textHeight=rect.height();
int desired=(int) (textHeight+getPaddingBottom()+getPaddingTop());
height=desired;
}
setMeasuredDimension(width, height);
}
引用 鸿洋大神的博客内容:
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:
重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
加监听重写onclick方法:
@Override
public void onClick(View v) {
textTitle= getText();
invalidate();
//postInvalidate();
}
private String getText() {
Random reRandom=new Random();
StringBuffer sb=new StringBuffer();
for (int i = 0; i < 4; i++) {
int a=reRandom.nextInt(10);
sb.append(a);
}
return sb.toString();
}
在onclick方法中调用了getText方法,产生一个随机的四位数字,在onclick中调用invalidate()方法更新界面此方法用在主线程,如果在工作线程中可以调用postInvalidate(),更新界面。
最后在布局文件中添加控件:
这里有个简单的方法添加控件,就是直接拖拽,在palette这个框中最下边的选项custom&library View点击一下,再点击刷新就能看见自己所写的控件了,直接拖拽就行了。
添加上自己所写的属性,不然会报空指针的。
<com.example.day1302.MyView
android:id="@+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:textTitle="2222"
app:textSize="60sp"
app:textColor="#f00f00"
android:padding="20dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="15dp"
android:layout_marginTop="92dp" />
本文详细介绍了如何从零开始自定义Android控件,包括创建项目、定义属性、实现自定义视图类以及添加监听和绘制逻辑。通过实例演示了如何在布局文件中引用并配置自定义控件。
1万+

被折叠的 条评论
为什么被折叠?



