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表示事件没有完全消费,将继续传递到子控件处理。