自定义View
除了onMeasure()
测量、onLayout()
布局、onDraw()
绘制 这三个比较重要的View回调方法外,还有以下几个比较重要的回调方法:
onFinishInflate()
:从XML加载组件后调用
onSizeChanged()
:组件改变大小时调用
onTouchEvent()
:监听到触摸事件后回调
有三种自定义控件的方式:
继承类:对现有控件进行扩展,比如:继承TextView,重写onDraw()方法,实现新的功能。注意,如果要绘制需要使用getPaint()获取同一个画笔
组合类:通过几个控件组合成一个新的控件
重写类:继承View,自己绘制
继承控件:
给TextView家加上矩形边框
public class MyTextView extends TextView {
private Paint mPaint;
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint = getPaint();
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
canvas.drawRect(10, 10, getMeasuredWidth()-10, getMeasuredHeight()-10, mPaint);
super.onDraw(canvas);
}
}
以上需要明确几点:
- 使用canvas绘制时,先绘制的图像置于底层,后绘制的图像将会覆盖之前绘制的图像
- getPaint()可以获取系统在绘制这个组件时使用的画笔,如果你需要使用或者修改这个画笔的属性(如颜色等),可以使用getPaint()方法。也可以自定义一个Paint
- getMeasuredHeight()和getMeasureWidth()可以获取这个控件测量后的值,即调用onMeasure()后得到的控件大小
复合控件
TopBar:
先看效果:
上图是一个控件,由两个Button和一个TextView组成:
<resources>
<!--定义属性-->
<attr name="titleColor" format="color"/>
<attr name="titleSize" format="dimension"/>
<attr name="titleText" format="string"/>
<attr name="leftTextColor" format="color"/>
<attr name="leftTextSize" format="dimension"/>
<attr name="leftText" format="string"/>
<attr name="rightTextColor" format="color"/>
<attr name="rightTextSize" format="dimension"/>
<attr name="rightText" format="string"/>
<!--给控件添加属性-->
<declare-styleable name="TopBar">
<attr name="titleColor"/>
<attr name="titleSize"/>
<attr name="titleText"/>
<attr name="leftTextColor"/>
<attr name="leftTextSize"/>
<attr name="leftText"/>
<attr name="rightTextColor"/>
<attr name="rightTextSize"/>
<attr name="rightText"/>
</declare-styleable>
</resources>
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Created by feathers on 16-11-13.
*/
public class TopBar extends LinearLayout {
private int titleTextColor;
private int leftTextColor;
private int rightTextColor;
private String titleText;
private String rightText;
private String leftText;
private float titleTextSize;
private float rightTextSize;
private float leftTextSize;
private Button leftButton;
private Button rightButton;
private TextView title;
private TopbarClickListener topBarListener;
public TopBar(Context context) {
this(context, null);
}
public TopBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TopBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
// 取出TopBar拥有的所有属性
TypedArray arrays = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
// 根据属性名指定属性值,参数二为如果使用者没有指定这个属性的值的默认值
titleTextColor = arrays.getColor(R.styleable.TopBar_titleColor, Color.BLACK);
leftTextColor = arrays.getColor(R.styleable.TopBar_leftTextColor, Color.BLACK);
rightTextColor = arrays.getColor(R.styleable.TopBar_rightTextColor, Color.BLACK);
titleText = arrays.getString(R.styleable.TopBar_titleText);
rightText = arrays.getString(R.styleable.TopBar_rightText);
leftText = arrays.getString(R.styleable.TopBar_leftText);
titleTextSize = arrays.getDimension(R.styleable.TopBar_titleSize, 10);
rightTextSize = arrays.getDimension(R.styleable.TopBar_rightTextSize, 10);
leftTextSize = arrays.getDimension(R.styleable.TopBar_leftTextSize, 10);
// 获取TypedArray后,调用recycle方法来避免重新创建的时候的错误
arrays.recycle();
// 将值设置到对应的组件中
leftButton = new Button(context);
rightButton = new Button(context);
title = new TextView(context);
leftButton.setTextColor(leftTextColor);
leftButton.setText(leftText);
leftButton.setTextSize(leftTextSize);
title.setTextColor(titleTextColor);
title.setText(titleText);
title.setTextSize(titleTextSize);
rightButton.setText(rightText);
rightButton.setTextSize(rightTextSize);
rightButton.setTextColor(rightTextColor);
// 把定义好的控件添加到view group中
LayoutParams params = new LayoutParams(context, attrs);
params.gravity = Gravity.CENTER_VERTICAL; // 相当于linear layout layout_gravity属性
this.setOrientation(HORIZONTAL);
this.setLayoutParams(params);
LayoutParams params1 = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params1.gravity = Gravity.CENTER; // 相当于linear layout gravity属性
title.setGravity(Gravity.CENTER); // text view 的 gravity属性,让文字居中
this.addView(leftButton, params1);
this.addView(title, params1);
this.addView(rightButton, params1);
leftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
topBarListener.onLeftClickListener();
}
});
rightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
topBarListener.onRightClickListener();
}
});
}
public void setClickListener(TopbarClickListener listener) {
this.topBarListener = listener;
}
/**
* Created by feathers on 16-11-13.
*/
public static interface TopbarClickListener { //提供接口
void onLeftClickListener();
void onRightClickListener();
}
}
使用:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.feathers.demo.MainActivity">
<com.example.feathers.demo.TopBar
android:id="@+id/top_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:titleText="im Feathers"
app:titleColor="#000000"
app:titleSize="10dp"
app:leftText="厉害"
app:leftTextSize="10sp"
app:rightText="牛X"
app:rightTextSize="10sp">
</com.example.feathers.demo.TopBar>
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TopBar topBar = (TopBar) findViewById(R.id.top_bar);
topBar.setClickListener(new TopBar.TopbarClickListener() {
@Override
public void onLeftClickListener() {
Toast.makeText(MainActivity.this, "left", Toast.LENGTH_SHORT).show();
}
@Override
public void onRightClickListener() {
Toast.makeText(MainActivity.this, "right", Toast.LENGTH_SHORT).show();
}
});
}
}
重写view绘制新的控件
需要熟练使用Canvas等类,要求较高