喜欢请给作者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() { } }