视图绘制有两种方法,分别是onDraw和dispatchDraw,它们的区别主要有下列两点:
(1)onDraw既可用于普通控件,也可用于布局类视图;而dispatchDraw专门用于布局类视图,像线性布局LinearLayout、相对布局RelativeLayout都属于布局类视图。
(2)onDraw方法先执行,dispatchDraw方法后执行,这两个方法中间再执行下级视图的绘制方法。
xml布局:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="5dp" >
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="40dp" >
-
- <TextView
- android:id="@+id/tv_draw"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentLeft="true"
- android:gravity="center"
- android:text="绘图方式:"
- android:textColor="@color/black"
- android:textSize="17sp" />
-
- <Spinner
- android:id="@+id/sp_draw"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_toRightOf="@+id/tv_draw"
- android:gravity="left|center"
- android:spinnerMode="dialog" />
- </RelativeLayout>
-
- <!-- 自定义的绘画视图,需要使用全路径 -->
- <com.example.myapplication.widget.DrawRelativeLayout
- android:id="@+id/drl_content"
- android:layout_width="match_parent"
- android:layout_height="150dp">
-
- <Button
- android:id="@+id/btn_center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:padding="10dp"
- android:text="我在中间"
- android:textColor="@color/black"
- android:textSize="20sp"
- android:visibility="gone" />
- </com.example.myapplication.widget.DrawRelativeLayout>
-
- </LinearLayout>
代码:
- package com.example.myapplication;
-
- import androidx.appcompat.app.AppCompatActivity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.Spinner;
- import com.example.myapplication.widget.DrawRelativeLayout;
-
- public class MainActivity extends AppCompatActivity
- {
-
- private DrawRelativeLayout drl_content; // 声明一个绘画布局对象
- private Button btn_center;
-
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
-
- // 从布局文件中获取名叫drl_content的绘画布局
- drl_content = findViewById(R.id.drl_content);
- btn_center = findViewById(R.id.btn_center);
-
- initTypeSpinner(); // 初始化绘图方式的下拉框
- }
-
- // 初始化绘图方式的下拉框
- private void initTypeSpinner()
- {
- ArrayAdapter<String> drawAdapter = new ArrayAdapter<String>(this, R.layout.item_select, descArray);
-
- Spinner sp_draw = findViewById(R.id.sp_draw);
-
- sp_draw.setPrompt("请选择绘图方式");
- sp_draw.setAdapter(drawAdapter);
- sp_draw.setOnItemSelectedListener(new DrawSelectedListener());
- sp_draw.setSelection(0);
- }
-
- private String[] descArray = {"不画图", "画矩形", "画圆角矩形", "画圆圈", "画椭圆", "onDraw画叉叉", "dispatchDraw画叉叉"};
-
- private int[] typeArray = {0, 1, 2, 3, 4, 5, 6};
-
- class DrawSelectedListener implements AdapterView.OnItemSelectedListener
- {
-
- public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3)
- {
- int type = typeArray[arg2];
-
- if (type == 5 || type == 6)
- {
- btn_center.setVisibility(View.VISIBLE);
- }
- else
- {
- btn_center.setVisibility(View.GONE);
- }
-
- drl_content.setDrawType(type); // 设置绘图布局的绘制类型
- }
-
- public void onNothingSelected(AdapterView<?> arg0) {}
- }
-
- }
DrawRelativeLayout
- package com.example.myapplication.widget;
-
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Paint.Style;
- import android.graphics.Rect;
- import android.graphics.RectF;
- import android.util.AttributeSet;
- import android.widget.RelativeLayout;
-
- public class DrawRelativeLayout extends RelativeLayout
- {
- private int mDrawType = 0; // 绘制类型
-
- private Paint mPaint = new Paint(); // 创建一个画笔对象
-
- private int mStrokeWidth = 3; // 线宽
-
- public DrawRelativeLayout(Context context)
- {
-
- this(context, null);
- }
-
- public DrawRelativeLayout(Context context, AttributeSet attrs)
- {
-
- super(context, attrs);
- mPaint.setAntiAlias(true); // 设置画笔为无锯齿
- mPaint.setDither(true); // 设置画笔为防抖动
- mPaint.setColor(Color.BLACK); // 设置画笔的颜色
- mPaint.setStrokeWidth(mStrokeWidth); // 设置画笔的线宽
- mPaint.setStyle(Style.STROKE); // 设置画笔的类型。STROKE表示空心,FILL表示实心
- }
-
- // onDraw方法在绘制下级视图之前调用
- @Override
- protected void onDraw(Canvas canvas)
- {
-
- super.onDraw(canvas);
-
- int width = getMeasuredWidth(); // 获得布局的实际宽度
- int height = getMeasuredHeight(); // 获得布局的实际高度
-
- if (width > 0 && height > 0) {
- if (mDrawType == 1) { // 绘制矩形
- Rect rect = new Rect(0, 0, width, height);
- canvas.drawRect(rect, mPaint); // 在画布上绘制矩形
- } else if (mDrawType == 2) { // 绘制圆角矩形
- RectF rectF = new RectF(0, 0, width, height);
- canvas.drawRoundRect(rectF, 30, 30, mPaint); // 在画布上绘制圆角矩形
- } else if (mDrawType == 3) { // 绘制圆圈
- int radius = Math.min(width, height) / 2 - mStrokeWidth;
- canvas.drawCircle(width / 2, height / 2, radius, mPaint); // 在画布上绘制圆圈
- } else if (mDrawType == 4) { // 绘制椭圆
- RectF oval = new RectF(0, 0, width, height);
- canvas.drawOval(oval, mPaint); // 在画布上绘制椭圆
- } else if (mDrawType == 5) { // 绘制矩形及其对角线
- Rect rect = new Rect(0, 0, width, height);
- canvas.drawRect(rect, mPaint); // 绘制矩形
- canvas.drawLine(0, 0, width, height, mPaint); // 绘制左上角到右下角的线段
- canvas.drawLine(0, height, width, 0, mPaint); // 绘制左下角到右上角的线段
- }
- }
- }
-
- // dispatchDraw方法在绘制下级视图之前调用
- @Override
- protected void dispatchDraw(Canvas canvas)
- {
- super.dispatchDraw(canvas);
-
- int width = getMeasuredWidth(); // 获得布局的实际宽度
- int height = getMeasuredHeight(); // 获得布局的实际高度
-
- if (width > 0 && height > 0)
- {
- if (mDrawType == 6) { // 绘制矩形及其对角线
- Rect rect = new Rect(0, 0, width, height);
- canvas.drawRect(rect, mPaint); // 绘制矩形
- canvas.drawLine(0, 0, width, height, mPaint); // 绘制左上角到右下角的线段
- canvas.drawLine(0, height, width, 0, mPaint); // 绘制左下角到右上角的线段
- }
- }
- }
-
- // 设置绘制类型
- public void setDrawType(int type)
- {
- setBackgroundColor(Color.WHITE); // 背景置为白色,目的是把画布擦干净
-
- mDrawType = type;
-
- invalidate(); // 立即重新绘图,此时会触发onDraw方法和dispatchDraw方法
- }
- }