View绘制
1 View树的绘制流程
measure -> layout -> draw 树形递归遍历
2 measure
ViewGroup.LayoutParams 设定宽高,match_parent, wrap_content
MeasureSpec 32位int,最高两位表示测量模式,后30位表示该模式下的尺寸大小
MeasureSpecMode:
EXACTLY 父容器为子视图确定容器的大小
AT_MOST 父容器为子视图指定一个最大尺寸
UNSPECIFIED
measure() -> onMeasure() -> setMeasuredDimension()
3 layout
树形结构递归遍历,位置摆放
4 onDraw
invalidate() 请求系统重新渲染,触发onDraw()
requestLayout() 绘画过程中手动调用该方法,触发onMeasure(), onLayout()
当更新View时,如果尺寸大小位置和内容均发生改变,推荐先调用requestLayout(),再invalidate()
事件分发机制
DecorView是PhoneWindow的内部类,PhoneWindow继承抽象类Window.
Activity -> PhoneWindow -> DecorView = StatusBar + RootView -> ViewGroup -> View
dispatchTouchEvent(VG) ->
onInterceptTouchEvent(VG) -> onTouchEvent (V)
ListView
recycleBin机制
recycleBin是AbsListView的内部类
ListView优化
- convertView重用,添加ViewHolder
- getView少做耗时操作
- 三级缓存
- 开启硬件加速
- 减少半透明元素
public View getView(.., View
covertView, ..) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflator….
… ...
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
}
class
ViewHolder { // 避免多次findViewById()
private ImageView image;
private TextView title;
}
解决在onCreate()过程中获取View的宽高为0的方法
1.监听draw/layout事件
view.getViewTreeObserver().addOnGlobalLayoutListener(
new
ViewTreeObserver.OnGlobalLayoutListener() {
2
@Override
3
public
void
onGlobalLayout() {
4 mScrollView.post(
new
Runnable() {
5
public
void
run() {
6 view.getHeight();
//
height is ready
7
}
8
});
9
}
10 });
2.View.post推荐
UI事件队列会按顺序处理事件。在
setContentView()
被调用后,事件队列中会包含一个要求重新layout的
message
,所以任何你post到队列中的东西都会在Layout发生变化后执行。
view.post(
new
Runnable() {
4
@Override
5
public
void
run() {
6 view.getHeight();
//
height is ready
7
}
8 });
大房子左右滑动,可点击空白区域
HorizontalScrollView
bitmap.getPixel
android:scrollX
UI线程和工作线程中刷新UI
postInvalidate() invalidate()
雾霾不用于多fragment间切换,并及时销毁
mHazeImage.recycle()
;
自定义控件的属性
<declare-styleable
name=
"ScrollLayout"
>
<attr
name=
"defaultScreen"
format=
"integer"
/>
<attr
name=
"customScreen"
format=
"integer"
/>
<attr
name=
"customScreenHeight"
format=
"integer"
/>
<attr
name=
"customScreenScale"
format=
"integer"
/>
</declare-styleable>
<com.honeywell.hch.airtouchv2.framework.view.ScrollLayout
android
:id=
"@+id/control_filter_scroll"
android
:layout_width=
"match_parent"
android
:layout_height=
"match_parent"
android
:orientation=
"vertical"
app
:customScreen=
"1"
app
:customScreenHeight=
"70"
app
:defaultScreen=
"0
”
>
帧动画
ImageView loadingImageView = (ImageView) findViewById(R.id.
enroll_loading_iv)
;
AnimationDrawable animationDrawable = (AnimationDrawable) loadingImageView.getDrawable()
;
animationDrawable.start()
;
<animation-list
android
:oneshot=
"false"
>
<item
android
:drawable=
"@drawable/enroll_loading_1"
android
:duration=
"1000"
></item>
<item
android
:drawable=
"@drawable/enroll_loading_2"
android
:duration=
"1000"
></item>
<item
android
:drawable=
"@drawable/enroll_loading_3"
android
:duration=
"1000"
></item>
</animation-list>
属性动画
AnimatorSet popAnimation =
new AnimatorSet()
;
popAnimation.playTogether(ObjectAnimator.
ofFloat(view
,
"alpha"
,
0
,
1)
, ObjectAnimator
.
ofFloat(view
,
"translationY"
, screenHeight - positionY
,
0))
;
popAnimation.setInterpolator(
new OvershootInterpolator())
;
popAnimation.setDuration(
mPopUpDuration[index])
;
popAnimation.start()
;
popAnimation.addListener(
new
Animator.AnimatorListener() {
动态加载View
<LinearLayout
android
:id=
"@+id/all_group_layout"
android
:layout_width=
"match_parent"
android
:layout_height=
"wrap_content"
android
:orientation=
"vertical"
>
<LinearLayout
android
:id=
"@+id/group_layout"
android
:layout_width=
"match_parent"
android
:layout_height=
"wrap_content"
android
:orientation=
"vertical"
>
</LinearLayout>
<LinearLayout
android
:id=
"@+id/un_group_layout"
android
:layout_width=
"match_parent"
android
:layout_height=
"wrap_content"
android
:orientation=
"vertical"
>
</LinearLayout>
</LinearLayout>
// add DIY views to one group
LinearLayout oneGroup =
new LinearLayout(
this)
;
oneGroup.setOrientation(LinearLayout.
VERTICAL)
;
AllDeviceGroupTitleView groupTitleView =
new AllDeviceGroupTitleView(
mContext)
; // 自定义控件
groupTitleView.setBackgroundResource(
R.drawable.
all_device_group_bg
)
;
groupTitleView.setGroupName(groupName)
;
oneGroup.addView(groupTitleView)
;
// add one group to group list
mGroupLayout
= (LinearLayout) findViewById(R.id.
group_layout
)
;
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams
(LinearLayout.LayoutParams.
MATCH_PARENT
, LinearLayout.LayoutParams.
WRAP_CONTENT)
;
params.
bottomMargin = DensityUtil.
dip2px(
20)
;
oneGroup.setLayoutParams(params)
;
mGroupLayout.addView(oneGroupLayout)
; // 注意区分点击Group还是Device
onTouchEvent()/onTouch() /onLongClick()返回true/false问题
返回true表示该事件已消费,不会被传导下去。
返回false表示事件没有完全消费,将继续传递到子控件处理。