Android的绘制系统(ActivityManagerService)
Android的事件系统(InputManagerService)
MeasureSpecs维护一个int型(32bit)数据,其中2bit表示mode,30bit表示size
size = MeasureSpec.getSize(measureSpec)
mode = MeasureSpec.getMode(measureSpec)
measureSpec = MeasureSpec.makeMeasureSpec(size, mode)
getDefaultSize(size,measureSpec)//根据View默认大小size和父类measureSpec来测量当前View的大小
例子:修改ListView的onMeasure使得ListView完全展开
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//int widthMeasureSpec, int heightMeasureSpec是父容器的测量spec,现将父容器的高度测量spec修改成足够大,并且是AT_MOST模式,这样子容器即ListView认为父容器有足够大的空间就会将所有的内容完全展开了。
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
view的layout过程:layout(确定自身的位置)->onLayout(空实现)
ViewGroup的layout过程:layout(确定自身的位置)->onLayout(确定子控件的位置,这里实现具体布局,并且会遍历子控件的layout对子控件执行layout)
例子:简单实现一个类似LinearLayout的横向布局
public class CustomViewGroup extends ViewGroup {
private final static String TAG = "CustomViewGroup";
// view 的间隔
private final static int VIEW_MARGIN = 0;
public CustomViewGroup(Context context) {
super(context);
}
// 重写它的onMeasure() 在该方法中进行对子View的大小进行测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
for (int index = 0; index < getChildCount(); index++) {
final View child = getChildAt(index);
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// 重写onLayout方法实现子View的定位
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
int row = 0;
//当前宽度
int lengthX = l;
//当前高度
int lengthY = t;
for (int i = 0; i < count; i++) {
final View child = this.getChildAt(i);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
lengthX += width + VIEW_MARGIN;
lengthY = row * (height + VIEW_MARGIN) + VIEW_MARGIN + height
+ t;
if (lengthX > r) {
lengthX = width + VIEW_MARGIN + l;
row++;
lengthY = row * (height + VIEW_MARGIN) + VIEW_MARGIN + height
+ t;
}
child.layout(lengthX - width, lengthY - height, lengthX, lengthY);
}
}
}
PS:invalidate最后也是通过调用rootView的performTraversals()来刷新视图的;而invalidate只会重新执行onDraw;requestLayout才会重新执行整个绘制过程
// 如果View是在Java代码里面new的,则调用第一个构造函数
public CarsonView(Context context) {
super(context);
}
// 如果View是在.xml里声明的,则调用第二个构造函数
// 自定义属性是从AttributeSet参数传进来的
public CarsonView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 不会自动调用
// 一般是在第二个构造函数里主动调用
// 如View有style属性时
public CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//API21之后才使用
// 不会自动调用
// 一般是在第二个构造函数里主动调用
// 如View有style属性时
public CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
view.getTop() 子View左上角距父View顶部的距离
view.getBottom() 子View右下角距父View顶部的距离
view.getLeft() 子View左上角距父View左侧
view.getRight() 子View右下角距父View左侧
event.getX()/event.getY() 触摸点相对于其所在组件坐标系的坐标
event.getRawX()/event.getRawY() 触摸点相对于屏幕默认坐标系的坐标
layout
offsetTopAndBottom、offsetLeftAndRight
scrollTo、scrollBy
LayoutParams
Scroller
在values目录下创建自定义属性的xml文件attrs_circle_view.xml
常用单位format:color boolean dimension float integer string fraction(百分比) enum(eg:orientation,单选) flag(eg:adjustResize|adjustPan,多选)
布局中进行设置
在代码中解析获取
mColor = a.getColor(R.styleable.CircleView_circle_color,Color.RED);
或者 直接在xml中使用(这种方式属性必须赋值否则会出错)
PS:还可以在lib中设置一个默认的style样式,在View直接取过来用。业务层需要使用时只需写一个同名的style则可覆盖之,达到全局修改样式的效果。
同样的也可用AnimationDrawable实现
平移动画TranslateAnimation、缩放动画ScaleAnimation、旋转动画RotateAnimatioin、透明度动画AlphaAnimation
自定义动画:通过矩阵变换来实现,可用来做3D动画!继承Animation,实现initialize(初始化工作)、applyTransFormation(动画实现)即可。
常见属性
自定义补间动画
public class MyAnimation extends Animation
{
@Override
protected void applyTransformation(float interpolatedTime,Transformation transformation){
//interpolatedTime表示动画的时间进行比,transformation表示补间动画在不同时刻对View的变形程度
}
}
为容器型控件里的子View设置动画 LayoutAnimationController
xml实现
android:delay子类动画时间间隔
android:animationOrder="random" 子类的显示方式 normal默认random随机reverse倒序
android:animation="@anim/slide_right" 表示孩子显示时的具体动画是什么
AnimationSet
ObjectAnimator//
.ofFloat(view, "rotationX", 0.0F, 360.0F)//
.setDuration(500)//
.start();
一次性修改多个属性
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
0f, 1f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
0, 1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY,pvhZ).setDuration(1000).start();
ValueAnimator animator = ValueAnimator.ofFloat(0, mScreenHeight
- mBlueBall.getHeight());
animator.setTarget(mBlueBall);
animator.setDuration(1000).start();
// animator.setInterpolator(value)
animator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
mBlueBall.setTranslationY((Float) animation.getAnimatedValue());
}
});
本质上过渡动画也还是属性动画,只不过做了一层封装,方便实现Activity和View的过渡效果
Interpplator 插值器
TypeEvaluator 类型估值,主要用于设置动画操作属性的值。设置 属性值 从初始值过渡到结束值 的变化具体数值
// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {
// 复写evaluate()在里面写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
//fraction为插值器getInterpolation()的返回值,startValue/endValue为初始值/结束值
// 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
// 根据fraction来计算当前动画的x和y的值
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
// 将计算后的坐标封装到一个新的Point对象中并返回
Point point = new Point(x, y);
return point;
}
}
overridePendingTransition