这是我参与「第四届青训营 」笔记创作活动的第3天
本节课的主要内容:Android的常规与高级UI编程
1,Android UI组件
常规的UI组件大多由Android Framework中的android.widget这个package提供。如:文本组件,图片组件,按钮组件,输入框组件等。
常规UI组件大多是View,高级UI组件大多是ViewGroup、比常规UI组件有多的功能。如滑动组件,列表组建等
2,布局
常用的布局有以下四种:
1,LinearLayout
以水平/垂直方向排列组件
所有子视图在单个方向(垂直或水平)保持水平
适用于线性排列场景
2,RelativeLayout
通过相对父容器/兄弟组件位置排列组件
可以消除嵌套视图组并使布局层次结构保持扁平化,从而提高性能
一个RelativeLayout就可以替换多个嵌套的LinearLayout组
适用于复杂场景
3,FrameLayout
以层级叠加的方式排列组件
适用于层级排列场景
4,ConstrainLayout
通过约束组件位置排列组件
扩展布局方式
所有的视图均根据同级视图与父布局之间的关系进行布局
可使用扁平视图层次结构(无嵌套视图组)创建复杂的大型布局
为获得更好的性能和工具支持,推进使用ConstrainLayout
适用于复杂场景
3,渲染
布局页面是如何渲染出来的?
布局加载->布局解析->UI渲染
布局加载
编写布局文件->注册Manifest->设置布局文件
setContentView的主要作用是创建了DecorView,最终由LayoutInflater来加载了XML文件
LayoutInflater的主要作用是解析XML文件,并根据XML文件生成View实例,并将View实例添加到其ViewGroup中
XML中的View根据View类名来找到相应的View,并将XML中的描述属性解析为AttributeSet,并作为第二个参数传给View构造器,以此来生成实例的。
页面绘制流程
UI渲染流程
渲染流程
4,交互
常用交互事件监听器
view的交互
获取view对象--findViewByid
交互事件监听器:View.OnClickListener,View.OnLongClickListener...
触摸事件--MotionEvent---ACTION_DOWN,ACTION_UP,ACTION_MOVE...
事件分发:
顺序---Activity->ViewGroup->View
核心方法---dispatchTouchEvent,onlnterceptTouchEvent,onTouchEvent
5,动画
帧动画
补间动画
属性动画
动画总结
6,自义定UI
自义定ui为在我们需要根据我们相应的需求来定义的UI。具体使用方法如下;
创建View
public SwitchButton(Context context) {
super(context);
init(context, null);
}
public SwitchButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
处理View布局
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){
widthMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_WIDTH, MeasureSpec.EXACTLY);
}
if(heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST){
heightMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_HEIGHT, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
float viewPadding = Math.max(shadowRadius + shadowOffset, borderWidth);
height = h - viewPadding - viewPadding;
width = w - viewPadding - viewPadding;
...
}
绘制View
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制白色背景的圆角矩形
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.FILL);
paint.setColor(background);
drawRoundRect(canvas, left, top, right, bottom, viewRadius, paint);
//绘制关闭状态的边框
paint.setStyle(Paint.Style.STROKE);
paint.setColor(uncheckColor);
drawRoundRect(canvas,left, top, right, bottom, viewRadius, paint);
...
//绘制按钮左边绿色长条遮挡
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
drawArc(canvas, left, top, left + 2 * viewRadius, top + 2 * viewRadius,90, 180, paint);
canvas.drawRect( left + viewRadius, top,viewState.buttonX,
top + 2 * viewRadius,paint);
...
//绘制按钮
drawButton(canvas, viewState.buttonX, centerY);
}
处理用户交互
public boolean onTouchEvent(MotionEvent event) {
if(!isEnabled()) { return false; }
switch (actionMasked){
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_MOVE:
if(isPendingDragState()){ //在准备进入拖动状态过程中,可以拖动按钮位置
...
}else if(isDragState()){ //拖动按钮位置,同时改变对应的背景颜色
...
}
break;
case MotionEvent.ACTION_UP:
if(System.currentTimeMillis() - touchDownTime <= 300){ //点击时间小于300ms,认为是点击操作
toggle();
}else if(isDragState()){ //在拖动状态,计算按钮位置,设置是否切换状态
...
}
break;
case MotionEvent.ACTION_CANCEL:
removeCallbacks(postPendingDrag);
break;
}
return true;
}
处理动画
valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.setDuration(effectDuration);
valueAnimator.setRepeatCount(0);
valueAnimator.addUpdateListener(animatorUpdateListener);
// 点击开关后启动动画
valueAnimator.start();
// 简单动画更新回调,触发View绘制
private ValueAnimator.AnimatorUpdateListener animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
switch (animateState) {
...
case ANIMATE_STATE_SWITCH:
viewState.buttonX = beforeState.buttonX + (afterState.buttonX - beforeState.buttonX) * value;
float fraction = (viewState.buttonX - buttonMinX) / (buttonMaxX - buttonMinX);
viewState.checkStateColor = (int) argbEvaluator.evaluate( fraction,uncheckColor,checkedColor);
viewState.radius = fraction * viewRadius;
viewState.checkedLineColor = (int) argbEvaluator.evaluate(fraction,Color.TRANSPARENT, checkLineColor);
break;
}
postInvalidate();
}
};
自义定UI小结
7,总结
本次课程学习让我一个小白了解到了关于UI编程的部分知识,虽然并未全部弄懂,但是对大概框架以及有了一定了解,对布局,绘制,交互以及动画的编程原理有了一定认识,期待后期的深入学习以及其他知识的扩充。