美文网首页
UI 09: 自定义ViewGroup

UI 09: 自定义ViewGroup

作者: xqiiitan | 来源:发表于2024-06-06 16:11 被阅读0次

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模式

    1. onMeasure 指定宽高:
      for循环测量子view,
      根据子view计算和指定自己的布局。
    1. onLayout() 摆放
      for循环,摆放所有的子view
    1. 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);
        }
    }
}

相关文章

网友评论

      本文标题:UI 09: 自定义ViewGroup

      本文链接:https://www.haomeiwen.com/subject/rbwuqjtx.html