LoadSir源码解读

GitHub地址

喜欢请给作者start

关键逻辑
  • 将target目标所对应的View,加入到LoadLayout布局中。

    • //LoadLayout中的setupSuccessLayout()方法会将SuccessCallback对象的rootView设置到loadLayout对象中。
      LoadService(Convertor<T> convertor, TargetContext targetContext, Callback.OnReloadListener onReloadListener, LoadSir.Builder builder) {
          //转换器
          this.convertor = convertor;
          //上下文
          Context context = targetContext.getContext();
          //target转化所对应的View
          View oldContent = targetContext.getOldContent();
          //target转化所对应的View布局参数
          ViewGroup.LayoutParams oldLayoutParams = oldContent.getLayoutParams();
          // 创建一个加载布局
          loadLayout = new LoadLayout(context, onReloadListener);
          //new SuccessCallback(oldContent, context, onReloadListener) 将target转化所对应的视图,上下文,重加载监听,加入Callback类中,创建一个SuccessCallback对象出来
          //setupSuccessLayout() 会将callback中rootView加入到loadLayout布局中
          loadLayout.setupSuccessLayout(new SuccessCallback(oldContent, context, onReloadListener));
          if (targetContext.getParentView() != null) {
              //获取target所对应的父容器,将加载布局对象loadLayout加入target 所在父容器的索引值位置,该加载布局对象loadLayout的布局参数也是target所对应View的布局参数
              //说白了,就是将target所对应的View对象转化为了loadLayout对象了,并且加入到了target所对应父容器中。
              //getTargetContext(Object target)方法中会将父容器中的target所对应的视图移除。
              targetContext.getParentView().addView(loadLayout, targetContext.getChildIndex(), oldLayoutParams);
          }
          initCallback(builder);
      }
      
    • public void setupSuccessLayout(Callback callback) {
          //将Callback对象放入Map中存储起来,键为Callback对应的类,值为Callback对象。
          addCallback(callback);
          //获取callback中的target转化所对应的View视图,也就是根视图
          View successView = callback.getRootView();
          //将target转化所对应的View视图GONE掉
          //在这里将rootView GONE掉是因为initCallback(builder)会重新显示defalutCallback对象
          successView.setVisibility(View.GONE);
          //将根视图加入到LoadLayout加载布局中
          addView(successView);
          //记录当前的callback类
          curCallback = SuccessCallback.class;
      }
      
    • private void initCallback(LoadSir.Builder builder) {
          //callbacks是用来存储各种ErrorCallback,EmptyCallback对象的集合
          List<Callback> callbacks = builder.getCallbacks();
          //defalutCallback 用来存储LoadingCallback.class类。
          Class<? extends Callback> defalutCallback = builder.getDefaultCallback();
          if (callbacks != null && callbacks.size() > 0) {
              for (Callback callback : callbacks) {
                  //将callback放入loadLayout布局容器中的callbacks Map集合中存储起来,该map集合中key是callbacks类型,值是callback对象
                  loadLayout.setupCallback(callback);
              }
          }
          if (defalutCallback != null) {
              //如果默认的callback不为null,则将该callback显示出来
              loadLayout.showCallback(defalutCallback);
          }
      }
      
  • 将初始化设置的Callback对象添加到LoadLayout中

    • //初始化设置Callback对象
      LoadSir.beginBuilder()
              .addCallback(new ErrorCallback())
              .addCallback(new EmptyCallback())
              .addCallback(new LoadingCallback())
              .addCallback(new TimeoutCallback())
              .addCallback(new CustomCallback())
              .setDefaultCallback(LoadingCallback.class)
              .commit();
      
    • //通过loadLayout中的setupCallback()方法,将callback对象存储在loadLayout中的Map集合中
      private void initCallback(LoadSir.Builder builder) {
          //callbacks是用来存储各种ErrorCallback,EmptyCallback对象的集合
          List<Callback> callbacks = builder.getCallbacks();
          //defalutCallback 用来存储LoadingCallback.class类。
          Class<? extends Callback> defalutCallback = builder.getDefaultCallback();
          if (callbacks != null && callbacks.size() > 0) {
              for (Callback callback : callbacks) {
                  //将callback放入loadLayout布局容器中的callbacks Map集合中存储起来,该map集合中key是callbacks类型,值是callback对象
                  loadLayout.setupCallback(callback);
              }
          }
          if (defalutCallback != null) {
              //如果默认的callback不为null,则将该callback显示出来
              loadLayout.showCallback(defalutCallback);
          }
      }
      
    • //该方法会将callback对象存入callbacks map集合当中
      public void addCallback(Callback callback) {
          if (!callbacks.containsKey(callback.getClass())) {
              callbacks.put(callback.getClass(), callback);
          }
      }
      
关键类解读
  • LoadSir:这个类是用来初始化的,设置callback对象。采用构建者模式,这段代码比较重要。register()方法很重要,在这个方法中需要设置目标对象,以后会获取该目标的父容器,最后会将LoadLayout布局加入到该目标对象的父容器中。

    /**
     * @param target           目标
     * @param onReloadListener 重载监听
     * @param convertor        可以为null
     * @param <T>
     * @return
     */
    public <T> LoadService register(Object target, Callback.OnReloadListener onReloadListener, Convertor<T> convertor) {
        //TargetContext 记录target所对应View在父容器中的位置等信息。
        TargetContext targetContext = LoadSirUtil.getTargetContext(target);
        return new LoadService<>(convertor, targetContext, onReloadListener, builder);
    }
    
  • LoadSirUtil:这个类是用来创建target对象的上下文,在这个类中有一行代码很重要,通过target获取它的父容器,并且将target删除,然后创建一个TargetContext对象。

    public class LoadSirUtil {
        public static TargetContext getTargetContext(Object target) {
            ViewGroup contentParent;
            Context context;
            if (target instanceof Activity) {
                //target 如果是 Activity对象
                Activity activity = (Activity) target;
                context = activity;
                contentParent = (ViewGroup) activity.findViewById(android.R.id.content);
            } else if (target instanceof View) {
                //如果target是View,就获取该View的父类容器
                View view = (View) target;
                context = view.getContext();
                contentParent = (ViewGroup) (view.getParent());
            } else {
                throw new IllegalArgumentException("The target must be within Activity, Fragment, View.");
            }
            //如果有父容器,则获取父容器中的子视图个数,如果没有父容器,则父容器中子视图个数为0.
            int childCount = contentParent == null ? 0 : contentParent.getChildCount();
            //传入的target转化后对应的视图
            View oldContent;
            // 父容器中,记录target转化后所对应视图的索引值
            int childIndex = 0;
            if (target instanceof View) {//如果target输入View视图
                oldContent = (View) target;
                for (int i = 0; i < childCount; i++) {
                    if (contentParent.getChildAt(i) == oldContent) {
                        childIndex = i;
                        break;
                    }
                }
            } else {//如果target属于activity
                // 父容器不为空,则传入的target所对应的视图为父容器中第一个子视图,否则target对应的视图就为null。
                oldContent = contentParent != null ? contentParent.getChildAt(0) : null;
            }
            if (oldContent == null) {
                //如果target对应的视图为null,则抛出异常
                throw new IllegalArgumentException(String.format("enexpected error when register LoadSir in %s", target.getClass().getSimpleName()));
            }
            if (contentParent != null) {
                //如果父容器不为null,则将父容器中的target所对应的视图移除,删除的视图稍后会加入到LoadLayout布局中。
                contentParent.removeView(oldContent);
            }
            //将context,父容器,target所对应的视图,target所对应视图在父容器中的索引值,构造一个TargetContext对象,并返回。
            return new TargetContext(context, contentParent, oldContent, childIndex);
        }
    
        public static boolean isMainThread() {
            return Looper.myLooper() == Looper.getMainLooper();
        }
    }
    
    
  • TargetContext:这个类是用来记录target对象的一些属性,转化为View,target父容器,target所在父容器中的索引位置。

    public class TargetContext {
        private Context context;
        private ViewGroup parentView;
        private View oldContent;
        private int childIndex;
        /**
         *  TargetContext 这个类中记录了Target对象所在父容器中的位置。
         * @param context 上下文
         * @param parentView target对应的父容器
         * @param oldContent target转化后所对应的View
         * @param childIndex target转化后所对应View在父容器中的索引值
         */
        public TargetContext(Context context, ViewGroup parentView, View oldContent, int childIndex) {
            this.context = context;
            this.parentView = parentView;
            this.oldContent = oldContent;
            this.childIndex = childIndex;
        }
        public Context getContext() {
            return context;
        }
        View getOldContent() {
            return oldContent;
        }
        int getChildIndex() {
            return childIndex;
        }
        ViewGroup getParentView() {
            return parentView;
        }
    }
    
  • LoadService:这个类有一个很重要的作用,将LoadLayout容器对象加入target对应的父容器中。

    public class LoadService<T> {
        private LoadLayout loadLayout;
        private Convertor<T> convertor;
    
        LoadService(Convertor<T> convertor, TargetContext targetContext, Callback.OnReloadListener onReloadListener, LoadSir.Builder builder) {
            //转换器
            this.convertor = convertor;
            //上下文
            Context context = targetContext.getContext();
            //target转化所对应的View
            View oldContent = targetContext.getOldContent();
            //target转化所对应的View布局参数
            ViewGroup.LayoutParams oldLayoutParams = oldContent.getLayoutParams();
            // 创建一个加载布局
            loadLayout = new LoadLayout(context, onReloadListener);
            //new SuccessCallback(oldContent, context, onReloadListener) 将target转化所对应的视图,上下文,重加载监听,加入Callback类中,创建一个SuccessCallback对象出来
            //setupSuccessLayout() 会将callback中rootView加入到loadLayout布局中
            loadLayout.setupSuccessLayout(new SuccessCallback(oldContent, context, onReloadListener));
            if (targetContext.getParentView() != null) {
                //获取target所对应的父容器,将加载布局对象loadLayout加入target 所在父容器的索引值位置,该加载布局对象loadLayout的布局参数也是target所对应View的布局参数
                //说白了,就是将target所对应的View对象转化为了loadLayout对象了,并且加入到了target所对应父容器中。
                //getTargetContext(Object target)方法中会将父容器中的target所对应的视图移除。
                targetContext.getParentView().addView(loadLayout, targetContext.getChildIndex(), oldLayoutParams);
            }
            initCallback(builder);
        }
    
        private void initCallback(LoadSir.Builder builder) {
            //callbacks是用来存储各种ErrorCallback,EmptyCallback对象的集合
            List<Callback> callbacks = builder.getCallbacks();
            //defalutCallback 用来存储LoadingCallback.class类。
            Class<? extends Callback> defalutCallback = builder.getDefaultCallback();
            if (callbacks != null && callbacks.size() > 0) {
                for (Callback callback : callbacks) {
                    //将callback放入loadLayout布局容器中的callbacks Map集合中存储起来,该map集合中key是callbacks类型,值是callback对象
                    loadLayout.setupCallback(callback);
                }
            }
            if (defalutCallback != null) {
                //如果默认的callback不为null,则将该callback显示出来
                loadLayout.showCallback(defalutCallback);
            }
        }
        public void showSuccess() {
            loadLayout.showCallback(SuccessCallback.class);
        }
        public void showCallback(Class<? extends Callback> callback) {
            loadLayout.showCallback(callback);
        }
        public void showWithConvertor(T t) {
            if (convertor == null) {
                throw new IllegalArgumentException("You haven't set the Convertor.");
            }
            loadLayout.showCallback(convertor.map(t));
        }
        public LoadLayout getLoadLayout() {
            return loadLayout;
        }
        public Class<? extends Callback> getCurrentCallback() {
            return loadLayout.getCurrentCallback();
        }
        /**
         * obtain rootView if you want keep the toolbar in Fragment
         * @since 1.2.2
         * @deprecated
         */
        public LinearLayout getTitleLoadLayout(Context context, ViewGroup rootView, View titleView) {
            LinearLayout newRootView = new LinearLayout(context);
            newRootView.setOrientation(LinearLayout.VERTICAL);
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
            newRootView.setLayoutParams(layoutParams);
            rootView.removeView(titleView);
            newRootView.addView(titleView);
            newRootView.addView(loadLayout, layoutParams);
            return newRootView;
        }
    
        /**
         * modify the callback dynamically
         *
         * @param callback  which callback you want modify(layout, event)
         * @param transport a interface include modify logic
         * @since 1.2.2
         */
        public LoadService<T> setCallBack(Class<? extends Callback> callback, Transport transport) {
            loadLayout.setCallBack(callback, transport);
            return this;
        }
    }
    
  • LoadLayout:存储target对应的View和一些callbcak所对应View视图的显示隐藏工作。当showCallback时,LoadLayout会将Callback对象中对应的rootView加入进LoadLayout中。

    public class LoadLayout extends FrameLayout {
        private Map<Class<? extends Callback>, Callback> callbacks = new HashMap<>();
        private Context context;
        private Callback.OnReloadListener onReloadListener;
        private Class<? extends Callback> preCallback;
        private Class<? extends Callback> curCallback;
        private static final int CALLBACK_CUSTOM_INDEX = 1;
    
        public LoadLayout(@NonNull Context context) {
            super(context);
        }
    
        public LoadLayout(@NonNull Context context, Callback.OnReloadListener onReloadListener) {
            this(context);
            this.context = context;
            this.onReloadListener = onReloadListener;
        }
    
        public void setupSuccessLayout(Callback callback) {
            //将Callback对象放入Map中存储起来,键为Callback对应的类,值为Callback对象。
            addCallback(callback);
            //获取callback中的target转化所对应的View视图,也就是根视图
            View successView = callback.getRootView();
            //将target转化所对应的View视图GONE掉
            successView.setVisibility(View.GONE);
            //将根视图加入到LoadLayout加载布局中
            addView(successView);
            //记录当前的callback类
            curCallback = SuccessCallback.class;
        }
    
        //该方法中的callback对象在Application的时候通过addCallback方法加入进来的
        public void setupCallback(Callback callback) {
            //克隆出来了一个callback 对象
            Callback cloneCallback = callback.copy();
            //将上下文和重新加载监听器设置到cloneCallback对象中
            cloneCallback.setCallback(null, context, onReloadListener);
            //将cloneCallback的类型作为key,将cloneCallback对象作为值存入Map中,放入LoadLayout对象中保存
            addCallback(cloneCallback);
        }
    
        public void addCallback(Callback callback) {
            if (!callbacks.containsKey(callback.getClass())) {
                callbacks.put(callback.getClass(), callback);
            }
        }
    
        //显示callback
        public void showCallback(final Class<? extends Callback> callback) {
            checkCallbackExist(callback);
            if (LoadSirUtil.isMainThread()) {
                showCallbackView(callback);
            } else {
                postToMainThread(callback);
            }
        }
    
        public Class<? extends Callback> getCurrentCallback() {
            return curCallback;
        }
    
        private void postToMainThread(final Class<? extends Callback> status) {
            post(new Runnable() {
                @Override
                public void run() {
                    showCallbackView(status);
                }
            });
        }
    
        private void showCallbackView(Class<? extends Callback> status) {
            //前一个callback不为null,且前一个callback与传入的status callback相等,则退出显示callback逻辑
            if (preCallback != null) {
                if (preCallback == status) {
                    return;
                }
                //获取前一个callback对象,使callback与loadLayout分离
                callbacks.get(preCallback).onDetach();
            }
            if (getChildCount() > 1) {
                //删除loadLayout 容器中的第一个callback中的rootView
                removeViewAt(CALLBACK_CUSTOM_INDEX);
            }
            //遍历callbacks map集合中的key
            for (Class key : callbacks.keySet()) {
                if (key == status) {
                    //如果key等于传入的callback类型,取出该类型对应的对象
                    SuccessCallback successCallback = (SuccessCallback) callbacks.get(SuccessCallback.class);
                    if (key == SuccessCallback.class) {
                        //拿到rootView 将rootView设置为显示状态
                        successCallback.show();
                    } else {
                        //callbacks.get(key).getSuccessVisible() 获取该Callback是否是显示的状态
                        //将 successCallback 显示或者隐藏
                        successCallback.showWithCallback(callbacks.get(key).getSuccessVisible());
                        //获取普通的callback rootView
                        View rootView = callbacks.get(key).getRootView();
                        //将rootView加入加载布局中
                        addView(rootView);
                        //将callback与loadLayout绑定
                        callbacks.get(key).onAttach(context, rootView);
                    }
                    //前一个callback对象赋值为该callback类型
                    preCallback = status;
                }
            }
            //当前callback对象赋值为该callback类型
            curCallback = status;
        }
    
        public void setCallBack(Class<? extends Callback> callback, Transport transport) {
            if (transport == null) {
                return;
            }
            checkCallbackExist(callback);
            //callbacks.get(callback).obtainRootView() 返回rootView
            transport.order(context, callbacks.get(callback).obtainRootView());
        }
    
        private void checkCallbackExist(Class<? extends Callback> callback) {
            if (!callbacks.containsKey(callback)) {
                throw new IllegalArgumentException(String.format("The Callback (%s) is nonexistent.", callback
                        .getSimpleName()));
            }
        }
    }
    
  • Callback:这个类主要就是存储状态视图的,经常需要从该类中获取viewRoot。当想自定义Callback类的时候,需要继承Callback类。实现onCreateView()方法,返回布局视图ID,当调用callback的getRootView()方法时候,如果构造callback对象时候没有传入taget值,那么返回的viewRoot则是onCreateView()方法返回的布局ID所对应的视图。

    public abstract class Callback implements Serializable {
        private View rootView;
        private Context context;
        private OnReloadListener onReloadListener;
        private boolean successViewVisible;
    
        public Callback() {
        }
    
        Callback(View view, Context context, OnReloadListener onReloadListener) {
            this.rootView = view;
            this.context = context;
            this.onReloadListener = onReloadListener;
        }
    
        public Callback setCallback(View view, Context context, OnReloadListener onReloadListener) {
            this.rootView = view;
            this.context = context;
            this.onReloadListener = onReloadListener;
            return this;
        }
        //返回目标视图
        public View getRootView() {
            //获取视图ID
            int resId = onCreateView();
            //如果视图ID为null,且target转化后所对应的view就是为rootView,该view如果不为null的话,就将target转化所对应的rootView返回
            if (resId == 0 && rootView != null) {
                return rootView;
            }
            if (onBuildView(context) != null) {
                rootView = onBuildView(context);
            }
            //如果target转化所对应的View为null,那么就使用onCreateView()返回的视图ID转化为rootView返回。
            if (rootView == null) {
                rootView = View.inflate(context, onCreateView(), null);
            }
            //为target转化所对应的View或者视图ID所对应的View设置点击事件,onReloadEvent(context, rootView)判断是否执行该点击事件,false为执行,true为不执行。
            // onReloadListener.onReload(v) 点击事件的执行逻辑写在onRload方法中,该方法在设置convert时可以具体实现。
            rootView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onReloadEvent(context, rootView)) {
                        return;
                    }
                    if (onReloadListener != null) {
                        onReloadListener.onReload(v);
                    }
                }
            });
            onViewCreate(context, rootView);
            //返回目标视图
            return rootView;
        }
    
        protected View onBuildView(Context context) {
            return null;
        }
    
        /**
         * if return true, the successView will be visible when the view of callback is attached.
         */
        public boolean getSuccessVisible() {
            return successViewVisible;
        }
    
        void setSuccessVisible(boolean visible) {
            this.successViewVisible = visible;
        }
    
        /**
         * @deprecated Use {@link #onReloadEvent(Context context, View view)} instead.
         */
        protected boolean onRetry(Context context, View view) {
            return false;
        }
    
        protected boolean onReloadEvent(Context context, View view) {
            return false;
        }
        //copy方法是将该对象读取出来,并返回。TODO 不清楚为什么这么写?
        public Callback copy() {
            ByteArrayOutputStream bao = new ByteArrayOutputStream();
            ObjectOutputStream oos;
            Object obj = null;
            try {
                oos = new ObjectOutputStream(bao);
                oos.writeObject(this);
                oos.close();
                ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bis);
                obj = ois.readObject();
                ois.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return (Callback) obj;
        }
    
        /**
         * 获取目标视图
         * @since 1.2.2
         */
        public View obtainRootView() {
            if (rootView == null) {
                rootView = View.inflate(context, onCreateView(), null);
            }
            return rootView;
        }
    
        public interface OnReloadListener extends Serializable {
            void onReload(View v);
        }
    
        /**
         * 返回视图的ID
         * @return
         */
        protected abstract int onCreateView();
    
        /**
         * Called immediately after {@link #onCreateView()}
         *
         * @since 1.2.2
         */
        protected void onViewCreate(Context context, View view) {
        }
    
        /**
         * Called when the rootView of Callback is attached to its LoadLayout.
         *
         * @since 1.2.2
         */
        public void onAttach(Context context, View view) {
        }
    
        /**
         * Called when the rootView of Callback is removed from its LoadLayout.
         *
         * @since 1.2.2
         */
        public void onDetach() {
        }
    }
    
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值