我是靠谱客的博主 有魅力香菇,这篇文章主要介绍QS快捷开关控制状态栏ICON显示,现在分享给大家,希望可以做个参考。

本文我们在QS下拉快捷开关添加一个"高铁模式"的按钮(实际功能只是模拟开启振动模式),点击后控制状态栏右上角ICON图标去显示一个"高铁icon"(类似飞行模型的飞机icon)。
首先添加状态栏"高铁模式"的icon
SystemUI的config.xml文件里面添加item项,见 注释为添加代码

复制代码
1
2
3
4
5
6
7
8
<string-array name="config_statusBarIcons"> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> <item><xliff:g id="id">@string/status_bar_train</xliff:g></item> <!--jiaxian--> <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> <string translatable="false" name="status_bar_train">train</string> <!--jiaxian-->

然后回到PhoneStatusBarPolicy.java中,控制其显示

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class PhoneStatusBarPolicy implements BluetoothController.Callback, CommandQueue.Callbacks, RotationLockControllerCallback, Listener, ZenModeController.Callback, DeviceProvisionedListener, KeyguardStateController.Callback, LocationController.LocationChangeCallback, RecordingController.RecordingStateChangeCallback, TrainController.Callback{ ... private final TrainController mTrainController; ... private final String mSlotTrain;//jiaxian ... @Inject public PhoneStatusBarPolicy(StatusBarIconController iconController, ... CommandQueue commandQueue, TrainController trainController){ ... mTrainController = trainController; ... mSlotTrain = resources.getString(com.android.internal.R.string.status_bar_train);//jiaxian } ... /** Initialize the object after construction. */ public void init() { // listen for broadcasts mIconController.setIcon(mSlotTrain, R.drawable.stat_sys_train, null); //先设置为false,不显示,后期是否为true的显示由QS设置后,通过回调函数来改变 mIconController.setIconVisibility(mSlotTrain, false); //把PhoneStatusBarPolicy加入回调函数中 mTrainController.addCallback(this); } //实现TrainController.Callback接口后,重写回调方法onTrainStateChange , //当在trainControllerImpl执行notifyAllChanged的时候,该方法就会被执行回调调用。 //回调传过来的enabled参数即是控制是否显示的参数。 @Override public void onTrainStateChange(boolean enabled) { Log.d("PhoneStatusBarPolicy","onTrainStateChange:"+enabled); updateTrain(); } //jiaxian train private final void updateTrain() { boolean trainVisible = false; //这里通过调用isTrainEnabled()来获取状态,其实也可以将onTrainStateChange的形参enabled //传过来,然后再传入setIconVisibility中 trainVisible = mTrainController.isTrainEnabled(); Log.d("PhoneStatusBarPolicy","updateTrain:"+trainVisible); mIconController.setIcon(mSlotTrain, R.drawable.stat_sys_train, null); mIconController.setIconVisibility(mSlotTrain, trainVisible); } }

添加QS快捷开关的流程参考之前写的文章:https://blog.csdn.net/z1804362542/article/details/126126003?spm=1001.2014.3001.5502
这里我们集中讲解需要我们自己创建的TrainTile.java类、TrainController类、TrainControllerImpl类所做的功能
重要的代码在于
1、trainController.observe(this, mCallback); 这个做的就是把当前的TrainTile类注册进回调里面,因为observe走下去其实就后会执行到addCallback(this),这个就是和上面PhoneStatusBarPolicy.java出现的一样了,PhoneStatusBarPolicy也是把自己addCallback(this)进去。
2、final boolean train = trainController.isTrainEnabled();通过trainController的方法来控制目前是显示还是隐藏的状态,这个方法TrainTile用到,PhoneStatusBarPolicy也会用到,这样大家才能一起同步目前的状态。而如何同步的关键在于,回调函数发送作用了。
3、

复制代码
1
2
3
4
5
6
7
private final TrainController.Callback mCallback = new TrainController.Callback() { @Override public void onTrainStateChange(boolean enabled) { refreshState(enabled); } };

这个是写在TrainTile方的一个回调方法,当在TrainControllerImpl的notifyAllChanged()通知触发的时候,这个回到就会被执行,然后回调修改状态,在PhoneStatusBarPolicy中就是我们的重写的onTrainStateChange方法,区别是PhoneStatusBarPolicy是通过实现implments TrainController.addCallback来实现的,而我这里是直接new TrainController.Callback然后匿名内部类实现的

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* * Copyright (c) 2016, The Android Open Source Project * Contributed by the Paranoid Android Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ape.systemui.qs.tiles; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.statusbar.policy.KeyguardStateController; import android.provider.Settings; import android.content.Context; import android.util.Log; import android.os.UserManager; import android.os.SystemClock; import android.content.Intent; import android.content.res.Resources; import android.service.quicksettings.Tile; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import android.widget.Toast; import android.content.Intent; import android.media.AudioManager; import com.android.systemui.statusbar.policy.TrainController.Callback; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; import com.android.systemui.statusbar.policy.TrainController; import com.android.systemui.R; import javax.inject.Inject; /** Quick settings tile: jiaxian.zhang add mute **/ public class TrainTile extends QSTileImpl<BooleanState> { //这个可以Intent到Settings中的音量设置页面,是Settings专门提供给我们的 private static final Intent SOUND_SETTINGS = new Intent(Settings.ACTION_SOUND_SETTINGS); private final TrainController trainController; public static final int MSG_RINGER_MODE_SILENT = 0; //静音模式 public static final int MSG_RINGER_MODE_NORMAL = 2; //一般模式 //注解不要少加 @Inject public TrainTile(QSHost host,TrainController trainController) { super(host); this.trainController = trainController; //在这里把TrainTile注册进去,自己成为一名观察者 trainController.observe(this, mCallback); } @Override public BooleanState newTileState() { return new BooleanState(); } @Override protected void handleLongClick() { } @Override public Intent getLongClickIntent() { Log.d("Mute","getLongClickIntent"); return new Intent(Settings.ACTION_SOUND_SETTINGS); } @Override public boolean isAvailable() { return mContext.getString(R.string.quick_settings_tiles_stock).contains("train"); } @Override protected void handleClick() { //当是打开时,mState.value为true。当是关闭是,mState.value为false //假如mState.value是false,获取后再取反,则最后setTrainEnabled();传进去的就是false final boolean newState = !mState.value; Log.d("Train","handleClick"+" mState.value:"+mState.value+" newState:"+newState); trainController.setTrainEnabled(!newState); refreshState(newState);//其实在这里refreshState也可以不写,参数newState也不重要, //因为我们的回调onTrainStateChange最终也会去执行refreshState() //然后最终还是会触发到我们的handleUpdateState进行QS图标和文件的刷新 } @Override public CharSequence getTileLabel() { return mContext.getString(R.string.quick_settings_mute_label); } /*这个方法的执行有2情况 1、下划QS面板的时候触发所有可选的Tile类的handleUpdateState()执行 2、执行refreshState()的时候,触发所在类的handleUpdateState()执行 */ @Override protected void handleUpdateState(BooleanState state, Object arg) { final boolean train = trainController.isTrainEnabled(); //trainController.isTrainEnabled();获得的就是传给setTrainEnabled的true或者false //所以我们这里要对它取反,以修改QS图标状态显示 Log.d("handleUpdateStatestate","isTrainEnabled()"+train); state.value = !train; Log.d("handleUpdateStatestate","state.value"+state.value); state.icon = ResourceIcon.get(R.drawable.ic_train_normal); state.label = mContext.getString(R.string.quick_settings_mute_label); state.contentDescription = getTrainString(); state.state = state.value ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE; } private String getTrainString() { return mContext.getString(R.string.quick_settings_mute_label); } @Override public int getMetricsCategory() { return MetricsEvent.QS_TRAIN; } @Override public void handleSetListening(boolean listening) { } private final TrainController.Callback mCallback = new TrainController.Callback() { @Override public void onTrainStateChange(boolean enabled) { //监听到其他类修改了后,作为观察者的"我"回调观察到了,回调执行的更新状态, //是谁执行了然后回调到我这里呢?执行者是在TrainControllerImpl类的notifyChanged()方法中。 //我只要被促发就执行refreshState(),refreshState会触发TrainTile类的handleUpdateState() //方法来改变QS的图标和文本,所以我们的 refreshState(enableld) 和 refreshState()是否传参数进去区别不大, //因为在handleUpdateState()是靠trainController来获取true、false控制区别状态的 refreshState(enableld); } }; } --------------------------------------------------------------------- 补充,我们也可以让TrainTilePhoneStatusBarPolicy的做法一样,实现类,重写方法,不用匿名内部类的实现方式 public class TrainTile extends QSTileImpl<BooleanState> implements TrainController.Callback { @Inject public TrainTile(QSHost host,TrainController trainController) { super(host); this.trainController = trainController; // trainController.observe(this, mCallback);改为: trainController.addCallback(this); } // private final TrainController.Callback mCallback = new TrainController.Callback() { // @Override // public void onTrainStateChange(boolean enabled) { // refreshState(enabled);//监听到其他类修改了后,作为观察者的"我"回调观察到了,回调执行的更新状态, // //是谁执行了然后回调到我这里呢?执行者是在RotationLockControllerImpl类的notifyChanged()方法中。 // } // }; 改为: @Override public void onTrainStateChange(boolean enabled) { //监听到其他类修改了后,作为观察者的"我"回调观察到了,回调执行的更新状态, //是谁执行了然后回调到我这里呢?执行者是在RotationLockControllerImpl类的notifyChanged()方法中。 refreshState(enabled); } }

现在看来TrainControllerImpl很重要,需要由它来发送通知同步状态、修改状态、获取状态。但在之前,我们需要先定义它的接口规范。TrainController继承父类CallbackController,所以TrainController是间接拥有addCallback等方法的。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.android.systemui.statusbar.policy; import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.TrainController.Callback; import java.util.Collection; import java.util.List; public interface TrainController extends CallbackController<Callback>{ boolean isTrainEnabled(); void setTrainEnabled(boolean enabled); public interface Callback { void onTrainStateChange(boolean enabled); } }

下面这个类系统提供,不用我们创建,我们只是让TrainController 实现它即可:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface CallbackController<T> { void addCallback(T listener); void removeCallback(T listener); /** * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state * and {@link #removeCallback(Object)} when not resumed automatically. */ default T observe(LifecycleOwner owner, T listener) { return observe(owner.getLifecycle(), listener); } /** * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state * and {@link #removeCallback(Object)} when not resumed automatically. */ default T observe(Lifecycle lifecycle, T listener) { lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> { if (event == Event.ON_RESUME) { addCallback(listener); } else if (event == Event.ON_PAUSE) { removeCallback(listener); } }); return listener; } }

创建实现类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.android.systemui.statusbar.policy; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.WeakHashMap; import android.media.AudioManager; import javax.inject.Inject; import javax.inject.Singleton; /** */ @Singleton public class TrainControllerImpl implements TrainController{ private static final String TAG = "TrainController"; //这个Callback即是TrainController.Callback //private ArrayList<Callback> mTrainChangeCallbacks = new ArrayList<>(); private Context mContext; private boolean mEnabled; private boolean mIsActive; private AudioManager mAudioManager; private final H mHandler; private int mState; /** */ @Inject public TrainControllerImpl(Context context,@Main Looper mainLooper) { mContext = context; mHandler = new H(mainLooper); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); } @Override public boolean isTrainEnabled() { Log.d("TrainController","mEnabled:"+mEnabled); return mEnabled; } @Override public void setTrainEnabled(boolean enabled) { Log.d("TrainController","enabled:"+enabled); //如果开关被点击是true,表激活,就开启振动模式 //开启高铁模式我们做的功能是开启震动模式,关闭就是恢复正常 if(enabling){ mAudioManager.setRingerMode(MSG_RINGER_MODE_VIBRATE); }else{ mAudioManager.setRingerMode(MSG_RINGER_MODE_NORMAL); } mEnabled = enabled; /*通知所有观察者改状态 直接调用notifyAllChanged()这种实现形式会有一个报错: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 也就是我们现在不是在创建线程的原始线程,所以不准,解决方式是模仿一下 LocationControllerImpl的构造方法中的参数@Main Looper mainLooper */ //notifyAllChanged(); //新的可实现方式,用mHandler发送信息给原始线程H来解决。 实现成功 mHandler.obtainMessage(H.CHANGE_ICON,isTrainEnabled()).sendToTarget(); } // public void notifyAllChanged(){ // for (Callback callback : mTrainChangeCallbacks) { // callback.onTrainStateChange(isTrainEnabled()); // } // } @Override public void addCallback(Callback cb) { mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget(); } @Override public void removeCallback(Callback cb) { mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget(); } //构造创建视图的原始线程来执行notifyAllChanged操作 private final class H extends Handler { private static final int MSG_ADD_CALLBACK = 3; private static final int MSG_REMOVE_CALLBACK = 4; private static final int CHANGE_ICON = 5; private ArrayList<TrainController.Callback> mTrainChangeCallbacks = new ArrayList<>(); H(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_ADD_CALLBACK: mTrainChangeCallbacks.add((TrainController.Callback) msg.obj); break; case MSG_REMOVE_CALLBACK: mTrainChangeCallbacks.remove((TrainController.Callback) msg.obj); break; case CHANGE_ICON: //取出 开关状态参数 传入notifyAllChanged boolean isTrainEnabled = (boolean)msg.obj; notifyAllChanged(isTrainEnabled); break; } } private void notifyAllChanged(boolean isTrainEnabled) { for (TrainController.Callback callback : mTrainChangeCallbacks) { callback.onTrainStateChange(isTrainEnabled); } } } }

最后

以上就是有魅力香菇最近收集整理的关于QS快捷开关控制状态栏ICON显示的全部内容,更多相关QS快捷开关控制状态栏ICON显示内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(94)

评论列表共有 0 条评论

立即
投稿
返回
顶部