继承已知Layout(如RelativeLayout和LinearLayout)
创建构造函数:
//该构造函数一般在Java文件中创建控件使用
public DefinedView( Context context ){
super( context );
LayoutInflater.from(context).inflate(R.layout.definedview, this);
...
//或者直接调用下一个函数,即使用以下代码:
this( context, null );
}
//该构造函数一般在xml文件中创建控件时候调用
public DefinedView( Context context, AttributeSet attrs){
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.definedview, this);
【1】...
}
对于这个构造函数,因为会在这里读取attrs文件来获得各项参数,所以还需要进行一系列复杂的操作:
1. 在res/values目录下创建一个attrs_definedview.xml文件,以resources作为最外层根标签,并定义styleable标签,如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DefinedView">
<attr name="textSize" format="dimension"/>
<attr name="text" format="string" />
<attr name="viewColor" format="color" />
<!--
dimension表示尺寸,一般是一些以dp,sp,px等单位的内容
string表示字符串,一般是text的内容
color表示颜色,一般是背景等的内容
-->
</declare-styleable>
</resources>
值得注意的是,在这里所创建的styleable name必须和控件的名称保持一致,才能够顺利的使用xml文件来指定该控件的某些属性。
2. 在构造函数(在之前的【1】处)中读取所创建的styleable内容
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.DefinedView);
//解析String
String text = array.getString(R.styleable.DefinedView_text);
if(text == null || text.isEmpty() )
text = "default text";
//解析Dimension
float textSize = array.getDimension(R.styleable.DefinedView_textSize, 20sp);
//解析color,后一个值表示未设置时的默认值
int color = array.getColor(R.styleable.DefinedView_viewColor, Color.RED);
//务必记得调用recycle()
array.recycle();
创建点击响应
Android的事件分发
- 分发函数
事件分发一般会分发给几个函数来处理:该View中用户所设定的onTouchListener的onTouch()函数,该View中系统自带的onTouchEvent()函数,该View中系统自带的dispatchTouchEvent()函数和只存在与ViewGroup和Activity中的onInterceptTouchEvent()函数。
- 分发函数返回值
对于以上函数而言,其返回值与作用如下
函数名称 | 返回true | 返回false |
---|---|---|
dispatchTouchEvent | 事件分配完成,本身不处理 | 事件分配失败,交由自身处理 |
onTouch | 事件处理完成 | 事件处理失败(或用户未定义) |
onTouchEvent | 事件处理完成 | 事件处理失败 |
onInterceptTouchEvent | 拦截事件自己处理 | 不拦截,分发事件 |
值得注意的是,onTouchEvent()函数是系统已经定义好的函数,对于View默认返回值时true,对于ViewGroup默认返回值时false。
- Event的分发顺序:
- Activity获取事件以后调用dispatchTouchEvent()进行事件分发。
- ViewGroup获取事件以后同样调用dispatchTouchEvent()尝试分发,而在dispatchTouchEvent()中,则会分别进行如下步骤:
1、onFilterTouchEventForSecurity()
//是否需要分发事件。当View上层有遮蔽且设置android:filterTouchesWhenObscured
//[对应Java文件内调用函数:setFilterTouchesWhenObscured(boolean) ]为true时,不分发事件,
函数返回false;否则需要分发事件,则进行目标和状态清空等操作。
2、onInterceptTouchEvent()
//是否需要拦截事件,返回true表示事件被拦截,不再分发给子View;默认
//值是false。
3、进行事件分发.
//如果之前的1、2步均表示需要进行事件分发,则此时将事件分发给其子
//View(ViewGroup)。
4、检查子View是否处理事件。
//如果子View顺利处理了该事件(即子View的onTouch或者onTouchEvent()
//返回了true),则此时dispatchTouchEvent()函数返回true,分发结束。
5、onTouchListener.onTouch()
//如果所有子View(ViewGroup)均表示处理不了,则由该ViewGroup自己调
//用其设定的onTouchListnener的onTouch函数,当返回true时表示事件处
//理完成,dispatchTouchEvent函数返回true,分发结束。
6、ViewGroup.onTouchEvent()
在这个过程中捕捉到ACTION_DOWN和ACTION_UP后会执行onClick()
//如果之前onTouch()也表示处理不了,或者压根就没设置,这时则由
//ViewGroup中已经定义好的onTouchEvent()函数来处理事件,当返回true
//时表示事件处理完成,函数返回true,分发成功结束;而当返回false的时候
//ViewGroup表示自己也未处理,则dispatchTouchEvent()函数返回false
View的点击位置及函数获取值
调用event.getX()和event.getY()获取到的X和Y的值均为相对于当前View的坐标,所以ACTION_MOVE和ACTION_UP这两个操作所得到的值均有可能超出View的范围,其值也有可能小于0。