1. 自定义View基础
1.1 分类
自定义View的实现方式有以下几种
1.2 View绘制流程
View的绘制基本由measure()、layout()、draw()这个三个函数完成
函数 | 作用 | 相关方法 |
---|---|---|
measure() | 测量View的宽高 | measure(),setMeasuredDimension(),onMeasure() |
layout() | 计算当前View以及子View的位置 | layout(),onLayout(),setFrame() |
draw() | 视图的绘制工作 | draw(),onDraw() |
绘制的基本要素:
-
重写
onDraw()
-
使用 Canvas 来绘制
-
使用 Paint 来配置
-
坐标系
-
尺寸单位是像素,而不是 dp。转换方式:
-
public static float dp2px(float dp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); }
在Android坐标系中,以屏幕左上角作为原点,这个原点向右是X轴的正轴,向下是Y轴正轴。如下所示:
除了Android坐标系,还存在View坐标系,View坐标系内部关系如图所示。
View获取自身高度
由上图可算出View的高度:
- width = getRight() - getLeft();
- height = getBottom() - getTop();
View的源码当中提供了getWidth()和getHeight()方法用来获取View的宽度和高度,其内部方法和上文所示是相同的,我们可以直接调用来获取View得宽高。
View自身的坐标
通过如下方法可以获取View到其父控件的距离。
- getTop();获取View到其父布局顶边的距离。
- getLeft();获取View到其父布局左边的距离。
- getBottom();获取View到其父布局顶边的距离。
- getRight();获取View到其父布局左边的距离。
2. 自定义组合控件
组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件。比如很多应用中普遍使用的标题栏控件,其实用的就是组合控件,那么下面将通过实现一个简单的标题栏自定义控件来说说组合控件的用法。
1、新建一个Android项目,创建自定义标题栏的布局文件title_bar.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#0000ff" >
<Button
android:id="@+id/left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_margin="5dp"
android:background="@drawable/back1_64" />
<TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="这是标题"
android:textColor="#ffffff"
android:textSize="20sp" />
</RelativeLayout>
可见这个标题栏控件还是比较简单的,其中在左边有一个返回按钮,背景是一张事先准备好的图片back1_64.png,标题栏中间是标题文字。
2、创建一个类TitleView,继承自RelativeLayout:
public class TitleView extends RelativeLayout {
// 返回按钮控件
private Button mLeftBtn;
// 标题Tv
private TextView mTitleTv;
public TitleView(Context context, AttributeSet attrs) {
super(context, attrs);
// 加载布局
LayoutInflater.from(context).inflate(R.layout.title_bar, this);
// 获取控件
mLeftBtn = (Button) findViewById(R.id.left_btn);
mTitleTv = (TextView) findViewById(R.id.title_tv);
}
// 为左侧返回按钮添加自定义点击事件
public void setLeftButtonListener(OnClickListener listener) {
mLeftBtn.setOnClickListener(listener);
}
// 设置标题的方法
public void setTitleText(String title) {
mTitleTv.setText(title);
}
}
在TitleView中主要是为自定义的标题栏加载了布局,为返回按钮添加事件监听方法,并提供了设置标题文本的方法。
3、在activity_main.xml中引入自定义的标题栏:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.test.TitleView
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</com.example.test.TitleView>
</LinearLayout>
4、在MainActivity中获取自定义的标题栏,并且为返回按钮添加自定义点击事件:
private TitleView mTitleBar;
mTitleBar = (TitleView) findViewById(R.id.title_bar);
mTitleBar.setLeftButtonListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "点击了返回按钮", Toast.LENGTH_SHORT)
.show();
finish();
}
});
5、运行效果如下:
这样就用组合的方式实现了自定义标题栏,其实经过更多的组合还可以创建出功能更为复杂的自定义控件,比如自定义搜索栏等。
3. 继承控件
就是继承已有的控件,创建新控件,保留继承的父控件的特性,并且还可以引入新特性。
3.1 继承View类系统控件
3.1.1 自定义属性
Android系统的控件以android开头的都是系统自带的属性。为了方便配置自定义View的属性,我们也可以自定义属性值。
Android自定义属性可分为以下几步:
- 自定义一个View
- 编写values/attrs.xml,在其中编写styleable和item等标签元素
- 在布局文件中View使用自定义的属性(注意namespace)
- 在View的构造方法中通过TypedArray获取
属性声明
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="test">
<attr name="text" format="string" />
<attr name="testAttr" format="integer" />
</declare-styleable>
</resources>
自定义view继承TextView
public class MyTextView extends TextView{
private static final String TAG = MyTextView.class.getSimpleName();
//在View的构造方法中通过TypedArray获取
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
String text = ta.getString(R.styleable.test_testAttr);
int textAttr = ta.getInteger(R.styleable.test_text, -1);
Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
ta.recycle();
}
}
在布局中使用
<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/com.example.test"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.test.MyTextView
android:layout_width="100dp"
android:layout_height="200dp"
app:testAttr="520"
app:text="helloworld" />
</RelativeLayout>
3.2 继承ViewGoup类系统控件
1.创建属性(这里我们定义了三个属性,文字内容、颜色以及要显示的元素。)
<resources>
<declare-styleable name="HeaderBar">
<attr name="title_text_clolor" format="color"></attr>
<attr name="title_text" format="string"></attr>
<attr name="show_views">
<flag name="left_text" value="0x01" />
<flag name="left_img" value="0x02" />
<flag name="right_text" value="0x04" />
<flag name="right_img" value="0x08" />
<flag name="center_text" value="0x10" />
<flag name="center_img" value="0x20" />
</attr>
</declare-styleable>
</resources>
2.在java代码中进行设置
public class YFHeaderView extends RelativeLayout {
private ImageView img_left;
private TextView text_center;
private ImageView img_right;
private RelativeLayout layout_root;
private Context context;
String element;
private int showView;
public YFHeaderView(Context context) {
super(context);
this.context = context;
initView(context);
}
public YFHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView(context);
initAttrs(context, attrs);
}
public YFHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView(context);
initAttrs(context, attrs);
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.HeaderBar);
String title = mTypedArray.getString(R.styleable.HeaderBar_title_text);
if (!TextUtils.isEmpty(title)) {
text_center.setText(title);
}
showView = mTypedArray.getInt(R.styleable.HeaderBar_show_views, 0x26);
text_center.setTextColor(mTypedArray.getColor(R.styleable.HeaderBar_title_text_clolor, Color.WHITE));
mTypedArray.recycle();
showView(showView);
}
private void showView(int showView) {
Long data = Long.valueOf(Integer.toBinaryString(showView));
element = String.format("%06d", data);
for (int i = 0; i < element.length(); i++) {
if(i == 0) ;
if(i == 1) text_center.setVisibility(element.substring(i,i+1).equals("1")? View.VISIBLE:View.GONE);
if(i == 2) img_right.setVisibility(element.substring(i,i+1).equals("1")? View.VISIBLE:View.GONE);
if(i == 3) ;
if(i == 4) img_left.setVisibility(element.substring(i,i+1).equals("1")? View.VISIBLE:View.GONE);
if(i == 5) ;
}
}
private void initView(final Context context) {
LayoutInflater.from(context).inflate(R.layout.view_header, this, true);
img_left = (ImageView) findViewById(R.id.header_left_img);
img_right = (ImageView) findViewById(R.id.header_right_img);
text_center = (TextView) findViewById(R.id.header_center_text);
layout_root = (RelativeLayout) findViewById(R.id.header_root_layout);
layout_root.setBackgroundColor(Color.BLACK);
text_center.setTextColor(Color.WHITE);
img_left.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, element + "", Toast.LENGTH_SHORT).show();
}
});
}
private void setTitle(String title) {
if (!TextUtils.isEmpty(title)) {
text_center.setText(title);
}
}
private void setLeftListener(OnClickListener onClickListener) {
img_left.setOnClickListener(onClickListener);
}
private void setRightListener(OnClickListener onClickListener) {
img_right.setOnClickListener(onClickListener);
}
}
3.在布局文件中进行设置
<com.example.yf.view.YFHeaderView
android:layout_width="match_parent"
android:layout_height="45dp"
app:title_text="标题"
app:show_views="center_text|left_img|right_img">
</com.example.yf.view.YFHeaderView>
下一篇会介绍直接继承view或者viewgoup,包括动画,属性动画,触摸事件响应,触摸反馈,单点触摸,多点触摸等。