自定义View学习笔记1
自定义View
自定义View是原生系统的控件无法满足,需要开发者自己去实现;
1.组合控件;
2.继承控件;
3.继承View。
onMeasure()
在绘制View时,一定会执行的方法,布局的宽高都是通过这个指定的
// 获取宽高的模式
int withMode = MeasureSpec.getMode(withMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
通过对宽高模式的对比可以进行执行某些操作,例如:
if(withMode == MeasureSpec.AT_MOST){
//编写执行相应的操作
}
其中宽高的模式有三种
MeasureSpec.AT_MOST:
布局中指定wrap_content
MeasureSpec.EXACTLY:
布局中指定确切的值、match_parent、fill_parent
MeasureSpec.UNSPECIFIED:
尽可能的大,比较少用到,ListView、ScrollView 在测量子布局的时候会用到
引出问题:ScrollView嵌套LIstView显示不全
解决方法:
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//解决显示不全的问题
//重新设置高度 heightMeasureSpec是个32位的值 其中的前30位现在变成了Integer.MAX_VALUE >> 2 , 后2位是信息的宽高模式MeasureSpec.AT_MOST
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2 ,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
onDraw()
/**
* 用于绘制
* @param canvas
* */
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画文本
canvas.drawText();
//画弧
canvas.drawArc();
//画圆
canvas.drawCircle();
//...
//...
}
onTouchEvent()
处理用户触摸事件
/**
* 处理跟用户交互,手指触摸等
* @param ev
* @return 此处涉及到一个设计模式:责任链模式
* 比如用户在屏幕上按下后滑动再起来这个过程,
* 如果返回true底层才会去不断循环用户事件的操作 如果返回false则只响应按下的操作就结束 如果返回super.onTouchEvent(ev)则交给父类去处理
* */
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
//手指在屏幕上按下
break;
case MotionEvent.ACTION_MOVE:
//手指在屏幕上移动
break;
case MotionEvent.ACTION_UP:
//手指在屏幕上抬起
break;
}
return super.onTouchEvent(ev);
}
//源码内容 当target不为空时则一直向下一个目标责任去取,直到执行完毕,所以如果外层返回了false则不再继续向下取责任,所以只执行一次
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
自定义属性
1.声明命名控件,然后在自己的自定义view中使用
xmlns:app="http://schemas.android.com/apk/res-auto"
<com.xxxx.MyTextView
app:myText="hello"
app:myTextColor="#878787"
app:myTextSize="15sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
自定义View中获取属性
//获取自定义属性
TypeArray array = context.obtainStyledAttributes(attrs,R.styleable.MyTextView);
mText = array.getString(R.styleable.MyTextView_myText);
mTextColor = array.getColor(R.styleable.MyTextView_myTextColor,mTextColor);
mTextSize = array.getDimensionPixelSize(R.styleable.MyTextView_myTextSize,mTextSize);
//回收
array.recycle();
以上是对Android/自定义控件/自定义View 的笔记1