Android 自定义View 手写瀑布流组件FlowLayout

Android 自定义View 手写瀑布流组件FlowLayout,第1张

Android 自定义View 手写瀑布流组件FlowLayout

纸上得来终觉浅,绝知此事要躬行。

动手实践是学习的最好的方式,对于自定义View来说,听和看只能是过一遍流程,能掌握个30%、40%就不错了,而且很快就会遗忘,想变成自己的东西必须动手来写几遍,细细体会其中的细节和系统API的奥秘、真谛。

进入主题,今天来手写一个瀑布流组件FlowLayout,温习下自定义view的流程和关键点,先来张效果图

                              

FlowLayout实现关键步骤: 1、创建一个view继承自ViewGroup
class ZSFlowLayout : ViewGroup {
    constructor(context: Context) : super(context) {}

    
    constructor(context: Context, attr: AttributeSet) : super(context, attr) {}

    constructor (context: Context, attr: AttributeSet, defZStyle: Int) : super(
        context,
        attr,
        defZStyle
    ) {
    }

}

  这里注意两个参数的构造函数是必须的构造函数,系统会通过反射来调用此构造方法完成view的创建,具体调用位置在LayoutInflater 的 createView方法中,如下(基于android-31):

省略了若干不相关代码,并写了重要的注释信息,请留意

 public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Objects.requireNonNull(viewContext);
        Objects.requireNonNull(name);

        //从缓存中取对应的构造函数
        Constructor constructor = sConstructorMap.get(name);
       
        Class clazz = null;

        try {
            
            if (constructor == null) {
                // 通过反射创建class对象
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);

                //创建构造函数  这里的mConstructorSignature 长这个样子
                //static final Class[] mConstructorSignature = new Class[] {
                //        Context.class, AttributeSet.class};
                //看到了没 就是我们第二个构造方法
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                //缓存构造方法
                sConstructorMap.put(name, constructor);
            } else {
                ...
            }

            
            try {
                //执行构造函数 创建出view
                final View view = constructor.newInstance(args);
                ...
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } catch (Exception e) {
            ...
        } finally {
            ...
        }
    }
2、重写并实现onMeasure方法
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

}

这里要重点解释下 widthMeasureSpec 和 heightMeasureSpec是怎么来的

3、重写并实现onLayout方法

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/5707710.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-17
下一篇2022-12-17

发表评论

登录后才能评论

评论列表(0条)

    保存