LayoutInflater源码分析与应用

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值