精确值模式,当我们将控件的layout_width属性或者layout_height属性指定为具体数值时,或者为match_parent属性时,系统使用的是EXACTLY模式。
(2)AT_MOST
The child can be as large as it wants up to the specified size.
最大值模式,当控件的layout_width属性或者layout_height属性为wrap_content时,控件大小一般随着控件的子控件或内容的变化而变化,此时控件的尺寸大小只要不超过父控件允许的最大尺寸即可。
(3)UNSPECTIFIED
The parent has not imposed any constraint on the child. It can be whatever size it wants.
它不指定测量模式,View想多大就多大,一般用作Android系统内部,或者ListView和ScrollView等滑动控件。
注意:
View类默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式。而如果要让自定义View支持wrap_content属性,那么必须重写onMeasure()方法来指定wrap_content时的大小,否则它会默认填充整个父布局,重写onMeasure()方法的目的,就是为了能给View一个wrap_content属性下的默认大小。
1.2 View的绘制
当测量好以后,我们就可以简单地重写onDraw()方法,并在Canvas对象上来绘制所需要的图形。要想在Android的界面中绘制相应的图像,就必须在Canvas上进行绘制。Canvas就好比是一块画布,使用Paint(相当于画笔)就可以在上面作画了,通常需要继承View并重写它的onDraw()方法来完成绘图。
onDraw()方法中有一个参数,就是Canvas canvas对象。使用这个对象,就可以进行绘制了。而在其他地方,通常需要使用代码来创建一个Canvas对象。代码如下:
Canvas canvas=new Canvas(bitmap);
传进去的bitmap对象的过程称之为装载画布。传进去的bitmap与创建的canvas对象紧紧的联系在一起,bitmap存储所有绘制在canvas上的像素信息,通过canvas.drwaXXX方法,可以进行一系列的绘图操作。
下面通过代码来实践一下吧。
布局文件activity_main.xml
<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="match_parent"
android:orientation="vertical"
tools:context="com.example.myview.MainActivity" >
<com.example.myview.MyCustomView
android:id="@+id/my_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
MyCustomView.java
package com.example.myview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Message;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
public class MyCustomView extends View implements Runnable{
private Paint mPaint;
private Context mContext;// 上下文环境引用
private int radiu;
public MyCustomView(Context context) {
super(context);
// TODO 自动生成的构造函数存根
}
public MyCustomView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
mContext = context;
// 初始化画笔
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
// TODO 自动生成的方法存根
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setAntiAlias(true);// 抗锯齿效果
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.CYAN);
mPaint.setStrokeWidth(5);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO 自动生成的方法存根
super.onDraw(canvas);
canvas.drawCircle(MeasureUtil.getScreenWidth(mContext)/2,
MeasureUtil.getScreenHeight(mContext)/2, radiu, mPaint);
}
@Override
public void run() {
// TODO 自动生成的方法存根
while (true) {
if (radiu <=200) {
radiu += 10;
//刷新View
postInvalidate();
} else {
radiu = 0;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
其中上面的是第28行,有一个initPaint()方法,其在初始化构造的方法里,我们去初始化画笔,尽量不要在onDraw()方法里去初始化画笔,因为我们的图,不只一次会调用到OnDraw()方法,这样就会重复的去初始化对象,耗内存和时间。
onDraw()方法中,只画了一个圆,圆的半径使用的是屏幕的宽度的一半。当然2D画图有丰富的API,可以绘制出更丰富的图,通过这个圆,我们可以类似的做一个下载进度条的功能。
MeasureUtil.java
package com.example.myview;
import android.content.Context;
import android.view.Window;
import android.view.WindowManager;
public class MeasureUtil {
public static int getScreenWidth(Context mContext) {
int width = mContext.getResources().getDisplayMetrics().widthPixels;
return width;
}
public static int getScreenHeight(Context mContext) {
int height = mContext.getResources().getDisplayMetrics().heightPixels;
return height;
}
}
MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
private MyCustomView myCustomView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myCustomView = (MyCustomView) findViewById(R.id.my_view);
new Thread(myCustomView).start();
}
}
后面还有3篇文章,会对3种自定义控件方法进行实践。