您的位置:首页 > 移动开发 > Android开发

Android自定义控件

2015-12-31 23:23 579 查看
前言:实在没想到2015年就这么过去了,真真实实的是颓废了一年,感觉自己没什么进步啊,而且退步明显。现在都已经半年没怎么写过代码了,趁着跨年的时间点回望了一下过去,在博客里突然发现还有好几篇大半年以前或一年以前写的文章还没有发布的,本想删了算了,犹豫了一下还是发布出来吧,毕竟那也是曾经的我啊~要是叫现在的我来写这篇文章的话还不一定写得出来,好多知识点都忘了。2016年得好好奋斗了,还得填了2015年留下的坑,出来混迟早要还的……

在Android 开发中,系统自带控件的功能和效果常常不满足于需求,因此需要创造一个一个的“新”控件来实现新功能。

Android中实现自定义控件的方式大体上就两种:

1、继承已有控件实现自定义控件。

2、继承View或ViewGroup,重新绘制新的控件。

其实已有控件都是继承自View的,因此自定义控件的本质可以说是对View的扩展。

自定义View的步骤大体如下:

定义自定义属性

在布局文件中引用自定义的命名空间,类似系统控件般的设置属性。

继承View,重写构造函数、onMesure、onDraw等函数。(有时候不止这三个,但一般情况下够了)并获取其自定义属性。

3.x:(对第3步进行说明)

测量:onMeasure 设置自己显示在屏幕上的宽高
布局:onLayout 设置自己显示在屏幕上的位置(只有在自定义ViewCroup中才用到)
绘制:onDraw 控制显示在屏幕上的样子(自定义Viewgroup时不需要这个)

下面就来实现一个简单的自定义View。

定义自定义属性:

这些资源通常是放在res/values/attrs.xm文件里。

<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="textSize" format="dimension"></attr>
<attr name="contentText" format="string"></attr>
</declare-styleable>

</resources>


这些代码声明了三个自定义属性:"showText"和"textSize"以及“contentText”,他们属于一个叫做PieChart的样式实体。按照惯例,样式实体的名字是和声明的自定义view类名是相同的。尽管遵循这个惯例不是绝对必要的,但很多有名的代码编写者都基于这个命名惯例来提供声明。

这里的format是自定义属性的值的类型。其类型共有10种:reference,float,string,color,demension,integer,enum,boolean,fraction,flag。

这里有一篇关于format的类型的文章,可以看看。http://www.cnblogs.com/rayray/p/3442026.html

在布局文件中引用自定义的命名空间,类似系统控件般的设置属性:

在布局文件activity_main.xml中实现如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.customview.MainActivity" >

<com.example.customview.view.PieChart
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:background="@android:color/holo_blue_light"
custom:contentText="你好"
custom:showText="true"
custom:textSize="50sp" />

</RelativeLayout>


其中xmlns:custom="http://schemas.android.com/apk/res/com.example.customview"是自定义的命名空间,其名称为custom。

其中custom:contentText="你好" custom:showText="true" custom:textSize="50sp"就是我们之前设置的属性。

注意:用来向布局中添加自定义view的XML标签的名字。这是自定义view类的完全表述。如果view内是一个内部类,必须使用外部类的名字进一步限定它。例如,PieChart类有一个叫做PieView的内部类。为了使用这个类中的自定义属性,必须使用标签com.example.customviews.charting.PieChart$PieView。

接下来,我们创建一个View包,新建一个PieChart类。如图所示:

public class PieChart extends View {

private boolean mShowText;
private int mTextSize;
private String mContentText;
private Paint mTextPaint;
private Rect mBound;
private int mAscent;

public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.PieChart, 0, 0);

try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
// mTextSize =
// a.getInteger(R.styleable.PieChart_textSize,12);//这是错误的获取方式
mTextSize = a.getDimensionPixelSize(R.styleable.PieChart_textSize,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
12, getResources().getDisplayMetrics()));
mContentText = a.getString(R.styleable.PieChart_contentText);
} finally {
a.recycle();
}
init();
}

public boolean ismShowText() {
return mShowText;
}

public void setmShowText(boolean mShowText) {
this.mShowText = mShowText;
invalidate();
requestLayout();
}

// setShowText调用了invalidate()和requestLayout() 。这些调用关键是为了保证view行为是可靠的。
// 你必须在改变这个可能改变外观的属性后废除这个view,这样系统才知道需要重绘。
// 同样,如果属性的变化可能影响尺寸或者view的形状,您需要请求一个新的布局。
// 忘记调用这些方法可能导致难以寻找的bug。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}

@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (mShowText)// 根据我们获取的设置的自定义属性,判断是否显示文本
canvas.drawText(mContentText, getWidth() / 2 - mBound.width() / 2,
getHeight() / 2 + mBound.height() / 2, mTextPaint);
// getWidth() / 2 - mBound.width() / 2,getHeight() / 2 + mBound.height()
// / 2
// 这是为了使文本居中
else
canvas.drawText("内容为空", getWidth() / 2 - mBound.width() / 2,
getHeight() / 2 + mBound.height() / 2, mTextPaint);

}

/**
* Determines the width of this view
*
* @param measureSpec
*            A measureSpec packed into an int
* @return The width of the view, honoring constraints from measureSpec
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = (int) mTextPaint.measureText(mContentText)
+ getPaddingLeft() + getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by
// measureSpec
result = Math.min(result, specSize);
}
}

return result;
}

/**
* Determines the height of this view
*
* @param measureSpec
*            A measureSpec packed into an int
* @return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

mAscent = (int) mTextPaint.ascent();
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()
+ getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by
// measureSpec
result = Math.min(result, specSize);
}
}
return result;
}

public int getmTextSize() {
return mTextSize;
}

public void setmTextSize(int mTextSize) {
this.mTextSize = mTextSize;
invalidate();
requestLayout();
}

public String getmContentText() {
return mContentText;
}

public void setmContentText(String mContentText) {
this.mContentText = mContentText;
invalidate();
requestLayout();
}

private void init() {
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setColor(0xff101010);
mBound = new Rect();
mTextPaint
.getTextBounds(mContentText, 0, mContentText.length(), mBound);

}
}


View Code

好吧,这篇文章就到此为止吧,主要是对自定义View做一个简单的了解。(虽然很简单,讲得也不是很清楚,我尽力了。)

参考文献:Android官网。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: