效果图
- In Activity or Fragment
In Any View
使用场景
我们的程序流程受很多因素影响不可能每次都能达到我们预期的状态,所以我们经常会在页面上添加一些请求失败、成功、空数据的对应布局。相相信很多人都这样做过。这样做肯定可以,但是比较繁琐,而且xml里也会多出一些感觉不太有用的布局(都是设为GONE),也影响我们看布局。今天我记录下翔神抽出来的LoadingAndRetryManager,代码其实很少就3个类。我今天主要来看看他实现的思路,使用方法也很简单,直接看他github是的readme好了
设计思路
其实这个逻辑并不复杂,整体的思路就是当程序出现不是我们预先设定步骤时,替换掉我们xml中的view显示相应的view(loading、retry、empty)
具体实现是自定义一个容器也就是项目中的LoadingAndRetryLayout,将各种场景下对应的view(mLoadingView、mRetryView、mContentView、mEmptyView)添加进去,是那种场景就将该场景下的view设置为visiable,其他设置为gone。
整个项目核心就是将xml中自己的view替换成我们自己的LoadingAndRetryLayout并将xml中的view赋值给LoadingAndRetryLayout的mContentView,接下来就看看翔神是怎么通过不同的场景替换赋值这个mContentView的,也就是LoadingAndRetryManager这个类实现的功能
activity
我们知道我们activity的页面都是通过setContentView()设置进去的,而这个页面会加到mDecor中id为android.R.id.content的容器中。具体参考翔神另一篇博客 Android 源码解析 之 setContentView,所以我们要替换activity的页面就直接在这个android.R.id.content的第1个child替换成我们自己的LoadingAndRetryLayout,将我们activity中setContentView()的view设置成LoadingAndRetryLayout的mContentView
fragment
fragment就直接 fragment.getView().getParent()的第一个孩子替换成我们的LoadingAndRetryLayout,并把fragment.getView()设置为LoadingAndRetryLayout的mContentView
view
view就是view.getParent(),然后遍历它找到view在parent中的位置,然后将view赋值给我们自己的LoadingAndRetryLayout的mContentView并把我们自己的LoadingAndRetryLayout替换原来view的位置
具体参考代码看下
public LoadingAndRetryManager(Object activityOrFragmentOrView, OnLoadingAndRetryListener listener)
{
if (listener == null) listener = DEFAULT_LISTENER;
ViewGroup contentParent = null;
Context context;
if (activityOrFragmentOrView instanceof Activity)
{
Activity activity = (Activity) activityOrFragmentOrView;
context = activity;
contentParent = (ViewGroup) activity.findViewById(android.R.id.content);
} else if (activityOrFragmentOrView instanceof Fragment)
{
Fragment fragment = (Fragment) activityOrFragmentOrView;
context = fragment.getActivity();
contentParent = (ViewGroup) (fragment.getView().getParent());
} else if (activityOrFragmentOrView instanceof View)
{
View view = (View) activityOrFragmentOrView;
contentParent = (ViewGroup) (view.getParent());
context = view.getContext();
} else
{
throw new IllegalArgumentException("the argument's type must be Fragment or Activity: init(context)");
}
int childCount = contentParent.getChildCount();
//get contentParent
int index = 0;
View oldContent;
if (activityOrFragmentOrView instanceof View)
{
oldContent = (View) activityOrFragmentOrView;
for (int i = 0; i < childCount; i++)
{
if (contentParent.getChildAt(i) == oldContent)
{
index = i;
break;
}
}
} else
{
oldContent = contentParent.getChildAt(0);
}
contentParent.removeView(oldContent);
//setup content layout
LoadingAndRetryLayout loadingAndRetryLayout = new LoadingAndRetryLayout(context);
ViewGroup.LayoutParams lp = oldContent.getLayoutParams();
contentParent.addView(loadingAndRetryLayout, index, lp);
loadingAndRetryLayout.setContentView(oldContent);
// setup loading,retry,empty layout
setupLoadingLayout(listener, loadingAndRetryLayout);
setupRetryLayout(listener, loadingAndRetryLayout);
setupEmptyLayout(listener, loadingAndRetryLayout);
//callback
listener.setRetryEvent(loadingAndRetryLayout.getRetryView());
listener.setLoadingEvent(loadingAndRetryLayout.getLoadingView());
listener.setEmptyEvent(loadingAndRetryLayout.getEmptyView());
mLoadingAndRetryLayout = loadingAndRetryLayout;
}
点击重试(retry)
这个就比较简单了,既然我们能得到retry的view 添加个点击事件就很简单了