将总是可见的聊天头的 LayoutParams 更改为不总是可见
Changing LayoutParams of always visible Chat Heads to not always visible
我正在尝试添加通过服务生成的视图。我使用的代码基于 Facebook Chatheads,无论应用程序状态如何,它始终可见。它们也显示在其他任何东西之上:
我现在希望将聊天头限制在活动应用程序中。具体来说,每当我将 Window.LayoutParams 从 TYPE_PHONE 更改为 TYPE_DRAWN_APPLICATION 时,我都会处理 Bad Token Exception。
我的问题:
我知道我需要将正确的 window 标记传递给 LayoutParams,但似乎无法弄清楚如何正确执行此操作。任何建议将不胜感激。
这是我的代码:
//主要Activity
private void addNewBubble() {
BubbleLayout bubbleView = (BubbleLayout)LayoutInflater.from(MainActivity.this).inflate(R.layout.bubble_layout, null);
bubblesManager.addBubble(bubbleView, 60, 20);
}
// initializes Bubbles Manager
private void initializeBubblesManager() {
bubblesManager = new BubblesManager.Builder(this)
.setTrashLayout(R.layout.task_bubble_trash_layout)
.setInitializationCallback(new OnInitializedCallback() {
@Override
public void onInitialized() {
addNewBubble(); // Called when addNewBubble is initialized and the bubble data is loaded. When used on devices running API 18 or below, this function is always called.
}
})
.build();
bubblesManager.initialize();
}
// initializes Bubbles Manager
private void initializeBubblesManager() {
bubblesManager = new BubblesManager.Builder(this)
.setTrashLayout(R.layout.task_bubble_trash_layout)
.setInitializationCallback(new OnInitializedCallback() {
@Override
public void onInitialized() {
addNewBubble(); // Called when addNewBubble is initialized and the bubble data is loaded. When used on devices running API 18 or below, this function is always called.
}
})
.build();
bubblesManager.initialize();
}
//XML - 自定义 Bubble_layout
<com.momely.bubbles.BubbleLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<ImageView
android:id="@+id/avatar"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_gravity="center"
android:background="@drawable/profile_decorator"
android:src="@drawable/round_button"
android:scaleType="centerCrop"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="15sp"
android:layout_marginTop="2dp"
android:layout_marginLeft="2dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:background="@drawable/bubble_counter_bkg"
android:text="1"/>
</com.momely.bubbles.BubbleLayout>
//在bubblesManager
public class BubblesManager {
private static BubblesManager INSTANCE;
private Context context;
private boolean bounded;
private BubblesService bubblesService;
private int trashLayoutResourceId;
private OnInitializedCallback listener;
//getInstance (called in Builder below)
private static BubblesManager getInstance(Context context){
if (INSTANCE == null) {
INSTANCE = new BubblesManager(context);
}
return INSTANCE;
}
//Binds the service to the application
private ServiceConnection bubbleServiceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service){
BubblesService.BubblesServiceBinder binder = (BubblesService.BubblesServiceBinder)service;
BubblesManager.this.bubblesService = binder.getService();
configureBubblesService();
bounded = true;
if(listener != null){
listener.onInitialized();
}
}
//Initializes Bubbles Manager
private BubblesManager(Context context){
this.context = context;
}
//Initializes the service
public void initialize(){
context.bindService(new Intent(context, BubblesService.class),
bubbleServiceConnection,
Context.BIND_AUTO_CREATE);
}
public void addBubble(BubbleLayout bubble, int x, int y){
if(bounded){
bubblesService.addBubble(bubble, x, y);
Log.d("Bubble", "Bubble created");
}
//Builder class
public static class Builder {
private BubblesManager bubblesManager;
//Builder constructor
public Builder(Context context){
this.bubblesManager = getInstance(context);
}
//Sets initialization Callbacks - a callback is when we provide a function as an argument to another function in order to enforce the order of operations.
public Builder setInitializationCallback(OnInitializedCallback listener){
bubblesManager.listener = listener;
return this;
}
//Sets Trash Layout
public Builder setTrashLayout(int trashLayoutResourceId){
bubblesManager.trashLayoutResourceId = trashLayoutResourceId;
return this;
}
//Triggers BubbleManager;
public BubblesManager build(){
return bubblesManager;
}
}
}
//在bubblesService中
imports...
public class BubblesService extends Service{
private BubblesServiceBinder binder = new BubblesServiceBinder();
private List<BubbleLayout> bubbles = new ArrayList<>();
private BubbleTrashLayout bubblesTrash;
private WindowManager windowManager;
private BubblesLayoutCoordinator layoutCoordinator;
//overrides the IBind method
@Override
public IBinder onBind(Intent intent){
return binder;
}
//overrides the onUnbind method
@Override
public boolean onUnbind(Intent intent){
for (BubbleLayout bubble : bubbles){
recycleBubble(bubble);
}
bubbles.clear();
return super.onUnbind(intent);
}
//Gets the Windows Manager
private WindowManager getWindowManager(){
if (windowManager ==null){
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
}
return windowManager;
}
// Adds view to the Window
public void addBubble(BubbleLayout bubble, int x, int y){
WindowManager.LayoutParams layoutParams = buildLayoutParamsForBubble(bubble, x,y);
layoutParams.token = bubble.getApplicationWindowToken();
bubble.setWindowManager(getWindowManager());
bubble.setViewParams(layoutParams);
bubble.setLayoutCoordinator(layoutCoordinator);
bubbles.add(bubble);
addViewToWindow(bubble);
}
// Initializes the Layout Cocordinator
private void initializeLayoutCoordinator(){
layoutCoordinator = new BubblesLayoutCoordinator.Builder(this)
.setWindowManager(getWindowManager())
.setTrashView(bubblesTrash)
.setTrashView(bubblesTrash)
.build();
}
//Adds view to the Window
private void addViewToWindow(final BubbleBaseLayout view){
new Handler(Looper.getMainLooper()).post(new Runnable(){
@Override
public void run(){
getWindowManager().addView(view, view.getViewParams());
}
});
}
//BUILDING LAYOUT PARAMS --> THIS IS WHERE THE TYPE IS SET
private WindowManager.LayoutParams buildLayoutParamsForBubble(BubbleLayout bubble, int x, int y){
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, //!!!! WHEN this is set to TYPE_PHONE the chat head stays on the screen even if the application is onPause.
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.START;
params.token = bubble.getApplicationWindowToken();
params.x = x;
params.y = y;
return params;
}
//defines the BubblesService Binder service
public class BubblesServiceBinder extends Binder {
public BubblesService getService(){
return BubblesService.this;
}
}
}
///我收到错误
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.momely.mascapone, PID: 16638
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:683)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at com.momely.bubbles.BubblesService.run(BubblesService.java:115)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
关于如何将聊天头像限制在应用程序 window 上而不在应用程序处于暂停状态时保留在屏幕上的任何建议?
Z
"I now wish to constrain the chat head to the active app."
我看到两个选项。作为一个简单的 hack(保留服务)使用 Option 1.
选项2表示将BubblesService.java
复制到BubblesLocal.java
,将BubblesManager.java
复制到BubblesManagerLocal.java
,
破解所有Service
代码。我建议 选项 1 是你想要的(更容易,你可以打开和关闭它)。
选项 1
当您的应用程序未处于活动状态时,只需隐藏 气泡。
将以下代码添加到您的项目中(已测试,有效):
MainActivity.java:
//update ActionBarActivity to AppCompatActivity
`public class MainActivity extends AppCompatActivity //ActionBarActivity`
private boolean mStarted = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
initializeBubblesManager();
mStarted = true;
//------------------------------------------------------------------------------------------------
@Override
protected void onResume()
{
Log.i("MainActivity:","onResume");
super.onResume();
if(mStarted) bubblesManager.showBubbles();
}
//------------------------------------------------------------------------------------------------
@Override
protected void onPause()
{
Log.i("MainActivity:","onPause");
super.onPause();
if(mStarted) bubblesManager.hideBubbles();
}
//------------------------------------------------------------------------------------------------
BubblesManager.java:
//------------------------------------------------------------------------------------------------
public void showBubbles()
{
if(bounded && bubbleServiceConnection != null)bubblesService.showBubbles();
}//showBubbles
//------------------------------------------------------------------------------------------------
public void hideBubbles()
{
if(bounded && bubbleServiceConnection != null)bubblesService.hideBubbles();
}//hideBubbles
//------------------------------------------------------------------------------------------------
BubblesService.java:
//------------------------------------------------------------------------------------------------
public void showBubbles()
{
if(bubbles.size() > 0)
{
for (BubbleLayout bubble : bubbles)
{
bubble.showBubble();
}
}
}//showBubbles
//------------------------------------------------------------------------------------------------
public void hideBubbles()
{
if(bubbles.size() > 0)
{
for (BubbleLayout bubble : bubbles)
{
bubble.hideBubble();
}
}
}//hideBubbles
//------------------------------------------------------------------------------------------------
BubbleLayout.java:
//------------------------------------------------------------------------------------------------
public void showBubble()
{
//View.GONE This view is invisible, and it doesn't take any space for layout purposes.
//View.INVISIBLE This view is invisible, but it still takes up space for layout purposes.
getRootView().setVisibility(View.VISIBLE);
}//showBubble
//------------------------------------------------------------------------------------------------
public void hideBubble()
{
getRootView().setVisibility(View.INVISIBLE);
}//hideBubble
//------------------------------------------------------------------------------------------------
我正在尝试添加通过服务生成的视图。我使用的代码基于 Facebook Chatheads,无论应用程序状态如何,它始终可见。它们也显示在其他任何东西之上:
我现在希望将聊天头限制在活动应用程序中。具体来说,每当我将 Window.LayoutParams 从 TYPE_PHONE 更改为 TYPE_DRAWN_APPLICATION 时,我都会处理 Bad Token Exception。
我的问题: 我知道我需要将正确的 window 标记传递给 LayoutParams,但似乎无法弄清楚如何正确执行此操作。任何建议将不胜感激。
这是我的代码:
//主要Activity
private void addNewBubble() {
BubbleLayout bubbleView = (BubbleLayout)LayoutInflater.from(MainActivity.this).inflate(R.layout.bubble_layout, null);
bubblesManager.addBubble(bubbleView, 60, 20);
}
// initializes Bubbles Manager
private void initializeBubblesManager() {
bubblesManager = new BubblesManager.Builder(this)
.setTrashLayout(R.layout.task_bubble_trash_layout)
.setInitializationCallback(new OnInitializedCallback() {
@Override
public void onInitialized() {
addNewBubble(); // Called when addNewBubble is initialized and the bubble data is loaded. When used on devices running API 18 or below, this function is always called.
}
})
.build();
bubblesManager.initialize();
}
// initializes Bubbles Manager
private void initializeBubblesManager() {
bubblesManager = new BubblesManager.Builder(this)
.setTrashLayout(R.layout.task_bubble_trash_layout)
.setInitializationCallback(new OnInitializedCallback() {
@Override
public void onInitialized() {
addNewBubble(); // Called when addNewBubble is initialized and the bubble data is loaded. When used on devices running API 18 or below, this function is always called.
}
})
.build();
bubblesManager.initialize();
}
//XML - 自定义 Bubble_layout
<com.momely.bubbles.BubbleLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<ImageView
android:id="@+id/avatar"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_gravity="center"
android:background="@drawable/profile_decorator"
android:src="@drawable/round_button"
android:scaleType="centerCrop"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="15sp"
android:layout_marginTop="2dp"
android:layout_marginLeft="2dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:background="@drawable/bubble_counter_bkg"
android:text="1"/>
</com.momely.bubbles.BubbleLayout>
//在bubblesManager
public class BubblesManager {
private static BubblesManager INSTANCE;
private Context context;
private boolean bounded;
private BubblesService bubblesService;
private int trashLayoutResourceId;
private OnInitializedCallback listener;
//getInstance (called in Builder below)
private static BubblesManager getInstance(Context context){
if (INSTANCE == null) {
INSTANCE = new BubblesManager(context);
}
return INSTANCE;
}
//Binds the service to the application
private ServiceConnection bubbleServiceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service){
BubblesService.BubblesServiceBinder binder = (BubblesService.BubblesServiceBinder)service;
BubblesManager.this.bubblesService = binder.getService();
configureBubblesService();
bounded = true;
if(listener != null){
listener.onInitialized();
}
}
//Initializes Bubbles Manager
private BubblesManager(Context context){
this.context = context;
}
//Initializes the service
public void initialize(){
context.bindService(new Intent(context, BubblesService.class),
bubbleServiceConnection,
Context.BIND_AUTO_CREATE);
}
public void addBubble(BubbleLayout bubble, int x, int y){
if(bounded){
bubblesService.addBubble(bubble, x, y);
Log.d("Bubble", "Bubble created");
}
//Builder class
public static class Builder {
private BubblesManager bubblesManager;
//Builder constructor
public Builder(Context context){
this.bubblesManager = getInstance(context);
}
//Sets initialization Callbacks - a callback is when we provide a function as an argument to another function in order to enforce the order of operations.
public Builder setInitializationCallback(OnInitializedCallback listener){
bubblesManager.listener = listener;
return this;
}
//Sets Trash Layout
public Builder setTrashLayout(int trashLayoutResourceId){
bubblesManager.trashLayoutResourceId = trashLayoutResourceId;
return this;
}
//Triggers BubbleManager;
public BubblesManager build(){
return bubblesManager;
}
}
}
//在bubblesService中
imports...
public class BubblesService extends Service{
private BubblesServiceBinder binder = new BubblesServiceBinder();
private List<BubbleLayout> bubbles = new ArrayList<>();
private BubbleTrashLayout bubblesTrash;
private WindowManager windowManager;
private BubblesLayoutCoordinator layoutCoordinator;
//overrides the IBind method
@Override
public IBinder onBind(Intent intent){
return binder;
}
//overrides the onUnbind method
@Override
public boolean onUnbind(Intent intent){
for (BubbleLayout bubble : bubbles){
recycleBubble(bubble);
}
bubbles.clear();
return super.onUnbind(intent);
}
//Gets the Windows Manager
private WindowManager getWindowManager(){
if (windowManager ==null){
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
}
return windowManager;
}
// Adds view to the Window
public void addBubble(BubbleLayout bubble, int x, int y){
WindowManager.LayoutParams layoutParams = buildLayoutParamsForBubble(bubble, x,y);
layoutParams.token = bubble.getApplicationWindowToken();
bubble.setWindowManager(getWindowManager());
bubble.setViewParams(layoutParams);
bubble.setLayoutCoordinator(layoutCoordinator);
bubbles.add(bubble);
addViewToWindow(bubble);
}
// Initializes the Layout Cocordinator
private void initializeLayoutCoordinator(){
layoutCoordinator = new BubblesLayoutCoordinator.Builder(this)
.setWindowManager(getWindowManager())
.setTrashView(bubblesTrash)
.setTrashView(bubblesTrash)
.build();
}
//Adds view to the Window
private void addViewToWindow(final BubbleBaseLayout view){
new Handler(Looper.getMainLooper()).post(new Runnable(){
@Override
public void run(){
getWindowManager().addView(view, view.getViewParams());
}
});
}
//BUILDING LAYOUT PARAMS --> THIS IS WHERE THE TYPE IS SET
private WindowManager.LayoutParams buildLayoutParamsForBubble(BubbleLayout bubble, int x, int y){
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, //!!!! WHEN this is set to TYPE_PHONE the chat head stays on the screen even if the application is onPause.
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.START;
params.token = bubble.getApplicationWindowToken();
params.x = x;
params.y = y;
return params;
}
//defines the BubblesService Binder service
public class BubblesServiceBinder extends Binder {
public BubblesService getService(){
return BubblesService.this;
}
}
}
///我收到错误
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.momely.mascapone, PID: 16638
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:683)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at com.momely.bubbles.BubblesService.run(BubblesService.java:115)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
关于如何将聊天头像限制在应用程序 window 上而不在应用程序处于暂停状态时保留在屏幕上的任何建议?
Z
"I now wish to constrain the chat head to the active app."
我看到两个选项。作为一个简单的 hack(保留服务)使用 Option 1.
选项2表示将BubblesService.java
复制到BubblesLocal.java
,将BubblesManager.java
复制到BubblesManagerLocal.java
,
破解所有Service
代码。我建议 选项 1 是你想要的(更容易,你可以打开和关闭它)。
选项 1
当您的应用程序未处于活动状态时,只需隐藏 气泡。
将以下代码添加到您的项目中(已测试,有效):
MainActivity.java:
//update ActionBarActivity to AppCompatActivity
`public class MainActivity extends AppCompatActivity //ActionBarActivity`
private boolean mStarted = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
initializeBubblesManager();
mStarted = true;
//------------------------------------------------------------------------------------------------
@Override
protected void onResume()
{
Log.i("MainActivity:","onResume");
super.onResume();
if(mStarted) bubblesManager.showBubbles();
}
//------------------------------------------------------------------------------------------------
@Override
protected void onPause()
{
Log.i("MainActivity:","onPause");
super.onPause();
if(mStarted) bubblesManager.hideBubbles();
}
//------------------------------------------------------------------------------------------------
BubblesManager.java:
//------------------------------------------------------------------------------------------------
public void showBubbles()
{
if(bounded && bubbleServiceConnection != null)bubblesService.showBubbles();
}//showBubbles
//------------------------------------------------------------------------------------------------
public void hideBubbles()
{
if(bounded && bubbleServiceConnection != null)bubblesService.hideBubbles();
}//hideBubbles
//------------------------------------------------------------------------------------------------
BubblesService.java:
//------------------------------------------------------------------------------------------------
public void showBubbles()
{
if(bubbles.size() > 0)
{
for (BubbleLayout bubble : bubbles)
{
bubble.showBubble();
}
}
}//showBubbles
//------------------------------------------------------------------------------------------------
public void hideBubbles()
{
if(bubbles.size() > 0)
{
for (BubbleLayout bubble : bubbles)
{
bubble.hideBubble();
}
}
}//hideBubbles
//------------------------------------------------------------------------------------------------
BubbleLayout.java:
//------------------------------------------------------------------------------------------------
public void showBubble()
{
//View.GONE This view is invisible, and it doesn't take any space for layout purposes.
//View.INVISIBLE This view is invisible, but it still takes up space for layout purposes.
getRootView().setVisibility(View.VISIBLE);
}//showBubble
//------------------------------------------------------------------------------------------------
public void hideBubble()
{
getRootView().setVisibility(View.INVISIBLE);
}//hideBubble
//------------------------------------------------------------------------------------------------