1.Activity的setContentView源码阅读
实现换肤:
- 每一次打开的都是新的皮肤(更换后的)
- 换肤之后所有的activity里面的View都要换肤
- 每次重新进入app也需要换肤
解决方案:
- 每一个activity里面都把需要换肤的View给找出来,然后调用代码去换肤(死板的方法);
- 获取activity里面的根布局,然后通过不断的循环获取子View,通过 tag;
- 拦截View的创建,这个目前是比较好的。
系统是怎样加载界面的
Activity->setContentView()->getWindow() instanceof PhoneWindow
PhoneWindow里面的setContentView(activity_main)
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
}
//inflate
mLayoutInflater.inflate(layoutResID, mContentParent);
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
做一系列的判断,去加载系统的layout资源文件
protected ViewGroup generateLayout(DecorView decor) {
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
//把系统的布局加入到了DecorView
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//找一个叫做android.R.id.content的一个FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
retuen contentParent;
}
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
2.AppCompatActivity的setcontentView源码阅读
AppCompatActivity
@Override
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
AppCompatDelegateImpl
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.bypassOnContentChanged(mWindow.getCallback());
}
2.1 extends Activity和AppCompateActivity的View的区别
extends AppCompatActivitymImageTv iscom.google.android.material.textview.MaterialTextViewextends ActivitymImageTv isandroid.widget.TextViewLog.d("tag", "onCreate: ________________${tv}")AppCompateActivity创建View的时候会被拦截,不会走系统的Layoutlnfater的创建,就会被替换掉一些特定的View
AppCompatViewInflater
public final View createView(@Nullable View parent, @NonNull final String name,@NonNull Context context,@NonNull AttributeSet attrs, boolean inheritContext,boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
view = createTextView(context, attrs);
verifyNotNull(view, name);
break;
case "ImageView":
view = createImageView(context, attrs);
verifyNotNull(view, name);
break;
case "Button":
view = createButton(context, attrs);
verifyNotNull(view, name);
break;
case "EditText":
view = createEditText(context, attrs);
verifyNotNull(view, name);
break;
case "Spinner":
view = createSpinner(context, attrs);
verifyNotNull(view, name);
break;
case "ImageButton":
view = createImageButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckBox":
view = createCheckBox(context, attrs);
verifyNotNull(view, name);
break;
case "RadioButton":
view = createRadioButton(context, attrs);
verifyNotNull(view, name);
break;
case "CheckedTextView":
view = createCheckedTextView(context, attrs);
verifyNotNull(view, name);
break;
case "AutoCompleteTextView":
view = createAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "MultiAutoCompleteTextView":
view = createMultiAutoCompleteTextView(context, attrs);
verifyNotNull(view, name);
break;
case "RatingBar":
view = createRatingBar(context, attrs);
verifyNotNull(view, name);
break;
case "SeekBar":
view = createSeekBar(context, attrs);
verifyNotNull(view, name);
break;
case "ToggleButton":
view = createToggleButton(context, attrs);
verifyNotNull(view, name);
break;
default:
// The fallback that allows extending class to take over view inflation
// for other tags. Note that we don't check that the result is not-null.
// That allows the custom inflater path to fall back on the default one
// later in this method.
view = createView(context, name, attrs);
}
return view;
}
3.LayoutInflater源码解读
LayoutInflater主要用来inflater我们的layout布局
View.inflate(this, R.layout.activity_main, null)LayoutInflater.from(this).inflate(R.layout.activity_main, null)LayoutInflater.from(this).inflate(R.layout.activity_main, null, false)
3.1LayoutInflater.from(this)获取的源码
//获取系统的服务
public static LayoutInflater from(@UiContext Context context) {
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
#ContextImpl
@Override
public Object getSystemService(String name) {
if (vmIncorrectContextUseEnabled()) {
// Check incorrect Context usage.
if (WINDOW_SERVICE.equals(name) && !isUiContext()) {
final String errorMessage = "Tried to access visual service " + SystemServiceRegistry.getSystemServiceClassName(name)+ " from a non-visual Context:" + getOuterContext();
final String message = "WindowManager should be accessed from Activity or other "+ "visual Context. Use an Activity or a Context created with " + "Context#createWindowContext(int, Bundle), which are adjusted to " + "the configuration and visual bounds of an area on screen.";
final Exception exception = new IllegalAccessException(errorMessage);
StrictMode.onIncorrectContextUsed(message, exception);
Log.e(TAG, errorMessage + " " + message, exception);
}
}
return SystemServiceRegistry.getSystemService(this, name);
}
SystemServiceRegistry.getSystemService(this, name)里:
//从一个静态的Map集合
final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
#SystemServiceRegistry.#static{}中
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}
});
LayoutInflater.from(this)是系统服务,是的单例设计模式(单个实例在内存中)。
3.2 实例化View的inflater方法的源码
LayoutInflater->
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {}->
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {}->
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) {}->
public final View tryCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {}
前提是看你有没有设置Factory继承AppCompatActivity他就设置了
public final View tryCreateView(@Nullable View parent, @NonNull String name,@NonNull Context context,@NonNull AttributeSet attrs) {
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}
View view;
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);
}
return view;
}
最终会调用@Nullable public final View createView(@NonNull Context viewContext, @NonNull String name, @Nullable String prefix, @Nullable AttributeSet attrs) throws ClassNotFoundException, InflateException {}通过反射创建View
try {
final View view = constructor.newInstance(args);
return view;
}
4.拦截View的创建
abstract class BaseSkinActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val inflater = LayoutInflater.from(this)
LayoutInflaterCompat.setFactory2(inflater, object : LayoutInflater.Factory2 {
override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? {
//拦截到View的创建
Log.d("BaseSkinActivity", "拦截到View的创建")
if (name == "Button") {
return TextView(this@BaseSkinActivity).apply {
text = "换肤"
}
}
return null
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
return null
}
})
super.onCreate(savedInstanceState)
}
}