全局View的样式
程序开发过程中,很多时候都会需要在多个界面都展示同一个View;看起来就像是微信中的聊天悬浮框:
在平时的应用APP中,需要在几个界面都展示的View一般都采用这两种方法:
- 在这几个界面的父界面添加View,然后再子界面中进行修改;
- 建立一个全局的View,然后提供修改方法供不同界面进行修改。
第一种方式使用面较窄,只适合几个左右滑动的Fragment中有同一个View,比如发布按钮这种;如果需要在不同的Activity中展示同一个View,这种方式就没法实现了。还有一种就是在每个界面都加上一个View,每进入一个界面就设置View为当前状态;这种实现方式不仅控制麻烦,而且界面跳转时效果可能很不友好。。。
第二种实现方式就比较一劳永逸了。只需要对外提供修改状态的方法或者接受通知自行修改状态即可,在所有界面都可以控制View的状态及显隐。主要是通过Service来创建View,下面提供这种方式的实现方法之一。
全局View的实现
最近接手的一个项目中就遇到了这种需要在不同界面展示同一个View的需求。一个是需要对APP和后台连接状态进行展示,另一个是需要对于门锁状态的展示。见图:
创建显示的View,首先就是编写布局文件:
layout_top_status.xml
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/colorPrimary">
<ImageView
android:id="@+id/iv_main_status_wifi"
android:layout_width="38dp"
android:layout_height="30dp"
android:layout_marginStart="42dp"
android:layout_marginTop="24dp"
android:src="@mipmap/ic_wifi_connect"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_main_status_wifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="@color/white"
android:textSize="@dimen/txt_main_status"
app:layout_constraintBottom_toBottomOf="@id/iv_main_status_wifi"
app:layout_constraintStart_toEndOf="@id/iv_main_status_wifi"
app:layout_constraintTop_toTopOf="@id/iv_main_status_wifi"
tools:text="已连接" />
<ImageView
android:id="@+id/iv_main_status_door"
android:layout_width="38dp"
android:layout_height="30dp"
android:layout_marginStart="20dp"
android:src="@mipmap/ic_lock"
app:layout_constraintBottom_toBottomOf="@id/tv_main_status_wifi"
app:layout_constraintStart_toEndOf="@id/tv_main_status_wifi"
app:layout_constraintTop_toTopOf="@id/tv_main_status_wifi" />
<TextView
android:id="@+id/tv_main_status_door"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="@color/white"
android:textSize="@dimen/txt_main_status"
app:layout_constraintBottom_toBottomOf="@id/iv_main_status_door"
app:layout_constraintStart_toEndOf="@id/iv_main_status_door"
app:layout_constraintTop_toTopOf="@id/iv_main_status_door"
tools:text="关闭中" />
</android.support.constraint.ConstraintLayout>
之后就是在Service创建时加载View然后显示出来,主要是通过WindowManager来进行操作:
TopStatusService.java 部分代码
private View mTopView;
private ImageView mIvWifiStatus;
private TextView mTvWifiStatus;
private ImageView mIvDoorStatus;
private TextView mTvDoorStatus;
@Override
public void onCreate() {
super.onCreate();
NoticeUtil.registNotice(this); //注册EventBus
int[] size = ScreenUtil.getScreenInfo(this);
WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (manager != null) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
params.x = params.y = 0;
params.width = size[0];
params.height = ScreenUtil.dp2px(this, 100);
params.gravity = Gravity.START | Gravity.TOP;
//加载View界面
mTopView = LayoutInflater.from(this).inflate(R.layout.layout_top_status, null);
initView(mTopView);
manager.addView(mTopView, params); //把View添加到WindowManager中
}
//View初始化
private void initView(View view) {
mIvWifiStatus = view.findViewById(R.id.iv_main_status_wifi);
mTvWifiStatus = view.findViewById(R.id.tv_main_status_wifi);
mIvDoorStatus = view.findViewById(R.id.iv_main_status_door);
mTvDoorStatus = view.findViewById(R.id.tv_main_status_door);
}
//刷新门状态
public void refreshDoorStatus(boolean open) {
if (open) {
mTvDoorStatus.setText(R.string.door_status_opening);
mTvDoorStatus.setTextColor(ContextCompat.getColor(this, R.color.color_err));
mIvDoorStatus.setImageResource(R.mipmap.ic_unlock);
} else {
mTvDoorStatus.setText(R.string.door_status_closing);
mTvDoorStatus.setTextColor(ContextCompat.getColor(this, R.color.white));
mIvDoorStatus.setImageResource(R.mipmap.ic_lock);
}
}
//刷新后台连接状态
public void refreshWifiStatus(boolean connect) {
if (connect) {
mTvWifiStatus.setText("已连接");
mTvWifiStatus.setTextColor(ContextCompat.getColor(this, R.color.white));
mIvWifiStatus.setImageResource(R.mipmap.ic_wifi_connect);
} else {
mTvWifiStatus.setText("已中断");
mTvWifiStatus.setTextColor(ContextCompat.getColor(this, R.color.color_err));
mIvWifiStatus.setImageResource(R.mipmap.ic_wifi_unconnect);
}
}
@Override
public void onDestroy() {
WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (manager != null && mTopView != null) {
manager.removeView(mTopView);
mTopView = null;
}
NoticeUtil.unRegistNotice(this); //EventBus解注册
super.onDestroy();
}
}
这样,在主Activity启动时,启动Service即可展示View,然后通过不同的事件通知来进行View的操作即可,如果使用EventBus就需要把Service注册到EventBus上(记得 在onDestroy时解注册!)。
//EventBus事件接收处理
@Subscribe(threadMode = ThreadMode.MAIN)
public void handlerMainEvent(EventBean bean) {
switch (bean.getMsg()) {
case EventBusMsg.DOOR_STATUS: //刷新门锁状态
refreshDoorStatus(bean.getStatus() == 200);
break;
case EventBusMsg.WIFI_STATUS: //刷新连接状态
refreshWifiStatus(bean.getStatus() == 200);
break;
}
}
当然,你也可以在外部调用Service的方法来进行View状态的修改,这样就需要一个全局的Service对象供调用。
以上就可以实现在APP中任何界面都展示出状态栏的View,而且任何地方都可以修改其状态,需要注意的是 需要在界面销毁时把服务也停止。