View的绘制流程 ViewRootImpl.java
--> requestLayout() // 绘制UI入口类。
-- scheduleTraversals()
-- mChoreographer.postCallback()
-- TraversalRunnable
-- doTraversal()
-- performTraversals()-- performMeasure()
第一步: performMeasure() 用于指定和测量 layout 中所有控件的宽高。根据测量模式来制定。
对于ViewGroup: 先去测量所有子View,然后根据子孩子的宽高 再来计算和指定自己的宽高。
对于View: 它的宽高是由自己 和 父布局(mode) 决定的。
第二步: performLayout() 用于摆放子布局,
for循环所有子View,调用child.layout()摆放childView.
-- View::host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight)
View::onLayout(changed, l, t, r, b) onLayout是接口, 具体不同实现类自己实现。
LinearLayout::onLayout()
-- layoutVertical(l,t, r,b)
获取childWidth, childHeight
-- setChildFrame()-- child.layout // for循环,摆放子布局(非Gone的状态时),
第三步:performDraw() 用于绘制自己布局,还有子View,
线绘制自己的背景,然后for循环绘制子view,调用子view的draw方法,对于view绘制自己的背景,绘制自己显示的内容(TextView)。
-- ViewRootImpl::draw(fullRedrawNeeded)
-- drawSoftware()
-- mView.draw(canvas) // mView是根布局DecorView
-- View::draw() // 里面的绘制(1-6),使用了模板设计模式。AsyncTask也使用了模板。
-- drawBackground() // 画背景
-- onDraw(canvas) // 画自己。ViewGroup默认,不会调用 onDraw()
-- dispatchDraw(canvas) // 循环遍历 画子view,不断循环调用子view的 draw()方法。
-- ViewGroup::drawChild() // for循环调用。
-- ViewGroup::child.draw()
其他:
- 获取View的宽高,前提是需要调用其测量方法,测量完毕后,才能获取宽高值。
- View的绘制流程,是在onResume()之后才开始的, (Activity启动流程的源码)
- addView, setVisibility..., 都会调用requestLayout(), 会重新走一遍View的绘制流程。
- 优化时,根据直到的源代码,去优化。onDraw(), 尽量不要布局嵌套、
2. 自定义ViewGroup,流式布局、Adapter模式
- onMeasure 指定宽高:
for循环测量子view,
根据子view计算和指定自己的布局。
- onMeasure 指定宽高:
- onLayout() 摆放
for循环,摆放所有的子view
- onLayout() 摆放
- onDraw() 不需要调用。样式由子view决定。背景是ViewGroup-View自带的。
3. 采用Adapter设计模式,不采用传字符数组,参考ListView的Adapter 去实现。
cmts:
利他主义,公司发展好了,个人才能发展好。
组件+模块部分:模块化之间,需要通信。 最终生成app
app基础库:
网络引擎实现(ok,Retrofit)、Base类(与业务逻辑有关)、
DB数据工厂、插件化封装、具体注解配置、图片加载切换工厂、Fragment管理
面相切面网络监测、Builder头部标题栏(多种方式)
通用基础库:
网络请求引擎、DB数据库、万能Dialog、注解引擎接口规范、
RecyclerView添加底部头部、Base基类(与业务逻辑无关部分)、标题栏头部规范、MVP基础框架、万能列表Adapter。
热修复,增量更新。
public abstract class BaseAdapter {
public abstract int getCount();
// 通过pos,获取View。
public abstract View getView(int position, ViewGroup parent);
// 观察者模式,及时通知更新UI。参考ListView
public void unregisterDataSetObserver(DataSetObserver observer) {
}
public void registerDataSetObserver(DataSetObserver observer) {
}
}
public class MainActivityUI09 extends AppCompatActivity {
private TagLayout mTagLayout;
private List<String> mData = new ArrayList();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_ui09);
mData.add("Aaaa");
mData.add("Abbbbb");
mData.add("Acccccc");
mData.add("Addddddd");
mData.add("Aeeeeeeee");
mData.add("Afff");
mData.add("Aggg");
mData.add("Ahhh");
mData.add("Aiiisadfasdfasdf");
mTagLayout = findViewById(R.id.tag_layout);
mTagLayout.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return mData.size();
}
@Override
public View getView(int position, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(MainActivityUI09.this);
TextView tagTv = (TextView) inflater.inflate(R.layout.item_tag, parent, false);
tagTv.setText(mData.get(position));
// tagTv.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// }
// });
// 返回具体的childView.
return tagTv;
}
});
}
}
/**
* Added by Tom on 2024/06/07.
* 流式布局
*/
public class TagLayout extends ViewGroup {
private List<List<View>> mChildViews = new ArrayList<>();
private BaseAdapter mAdapter;
public TagLayout(Context context) {
this(context, null);
}
public TagLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mChildViews.clear(); // 清空集合
int childCount = getChildCount();
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = getPaddingTop() + getPaddingBottom(); // 待计算的高度
int lineWidth = getPaddingLeft(); // 一行的宽度
ArrayList<View> childViews = new ArrayList(); // 一行中的元素
mChildViews.add(childViews);
int maxHeight = 0; // 子view高度不一致处理
for (int i = 0; i < childCount; i++) {
// 2.1 for循环测量子view,
View childView = getChildAt(i);
if (childView.getVisibility() == View.GONE) {
continue; // 不可见,跳出循环。
}
// measureChild 执行后,可以获取View的宽高,因为会调用子view的onMeasure方法。
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
// 获取margin值 MarginLayoutParams, 参考LinearLayout 会复写,generate
ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
// 2.2 根据子view计算和指定自己的布局。
// 一行不够时,需要换行。
if (lineWidth + (childView.getMeasuredWidth() + params.leftMargin + params.rightMargin) > width) {
// 换行,高度累加. 宽度重新赋值。
lineWidth = childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
childViews = new ArrayList<>(); // 重新new 一个List。
mChildViews.add(childViews);
height += maxHeight;
maxHeight = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
} else { // 不换行。
lineWidth += childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
maxHeight = Math.max(maxHeight, childView.getMeasuredHeight() + params.topMargin + params.bottomMargin);
}
childViews.add(childView);
}
height += maxHeight;
// 设置自己的宽高
setMeasuredDimension(width, height);
Log.d("TAG", "w:" + width + " h:" + height + " lines:" + mChildViews.size());
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return super.generateLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left, top = getPaddingTop(), right, bottom;
for (List<View> childViews : mChildViews) { // 摆放所有行
left = getPaddingLeft(); // 换行
int maxHeight = 0; // 一行的高度
for (View childView : childViews) { // 摆放一行内所有元素。
if (childView.getVisibility() == View.GONE) {
continue; // 不可见,跳出循环。
}
ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
left += params.leftMargin;
int childTop = top + params.topMargin;
right = left + childView.getMeasuredWidth();
bottom = childTop + childView.getMeasuredHeight();
// 摆放
childView.layout(left, childTop, right, bottom);
// left 累加
left += childView.getMeasuredWidth() + params.rightMargin;
maxHeight = Math.max(maxHeight, childView.getHeight() + params.topMargin + params.bottomMargin);
}
// top值累加,摆放下一行items
top += maxHeight;
}
}
// 设置适配器。
public void setAdapter(BaseAdapter adapter) {
if (adapter == null) {
throw new IllegalArgumentException("adapter不能为空");
}
removeAllViews(); // 清空之前的子View。
this.mAdapter = adapter;
int childCount = mAdapter.getCount();
for (int i = 0; i < childCount; i++) {
View childView = mAdapter.getView(i, this);
addView(childView);
}
}
}
网友评论