1.简介
相比苹果封闭的IOS系统,Android系统的开放性带来了很多的优势。与此同时,也带来了严重的碎片化问题,包括硬件的碎片化和软件碎片化。这里,我们主要说的是软件方面。各Android设备厂商,受限于成本和技术原因,往往无法及时把系统版本更新到最新的Android版本。这导致低版本的Android系统在市场上仍然有很高的占有率。如下是2019年7月统计的Android设备中系统版本的市场占有率分布情况。注:此数据来源于Google Play,有可能没有包含国内厂商不支持GMS的系统。
,
额外说一句,统计各版本系统在市场上的占有率,有什么意义呢?对于App开发者而言,当然是希望自己的应用,能在尽可能多的设备上运行,服务更多的用户(赚更多的钱),那么,当前市场上,什么版本的系统最多,自然是开发者需要考虑着重适配的系统版本了。举个栗子,如果你的app,只能支持Android5.0(Lollipop),其他版本都不能运行,那么,就算所有5.0的机器都装上了你的App,也只有3%的市场份额而已。
所以,支持高版本系统,同时兼容低版本系统,一直是App开发者的心头好。
2.AppCompat特点
- 以Library的形式, 独立包含在应用中 ,为App提供在低版本Android系统上运行的兼容性支持。
- AppCompat最早出现在V7 Support Library中,现已迁移到AndroidX,后续会跟随AndroidX库持续更新。下图展示的是AppCompat与Jetpack、Support Library以及Androidx的关系。
3.主要组件及使用方法
本文将主要说明AndroidX中AppCompat的使用。
Tool Bar
直译为工具栏(官方称应用栏),或称Action Bar,提供显示在应用顶部快捷操作的实现方案。如下图示:
可以通过添加操作按钮,可以将在当前上下文中最重要的操作放在应用顶部。
例如,当用户浏览文件列表时,可以再应用的顶部显示“搜索”按钮;当用户浏览单张图片时,可以在应用顶部显示“收藏“按钮;也可以提供快速跳转到“设置”的按钮,完成
工具栏中的空间有限。如果应用声明的操作数量过多,导致应用栏无法容纳,则应用栏会将无法容纳的操作发送到“溢出”菜单。应用还可以指定某项操作应始终显示在溢出菜单中,而不是显示在应用栏中。
下面介绍在Fragment中添加Tool Bar的步骤;如果需要在Activity中添加Tool Bar,与此类似,也可以参考[官方文档]: https://developer.android.google.cn/training/appbar/setting-up.html “设置应用栏”
- 装载Fragment的Activity需要扩展
androidx.appcompat.app.AppCompatActivity
。
public class MyActivity extends AppCompatActivity {
// ...
}
- 在应用清单中,将 元素设为使用 appcompat 的其中一个 NoActionBar 主题背景。使用其中一个主题背景可以防止应用使用原生 ActionBar 类提供应用栏。例如:
<application
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
/>
也可以把Theme定义在styles.xml文件中,如下所示,
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
</style>
</resources>
然后在清单文件中,使用这个Theme
<application
android:theme="@style/AppTheme">
- 向Fragment的布局文件中添加一个ToolBar,下面的示例将在Fragment界面的顶部添加一个Toolbar.
<androidx.appcompat.widget.Toolbar
android:id="@+id/demo_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
注:android:elevation
是应用栏的高度,可参阅官方文档 Material Design 规范。android:theme
是指ToolBar的Theme。app:popupTheme
是指ToolBar中点击“溢出”图标,弹出的菜单的Theme。
- 在 Fragment的
onCreateView()
方法中,调用AppCompatActivity
的setSupportActionBar()
方法,并传入工具栏。同时,必须调用setHasOptionsMenu(true)
方法,设置ToolBar可用,否则工具栏将不会被创建和显示。如下示例:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragmentView = inflater.inflate(
R.layout.fragment_tool_bar_demo, container, false);
mToolbar = fragmentView.findViewById(R.id.demo_toolbar);
mSearResultDemo = fragmentView.findViewById(R.id.search_demo_tv);
((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar);
setHasOptionsMenu(true);//必须调用此方法
return fragmentView;
}
- 自定义ToolBar上的按钮。
ToolBar上的操作按钮可以在menu的XML文件中定义,可设置操作按钮的文字,图标,显示状态等信息,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_action_1"
android:title="@string/menu_action_1"
app:showAsAction="never" />
<item
android:id="@+id/menu_action_2"
android:title="@string/menu_action_2"
app:showAsAction="never" />
<item
android:id="@+id/menu_action_3"
android:title="@string/menu_action_3"
app:showAsAction="never" />
<item
android:id="@+id/menu_action_search"
android:title="@string/menu_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_action_share"
android:title="@string/menu_action_share"
app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_action_add_favorite"
android:title="@string/menu_action_favorite"
android:icon="@drawable/favorite"
app:showAsAction="always" />
</menu>
注:app:showAsAction
用來标识这个Menu Item在ToolBar上的显示状态,包含如下几种:
always
— 一直显示;ifRoom
— 如果ToolBar上有足够的空间,则显示,否则会放到“溢出”列表;never
— 始终放到“溢出“列表,不管是否有空间。
- 加载自定义的menu布局
重写Fragment中的onCreateOptionsMenu()
方法,加载上面的menu xml文件。例如:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.tool_bar_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
- 响应点击事件
重写Fragment中的onOptionsItemSelected()
方法,处理按钮的点击事件,如下所示:
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch(item.getItemId()) {
case R.id.menu_action_1:
// Add your code for action_1 button clicked
break;
case R.id.menu_action_2:
break;
case R.id.menu_action_3:
break;
case R.id.menu_action_add_favorite:
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
- 使用Tool Bar的Utility方法
将工具栏设为 Fragment的应用栏后,如果要使用 ToolBar的Utility方法,可调用 Activity的getSupportActionBar()
方法。此方法将返回对 appcompat ActionBar 对象的引用。获得该引用后,您就可以调用任何 ActionBar 方法来调整应用栏。例如,要隐藏应用栏,可调用ActionBar.hide()
。
AppCompatActivity
可作为应用的Activity的基类,替换原生的Activity基类,实现有ToolBar的应用界面;目前,AppCompatActivity 通过间接继承ComponentActivity,现了LifecycleOwner接口,在应用自己的Activity中,可以通过 getLifecycle()
方法拿到 Lifecycle ,并添加 Observer 来实现对 Activity 生命周期的监听。 如下示例:
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testLifecycle();
}
private void testLifecycle() {
getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume(){
Log.d(TAG, "onResume: called by LifecycleObserver");
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestroy() {
}
});
}
}
AppCompatDialog
对话框类,使用此基类配合AppCompatActivity,可以实现与App其他界面风格统一的对话框。
- 在
styles.xml
文件定义如下的风格样式
<resources>
<style name="myDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">@color/myColorAccent</item>
<item name="android:textColorPrimary">@color/myTextColorPrimary</item>
<item name="android:background">@color/myDialogBackGroundColor</item>
</style>
</resources>
- 构建
AppCompatDialog
时指定样式
private void testAppCompatDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this,R.style.myDialogStyle);
builder.setTitle("AppCompatDialog");
builder.setMessage("This is a demo dialog");
builder.setPositiveButton("OK", null);
builder.setNegativeButton("Cancel", null);
builder.show();
}
- 效果展示
ShareActionProvider
在应用栏上支持标准化分享操作,从而在应用之间通过Intent共享数据。例如发送电子邮件或发布到社交应用。
可以在onCreateOptionsMenu()
中,初始化ShareActionProvider,并设置Intent,示例如下:
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
MenuItem shareItem = menu.findItem(R.id.menu_action_share);
mShareActionProvider = (ShareActionProvider)
MenuItemCompat.getActionProvider(shareItem);
Intent myShareIntent = new Intent(Intent.ACTION_SEND);
myShareIntent.setType("text/plain");
myShareIntent.putExtra(Intent.EXTRA_TEXT, "a single string for share");
mShareActionProvider.setShareIntent(myShareIntent);
super.onCreateOptionsMenu(menu, inflater);
}
参考资料
https://developer.android.google.cn/training/appbar https://developer.android.google.cn/reference/androidx/appcompat/app/AppCompatActivity.html