实现的效果
继承View
继承ViewGroup实现一个顶栏
下面来实现上面效果
自定义View基础:Android开发之自定义View基础
继承View
需要自定义绘制内容,需要继承View,必须要重写onDraw方法,在onDraw方法中来进行绘制,实现onMeasure方法,来测量控件的空间。
创建类,继承View或View的子类,并提供相关的构造方法
public class SimpleView extends View {
public SimpleView(Context context) {
super(context);
}
public SimpleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
}
重写onMeasure()方法,并调用setMeasuredDimension(int width, int height)设置控件的大小(因为Demo只是简单的画了几个图形,所以没重写)
重写onDraw()方法,实现绘制特定内容
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
重写onTouchEvent()方式处理触摸事件
此处不作处理
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
开始绘制
1.说到绘制,当然需要画笔了。初始化画笔
/**
* 初始化画笔
*/
private void initPaint() {
// 定义画笔
paint = new Paint();
// 设置画笔的字体大小
paint.setTextSize(100);
// 线条的样式 - 粗体 斜线
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setStrokeWidth(10);
// 设置画笔的颜色
paint.setColor(Color.RED);
// 设置画笔的样式
// Paint.Style.STROKE - 边线样式 - 圆(空心圆)
// Paint.Style.FILL - 填充样式 - 圆(实心圆)
paint.setStyle(Paint.Style.STROKE);
//paint.setStyle(Paint.Style.FILL);
// 是否抗锯齿
paint.setAntiAlias(true);
}
2.绘制图形
/**
* 用于绘制
* 当初始化View(在代码中添加自定义控件,或者在布局中添加加载时),此方法会调用
*
* @param canvas - 画布
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/****************绘制内容***************/
// 绘制线(float startX, float startY, float stopX, float stopY, Paint paint)
canvas.drawLine(100, 100, 600, 300, paint);
// 绘制圆(float cx, float cy, float radius, Paint paint)
canvas.drawCircle(500, 500, 200, paint);
// 绘制文字(String text, float x, float y, Paint paint)
canvas.drawText("绘制内容", 450, 750, paint);
// 绘制矩形1(float left, float top, float right, float bottom, Paint paint)
canvas.drawRect(400, 950, 600, 1100, paint);
// 绘制矩形2
RectF rect = new RectF();
rect.left = 400;
rect.top = 950;
rect.right = 600;
rect.bottom = 1100;
//canvas.drawRect(rect,paint);
}
在布局文件中使用<类全名>并设置属性(或在Java代码中使用)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.rair.customview.SimpleViewActivity">
<com.rair.customview.view.SimpleView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
运行
完整代码
public class SimpleView extends View {
// 声明画笔
private Paint paint;
// 创建构造方法
public SimpleView(Context context) {
//super(context);
// 调用当前类的其他的构造方法
this(context, null);
}
/**
* 如果在布局中添加自定义控件,则需要添加如下构造方法
*
* @param context - 上下文
* @param AttributeSet set - 属性对象,它包含这当前控件所有的属性
*/
public SimpleView(Context context, AttributeSet set) {
super(context, set);
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
// 定义画笔
paint = new Paint();
// 设置画笔的字体大小
paint.setTextSize(100);
// 线条的样式 - 粗体 斜线
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setStrokeWidth(10);
// 设置画笔的颜色
paint.setColor(Color.RED);
// 设置画笔的样式
// Paint.Style.STROKE - 边线样式 - 圆(空心圆)
// Paint.Style.FILL - 填充样式 - 圆(实心圆)
paint.setStyle(Paint.Style.STROKE);
//paint.setStyle(Paint.Style.FILL);
// 是否抗锯齿
paint.setAntiAlias(true);
}
/**
* 用于绘制
* 当初始化View(在代码中添加自定义控件,或者在布局中添加加载时),此方法会调用
*
* @param canvas - 画布
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/****************绘制内容***************/
// 绘制线
canvas.drawLine(100, 100, 600, 300, paint);
// 绘制圆
canvas.drawCircle(500, 500, 200, paint);
// 绘制文字
canvas.drawText("绘制内容", 450, 750, paint);
// 绘制矩形
RectF rect = new RectF();
rect.left = 400;
rect.top = 950;
rect.right = 600;
rect.bottom = 1100;
//canvas.drawRect(rect,paint);
canvas.drawRect(400, 950, 600, 1100, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
}
继承ViewGroup
- 绘制,需要绘制控件,重写onDraw方法
- 通过在ViewGroup中通过this.addView(<控件对象>)来添加控件
- onMeasure方法,来测量控件的空间
- onLayout方法必须实现,在此方法中处理子控件的位置。当视图初始化,或者视图位置发生改变时候,调用此方法。
创建类,继承ViewGroup
public class GroupView extends ViewGroup {
public GroupView(Context context) {
this(context, null);
}
public GroupView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
继承的ViewGroup,通过addView添加子View
效果中左为Imageiew,中间是TextView
private ImageView imageView;
private TextView tvTitle;
/**
* 添加子控件
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void addViews(Context context) {
imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setBackground(backDrawable);
LayoutParams layoutParam = new LayoutParams(100, 100);
imageView.setLayoutParams(layoutParam);
// 添加一个ImageView
this.addView(imageView);
// 添加一个TextView
tvTitle = new TextView(context);
tvTitle.setTextSize(20);
tvTitle.setText(title);
tvTitle.setTextColor(titleColor);
this.addView(tvTitle);
}
重写onMeasure()方法,并调用setMeasuredDimension(int width, int height)设置控件的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 要测量子控件的高度和宽度
// 计算子控件
measureChildren(widthMeasureSpec, heightMeasureSpec);
// 先计量控件的宽度
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int measureWidth = 0;
// 确切大小 500dp 或者 match_parent
if (widthMode == MeasureSpec.EXACTLY) {
measureWidth = MeasureSpec.getSize(widthMeasureSpec);
}
// wrap_content
else if (widthMode == MeasureSpec.AT_MOST) {
// 设置屏幕的宽度
measureWidth = screenWidth;
}
// 高度计算,获取最高的控件
int measureHeight = 0;
int childCount = this.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
// child.getMeasuredHeight()能够获取到具体的控件大小
// 必须先调用measureChildren(widthMeasureSpec,heightMeasureSpec);
// 判断哪个控件的高度最大,来作为视图容器的高度
if (child.getMeasuredHeight() > measureHeight) {
measureHeight = child.getMeasuredHeight();
}
}
setMeasuredDimension(measureWidth, measureHeight + 50);
}
- int MeasureSpec.getMode(mSpec) 获取控件大小模式
- MeasureSpec.EXACTLY,确切空间,布局中的属性值一般为match_parent或确切固定的值(如指定了160dp)
- MeasureSpec.AT_MOST 尽量多的空间,布局中的属性值wrap_content
- MeasureSpec.UNSPECIFIED 未指定的,一般在父控件中使用
- MeasureSpec.getSize(mSpec) 获取控件大小
添加自定义View属性(使控件更灵活)
<resources>
<declare-styleable name="GroupView">
<!-- 背景 -->
<attr name="nav_bg" format="color|reference" />
<!-- 标题颜色 -->
<attr name="title_color" format="color|reference" />
<!-- 标题文字 -->
<attr name="title_text" format="string|reference" />
<!-- 标题文字大小 -->
<attr name="title_size" format="dimension|reference" />
<!-- 返回图标 -->
<attr name="back_image" format="reference" />
</declare-styleable>
</resources>
解析自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GroupView);
title = array.getString(R.styleable.GroupView_title_text);
bgColor = array.getColor(R.styleable.GroupView_nav_bg, 0x0);
titleColor = array.getColor(R.styleable.GroupView_title_color, 0xF);
backDrawable = array.getDrawable(R.styleable.GroupView_back_image);
titleSize = array.getDimensionPixelSize(R.styleable.GroupView_title_size, 10);
array.recycle();
// 设置背景颜色
this.setBackgroundColor(bgColor);
// 初始化画笔
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(bgColor);
实现onLayout方法(俗称定位,就是确定每个子View的位置)
onLayout方法必须实现,在此方法中处理子控件的位置。当视图初始化,或者视图位置发生改变时候,调用此方法。
具体按需求计算
/**
* 当视图初始化,或者视图位置发生改变时候
*
* @param changed 是否改变
* @param l 左
* @param t 上
* @param r 右
* @param b 下
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//getWidth()
// 需要设置子控件显示的位置
//getChildAt(0).layout();
// 控件的宽度
int viewWidth = getMeasuredWidth();
int viewHeight = getMeasuredHeight();
int left = 0, top = 0, right = 0, bottom = 0;
int childCount = this.getChildCount();
View view = null;
for (int i = 0; i < childCount; i++) {
view = this.getChildAt(i);
if (i == 0) {
left = view.getMeasuredWidth() / 3;
} else if (i == 1) {
left = (viewWidth - view.getMeasuredWidth()) / 2;
}
top = (viewHeight - view.getMeasuredHeight()) / 2;
right = left + view.getMeasuredWidth();
bottom = top + view.getMeasuredHeight();
view.layout(left, top, right, bottom);
}
}
添加回调接口(返回按钮点击事件)
public interface OnFinishListener {
void onFinish();
}
private OnFinishListener listener;
public void setOnFinishListener(OnFinishListener listener) {
this.listener = listener;
}
在布局文件中使用<类全名>并设置属性(或在Java代码中使用)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.rair.customview.GroupViewActivity">
<com.rair.customview.view.GroupView
android:id="@+id/m_groupview"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:back_image="@drawable/ic_arrow_back_black_24dp"
app:nav_bg="@color/colorAccent"
app:title_color="@color/white"
app:title_size="25sp"
app:title_text="首页" />
<com.rair.customview.view.GroupView
android:id="@+id/m_groupview1"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_marginTop="10dp"
app:back_image="@drawable/ic_arrow_back_black_24dp"
app:nav_bg="@color/colorAccent"
app:title_color="@color/white"
app:title_size="25sp"
app:title_text="首页" />
</LinearLayout>
设置事件监听(点击返回退出Activity)
public class GroupViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_view);
GroupView groupView = (GroupView) findViewById(R.id.m_groupview);
groupView.setOnFinishListener(new GroupView.OnFinishListener() {
@Override
public void onFinish() {
finish();
}
});
}
}
运行
Demo已上传
GitHub:https://github.com/Rairmmd/RairDemo/tree/master/CustomViewDemo
Coding:https://git.coding.net/Rair/RairDemo.git
网友评论