从源码中我们可以分析出,如何通过LayoutInflater.from()方法获取LayoutInflater实例。该方法接收一个Context参数,并通过context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)获取LayoutInflater对象。如果获取到的对象为空,则抛出一个断言错误。最终返回获取到的LayoutInflater对象。
代码如下:
```java
public abstract class LayoutInflater {
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
}
```
在Android系统中,除了LayoutInflater之外,还有许多其他类型的服务可以通过context.getSystemService()方法获取。这些服务包括:
1. WindowManager:管理打开的窗口程序;
2. LayoutInflater:取得xml里定义的view;
3. ActivityManager:管理应用程序的系统状态;
4. PowerManger:电源的服务;
5. AlarmManager:闹钟的服务;
6. NotificationManager:状态栏的服务;
7. KeyguardManager:键盘锁的服务;
8. LocationManager:位置的服务,如GPS;
9. SearchManager:搜索的服务;
10. VibratorService:震动服务。
ebrator:手机震动的服务
CONNECTIVITY_SERVICE:网络连接的服务
WIFI_SERVICE:Wifi服务
TELEPHONY_SERVICE:电话服务
获取LayoutInflater服务
```java
class ContextImpl extends Context {
/***部分代码省略****/
static {
/***部分代码省略****/
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}
});
/***部分代码省略****/
}
/***部分代码省略****/
}
```
以下是重构后的内容:
```java
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME = "com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy) policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
} catch (InstantiationException ex) {
throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be accessed", ex);
}
}
/**
* Create a new LayoutInflater instance using the policy implementation.
* @param context The context to create the LayoutInflater with.
* @return A new LayoutInflater instance.
*/
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
}
interface IPolicy {
LayoutInflater makeNewLayoutInflater(Context context);
}
public class Policy implements IPolicy {
/**
* Create a new LayoutInflater instance using this implementation of the policy.
* @param context The context to create the LayoutInflater with.
* @return A new LayoutInflater instance.
*/
@Override
public LayoutInflater makeNewLayoutInflater(Context context) {
return new PhoneLayoutInflater(context);
}
}
```
您好!PhoneLayoutInflater是Android中的一个类,它可以将xml的布局文件解析为view。它是一个抽象类,实现类是PhoneLayoutInflater。 LayoutInflater会遍历View树,然后根据View的全路径名利用反射获取构造器,从而实现View实例。 PhoneLayoutInflater实现了onCreateView ()方法,当调用LayoutInflater的inflate ()时,最终就会调用onCreateView (),从而创建出view。
这段代码是一个名为PhoneLayoutInflater的自定义类,它继承自Android的LayoutInflater。这个类主要用于处理与手机屏幕相关的视图布局。
```java
public class PhoneLayoutInflater extends LayoutInflater {
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
public PhoneLayoutInflater(Context context) {
super(context);
}
protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
}
/**
* Override onCreateView to instantiate names that correspond to the widgets known to the Widget factory.
* If we don't find a match, call through to our super class.
*/
@Override
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attrs);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
// In this case we want to let the base class take a crack at it.
}
}
return super.onCreateView(name, attrs);
}
/**
* Clone in context method allows us to create a new instance of PhoneLayoutInflater with a different context.
* This is useful when we want to share views across activities or fragments.
*/
public LayoutInflater cloneInContext(Context newContext) {
return new PhoneLayoutInflater(this, newContext);
}
}
```
在Android中,LayoutInflater最常使用的情况基本都是调用inflate方法用来构造View对象。 LayoutInflater的用法详解可以参考CSDN博客上的这篇文章。
```java
public abstract class LayoutInflater {
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (0x" + Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
/**
* @param parser xml数据结构
* @param root 一个可依附的rootview
* @param attachToRoot 是否将parser解析生产的View添加在root上
*/
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
//当前上下文环境
final Context inflaterContext = mContext;
//所有的属性集合获取类
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
//根节点
View result = root;
try {
// Look for the root node.寻找根节点
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
// Empty
}
//找不到根节点抛出异常
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription() + ": No start tag found!");
}
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: " + name);
System.out.println("**************************");
}
//merge标签解析
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("
}
//递归调用,添加root的孩子节点
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml.根据当前的attrs和xml创建一个xml根view
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " + root);
}
// Create layout params that match root, if supplied.构造LayoutParams
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("------> start inflating children");
}
// Inflate all children under temp against its context.递归调用,添加temp的孩子节点
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("------> done inflating children");
}
// We are supposed to attach all the views we found (int temp) to root. Do that now.将xml解析出来的viewgroup添加在root的根下。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的root。如果root为空或不附加到root上,则返回传入的
以下是内容重构后的代码:
```java
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
// LayoutInflater的使用中重点关注inflate方法的参数含义
Context context = getContext();
if (resource == 0) {
throw new IllegalArgumentException("XML布局资源ID不能为0");
}
LayoutInflater layoutInflater = LayoutInflater.from(context);
View view = null;
switch (resource) {
case R.layout.example_view:
view = layoutInflater.inflate(resource, root, attachToRoot);
break;
default:
view = layoutInflater.inflate(resource, root, false);
break;
}
return view;
}
```
这段代码是LayoutInflater类的一个抽象方法,用于将parser解析器中包含的view结合属性标签attrs生产view添加在parent容器中。这个方法有5个参数,分别是XmlPullParser类型的parser,View类型的parent,AttributeSet类型的attrs,boolean类型的finishInflate。
这个方法首先调用rInflate方法,然后根据解析器的深度、类型等信息进行循环判断,如果是请求焦点标签TAG_REQUEST_FOCUS,就调用parseRequestFocus方法;如果是tag标签TAG_TAG,就调用parseViewTag方法;如果是include标签TAG_INCLUDE,就调用parseInclude方法;如果是merge标签TAG_MERGE,就抛出异常;如果是其他view标签,就创建View对象,并将其添加到父容器中。
最后,如果finishInflate为true,则调用parent的onFinishInflate方法。
单个View布局的解析过程如下:
1. 首先,调用`createViewFromTag`方法根据tag创建View对象;
2. 接着,设置View的Theme属性;
3. 最后,调用`CreateView`方法创建view。
以下是对应的代码实现:
```java
public View createViewFromTag(ViewGroup parent, int tag) {
View view = getChildView(parent, tag);
if (view == null) {
return null;
}
// 设置View的Theme属性
setTheme(view);
// 调用CreateView方法创建view
view = CreateView(view);
return view;
}
private void setTheme(View view) {
// 根据需要设置View的主题,例如:
int themeId = R.style.AppTheme;
view.setTheme(getApplicationContext());
TypedArray typedArray = getLayoutInflater().getContext().obtainStyledAttributes(themeId);
try {
// 从styledAttributes中获取属性值并设置到View上,例如:
int textColor = typedArray.getColor(R.styleable.AppTheme_textColor, Color.BLACK);
view.setTextColor(textColor);
} finally {
typedArray.recycle();
}
}
private View CreateView(View view) {
// 根据需要自定义View的创建过程,例如:
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View createdView = inflater.inflate(R.layout.custom_view, null);
// 在此处可以对createdView进行一些自定义操作,例如添加监听器、设置数据等
return createdView;
}
```
```java
public abstract class LayoutInflater {
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException {
return onCreateView(name, attrs);
}
/**
* 将parser解析器中包含的view结合属性标签attrs生产view添加在parent容器中
* @param parent 父容器
* @param name view名称
* @param context 上下文环境
* @param attrs 属性标签集合
*/
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}
// ...其余代码保持不变
}
```
以下是重构后的内容:
在Android开发中,LayoutInflater是一个非常关键的类,它的主要作用是将布局文件(XML)转换为相应的View对象。本文将详细介绍LayoutInflater如何创建View的过程,并重点介绍其内部的Factory接口。
一、LayoutInflater创建View的过程
1. 通过Resource.getLayout(resource)方法获取XmlResourceParser对象;
2. 利用该对象实例化XmlPullAttributes,以便于解析XML标签中的属性;
3. 将父容器、XML中View的名称、属性标签传递给rInflate方法,解析对应的XML文件;
4. 将(父容器、XML中View的名称、属性标签)传递给createViewFromTag方法创建对应的View;
5. 在createViewFromTag方法中,执行LayoutInflater.Factory或者LayoutInflater的createView方法。
6. 在createView方法中,已知View的类名和View的属性标签集合,通过Java反射执行View的构造方法创建View对象。这也就是为什么我们在自定义View的时候必须复写View的构造函数`View(Context context, AttributeSet attrs)`。
二、LayoutInflater.Factory简介
LayoutInflater.Factory这个类在我们开发的过程中很少使用到。但是在查看LayoutInflater解析View源码的过程中可以看到,如果LayoutInflater中有mFactory这个实例,那么可以通过mFactory创建View,同时也能修改入参AttributeSet属性值。
```java
public abstract class LayoutInflater { /***部分代码省略****/ public interface Factory { public View onCreateView(String name, Context context, AttributeSet attrs); } public interface Factory2 extends Factory { public View onCreateView(View parent, String name, Context context, AttributeSet attrs); } /***部分代码省略****/ }
```
在 Android 开发中,`LayoutInflater` 是用于将布局文件转化为 View 对象的关键类。它包含两个工厂类:`Factory` 和 `Factory2`,它们之间的主要区别在于参数的设置。
`Factory` 是一个基本的工厂类,用于创建视图对象。当使用 `Factory` 时,你需要手动指定需要替换的布局文件 ID、视图名称以及该视图所在的父容器。例如:
```java
LayoutInflater factory = LayoutInflater.from(context);
View view = factory.inflate(R.layout.my_layout, parent);
```
上述代码中,`R.layout.my_layout` 是需要替换的布局文件 ID,`parent` 是视图所在的父容器。通过这种方式,你可以将布局文件中的某个视图替换为新的视图对象。
然而,有时候你可能希望获取到一个已经存在的 View,并将其放置在另一个父容器中。这时,`Factory2` 就派上了用场。`Factory2` 可以接受一个父容器作为参数,然后将布局文件中的视图添加到该父容器中。与 `Factory` 相比,使用 `Factory2` 可以简化代码并提高可重用性。以下是 `Factory2` 的使用示例:
```java
LayoutInflater factory = LayoutInflater.from(context);
ViewGroup parent = findViewById(R.id.parent_container); // 假设 parent_container 是目标父容器的 ID
View view = factory.inflate(R.layout.my_layout, parent);
```
在上述代码中,`R.layout.my_layout` 是需要替换的布局文件 ID,而 `parent_container` 是目标父容器的引用。通过 `Factory2`,你可以将布局文件中的视图添加到目标父容器中,而不需要显式地指定父容器。
总结一下,`Factory` 和 `Factory2` 的主要区别在于参数设置的方式。如果你只需要简单地替换一个视图或获取一个已存在的视图,可以使用 `Factory`;而如果需要将一个视图添加到另一个父容器中,推荐使用 `Factory2`,因为它更加灵活且易于复用。
public abstract class LayoutInflater {
/***部分代码省略****/
/**
* 设置工厂方法
* @param factory 要设置的工厂对象
* @throws IllegalStateException 如果已经在此LayoutInflater上设置了工厂
* @throws NullPointerException 如果给定的工厂为空
*/
public void setFactory(Factory factory) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = factory;
} else {
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}
}
/**
* 设置第二个工厂方法
* @param factory2 要设置的工厂对象2
* @throws IllegalStateException 如果已经在此LayoutInflater上设置了工厂
* @throws NullPointerException 如果给定的工厂为空
*/
public void setFactory2(Factory2 factory2) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory2 == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = mFactory2 = factory2;
} else {
mFactory = mFactory2 = new FactoryMerger(factory2, factory2, mFactory, mFactory2);
}
}
/***部分代码省略****/
}
这两个方法的功能基本是一致的,setFactory2是在Android 30之后引入的,所以我们需要根据SDK的版本选择调用上述方法。在supportv4中也有LayoutInflaterCompat可以做相同的操作。
以下是代码示例:
```java
public class LayoutInflaterCompat {
/***部分代码省略****/
static final LayoutInflaterCompatImpl IMPL;
static {
final int version = Build.VERSION.SDK_INT;
if (version >= 21) {
IMPL = new LayoutInflaterCompatImplV21();
} else if (version >= 11) {
IMPL = new LayoutInflaterCompatImplV11();
} else {
IMPL = new LayoutInflaterCompatImplBase();
}
}
private LayoutInflaterCompat() {
}
public static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
IMPL.setFactory(inflater, factory);
}
}
```
接下来,我们可以使用LayoutInflaterCompat来找到当前Activity中的id=R.id.text的TextView并将其替换为Button,同时修改BackgroundColor。以下是代码示例:
```java
// 在Activity中获取LayoutInflater对象
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 使用setFactory方法设置LayoutInflaterFactory
LayoutInflaterCompat.setFactory(inflater, new MyLayoutInflaterFactory());
// 通过LayoutInflater对象找到id为R.id.text的TextView并替换为Button
View textView = inflater.inflate(R.layout.my_text_view, null);
Button button = new Button(this);
button.setText("点击我");
button.setBackgroundColor(Color.BLUE);
LinearLayout layout = (LinearLayout) findViewById(R.id.my_layout);
layout.removeAllViews();
layout.addView(textView); // 将TextView添加到布局中
layout.addView(button); // 将Button添加到布局中
```
```java
public class MainActivity extends Activity {
private final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater.from(this).setFactory(new LayoutInflater.Factory() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if ("TextView".equals(name)) {
Log.e(TAG, "name = " + name);
int n = attrs.getAttributeCount();
//打印所有属性标签
for (int i = 0; i < n; i++) {
Log.e(TAG, attrs.getAttributeName(i) + ", " + attrs.getAttributeValue(i));
}
for (int i = 0; i < n; i++) {
if (attrs.getAttributeName(i).equals("id")) {
String attributeValue = attrs.getAttributeValue(i);
String id = attributeValue.substring(1, attributeValue.length());
if (R.id.text == Integer.valueOf(id)) {
Button button = new Button(context, attrs);
button.setBackgroundColor(Color.RED);
return button;
}
}
}
}
return null;
}
});
setContentView(R.layout.activity_main);
}
}
```
在Android开发中,我们经常使用LayoutInflater来加载布局文件。有时候,我们需要自定义一个LayoutInflater的Factory,以便在加载布局文件时可以对布局进行一些处理。下面是一个简单的例子:
```java
public class MyLayoutInflaterFactory implements LayoutInflater.Factory2 {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) throws ClassNotFoundException {
// 在这里可以对布局进行一些处理,例如修改属性等
return super.onCreateView(name, context, attrs);
}
}
```
然后,在Activity中设置这个Factory:
```java
public class MainActivity extends AppCompatActivity {
private MyLayoutInflaterFactory myFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myFactory = new MyLayoutInflaterFactory();
LayoutInflater layoutInflater = LayoutInflater.from(this);
layoutInflater.setFactory2(myFactory);
}
}
```
通过这种方式,我们可以在加载布局文件时对布局进行一些处理。但是,如果我们将MainActivity的父类修改为AppCompatActivity,就会出现一个问题:
```java
public class MyAppCompatActivity extends AppCompatActivity {
private MyLayoutInflaterFactory myFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myFactory = new MyLayoutInflaterFactory();
LayoutInflater layoutInflater = LayoutInflater.from(this);
layoutInflater.setFactory2(myFactory);
}
}
```
运行这段代码,会抛出一个异常:
```java
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.tzx/com.example.tzx.MyAppCompatActivity}: java.lang.IllegalStateException: A factory has already been set on this LayoutInflater
```
这是因为在AppCompatActivity中已经有一个默认的LayoutInflater Factory了,所以不能再设置一个新的Factory。为了解决这个问题,我们可以将自定义的Factory设置到AppCompatActivity的父类中,或者使用其他方法来实现我们的需求。
根据提供的代码,这是一段针对不同SDK版本的AppCompatActivity代码。为了适配不同的SDK版本,我们可以使用`getActionBar()`方法来获取当前设备的ActionBar实例,然后根据其类型来设置DrawerToggle和ToolBar。以下是重构后的代码:
```java
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentActivity;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentActivity;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.view.GravityCompat;
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback, TaskStackBuilderSupportParentable, ActionBarDrawerToggleDelegateProvider {
private DrawerLayout mDrawerLayout;
private AppBarLayout mAppBarLayout;
private CollapsingToolbarLayout mCollapsingToolbarLayout;
private AppCompatDelegate mDelegate;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mDelegate = AppCompatDelegate.create(this, this);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
@Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerLayout = findViewById(R.id.drawer_layout);
mAppBarLayout = findViewById(R.id.app_bar_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar_layout);
setSupportActionBar(mAppBarLayout);
setupDrawerContent();
}
private void setupDrawerContent() {
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, mDrawerLayout, mAppBarLayout, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
mCollapsingToolbarLayout.setTitle(getString(R.string.app_name));
mCollapsingToolbarLayout.setCollapsedTitleTextAppearance(R.style.AppTheme_CollapsingToolbar);
mCollapsingToolbarLayout.setExpandedTitleTextAppearance(R.style.AppTheme_CollapsingToolbar_Expanded);
}
};
toggle.syncState();
mDrawerLayout.addDrawerListener(toggle);
toggle.setDrawerIndicatorEnabled(true);
mDelegate = AppCompatDelegateWrapperImplV7142319500860InstanceFactoryHolderImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryBaseImplV7142319500860InstanceFactoryBaseImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryImplV7142319500860InstanceFactoryBaseImplV7142319500860InstanceFactoryBaseImplV7142319500860InstanceFactoryBaseImplV714231950086();
getDelegate().setCustomViewHierarchyContextCallback(new CustomViewHierarchyCallback()); // 在此处替换为实际的回调类名和构造函数参数,例如:`new CustomViewHierarchyCallback
```java
public abstract class AppCompatDelegate {
/**
* 创建AppCompatDelegate实例
* @param activity 当前活动
* @param callback 回调
* @return 返回AppCompatDelegate实例
*/
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
/**
* 对于不同的版本做适配
* @param context 上下文
* @param window 窗口
* @param callback 回调
* @return 返回AppCompatDelegate实例
*/
private static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) {
final int sdk = Build.VERSION.SDK_INT;
if (sdk >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else if (sdk >= 14) {
return new AppCompatDelegateImplV14(context, window, callback);
} else if (sdk >= 11) {
return new AppCompatDelegateImplV11(context, window, callback);
} else {
return new AppCompatDelegateImplV7(context, window, callback);
}
}
}
```
```java
public class AppCompatDelegateImplV7 extends AppCompatDelegateImplBase {
/**
* Interface for the callback to be used when creating views.
*/
public interface MenuBuilderCallback {
void onCreateOptionsMenu(Menu menu);
// other required methods for the interface
}
/**
* Interface for the callback to be used when inflating views from layout XML files.
*/
public interface LayoutInflaterFactoryCallback {
LayoutInflater getLayoutInflater();
// other required methods for the interface
}
/**
* Method to install the view factory.
*/
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
// Set the factory for the layout inflater
LayoutInflaterCompat.setFactory(layoutInflater, this);
} else {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed so we can not install AppCompat's");
}
}
/**
* Method to create a view using the specified attributes and context. This method will also ensure that the view inherits its context correctly based on the Android version being used.
* @param parent The parent view of the view to be created.
* @param name The name of the view to be created.
* @param context The context in which the view should be created.
* @param attrs The attributes of the view to be created.
* @return The created view.
*/
public View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs) {
final boolean isPre21 = Build.VERSION.SDK_INT < 21;
if (mAppCompatViewInflater == null) {
mAppCompatViewInflater = new AppCompatViewInflater();
}
// We only want the View to inherit it's context if we're running pre-v21 and we have a sub-decorator installed that should inherit the context.
final boolean inheritContext = isPre21 && mSubDecorInstalled && shouldInheritContext((ViewParent) parent);
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext, isPre21, true /* Only read android:theme pre-L (L+ handles this anyway) */);
}
}
```
在Android开发中,`LayoutInflater`是用于将布局文件转化为View对象的重要工具。它有三个不同的版本:
1. `getLayoutInflater()`:这个方法返回的是一个`LayoutInflater`实例,这个实例可以用于解析各种来源的布局,例如XML文件、字符串或者资源ID。
2. `createFromResource(int r, TypedArray a, ClassLoader cl)`:这个方法用于从资源文件中创建一个`LayoutInflater`。
3. `create(Context context, String name, AttributeSet attrs)`:这个方法用于从XML文件中创建一个`LayoutInflater`。
以下是如何使用这些不同的`LayoutInflater`实例的方法:
```java
// 从XML文件中获取LayoutInflater
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (inflater != null){
View layout = inflater.inflate(R.layout.your_layout, null);
}
// 从资源ID获取LayoutInflater
LayoutInflater inflater2 = LayoutInflater.from(this);
if (inflater2 != null){
View layout2 = inflater2.inflate(R.layout.your_layout, null);
}
// 从字符串和属性获取LayoutInflater
String inflater3Xml = "
TypedArray a = getResources().obtainAttributes(R.attr.textViewStyle);
a.setValue(R.style.MyTextView, "true");
LayoutInflater inflater4= LayoutInflater.from(this, inflater3Xml, a, null);
View view4 = inflater4.inflate(R.layout.your_layout, null);
if (view4 != null){
// ...
}else{
// ...
}
```
注意,每个版本的`LayoutInflater`都有其特定的用途和限制。你应该根据你的具体需求选择合适的版本。
```java
public final class LayoutInflaterCompat {
interface LayoutInflaterCompatImpl {
void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory);
LayoutInflaterFactory getFactory(LayoutInflater layoutInflater);
}
static class LayoutInflaterCompatImplBase implements LayoutInflaterCompatImpl {
@Override
public void setFactory(LayoutInflater layoutInflater, LayoutInflaterFactory factory) {
LayoutInflaterCompatBase.setFactory(layoutInflater, factory);
}
@Override
public LayoutInflaterFactory getFactory(LayoutInflater layoutInflater) {
return LayoutInflaterCompatBase.getFactory(layoutInflater);
}
}
static final LayoutInflaterCompatImpl IMPL;
static {
final int version = Build.VERSION.SDK_INT;
if (version >= 21) {
IMPL = new LayoutInflaterCompatImplV21();
} else if (version >= 11) {
IMPL = new LayoutInflaterCompatImplV11();
} else {
IMPL = new LayoutInflaterCompatImplBase();
}
}
/***部分代码省略****/
private LayoutInflaterCompat() {
}
public static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
IMPL.setFactory(inflater, factory);
}
public static LayoutInflaterFactory getFactory(LayoutInflater inflater) {
return IMPL.getFactory(inflater);
}
}
```
以下是重构后的代码:
```java
public class FactoryWrapper implements LayoutInflater.Factory {
private static LayoutInflater mLayoutInflater;
public static void setLayoutInflaterFactory(LayoutInflater inflater) {
mLayoutInflater = inflater;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return mLayoutInflater.createViewFromTag(name, context, attrs);
}
}
```
在这个类中,我们实现了`LayoutInflater.Factory`接口,并在`onCreateView`方法中调用了`LayoutInflater.createViewFromTag`方法。同时,我们还提供了一个静态方法`setLayoutInflaterFactory`,用于设置`LayoutInflaterFactory`的实例。这样,在创建视图的时候,就可以使用`FactoryWrapper`来替代默认的`LayoutInflaterFactory`,从而实现自定义的功能。
```java
public interface LayoutInflaterFactory {
public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}
class LayoutInflaterCompatBase {
static class FactoryWrapper implements LayoutInflater.Factory {
final LayoutInflaterFactory mDelegateFactory;
FactoryWrapper(LayoutInflaterFactory delegateFactory) {
mDelegateFactory = delegateFactory;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
//调用LayoutInflaterFactory实现类的onCreateView(null, name, context, attrs)方法
return mDelegateFactory.onCreateView(null, name, context, attrs);
}
public String toString() {
return getClass().getName() + "{" + mDelegateFactory + "}";
}
}
static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
//最终调用了LayoutInflater的setFactory方法,对Factory进行设置
inflater.setFactory(factory != null ? new FactoryWrapper(factory) : null);
}
static LayoutInflaterFactory getFactory(LayoutInflater inflater) {
LayoutInflater.Factory factory = inflater.getFactory();
if (factory instanceof FactoryWrapper) {
return ((FactoryWrapper) factory).mDelegateFactory;
}
return null;
}
}
```
在`LayoutInflaterFactory`的实现类之一`AppCompatDelegateImplV7`中,我们找到了`setFactory`的实际使用意义。实际上,`setFactory`方法用于设置一个自定义的`LayoutInflaterFactory`,以便在创建视图时可以使用自定义的工厂方法。
以下是一个简单的示例:
```java
// 首先,创建一个自定义的 LayoutInflaterFactory 实现类
public class CustomLayoutInflaterFactory implements LayoutInflaterFactory {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
// 在这里,你可以根据 name、context 和 attrs 来自定义创建 View 的过程
// ...
return view; // 返回创建好的 View
}
}
// 然后,在 AppCompatDelegateImplV7 中使用 setFactory 设置自定义的 LayoutInflaterFactory
public class AppCompatDelegateImplV7 extends AppCompatDelegateWrapper {
private static final String CUSTOM_LAYOUT_FACTORY = "CustomLayoutFactory";
public AppCompatDelegateImplV7() {
setFactory(CUSTOM_LAYOUT_FACTORY, new CustomLayoutInflaterFactory());
}
}
```
在这个示例中,我们首先创建了一个名为`CustomLayoutInflaterFactory`的自定义`LayoutInflaterFactory`实现类。然后,在`AppCompatDelegateImplV7`中,我们使用`setFactory`方法将自定义的工厂设置为`CUSTOM_LAYOUT_FACTORY`。这样,在创建视图时,系统会使用我们提供的自定义工厂方法来创建视图。
```java
class AppCompatViewInflater {
/*****部分代码省略****/
public final View createView(View parent, String name, Context context, AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
Context originalContext = context;
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
view = new AppCompatTextView(context, attrs);
break;
case "ImageView":
view = new AppCompatImageView(context, attrs);
break;
case "Button":
view = new AppCompatButton(context, attrs);
break;
case "EditText":
view = new AppCompatEditText(context, attrs);
break;
case "Spinner":
view = new AppCompatSpinner(context, attrs);
break;
case "ImageButton":
view = new AppCompatImageButton(context, attrs);
break;
case "CheckBox":
view = new AppCompatCheckBox(context, attrs);
break;
case "RadioButton":
view = new AppCompatRadioButton(context, attrs);
break;
case "CheckedTextView":
view = new AppCompatCheckedTextView(context, attrs);
break;
case "AutoCompleteTextView":
view = new AppCompatAutoCompleteTextView(context, attrs);
break;
case "MultiAutoCompleteTextView":
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
break;
case "RatingBar":
view = new AppCompatRatingBar(context, attrs);
break;
case "SeekBar":
view = new AppCompatSeekBar(context, attrs);
break;
}
if (view == null && originalContext != context) { // If the original context does not equal our themed context, then we need to manually inflate it using the name so that android:theme takes effect.
view = createViewFromTag(context, name, attrs); // If we have created a view, check it's android:onClick checkOnClickListener(view, attrs); } return view; } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } }{return this.createView(parent, name, context, attrs, inheritContext, readAndroidTheme, readAppTheme, wrapContext)}// This method is used to create a View based on the provided parameters and attributes. It first checks if the parent context is valid and if the inheritContext flag is set to true. If so, it updates the context to be the parent's context. Then it checks if the readAndroidTheme or readAppTheme flag is set to true. If so, it applies the theme to the context. Finally, it creates a View based on the provided name and attributes and returns it. If no View is created and the original context is different from the updated context, it tries to create a View from a tag using the original context.
AppCompatViewInflater作为LayoutInflaterFactory的最终实现类,负责在onCreateView方法中替换一些我们想要自己替换的View。通过createView方法,AppCompatViewInflater能够自动将原始View替换为实际创建的View,例如:
- 原始View:TextView
实际创建的View:AppCompatTextView
- 原始View:ImageView
实际创建的View:AppCompatImageView
- 原始View:Button
实际创建的View:AppCompatButton
在实际应用中,我们可以使用自定义的LayoutInflater.Factory来替代默认的AppCompatViewInflater。这里有两种书写方式:
1. 使用super.onCreate():
```java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...其他代码...
int viewType = getItemViewType(position);
View view = inflater.inflate(R.layout.item_view, container, false);
// ...其他代码...
return view;
}
```
2. 继续使用`LayoutInflater.from(this).setFactory()`方法:
```java
class MyLayoutInflaterFactory implements LayoutInflater.Factory {
@Override
public View createView(String name, Context context, AttributeSet attrs) throws ClassNotFoundException {
// ...根据name和attrs创建自定义View...
}
}
```
然后在Activity或Fragment中设置自定义的LayoutInflater.Factory:
```java
LayoutInflater.from(this).setFactory(new MyLayoutInflaterFactory());
```
您提供的代码是使用自定义LayoutInflater来创建视图,并在创建TextView时设置背景颜色和文本属性。以下是对这段代码的重构:
```java
private static class CustomLayoutInflater extends LayoutInflater {
private AppCompatDelegate mDelegate;
public CustomLayoutInflater(Context context) {
super(context);
mDelegate = getDelegate();
}
public CustomLayoutInflater setFactory(LayoutInflater.Factory factory) {
mDelegate = (AppCompatDelegate) factory.createView(null, null, null, null);
return this;
}
@Override
protected View onCreateView(String name, Context context, AttributeSet attrs) {
Log.e(TAG, "name = " + name);
int n = attrs.getAttributeCount();
for (int i = 0; i < n; i++) {
Log.e(TAG, attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));
}
for (int i = 0; i < n; i++) {
if (attrs.getAttributeName(i).equals("id")) {
String attributeValue = attrs.getAttributeValue(i);
String id = attributeValue.substring(1, attributeValue.length());
if (R.id.text == Integer.valueOf(id)) {
Button button = new Button(context, attrs);
button.setBackgroundColor(Color.RED);
button.setAllCaps(false);
return button;
}
}
}
return super.onCreateView(name, context, attrs);
}
}
```
然后您可以在需要的地方使用这个自定义的LayoutInflater:
```java
CustomLayoutInflater customLayoutInflater = new CustomLayoutInflater(this);
customLayoutInflater.setFactory(new CustomLayoutInflater.Factory() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
View view = getDelegate().createView(null, name, context, attrs);
if (TextUtils.equals("TextView", name)) {
Log.e(TAG, "name = " + name);
int n = attrs.getAttributeCount();
for (int i = 0; i < n; i++) {
Log.e(TAG, attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));
}
for (int i = 0; i < n; i++) {
if (attrs.getAttributeName(i).equals("id")) {
String attributeValue = attrs.getAttributeValue(i);
String id = attributeValue.substring(1, attributeValue.length());
if (R.id.text == Integer.valueOf(id)) {
Button button = new Button(context, attrs);
button.setBackgroundColor(Color.RED);
button.setAllCaps(false);
return button;
} }
} } return view; } });
View view = customLayoutInflater.inflate(R.layout.your_layout, null); // your_layout 是包含TextView的布局文件名。
使用`LayoutInflaterCompat.setFactory()`方法,您可以设置一个新的`LayoutInflaterFactory`,以便在运行时替换默认的`LayoutInflater`实现。这对于在应用启动时动态更改布局解析器或在特定情况下需要特殊处理布局的情况非常有用。
以下是一个简单的示例:
```kotlin
import androidx.core.view.LayoutInflaterCompat
import android.content.Context
import android.view.LayoutInflater
import android.view.View
class CustomLayoutInflaterFactory(private val context: Context) : LayoutInflaterFactory {
override fun create(name: String): LayoutInflater {
// 在这里创建并返回一个自定义的 LayoutInflater,可以根据需要修改参数和实现
return LayoutInflater.from(context).apply {
// 对 LayoutInflater 进行自定义操作,例如添加自定义属性、扩展功能等
}
}
}
fun main() {
val context = applicationContext // 或者您的其他上下文对象,例如 Activity 或 Service
val customFactory = CustomLayoutInflaterFactory(context)
LayoutInflaterCompat.setFactory(context, customFactory) // 将自定义工厂设置为默认工厂
}
```
在这个例子中,我们创建了一个名为`CustomLayoutInflaterFactory`的新类,它继承自`LayoutInflaterFactory`。然后我们在`create()`方法中创建并返回一个自定义的`LayoutInflater`实例。最后,在`main()`函数中,我们创建了一个`CustomLayoutInflaterFactory`实例,并将其设置为默认的`LayoutInflaterFactory`。这样,当您需要使用`LayoutInflaterCompat.inflate()`方法解析布局时,将使用我们自定义的`LayoutInflater`实现。
重构后的代码如下:
```java
public class MyLayoutInflaterFactory implements LayoutInflaterFactory {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//appcompat 创建view代码
AppCompatDelegate delegate = getDelegate();
View view = delegate.createView(parent, name, context, attrs);
if (TEXT_VIEW_NAME.equals(name)) {
Log.e(TAG, "name = " + name);
int n = attrs.getAttributeCount();
for (int i = 0; i < n; i++) {
Log.e(TAG, attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));
}
for (int i = 0; i < n; i++) {
if (attrs.getAttributeName(i).equals("id")) {
String attributeValue = attrs.getAttributeValue(i);
String id = attributeValue.substring(1, attributeValue.length());
if (R.id.text == Integer.valueOf(id)) {
Button button = new Button(context, attrs);
button.setBackgroundColor(Color.RED);
button.setAllCaps(false);
return button;
}
}
}
}
return view;
}
}
```
两种写法的原理是相同的。因为在上面所讲述的LayoutInflater.Factory实现类FactoryWrapper中,当它实现onCreateView方法时,会调用AppCompatDelegate.onCreateView方法。在这个调用过程中,第一个参数传递的值就是null。
实际上,Android应用中的换肤(夜间模式)也是利用了类似的机制来实现的。这也是为什么我们在下一篇关于Android换肤的文章中将会看到更多关于这一主题的内容。
总结一下,虽然具体的实现方式有所不同,但无论是通过修改资源文件还是通过使用LayoutInflater.Factory,Android系统都可以根据不同的配置来动态地创建并显示不同的视图。这种灵活性使得开发者可以更容易地为用户提供个性化的界面体验。