如何在Fragment里使用自定义的TitleBar-Android开发问答

如何在Fragment里使用自定义的TitleBar-Android开发问答,第1张

根据你的描述: Fragment没有window所有这个方法不可用,必须在Activity中 *** 作,所以你要FragmentActivity中根据不同的fragment来切换不同的titlebar

我们知道,在 Android 中,View 绘制主要包含 3 大流程:

Android 中,主要有两种视图: View 和 ViewGroup ,其中:

虽然 ViewGroup 继承于 View ,但是在 View 绘制三大流程中,某些流程需要区分 View 和 ViewGroup ,它们之间的 *** 作并不完全相同,比如:

对 View 进行测量,主要包含两个步骤:

对于第一个步骤,即求取 View 的 MeasureSpec ,首先我们来看下 MeasureSpec 的源码定义:

MeasureSpec 是 View 的一个公有静态内部类,它是一个 32 位的 int 值,高 2 位表示 SpecMode(测量模式),低 30 位表示 SpecSize(测量尺寸/测量大小)。

MeasureSpec 将两个数据打包到一个 int 值上,可以减少对象内存分配,并且其提供了相应的工具方法可以很方便地让我们从一个 int 值中抽取出 View 的 SpecMode 和 SpecSize。

一个 MeasureSpec 表达的是:该 View 在该种测量模式(SpecMode)下对应的测量尺寸(SpecSize)。其中,SpecMode 有三种类型:

对 View 进行测量,最关键的一步就是计算得到 View 的 MeasureSpec ,子View 在创建时,可以指定不同的 LayoutParams (布局参数), LayoutParams 的源码主要内容如下所示:

其中:

LayoutParams 会受到父容器的 MeasureSpec 的影响,测量过程会依据两者之间的相互约束最终生成子View 的 MeasureSpec ,完成 View 的测量规格。

简而言之,View 的 MeasureSpec 受自身的 LayoutParams 和父容器的 MeasureSpec 共同决定( DecorView 的 MeasureSpec 是由自身的 LayoutParams 和屏幕尺寸共同决定,参考后文)。也因此,如果要求取子View 的 MeasureSpec ,那么首先就需要知道父容器的 MeasureSpec ,层层逆推而上,即最终就是需要知道顶层View(即 DecorView )的 MeasureSpec ,这样才能一层层传递下来,这整个过程需要结合 Activity 的启动过程进行分析。

我们知道,在 Android 中, Activity 是作为视图组件存在,主要就是在手机上显示视图界面,可以供用户 *** 作, Activity 就是 Andorid 中与用户直接交互最多的系统组件。

Activity 的基本视图层次结构如下所示:

Activity 中,实际承载视图的组件是 Window (更具体来说为 PhoneWindow ),顶层View 是 DecorView ,它是一个 FrameLayout , DecorView 内部是一个 LinearLayout ,该 LinearLayout 由两部分组成(不同 Android 版本或主题稍有差异): TitleView 和 ContentView ,其中, TitleView 就是标题栏,也就是我们常说的 TitleBar 或 ActionBar , ContentView 就是内容栏,它也是一个 FrameLayout ,主要用于承载我们的自定义根布局,即当我们调用 setContentView() 时,其实就是把我们自定义的布局设置到该 ContentView 中。

当 Activity 启动完成后,最终就会渲染出上述层次结构的视图。

因此,如果我们要求取得到子View 的 MeasureSpec ,那么第一步就是求取得到顶层View(即 DecorView )的 MeasureSpec 。大致过程如下所示:

经过上述步骤求取得到 View 的 MeasureSpec 后,接下来就可以真正对 View 进行测量,求取 View 的最终测量宽/高:

Android 内部对视图进行测量的过程是由 View#measure(int, int) 方法负责的,但是对于 View 和 ViewGroup ,其具体测量过程有所差异。

因此,对于测量过程,我们分别对 View 和 ViewGroup 进行分析:

综上,无论是对 View 的测量还是 ViewGroup 的测量,都是由 View#measure(int widthMeasureSpec, int heightMeasureSpec) 方法负责,然后真正执行 View 测量的是 View 的 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法。

具体来说, View 直接在 onMeasure() 中测量并设置自己的最终测量宽/高。在默认测量情况下, View 的测量宽/高由其父容器的 MeasureSpec 和自身的 LayoutParams 共同决定,当 View 自身的测量模式为 LayoutParamsUNSPECIFIED 时,其测量宽/高为 android:minWidth / android:minHeight 和其背景宽/高之间的较大值,其余情况皆为自身 MeasureSpec 指定的测量尺寸。

而对于 ViewGroup 来说,由于布局特性的丰富性,只能自己手动覆写 onMeasure() 方法,实现自定义测量过程,但是总的思想都是先测量 子View 大小,最终才能确定自己的测量大小。

当确定了 View 的测量大小后,接下来就可以来确定 View 的布局位置了,也即将 View 放置到屏幕具体哪个位置。

View 的布局过程由 View#layout() 负责,其源码如下:

View#layout() 主要就做了两件事:

ViewGroup 的布局流程由 ViewGroup#layout() 负责,其源码如下:

可以看到, ViewGroup#layout() 最终也是通过 View#layout() 完成自身的布局过程,一个注意的点是, ViewGroup#layout() 是一个 final 方法,因此子类无法覆写该方法,主要是 ViewGroup#layout() 方法内部对子视图动画效果进行了相关设置。

由于 ViewGroup#layout() 内部最终调用的还是 View#layout() ,因此, ViewGroup#onLayout() 就会得到回调,用于处理 子View 的布局放置,其源码如下:

由于不同的 ViewGroup ,其布局特性不同,因此 ViewGroup#onLayout() 是一个抽象方法,交由 ViewGroup 子类依据自己的布局特性,摆放其 子View 的位置。

当 View 的测量大小,布局位置都确定后,就可以最终将该 View 绘制到屏幕上了。

View 的绘制过程由 View#draw() 方法负责,其源码如下:

其实注释已经写的很清楚了, View#draw() 主要做了以下 6 件事:

我们知道,在 Activity 启动过程中,会调用到 ActivityThreadhandleResumeActivity() ,该方法就是 View 视图绘制的起始之处:

可以看到, ActivityThreadhandleResumeActivity() 主要就是获取到当前 Activity 绑定的 ViewManager ,最后调用 ViewManageraddView() 方法将 DecorView 设置到 PhoneWindow 上,也即设置到当前 Activity 上。 ViewManager 是一个接口, WindowManager 继承 ViewManager ,而 WindowManagerImpl 实现了接口 WindowManager ,此处的 ViewManageraddView() 实际上调用的是 WindowManagerImpladdView() ,源码如下所示:

WindowManagerImpladdView() 内部转发到 WindowManagerGlobaladdView() :

在 WindowManagerGlobaladdView() 内部,会创建一个 ViewRootImpl 实例,然后调用 ViewRootImplsetView() 将 ViewRootImpl 与 DecorView 关联到一起:

ViewRootImplsetView() 内部首先关联了传递过来的 DecorView (通过属性 mView 指向 DecorView 即可建立关联),然后最终调用 requestLayout() ,而 requestLayout() 内部又会调用方法 scheduleTraversals() :

ViewRootImplscheduleTraversals() 内部主要做了两件事:

ChoreographerpostCallback() 会申请一次 VSYNC 中断信号,当 VSYNC 信号到达时,便会回调 ChoreographerdoFrame() 方法,内部会触发已经添加的回调任务, Choreographer 的回调任务有以下四种类型:

因此, ViewRootImplscheduleTraversals() 内部通过 mChoreographerpostCallback(ChoreographerCALLBACK_TRAVERSAL, mTraversalRunnable, null) 发送的异步视图渲染消息就会得到回调,即回调 mTra

事情很简单,一个返回按钮设置了点击事件,但是无论如何都触发不了。

布局也很简单,就是一个 LinearLayout 中,包含一个 TitleBar 和一个 RecyclerView:

首先排除了一些小白错误,确定了点击事件确实绑定上了,但是却无法点击。

在网上搜索时,看到有人说到一种情况,就是按钮被上面的控件覆盖了,导致点击事件没有传递过来。

但是,这个界面很简单啊,不可能存在被覆盖的情况吧?

虽然感觉问题不大,但是还是点开了 Layout Inspector 查看了实时布局。

不看不知道,一看吓一跳:

原来他是一个透明的 ActionBar?这也太坑了吧!

找到问题就好解决了,直接隐藏了系统的 ActionBar 即可。

问题一:App 安卓7201280的状态栏、导航栏、主菜单高度分别是50、96、96,那么1080x1920的呢? 7201280的密度与10801920的密度比 乘以高度就好了

问题二:如何修改手机状态栏大小和高度 反编译framework-resapk

2打开res/values/dimensxml文件

3修改如下代码:

250dip

250dip

4第一个是状态栏的高度,250dip是我们现在看到的高度

5第二个是图标的高度

6回编译,替换resourcesarsc到原来的apk里

framework-resapk文件位于/system/framework文件夹中

问题三:android 状态栏高度是多少 可以通过以下方法获取:

public static int getStatusBarHeight(Context context) {

int result = 0;

int resourceId = contextgetResources()getIdentifier(

status_bar_height, dimen, android);

if (resourceId > 0) {

result = contextgetResources()getDimensionPixelSize(resourceId);

}

return result;

}

问题四:android 状态栏高度是多少 可通过下面调用系统方法获取精确的高度:

int resourceId = getResources()getIdentifier(status_bar_height, dimen, android);

获取状态栏高度

int statusBarHeight = getResources()getDimensionPixelSize(resourceId);

问题五:android状态栏是多少dp 可以获取到的。

Rect frame = new Rect();

getWindow()getDecorView()getWindowVisibleDisplayFrame(frame);

int statusBarHeight = frametop;

具体大小我好像记得是20dp不太记得了

问题六:android状态栏高度是多少 一般我觉得45dp就可以了

问题七:android中怎么计算标题栏高度 getWindow()getDecorView()getWindowVisibleDisplayFrame(rect);/取得整个视图部分,注意,如果你要设置标题样式,这个必须出现在标题样式之后,否则会出错

int top = recttop;状态栏的高度,所以rectheight,rectwidth分别是系统的高度的宽度

View v = getWindow()findViewById(WindowID_ANDROID_CONTENT);/获得根视图

int top2 = vgetTop();/状态栏标题栏的总高度,所以标题栏的高度为top2-top

int width = vgetWidth();/视图的宽度,这个宽度好像总是最大的那个

int height = vgetHeight();视图的高度,不包括状态栏和标题栏

如果只想取得屏幕大小,可以用

Display display = getWindowManager()getDefaultDisplay() ;

displaygetWidth();

displaygetHeight();代码见@Overridepublic void onWindowFocusChanged(boolean hasFocus) {

TODO Auto-generated method stub

superonWindowFocusChanged(hasFocus);

Rect frame = new Rect();

getWindow()getDecorView()getWindowVisibleDisplayFrame(frame);

状态栏高度

int statusBarHeight = frametop;

View v = getWindow()findViewById(WindowID_ANDROID_CONTENT);

int contentTop = vgetTop();

statusBarHeight是上面所求的状态栏的高度

int titleBarHeight = contentTop - statusBarHeight;

textView = (TextView) findViewById(RidtextView1);

textViewsetText(标题栏的高度 + IntegertoString(titleBarHeight) +

+ 标题栏高度 + statusBarHeight +

+ 视图的宽度 + vgetWidth()

+

+ 视图的高度(不包含状态栏和标题栏) + vgetHeight());

问题八:安卓480x800状态栏和导航栏高度是多少 分别是12,48 dp @android/style里面有写的

问题九:如何修改 Android 状态栏高度 反编译framework-resapk2打开res/values/dimensxml文件3修改如下代码:250dip250dip4第一个是状态栏的高度,250dip是我们现在看到的高度5第二个是图标的高度6回编译,替换resourcesarsc到原来的apk里

framework-resapk文件位于/system/framework文件夹中

问题十:android 状态栏高度多少25dp 可以通过以下方法获取:

public static int getStatusBarHeight(Context context) {

int result = 0;

int resourceId = contextgetResources()getIdentifier(

status_bar_height, dimen, android");

if (resourceId > 0) {

result = contextgetResources()getDimensionPixelSize(resourceId);

}

return result;

}

首先,将apk文件的后缀名改为zip然后打开该压缩包点开res文件夹点开drawable文件夹(可能有多个,随便开一个即可)当中大多是文件,其中名为ic_launcher的文件一般就是这个安装文件的图标了

可以再AndroidManifestxml文件中给对应的activity配置属性实现

(1)仅去除TitleBar

android:theme="@android:style/ThemeNoTitleBar"

(1)去除TitleBar、状态栏

android:theme="@android:style/ThemeNoTitleBarFullscreen"

"WindowFEATURE_NO_TITLE"这个属性在2x上应该也是生效的,但估计得真机才行,如果不能用,你在代码中这样配置,会编译报错。

application标签中的@style/AppTheme引用自哪个文件夹中的stylesxml,这是根据运行此程序的手机系统来决定的,如果手机系统的API版本是11以上就是v11/stylesxml,API版本是14以上就是v14/stylesxml,以此类推。我们可以通过修改AppBaseTheme的父主题来实现我们需要的样式,此文章主要就是来讨论这个主题如何修改。

使用android系统中自带的主题要加上"android:",如:android:ThemeBlack

使用v7兼容包中的主题不需要前缀,如:ThemeAppCompat

系统自带主题:

API 1:

android:Theme 根主题

android:ThemeBlack 背景黑色

android:ThemeLight 背景白色

android:ThemeWallpaper 以桌面墙纸为背景

android:ThemeTranslucent 透明背景

android:ThemePanel 平板风格

android:ThemeDialog 对话框风格

API 11:

android:ThemeHolo Holo根主题

android:ThemeHoloBlack Holo黑主题

android:ThemeHoloLight Holo白主题

API 14:

android:ThemeDeviceDefault 设备默认根主题

android:ThemeDeviceDefaultBlack 设备默认黑主题

android:ThemeDeviceDefaultLight 设备默认白主题

API 21: (网上常说的 Android Material Design 就是要用这种主题)

android:ThemeMaterial Material根主题

android:ThemeMaterialLight Material白主题

兼容包v7中带的主题:

ThemeAppCompat 兼容主题的根主题

ThemeAppCompatBlack 兼容主题的黑色主题

ThemeAppCompatLight 兼容主题的白色主题

以下都是指“包含”,比如包含“Dialog”表示对话框风格

比如ThemeDialog、ThemeHoloDialog、ThemeMaterialDialog、ThemeAppCompatDialog都是对话框风格

Black 黑色风格

Light 光明风格

Dark 黑暗风格

DayNight 白昼风格

Wallpaper 墙纸为背景

Translucent 透明背景

Panel 平板风格

Dialog 对话框风格

NoTitleBar 没有TitleBar

NoActionBar 没有ActionBar

Fullscreen 全屏风格

MinWidth 对话框或者ActionBar的宽度根据内容变化,而不是充满全屏

WhenLarge 对话框充满全屏

TranslucentDecor 半透明风格

NoDisplay 不显示,也就是隐藏了

WithActionBar 在旧版主题上显示ActionBar

以上就是关于如何在Fragment里使用自定义的TitleBar-Android开发问答全部的内容,包括:如何在Fragment里使用自定义的TitleBar-Android开发问答、Android - View 绘制流程、【Android】记一次点击事件失效排查等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/10075324.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-05
下一篇 2023-05-05

发表评论

登录后才能评论

评论列表(0条)

保存