之前对构造方法、测量方法的简单使用的了解,下面对onMeasure()之后的onLayout()、onDraw()方法进行简单说明。
onLayout()布局方法一般是在自定义布局控件继承自ViewGroup类时比较常用,用于对子控件进行布局摆放,确定位置之后子控件自己绘制。
只是简单的布局,效果图:
xml 文件如下:
<span style="font-size:14px;"><span style="font-size:14px;"> <com.example.administrator.mydemo.widget.MyViewGroup
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:text="垂直线性布局"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="点击也没用1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:text="点击也没用"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.example.administrator.mydemo.widget.MyViewGroup></span></span>
代码如下:
<span style="font-size:14px;"><span style="font-size:14px;"> @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
<strong>measureChildren(widthMeasureSpec, heightMeasureSpec);</strong>
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean b, int i0, int i1, int i2, int i3) {
// 记录总高度
int mTotalHeight = 0;
int mTotalWidth = 0;
// 遍历所有子视图
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
// 获取在onMeasure中计算的视图尺寸
int measureHeight = childView.getMeasuredHeight();
int measuredWidth = childView.getMeasuredWidth();
// 对角线布局
childView.layout( mTotalWidth, mTotalHeight, mTotalWidth + measuredWidth, mTotalHeight
+ measureHeight);
mTotalHeight += measureHeight;
mTotalWidth += measuredWidth;
}
}</span></span>
类似的这种ViewGroup,重点是onMeasure()方法中加粗的部分,将子控件进行测量。
onLayout()方法似乎也没什么说的唯一调用的方法childView.layout(左,上,右,下)将子控件布局到对应的位置。但是也仅限这种简单情况,实际的时候肯定复杂的多。
onDraw()方法,即是绘制图像时调用的方法,在测量、布局之后接着调用。这里不多说,主要简单的了解一下Canvas这个类。点进去常用的drawXXX()方法方法还是挺多的。
顾名思义Canvas可以绘制的对象有:弧线(arcs)、填充颜色(argb和color)、 Bitmap、圆(circle)、点(point)、线(line)、矩形(Rect)、椭圆(oval)、图片(Picture)、圆角矩形 (RoundRect)、文本(text)、顶点(Vertices)、路径(path)等等。
同时Android提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲),进一步增加图形绘制的灵活性。
位置转换方法详细介绍点这里:Canvas的位置转换方法。
下面模仿支付宝的芝麻信用潘绘制一个简单的自定义控件对方法进行简单的介绍,成品图如下:
直接上代码(其中两个字符数组省略):
<span style="font-size:14px;">import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowManager;
import com.example.administrator.mydemo.R;
/**
* Created by fengzhen.
* <p/>
* 自定义刻度盘
*/
public class DailView extends View {
private Paint mPaintDegree;
private Paint mPaintTotal;
private Paint mPaintDail;
private Paint mPaintShallow;
private Paint mPaintDeep;
private Paint mPaintText;
private Paint mPaintTextC;
/* 信用分数 */
private String[] dailCredit;
/* 信用等级 */
private String[] dailCreditLevel;
private int mWidth;
private int mHeight;
public DailView(Context context) {
this(context, null);
}
public DailView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 信用分数
dailCredit = getResources().getStringArray(R.array.dail_credit);
// 信用等级
dailCreditLevel = getResources().getStringArray(R.array.dail_credit_level);
// 进度画笔
mPaintDegree = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintDegree.setColor(Color.argb(255, 255, 255, 255)); // 白色
mPaintDegree.setStrokeWidth(3); // 宽度 4
mPaintDegree.setStyle(Paint.Style.STROKE); // 画线
// 总进度画笔
mPaintTotal = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintTotal.setColor(Color.argb(40, 255, 255, 255)); // 灰色
mPaintTotal.setStrokeWidth(5); // 宽度 5
mPaintTotal.setStyle(Paint.Style.STROKE); // 画线
// 刻度条
mPaintDail = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintDail.setColor(Color.argb(40, 255, 255, 255)); // 灰色
mPaintDail.setStrokeWidth(14); // 宽度 14
mPaintDail.setStyle(Paint.Style.STROKE); // 画线
// 浅刻度
mPaintShallow = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintShallow.setColor(Color.argb(70, 255, 255, 255));
mPaintShallow.setStrokeWidth(3); // 宽度 14
mPaintShallow.setStyle(Paint.Style.STROKE); // 画线
// 深刻度
mPaintDeep = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintDeep.setColor(Color.argb(200, 255, 255, 255));
mPaintDeep.setStrokeWidth(3); // 宽度 14
mPaintDeep.setStyle(Paint.Style.STROKE); // 画线
// 刻度盘文字
mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
mPaintText.setColor(Color.argb(200, 255, 255, 255));
mPaintText.setTextSize(20);
// 中间文字
mPaintTextC = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
mPaintTextC.setColor(Color.RED);
mPaintTextC.setTextSize(50);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 移动画布 使圆心水平居中 距离上边距20
canvas.translate(canvas.getWidth() / 2 - 200, 20);
// 总进度弧
RectF totalRectF = new RectF(0, 0, 400, 400);
canvas.drawArc(totalRectF, 150, 240, false, mPaintTotal);
// 具体进度弧
RectF degreeRectF = new RectF(0, 0, 400, 400);
canvas.drawArc(degreeRectF, 150, 150 + 49, false, mPaintDegree);
canvas.save();
// 刻度条 画布移动
canvas.translate(20, 20);
RectF dailRectF = new RectF(0, 0, 360, 360);
canvas.drawArc(dailRectF, 150, 240, false, mPaintDail);
// 保存画布
canvas.save();
//画刻度 旋转画布
canvas.rotate(-30, 180, 180);
// 画刻度 和 文字
int flag = 0;
for (int i = 0; i <= 240; i += 8) {
if (i % 48 == 0) {
// 深刻度线 线的坐标这里不做详细说明
canvas.drawLine(-7, 180, 7, 180, mPaintDeep);
} else {
// 浅刻度线
canvas.drawLine(-7, 180, 7, 180, mPaintShallow);
}
canvas.rotate(8, 180, 180);
}
// 信用分数
// 画布位置还原到上一次保存的状态
canvas.restore();
// 再次移动画布,是圆心校准
canvas.translate(20, 20);
canvas.rotate(-30, 160, 160);
flag = 0;
for (int i = 0; i <= 240; i+=8) {
// 等级
if ((i-24)%48 ==0){
Path path = new Path();
path.addArc(new RectF(0,0,320,320),180-7,180+7);
// 两个参数 分别是水平偏移和垂直偏移量
canvas.drawTextOnPath(dailCreditLevel[flag-1],path,0,14,mPaintText);
}
// 分数
if (i % 48 == 0) {
Path path = new Path();
path.addArc(new RectF(0,0,320,320),180-6,180+6);
// 两个参数 分别是水平偏移和垂直偏移量
canvas.drawTextOnPath(dailCredit[flag],path,0,14,mPaintText);
flag++;
}
canvas.rotate(8, 160, 160);
}
// 中间文字显示
canvas.restore();
Rect rect = new Rect();
String text = "信用表盘";
mPaintTextC.getTextBounds(text,0,text.length(),rect);
canvas.drawText(text,100,200,mPaintTextC);
}
}</span>
这个Deme还有很多问题,比如画笔的复用这些,其中的很多数值都是直接用的固定值,也没有将px转换成dp,这里只是单纯的使用Canvas.drawXXX()方法。由于没有进行测量,所以使用的时候请给出具体的大小,哈哈。具体的代码解析在注释中已经很明显了,有不是很确定清楚的地方,修改一下多试试几乎就明白了。
主要是画弧、画线、画字,以及对Canvas的操作来实现旋转效果,最关键的主要还是各种角度的计算,以及位置的判定。
到此,构造方法以及三个必须方法就已经简单的跑了一遍,现在能够自定义一些简单的、静态的控件。下篇就说说与用户交互相关的东西,实现与用户能够优良交互的完整自定义控件。
最后跟上这个Demo的GitHub地址:GitHub简单自定义View Demo地址