1.android 什么时候必须用inflater
2.详解LayoutInflater.inflate()
3.layoutinflater.inflate å view.inflate çåºå«
4.❤️ Android 源码解读-从setContentView深入了解 Window|Activity|View❤️
5.一个神奇的源码框架——Skins换肤框架
android 什么时候必须用inflater
在实际开发中LayoutInflater这个类还是非常有用的,它的源码作用类似于findViewById()。不同点是源码LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是源码找xml布局文件下的具体widget控件(如Button、TextView等)。源码 具体作用: 1、源码蓝天系源码对于一个没有被载入或者想要动态载入的源码界面,都需要使用LayoutInflater.inflate()来载入;
2、源码对于一个已经载入的源码界面,就可以使用Activiyt.findViewById()方法来获得其中的源码界面元素。
LayoutInflater 是源码一个抽象类,在文档中如下声明:
publicabstractclass LayoutInflater extends Object
获得 LayoutInflater 实例的源码三种方式
1.LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater()
2.LayoutInflater localinflater =(LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
3. LayoutInflater inflater = LayoutInflater.from(context);
其实,这三种方式本质是源码相同的,从源码中可以看出:
getLayoutInflater():
Activity 的源码 getLayoutInflater() 方法是调用 PhoneWindow 的getLayoutInflater()方法,看一下该源代码:
public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); }
可以看出它其实是源码调用 LayoutInflater.from(context)。
LayoutInflater.from(context):
publicstatic LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { thrownew AssertionError("LayoutInflater not found."); } return LayoutInflater; }
可以看出它其实调用 context.getSystemService()。
结论:所以这三种方式最终本质是都是调用的Context.getSystemService()。
inflate 方法 通过 sdk 的 api 文档,可以知道该方法有以下几种过载形式,返回值均是 View 对象,如下:
public View inflate (int resource,nfc苹果源码 ViewGroup root) public View inflate (XmlPullParser parser, ViewGroup root) public View inflate (XmlPullParser parser, ViewGroup root, booleanattachToRoot) public View inflate (int resource, ViewGroup root, boolean attachToRoot)
示意代码:
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.custom, (ViewGroup)findViewById(R.id.test)); //EditText editText = (EditText)findViewById(R.id.content);// error EditText editText = (EditText)view.findViewById(R.id.content);
对于上面代码,指定了第二个参数 ViewGroup root,当然你也可以设置为 null 值。
注意:
·inflate方法与 findViewById 方法不同;
·inflater 是用来找 res/layout下的 xml 布局文件,并且实例化;
·findViewById() 是找具体 xml 布局文件中的具体 widget 控件(如:Button、TextView 等)。
详解LayoutInflater.inflate()
LayoutInflater.inflate()是Android开发中常见方法,广泛应用于Fragment添加布局文件或RecyclerView Adapter为item添加布局。此方法接收三个参数:resource、root 和 attachToRoot。resource参数是目标布局文件,root参数为布局的根参数,attachToRoot参数决定是否将resource依附于root。
官方文档解释,attachToRoot为true时,resource指定的布局文件会依附于root指定的ViewGroup,方法返回root参数,反之返回填充并返回resource指定的布局文件。具体实现参见源码。
总结而言,attachToRoot参数决定布局文件的参数比较源码依附性,false时root仅用于创建正确的LayoutParams的子类,true时布局文件依附root。
具体应用场景如下:
1. inflate(R.layout.xxx,null):生成布局文件,宽高属性参照父布局,无效的宽高修改不会影响显示效果。
2. inflate(R.layout.xxx,parent,false):加入父布局,布局文件的宽高属性有效,显示宽度和高度符合布局文件设定。
3. inflate(R.layout.xxx,parent,true):加入父布局且依附root,布局文件依附root。若root为ListView等,调用addView方法时出错,因此在Adapter内使用attachToRoot为true会报错。适用于为Fragment创建布局时,布局文件会添加到父activity中盛放fragment的布局中。
layoutinflater.inflate å view.inflate çåºå«
å¹³æ¶ListViewå è½½itemä¸ï¼adapterçgetViewæ¹æ³ä¸ï¼æ们ç»å¸¸ç¨å°ï¼
LayoutInflater.from(mContext).inflate(R.layout.it,parent,false);
è¿æ ·çæ¹æ³æ¥å è½½å¸å±xml,å¹³æ¶ä¸ç´å°±æ¯è¿ä¹ç¨çï¼ä¹æ²¡ä»ä¹çé®ãä»å¤©ç½ä¸çäºä¸ªèªå®ä¹å¸å±çæºç ï¼èªå®ä¹å¸å±ä¸å è½½å¸å±xmlç¨çView.inflateæ¹æ³ï¼
public class SettingItemView extends RelativeLayout {
private CheckBox cb_status;
private TextView tv_description;
private TextView tv_title;
private String desc_on;
private String desc_off;
private void iniView(Context context) {
View.inflate(context, R.layout.setting_item_view, this);//第ä¸ä¸ªåæ°ä¼ å¸å±æ件çç¶ç±»
cb_status=(CheckBox) this.findViewById(R.id.cb_status);
tv_description=(TextView) this.findViewById(R.id.tv_description);
tv_title=(TextView) this.findViewById(R.id.tv_title);
}
第ä¸æ¬¡è§ç¨è¿ç§æ¹å¼æ¥å è½½å¸å±çï¼çäºä¸ä»çlistviewå è½½item,ä¹æ¯ç¨è¿ç§æ¹å¼ï¼
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder;
if(convertView==null){
view=View.inflate(getApplicationContext(), R.layout.list_item_callsms, null);//æåä¸ä¸ªä¼ äºnull
holder=new ViewHolder();
holder.tv_number=(TextView) view.findViewById(R.id.tv_black_number);
holder.tv_mode=(TextView) view.findViewById(R.id.tv_black_mode);
holder.iv_delete=(ImageView) view.findViewById(R.id.iv_delete);
view.setTag(holder);
好å§ï¼çä¸ä¸View.inflateç说æï¼
Open Declaration View android.view.View.inflate(Context context, int resource, ViewGroup root)
Inflate a view from an XML resource. This convenience method wraps the
LayoutInflater class, which provides a full range of options for view
inflation.
Parameters: context The Context object for your activity or
application. resource The resource ID to inflate root A view group
that will be the parent. Used to properly inflate the layout_
*parameters.
See Also: LayoutInflater
æåæä¸å¥è®©ä½ çLayoutInflaterè¿ä¸ªç±»ï¼æçå®å é¨ä¹æ¯ç¨LayoutInflaterå®ç°çï¼è¿å ¥æºç ï¼
public static View inflate(Context context, int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
æç¶å é¨ä¹æ¯ç¨LayoutInflaterå®ç°çï¼ä¸ç¥é为å¥androidè¿è¦ç¨View.inflatå°è£ ä¸ä¸ããão(ãââ½âã)o
å ¶ä¸LayoutInflaterçInflateçä¸ä¸ªåæ°ææ为ï¼
对äºInflateçä¸ä¸ªåæ°(int resource, ViewGroup root, boolean attachToRoot)
å¦æinflate(layoutId, null )ålayoutIdçæå¤å±çæ§ä»¶ç宽é«æ¯æ²¡æææç
å¦æinflate(layoutId, root, false ) å认为åä¸é¢æææ¯ä¸æ ·ç
å¦æinflate(layoutId, root, true ) å认为è¿æ ·çè¯layoutIdçæå¤å±æ§ä»¶ç宽é«æè½æ£å¸¸æ¾ç¤º
对è¿ä¸ä¸ªåæ°åºå«ä¸ç解çè¯å¯ä»¥çè¿ç¯æç« ï¼
inflate第ä¸ä¸ªåæ°ææ
ä»æºç è§åº¦è§£æçæé大ç¥çï¼
Android LayoutInflateråçåæï¼å¸¦ä½ ä¸æ¥æ¥æ·±å ¥äºè§£View(ä¸)
以åå¦ä¸ç¯æè§å¾ä¸éçï¼
Android LayoutInflate深度解æ ç»ä½ 带æ¥å ¨æ°ç认è¯
çå®ï¼ä½ åºè¯¥ç¥éè¿ä¸ªåæ°ææäºï¼okï¼åæ¥çä¸é¢ä»£ç ï¼ è¿æ¶å°±å¯ä»¥æ¿æ¢ä¸ºlayoutInflaterçæ¹å¼äºï¼
对äºç¬¬ä¸ä¸ªèªå®ä¹å¸å±ï¼
//View.inflate(context, R.layout.setting_item_view, this);//第ä¸ä¸ªåæ°ä¼ å¸å±æ件çç¶ç±»
LayoutInflater.from(context).inflate(R.layout.setting_item_view, this, true);//çä»·äºä¸é¢
第äºä¸ªéé å¨ä¸getViewï¼
//view=View.inflate(getApplicationContext(), R.layout.list_item_callsms, null);
view=LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_item_callsms,parent,false);
❤️ Android 源码解读-从setContentView深入了解 Window|Activity|View❤️
Android系统中,Window、Activity、View之间的关系是紧密相连且相互作用的。了解这三者之间的axture的源码关系,有助于深入理解Android应用的渲染和交互机制。
在Android中,通常在创建Activity时会调用`setContentView()`方法,以指定显示的布局资源。这个方法主要作用是将指定的布局添加到一个名为`DecorView`的容器中,并最终将其显示在屏幕上。这一过程涉及到多个组件的交互,下面分步骤解析。
在`Activity`类中,`setContentView()`方法调用`getWindow()`方法获取`Window`对象,而`Window`对象在`Activity`的`attach()`方法中被初始化。`Window`对象是一个抽象类,其默认实现为`PhoneWindow`,这是Android特定的窗口实现。
`PhoneWindow`在创建时会通过`setWindowManager()`方法与`WindowManager`进行关联。`WindowManager`是系统级组件,用于管理所有的窗口,包括窗口的创建、更新、删除等操作。马琳源码`WindowManager`的管理最终由`WindowManagerService`(WMS)执行,这是一个运行在系统进程中的服务。
在`PhoneWindow`中,`installDecor()`方法会初始化`DecorView`和`mContentParent`。`mContentParent`是一个`ViewGroup`,用于存放`setContentView()`传入的布局。通过`mLayoutInflater`的`inflate()`方法,将指定的布局资源添加到`mContentParent`中。
`DecorView`是一个特殊的`FrameLayout`,包含了`mContentParent`。在完成布局的添加后,`DecorView`本身并没有直接与`Activity`建立联系,也没有被绘制到屏幕上显示。`DecorView`的绘制和显示发生在`Activity`的`onResume()`方法执行后,这时`Activity`中的内容才真正可见。
当`Activity`执行到`onCreate()`阶段时,其内容实际上并没有显示在屏幕上,直到执行到`onResume()`阶段,`Activity`的内容才被真正显示。这一过程涉及到`ActivityThread`中的`handleResumeActivity()`方法,该方法会调用`WindowManager`的`addView()`方法,将`DecorView`添加到`WindowManagerService`中,完成`DecorView`的绘制和显示。
`WindowManagerService`通过`addView()`方法将`DecorView`添加到显示队列中,并且在添加过程中,会创建关键的`ViewRootImpl`对象,进一步管理`DecorView`的布局、测量和绘制。`ViewRootImpl`会调用`mWindowSession`的`addToDisplay()`方法,将`DecorView`添加到真正的显示队列中。
`mWindowSession`是`WindowManagerGlobal`中的单例对象,其内部实际上是一个`IWindowSession`类型,通过`AIDL`接口与系统进程中的`Session`对象进行通信,最终实现`DecorView`的添加和显示。
通过`setView()`方法的实现,可以看到除了调用`IWindowSession`进行跨进程添加`View`之外,还会设置输入事件处理。当触屏事件发生时,这些事件首先通过驱动层的优化计算,通过`Socket`跨进程通知`Android Framework`层,最终触屏事件会通过输入管道传送到`DecorView`处理。
在`DecorView`内部,触屏事件会通过`onProcess`方法传递给`mView`,即`PhoneWindow`中的`DecorView`。最终,事件传递到`PhoneWindow`中的`View.java`实现的`dispatchPointerEvent()`方法,并调用`Window.Callback`的`dispatchTouchEvent(ev)`方法。对于`Activity`来说,`dispatchTouchEvent()`方法最终还是会调用`PhoneWindow`的`superDispatchTouchEvent()`,然后传递给`DecorView`的`superDispatchTouchEvent()`方法,完成事件的分发和处理。
综上所述,通过`setContentView()`的过程,我们可以清晰地看到`Activity`、`Window`、`View`之间的交互关系。整个过程主要由`PhoneWindow`组件主导,而`Activity`主要负责提供要显示的布局资源,其与屏幕的直接交互则通过`WindowManager`和`WindowManagerService`实现。
一个神奇的框架——Skins换肤框架
作者:dora
为什么会有换肤的需求?app的换肤,可以降低用户的审美疲劳。持续不变的UI设计,会让用户体验大打折扣,即使表面上用户不说话,但内心中多少会有些不爽。因此,app的界面需要适当的改版,否则用户体验会大受影响,尤其是当UI设计相对较差时。
换肤是什么?换肤是指将app的背景色、文字颜色以及资源进行一键全部切换的过程。这包括资源和颜色资源的切换。
Skins是如何解决换肤需求的?Skins是一个专门解决换肤需求的框架。
以更换皮肤颜色为例,打开res/colors.xml文件,将所有需要换肤的颜色,添加skin_前缀和_skinname后缀,不加后缀的颜色就是默认皮肤。然后在启动页应用预设的皮肤类型。在布局layout文件中使用默认皮肤的资源名称,例如R.color.skin_theme_color,框架会自动帮你替换。要让框架自动替换,需要让所有要换肤的Activity继承BaseSkinActivity。
在代码中使用换肤时,与布局文件中的定义有一些不同。拿到的skinThemeColor就是当前皮肤下的真正颜色值,比如R.color.skin_theme_color_orange的颜色值“#ff”或R.id.skin_theme_color_blue的颜色值“#e9”。此外,SkinLoader还提供了更简洁的设置View颜色的方法。
框架的原理解析,我们先看BaseSkinActivity的源码。此框架继承了dora.BaseActivity,因此需要依赖dora框架。如果不依赖dora框架,可以不使用Skins,但不建议这样做。Skins自动配置了对Dora生命周期注入特性的依赖。关键代码为LayoutInflaterCompat.setFactory(layoutInflater, this),这行代码干预了所有Activity的onCreateView时的布局加载过程。在SkinAttrSupport.getSkinAttrs中解析了AttributeSet,只干预skin_开头的资源加载过程,从而得到我们需要的属性,最后得到SkinAttr列表返回。Skins框架定义了几种主要的换肤属性,理解原理后,可以自行扩展,比如RadioButton的button属性等。
Android学习资源:从性能优化到车载开发,从逆向安全到Framework底层原理,再到音视频、Jetpack全家桶、Kotlin、Gradle、OkHttp源码解析和Flutter等,涵盖了Android开发的多个方面。