Android允许自定义控件,来弥补原生控件的不足。实现自定义View的方式,分为三种:组合控件,继承控件,自绘控件。然后根据需要来添加自定义的属性。
1. 组合控件
就是将系统原有的控件进行组合,构成一个新的控件。这种方式下,不需要开发者自己去绘制图上显示的内容,也不需要开发者重写onMeasure,onLayout,onDraw方法来实现测量、布局以及draw流程。实际开发中,标题栏是一个比较常见的例子。
- 编写布局文件,写入组合到一起的控件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tv_custom_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textSize="35dp"
android:gravity="center"
android:background="@color/black"
android:textColor="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="返回"
android:textSize="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
- 根据给定布局,实现自定义View
public class CustomTitleView extends FrameLayout implements View.OnClickListener {
private TextView tv_custom_title;
private Button btn_back;
private View.OnClickListener backOnClickListener;
//这里使用其他参数的构造器会闪退,我也不清楚为啥
public CustomTitleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//初始化
LayoutInflater.from(context).inflate(R.layout.custom_title_view, this);
tv_custom_title = findViewById(R.id.tv_custom_title);
btn_back = findViewById(R.id.btn_back);
btn_back.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_back){
if (backOnClickListener != null){
backOnClickListener.onClick(view);
}
}
}
//给定两个接口,可以对控件进行修改
public void setTitle(String str){
tv_custom_title.setText(str);
}
public void setBackOnClickListener(View.OnClickListener backOnClickListener){
this.backOnClickListener = backOnClickListener;
}
}
- 在MainActivity中的布局文件中引入
<com.example.day8_view.CustomTitleView
android:id="@+id/custom_title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
- 在MainActivity中使用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
CustomTitleView custom_title_view = findViewById(R.id.custom_title_view);
custom_title_view.setTitle("这是标题栏");
custom_title_view.setBackOnClickListener(this);
}
@Override
public void onClick(View view) {
Toast.makeText(this, "您点击了返回", Toast.LENGTH_SHORT).show();
}
}
2. 继承控件
通过继承系统控件(View子类控件或ViewGroup子类控件)来完成自定义View,一般是希望在原有系统控件基础上做一些修饰性的修改,而不会做大幅度的改动,如在TextView的文字下方添加下划线,在LinearLayout布局中加一个蒙板等。这种方式往往都会复用系统控件的onMeasure和onLayout方法,而只需要重写onDraw方法,在其中绘制一些需要的内容。下面会分别继承View类控件和ViewGroup类控件来举例说明。
2.1 继承View类系统控件
示例:在TextView文字下方显示红色下划线
- 继承TextView实现UnderlineTextView,重写onDraw方法
@SuppressLint("AppCompatCustomView")
public class UnderlineTextView extends TextView {
public UnderlineTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
//设置画笔宽度
paint.setStrokeWidth(5);
int width = getWidth();
//确定文本基线位置
int height = getBaseline();
//起点的X坐标、起点的Y坐标、终点的X坐标、终点的Y坐标、绘制样式
canvas.drawLine(0, height, width, height, paint);
}
}
- 在MainActivity的布局文件中引入
<com.example.day8_view.UnderlineTextView
android:id="@+id/underline_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hello world!"
android:textSize="35dp"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
2.2 继承ViewGroup类系统控件
示例:在layout布局上添加一个浅红色的半透明蒙板
- 继承LinearLayout重写diapatchDraw方法
public class ForegroundLinearLayout extends LinearLayout {
public ForegroundLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void dispatchDraw(@NonNull Canvas canvas) {
super.dispatchDraw(canvas);
canvas.drawColor(Color.parseColor("#50FF0000"));
}
}
- 在布局文件中使用
<com.example.day8_view.ForegroundLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<com.example.day8_view.UnderlineTextView
android:id="@+id/underline_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hello world!"
android:textSize="35dp"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</com.example.day8_view.ForegroundLinearLayout>
3. 自绘控件
采用自绘控件这种方式时,如果自定义View为最终的叶子控件,那么需要直接继承View;当自定义View为容器类控件,则需要直接继承ViewGroup
3.1 继承View
示例:绘制直方图
- 建立自定义View的类HistogramView,继承View,由于自定义View是叶子节点,所以没有子控件,只重写onDraw方法
public class HistogramView extends View {
private Paint mPaint;
private Path mPath;
public HistogramView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制坐标轴
mPaint.reset();
mPath.reset();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPath.moveTo(100,100);
mPath.rLineTo(0,402);
mPath.rLineTo(800,0);
canvas.drawPath(mPath,mPaint);
//绘制文字
mPaint.reset();
mPaint.setTextSize(30);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText("Froyo",160,540,mPaint);
canvas.drawText("CB",280,540,mPaint);
canvas.drawText("ICS",380,540,mPaint);
canvas.drawText("J",480,540,mPaint);
canvas.drawText("KitKat",560,540,mPaint);
canvas.drawText("L",690,540,mPaint);
canvas.drawText("M",790,540,mPaint);
//绘制直方图,柱形图是用较粗的直线来实现的
mPaint.reset();
mPaint.setColor(Color.GREEN);
mPaint.setStrokeWidth(80);
float[] lines3={
200,500,200,495,
300,500,300,480,
400,500,400,480,
500,500,500,300,
600,500,600,200,
700,500,700,150,
800,500,800,350,
};
canvas.drawLines(lines3,mPaint);
}
}
- 在布局文件中引用即可
<com.example.day8_view.HistogramView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"/>
3.2 继承ViewGroup
这里通过自定义一个父布局控件,并添加一个子view来作为例子讲解该方法的实现。
4. 在自定义View中使用自定义属性
Android --- 自定义View的三种实现方式及自定义属性使用介绍_android 自定义view-CSDN博客
本系列【安卓基础重点知识】是刚开始学习android的时候记录的,其中部分内容来自网页,忘记记录来源了,如需添加引用,联系我即可