LayoutInflater源码分析
layoutInflater是用来解析布局文件的。在onCreate方法中的setContentView方法内部是使用layoutInflater进行布局文件的解析的。看LayoutInflater开头的注释如下:
For performance reasons, view inflation relies heavily on pre-processing of
XML files that is done at build time. Therefore, it is not currently possible
to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
it only works with an XmlPullParser returned from a compiled resource
简单翻译一下:因为性能原因,视图的inflation 是在构建的时候进行预编译解析的。因此,在运行期间不可能用layoutflater内部XmlPullParser尽行XML文件的解析。它只能返回一个已经编译好的文件。
我的理解就是布局文件在编译期间就已经被提前解析成xml文件对象了,下面看源码的时候会发现,布局文件解析成VIew的时候是通过反射进行创建View对象的。之所以这样做就是性能的原因。
接下来看一下LayoutInflater的类结构
内部有三个接口和一些get、set方法。整个类代码量并不多,只有1千行左右。接下来看看接口和这些方法的作用。
先上一张图流程图:
哪里用到inflate方法
虽然只有一千行左右的代码量,但是内部还是有点复杂的。看流程图有两个重要的方法,inflate和createView,我们通常用到layoutInflater的地方
- activity的setContentView
- 适配器里LayoutInflater.inflate
layoutInflater实例的获取
setContentView相当于LayoutInflater.inflate的一个封装,在setContentView的源码里我们可以看到
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
mLayoutInflater.inflate(layoutResID, mContentParent); 这行代码就是进行布局解析的。它是直接用mLayoutInflater。mLayoutInflater的获取有三种常用的方法
- LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater()
- LayoutInflater localinflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- LayoutInflater inflater = LayoutInflater.from(context);
但是这三种方式最终本质是都是调用的Context.getSystemService()
inflate方法源码分析
inflate有四个重载方法,最终是调用最后一个
public View inflate(XmlPullParser parser, @Nullable ViewGroup root,boolean attachToRoot)
第一个参数是xml解析器,第二个父布局对象,第三个是否添加到父布局对象中。
下面的源码是经过删减的伪代码。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
//图中第一个分支
if (TAG_MERGE.equals(name)) {
//如果是merge标签进行递归
rInflate(parser, root, inflaterContext, attrs, false);
} else {
//不是merge创建View
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
if (root != null) {
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
//接着递归子布局
rInflateChildren(parser, temp, attrs, true);
//第三个参数,如果是true则添加view到第二个参数父布局中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
}
return result;
}
}
inflate方法中一个重要的方法createViewFromTag源码:
可以看到里面有factory的,后面会介绍factory。
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr)
try {
View view;
//factory后面说
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
//重要代码开始创建View了
view = onCreateView(parent, name, attrs);
} else {
//重要代码开始创建View了
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
}
这里开始createView方法
上面说到createViewFromTag,这个方法主要是处理factory的,相当于对createVeiw进行了一次包装,如果没有自定义factory,则直接调用createView进行View的创建。下面是createView的伪源码:
public final View createView(String name, String prefix, AttributeSet attrs){
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
if (constructor == null) {
//反射调用View
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
} else {
}
//创建View
final View view = constructor.newInstance(args);
return view;
}
}
到这里正个View解析创建的流程就已经全部走完,其中递归创建子视图,还有一个过滤器等操作,大体上就是上面三个步骤。整个LayoutInflater源码分析到这里已经完了,下面是一个例子。
应用(小红书引导界面)分析
惯例先上一张逻辑图
类结构
ParallaxContainer
这个类是控制类,本身相当于自定义ViewGroup他是继承FrameLayout的。简单点说它就是一个自定义ViewGroup.源码没有删剪
源码分析:
public class ParallaxContainer extends FrameLayout {
private String TAG = "ParallaxContainer";
private List<View> parallaxViews = new ArrayList<View>();
private ViewPager viewPager;
private int pageCount = 0;
private int containerWidth;
private boolean isLooping = false;
private final ParallaxPagerAdapter adapter;
Context context;
public ViewPager.OnPageChangeListener mCommonPageChangeListener;
public int currentPosition = 0;
public ParallaxContainer(Context context) {
super(context);
this.context = context;
adapter = new ParallaxPagerAdapter(context);
}
public ParallaxContainer(Context context, AttributeSet attrs) {
super(context, attrs);
adapter = new ParallaxPagerAdapter(context);
}
public ParallaxContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
adapter = new ParallaxPagerAdapter(context);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
containerWidth = getMeasuredWidth();
if (viewPager != null) {
mCommonPageChangeListener.onPageScrolled(viewPager.getCurrentItem(), 0, 0);
}
super.onWindowFocusChanged(hasFocus);
}
//控制人物动画
public void setLooping(boolean looping) {
isLooping = looping;
updateAdapterCount();
}
ImageView iv;
public void setImage(ImageView iv) {
this.iv = iv;
}
private void updateAdapterCount() {
adapter.setCount(isLooping ? Integer.MAX_VALUE : pageCount);
}
public void setupChildren(LayoutInflater inflater, int... childIds) {
if (getChildCount() > 0) {
throw new RuntimeException("setupChildren should only be called once when ParallaxContainer is empty");
}
ParallaxLayoutInflater parallaxLayoutInflater = new ParallaxLayoutInflater(
inflater, getContext());
Log.e(TAG, "setupChildren: "+ pageCount);
//初始化6个布局
for (int childId : childIds) {
parallaxLayoutInflater.inflate(childId, this);
}
pageCount = getChildCount();
Log.e(TAG, "setupChildren: "+ pageCount);
for (int i = 0; i < pageCount; i++) {
View view = getChildAt(i);
addParallaxView(view, i);
}
updateAdapterCount();
viewPager = new ViewPager(getContext());
viewPager.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
viewPager.setId(R.id.parallax_pager);
attachOnPageChangeListener();
viewPager.setAdapter(adapter);
addView(viewPager, 0);
}
/** 至少持续时间 */
private static final long DELAY_TIME = 600;
protected void attachOnPageChangeListener() {
mCommonPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int state) {
Log.v(TAG, "onPageScrollStateChanged" + state);
iv.setBackgroundResource(R.anim.man_run);
final AnimationDrawable animationDrawable = (AnimationDrawable) iv.getBackground();
switch (state) {
case 0:
finishAnim(animationDrawable);
break;
case 1:
isEnd = false;
animationDrawable.start();
break;
case 2:
finishAnim(animationDrawable);
break;
}
}
boolean isleft = false;
@Override
public void onPageScrolled(int pageIndex, float offset, int offsetPixels) {
// Log.v(TAG, "onPageScrolled" + pageIndex + " offset" + offset + " offsetPixels" + offsetPixels);
if (offsetPixels < 10) {
isleft = false;
}
if (pageCount > 0) {
pageIndex = pageIndex % pageCount;
}
if (pageIndex == 3) {
if (isleft) {
} else {
iv.setX(iv.getLeft() - offsetPixels);
}
}
ParallaxViewTag tag;
for (View view : parallaxViews) {
tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
if (tag == null) {
continue;
}
if ((pageIndex == tag.index - 1 || (isLooping && (pageIndex == tag.index
- 1 + pageCount)))
&& containerWidth != 0) {
// make visible
view.setVisibility(VISIBLE);
// slide in from right
view.setTranslationX((containerWidth - offsetPixels) * tag.xIn);
// slide in from top
view.setTranslationY(0 - (containerWidth - offsetPixels) * tag.yIn);
// fade in
view.setAlpha(1.0f - (containerWidth - offsetPixels) * tag.alphaIn / containerWidth);
} else if (pageIndex == tag.index) {
// make visible
view.setVisibility(VISIBLE);
// slide out to left
view.setTranslationX(0 - offsetPixels * tag.xOut);
// slide out to top
view.setTranslationY(0 - offsetPixels * tag.yOut);
// fade out
view.setAlpha(1.0f - offsetPixels * tag.alphaOut / containerWidth);
} else {
view.setVisibility(GONE);
}
}
}
@Override
public void onPageSelected(int position) {
Log.v(TAG, "onPageSelected" + position);
currentPosition = position;
}
};
viewPager.setOnPageChangeListener(mCommonPageChangeListener);
}
boolean isEnd = false;
private synchronized void finishAnim(final AnimationDrawable animationDrawable)
{
if(isEnd)
{
return;
}
isEnd = true;
final long delay = DELAY_TIME ;
new Thread(new Runnable() {
@Override
public void run() {
Log.v(TAG, "onPageScrollStateChanged delay" + delay);
if(delay > 0)
{
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (animationDrawable.isRunning() && isEnd) {
animationDrawable.stop();
}
}
}).start();
}
private void addParallaxView(View view, int pageIndex) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0, childCount = viewGroup.getChildCount(); i < childCount; i++) {
addParallaxView(viewGroup.getChildAt(i), pageIndex);
}
}
ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
if (tag != null) {
tag.index = pageIndex;
parallaxViews.add(view);
}
}
}
下面的源码都是辅助性质的类
ParallaxLayoutInflater
视图inflater这里关键地方。在这里进行了自定义factory的设置
public class ParallaxLayoutInflater extends LayoutInflater {
protected ParallaxLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
//设置自定义factory
setFactory(new ParallaxFactory(this));
}
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new ParallaxLayoutInflater(this, newContext);
}
}
ParallaxFactory
自定义factory,这是实现自定义factory的关键之处,所有的View都是经过这里进行解析的。
public class ParallaxFactory implements LayoutInflater.Factory {
private ParallaxLayoutInflater mInflater;
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.view."
};
public ParallaxFactory(ParallaxLayoutInflater inflater) {
mInflater = inflater;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
View view = null;
if (view == null) {
view = createViewOrFailQuietly(name, context, attrs);
}
if (view != null) {
onViewCreated(view, context, attrs);
}
return view;
}
protected View createViewOrFailQuietly(String name, Context context, AttributeSet attrs) {
if (name.contains(".")) {
return createViewOrFailQuietly(name, null, context, attrs);
}
for (final String prefix : sClassPrefixList) {
final View view = createViewOrFailQuietly(name, prefix, context, attrs);
if (view != null) {
return view;
}
}
return null;
}
protected View createViewOrFailQuietly(String name, String prefix, Context context,
AttributeSet attrs) {
try {
return mInflater.createView(name, prefix, attrs);
} catch (Exception ignore) {
return null;
}
}
protected void onViewCreated(View view, Context context, AttributeSet attrs) {
int[] attrIds =
{ R.attr.a_in, R.attr.a_out, R.attr.x_in, R.attr.x_out, R.attr.y_in, R.attr.y_out, };
TypedArray a = context.obtainStyledAttributes(attrs, attrIds);
if (a != null) {
if (a.length() > 0) {
ParallaxViewTag tag = new ParallaxViewTag();
tag.alphaIn = a.getFloat(0, 0f);
tag.alphaOut = a.getFloat(1, 0f);
tag.xIn = a.getFloat(2, 0f);
tag.xOut = a.getFloat(3, 0f);
tag.yIn = a.getFloat(4, 0f);
tag.yOut = a.getFloat(5, 0f);
view.setTag(R.id.parallax_view_tag, tag);
}
a.recycle();
}
}
}
ParallaxPagerAdapter
viewpager的Adapter.主要是viewpager的滑动操作需要这里进行适配器创建视图,通常是在这里进行视图的创建和回收,但是上面我们已经自定义factory进行View视图的解析与创建,这里就没什么要做的了。只是控制一下滑动操作的次数控制
public class ParallaxPagerAdapter extends PagerAdapter {
private int count = 0;
private final Context context;
private final LinkedList<View> recycleBin = new LinkedList<View>();
public ParallaxPagerAdapter(Context context) {
this.context = context;
}
public void setCount(int count) {
this.count = count;
}
@Override public int getCount() {
return count;
}
@Override public Object instantiateItem(ViewGroup container, int position) {
View view;
if (!recycleBin.isEmpty()) {
view = recycleBin.pop();
} else {
view = new View(context);
view.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
container.addView(view);
return view;
}
@Override public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
recycleBin.push(view);
}
@Override public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
}
ParallaxViewTag
public class ParallaxViewTag {
protected int index;
protected float xIn;
protected float xOut;
protected float yIn;
protected float yOut;
protected float alphaIn;
protected float alphaOut;
}
使用
public class MainActivity extends Activity {
ImageView iv_man;
ParallaxContainer parallaxContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_man = (ImageView) findViewById(R.id.iv_man);
parallaxContainer = (ParallaxContainer) findViewById(R.id.parallax_container);
if (parallaxContainer != null) {
parallaxContainer.setImage(iv_man);
parallaxContainer.setLooping(false);
iv_man.setVisibility(View.VISIBLE);
parallaxContainer.setupChildren(getLayoutInflater(),
R.layout.view_intro_1, R.layout.view_intro_2,
R.layout.view_intro_3, R.layout.view_intro_4,
R.layout.view_intro_5, R.layout.view_login);
}
}
}
应用(android5.0着色器Tint)
应用(多主题方案)
参考:
http://blog.csdn.net/lmj623565791/article/details/38171465
http://blog.csdn.net/lmj623565791/article/details/51503977
http://blog.csdn.net/guolin_blog/article/details/12921889
http://blog.csdn.net/ouyang_peng/article/details/46957281
http://blog.csdn.net/qq_22644219/article/details/69367150
http://www.cnblogs.com/qinhe/p/6370781.html
http://www.sunnyang.com/661.html