基础知识
Android的绘制系统(ActivityManagerService)
Android的事件系统(InputManagerService)
measure
MeasureSpecs
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的大小
一个View的大小由父容器的MeasureSpec和自身的LayoutParams决定
测量过程
例子:修改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);
}
layout
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);
}
}
}
draw
PS:invalidate最后也是通过调用rootView的performTraversals()来刷新视图的;而invalidate只会重新执行onDraw;requestLayout才会重新执行整个绘制过程
自定义View的四种构造函数及常见函数
// 如果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() 触摸点相对于屏幕默认坐标系的坐标
view的移动方式
layout
offsetTopAndBottom、offsetLeftAndRight
scrollTo、scrollBy
LayoutParams
Scroller
颜色
自定义属性
在values目录下创建自定义属性的xml文件attrs_circle_view.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircleView">
<attr name="circle_color" format="color"/>
</declare-styleable>
</resources>
常用单位format:color boolean dimension float integer string fraction(百分比) enum(eg:orientation,单选) flag(eg:adjustResize|adjustPan,多选)
布局中进行设置
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<scut.carson_ho.diy_view.CircleView
app:circle_color="#FF4081"
/>
</RelativeLayout>
在代码中解析获取
mColor = a.getColor(R.styleable.CircleView_circle_color,Color.RED);
或者 直接在xml中使用(这种方式属性必须赋值否则会出错)
<TextView
android:id="@+id/dialog_loading_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/circle_color"/>
PS:还可以在lib中设置一个默认的style样式,在View直接取过来用。业务层需要使用时只需写一个同名的style则可覆盖之,达到全局修改样式的效果。
其他小细节
- post替代handler,View的内部本身提供了post系列的方法,完全可以替代Handler的作用,使用起来更加方便、直接。
- onAttachedToWindow,调用的时机是当包含View的Activity启动的时刻,适合启动线程/动画/注册各种东东
- onDetachedFromWindow,调用的时机是当包含View的Activity退出或当前View被remove的时刻,适合结束线程/动画/注销各种东东
帧动画
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/a_0"
android:duration="100" />
<item
android:drawable="@drawable/a_1"
android:duration="100" />
<item
android:drawable="@drawable/a_2"
android:duration="100" />
</animation-list>
同样的也可用AnimationDrawable实现
补间动画
平移动画TranslateAnimation、缩放动画ScaleAnimation、旋转动画RotateAnimatioin、透明度动画AlphaAnimation
自定义动画:通过矩阵变换来实现,可用来做3D动画!继承Animation,实现initialize(初始化工作)、applyTransFormation(动画实现)即可。
常见属性
- < translate>
■ 表示x的起始值
■ 表示x的结束值
■ 表示y的起始值
■ 表示y的结束值 - < scale>
■ fromX 水平方向缩放起始值
■ toX 水平方向缩放结束值
■ fromY
■ toY
■ pivotX 缩放的轴点的x坐标
■ pivotY 缩放的轴点的y坐标
■ pivotXType x轴的所发模式,即中心点相对于哪个物体Animation.ABSOLUTE/RELATIVE_TO_SELF/RELATIVE_TO_PARENT
■ pivotYType y轴的所发模式 - < rotate>
■ fromeDegres旋转开始的角度
■ toDegrees
■ pivotX旋转的轴点的x坐标
■ pivotY - < alpha>
■ fromAlpha透明度起始值
■ toAlpha透明度结束值 - 共有属性
■duration动画持续时间
■ fillAfter动画结束后是否停留在结束位置
自定义补间动画
public class MyAnimation extends Animation
{
@Override
protected void applyTransformation(float interpolatedTime,Transformation transformation){
//interpolatedTime表示动画的时间进行比,transformation表示补间动画在不同时刻对View的变形程度
}
}
布局动画LayoutAnimation
为容器型控件里的子View设置动画 LayoutAnimationController
xml实现
android:delay子类动画时间间隔
android:animationOrder="random" 子类的显示方式 normal默认random随机reverse倒序
android:animation="@anim/slide_right" 表示孩子显示时的具体动画是什么
组合动画
AnimationSet
属性动画
ObjectAnimator
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
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());
}
});
常见属性
Transtion 过度动画
本质上过渡动画也还是属性动画,只不过做了一层封装,方便实现Activity和View的过渡效果
绘制辅助
Interpplator 插值器
- AccelerateDecelerateInterpolator:开始与结束时速度快,中间慢
- AccelerateInterpolator:开始慢然后加速
- CycleInterpolator:动画循环播放特定的次数,速率改变沿着正弦曲线
- DecelerateInterpolator:开始速度快然后减速
- LinearInterpolator:均匀的改变速度
TypeEvaluator 类型估值,主要用于设置动画操作属性的值。设置 属性值 从初始值过渡到结束值 的变化具体数值
- IntEvaluator Int类型估值器,返回int类型的属性改变
- FloatEvaluator Float类型估值器,返回Float类型属性改变
- ArgbEvaluator 颜色类型估值器
// 实现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;
}
}
Activity转场动画
overridePendingTransition