将始终可见的聊天头的 LayoutParams 更改为不总是可见 [英] Changing LayoutParams of always visible Chat Heads to not always visible

查看:19
本文介绍了将始终可见的聊天头的 LayoutParams 更改为不总是可见的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试添加我通过服务生成的视图.我使用的代码基于

我现在希望将聊天头限制为活动应用程序.具体来说,每当我将 Window.LayoutParams 从 TYPE_PHONE 更改为 TYPE_DRAWN_APPLICATION 时,我都在处理 Bad Token Exception.

我的问题:我知道我需要将正确的窗口令牌传递给 LayoutParams,但似乎无法弄清楚如何正确执行此操作.任何建议将不胜感激.

这是我的代码:

//主要活动

private void addNewBubble() {BubbleLayout bubbleView = (BubbleLayout)LayoutInflater.from(MainActivity.this).inflate(R.layout.bubble_layout, null);bubblesManager.addBubble(bubbleView, 60, 20);}//初始化气泡管理器私有无效 initializeBubblesManager() {bubblesManager = new BubblesManager.Builder(this).setTrashLayout(R.layout.task_bubble_trash_layout).setInitializationCallback(new OnInitializedCallback() {@覆盖公共无效 onInitialized() {addNewBubble();//在初始化 addNewBubble 并加载气泡数据时调用.在运行 API 18 或更低版本的设备上使用时,始终调用此函数.}}).建造();气泡管理器.初始化();}//初始化气泡管理器私有无效 initializeBubblesManager() {bubblesManager = new BubblesManager.Builder(this).setTrashLayout(R.layout.task_bubble_trash_layout).setInitializationCallback(new OnInitializedCallback() {@覆盖公共无效 onInitialized() {addNewBubble();//在初始化 addNewBubble 并加载气泡数据时调用.在运行 API 18 或更低版本的设备上使用时,始终调用此函数.}}).建造();气泡管理器.初始化();}

//XML - 自定义气泡布局

<图像视图android:id="@+id/头像"android:layout_width="70dp"android:layout_height="70dp"机器人:layout_gravity="中心"android:background="@drawable/profile_decorator"android:src="@drawable/round_button"android:scaleType="centerCrop"/><文本视图android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="@color/white"android:textSize="15sp"android:layout_marginTop="2dp"机器人:layout_marginLeft="2dp"机器人:paddingLeft =4dp"机器人:paddingRight="4dp"android:background="@drawable/bubble_counter_bkg"机器人:文本=1"/></com.momely.bubbles.BubbleLayout>

//在气泡管理器中

公共类 BubblesManager {私有静态 BubblesManager 实例;私有上下文上下文;私人布尔有界;私人气泡服务气泡服务;私有 int 垃圾布局资源 ID;私有 OnInitializedCallback 监听器;//getInstance(在下面的Builder中调用)私有静态 BubblesManager getInstance(上下文上下文){如果(实例 == 空){INSTANCE = new BubblesManager(context);}返回实例;}//将服务绑定到应用程序私人服务连接气泡服务连接=新服务连接(){@覆盖公共无效 onServiceConnected(组件名称名称,IBinder 服务){BubblesService.BubblesServiceBinder binder = (BubblesService.BubblesServiceBinder)service;BubblesManager.this.bubblesService = binder.getService();configureBubblesService();有界 = 真;如果(监听器!= null){listener.onInitialized();}}//初始化气泡管理器私人气泡管理器(上下文上下文){this.context = 上下文;}//初始化服务公共无效初始化(){context.bindService(new Intent(context, BubblesService.class),气泡服务连接,Context.BIND_AUTO_CREATE);}公共无效 addBubble(BubbleLayout 气泡,int x,int y){如果(有界){bubblesService.addBubble(bubble, x, y);Log.d("Bubble", "Bubble created");}//生成器类公共静态类生成器{私人 BubblesManager 气泡管理器;//构造器构造器公共生成器(上下文上下文){this.bubblesManager = getInstance(context);}//设置初始化回调 - 回调是当我们将一个函数作为参数提供给另一个函数以强制执行操作顺序时.公共生成器 setInitializationCallback(OnInitializedCallback 监听器){bubblesManager.listener = 监听器;返回这个;}//设置垃圾箱布局公共生成器 setTrashLayout(inttrashLayoutResourceId){bubblesManager.trashLayoutResourceId =trashLayoutResourceId;返回这个;}//触发气泡管理器;公共气泡管理器构建(){返回气泡管理器;}}}

//在气泡服务中

导入...公共类 BubblesService 扩展服务{private BubblesServiceBinder binder = new BubblesServiceBinder();私人列表气泡 = 新的 ArrayList<>();私人 BubbleTrashLayout 气泡垃圾;私有窗口管理器窗口管理器;私人 BubblesLayoutCoordinator layoutCoordinator;//覆盖IBind方法@覆盖公共 IBinder onBind(意图意图){返回活页夹;}//覆盖onUnbind方法@覆盖公共布尔onUnbind(意图意图){for (BubbleLayout 气泡:气泡){回收泡泡(泡泡);}气泡.清除();返回 super.onUnbind(intent);}//获取Windows管理器私有窗口管理器 getWindowManager(){如果(窗口管理器 == 空){windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);}返回窗口管理器;}//向窗口添加视图公共无效 addBubble(BubbleLayout 气泡,int x,int y){WindowManager.LayoutParams layoutParams = buildLayoutParamsForBubble(bubble, x,y);layoutParams.token = bubble.getApplicationWindowToken();气泡.setWindowManager(getWindowManager());气泡.setViewParams(layoutParams);气泡.setLayoutCoordinator(layoutCoordinator);气泡.添加(气泡);addViewToWindow(气泡);}//初始化布局协调器私有无效 initializeLayoutCoordinator(){layoutCoordinator = new BubblesLayoutCoordinator.Builder(this).setWindowManager(getWindowManager()).setTrashView(bubblesTrash).setTrashView(bubblesTrash).建造();}//将视图添加到窗口私有无效 addViewToWindow(最终 BubbleBaseLayout 视图){new Handler(Looper.getMainLooper()).post(new Runnable(){@覆盖公共无效运行(){getWindowManager().addView(view, view.getViewParams());}});}//构建布局参数 -->这是设置类型的地方私有 WindowManager.LayoutParams buildLayoutParamsForBubble(BubbleLayout 气泡,int x,int y){WindowManager.LayoutParams params = 新的 WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION,//!!!!!!当此设置为 TYPE_PHONE 时,即使应用程序处于 onPause 状态,聊天头也会停留在屏幕上.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSPARENT);params.gravity = Gravity.TOP |Gravity.START;params.token = bubble.getApplicationWindowToken();参数.x = x;参数.y = y;返回参数;}//定义BubblesService Binder服务公共类 BubblesServiceBinder 扩展了 Binder {公共气泡服务 getService(){返回 BubblesService.this;}}}

///我收到的错误

E/AndroidRuntime:致命异常:main进程:com.momely.mascapone,PID:16638android.view.WindowManager$BadTokenException: 无法添加窗口 -- 令牌 null 不适用于应用程序在 android.view.ViewRootImpl.setView(ViewRootImpl.java:683)在 android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)在 android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)在 com.momely.bubbles.BubblesService$2.run(BubblesService.java:115)在 android.os.Handler.handleCallback(Handler.java:751)在 android.os.Handler.dispatchMessage(Handler.java:95)在 android.os.Looper.loop(Looper.java:154)在 android.app.ActivityThread.main(ActivityThread.java:6119)在 java.lang.reflect.Method.invoke(Native Method)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

关于如何在应用程序处于暂停状态时将聊天头限制在应用程序窗口而不让它停留在屏幕上的任何建议?

Z

解决方案

我现在希望将聊天头限制为活动应用."

我看到了两个选项.作为一个简单的技巧(保留服务),请使用选项 1.
选项 2 表示将 BubblesService.java 复制到 BubblesLocal.java 并将 BubblesManager.java 复制到 BubblesManagerLocal.java,并破解所有Service代码.我建议选项 1 是您想要的(更简单,您可以打开和关闭它).

选项 1

当您的应用程序处于非活动状态时,只需隐藏气泡.
将以下代码添加到您的项目中(已测试、可运行):

MainActivity.java:

//更新ActionBarActivity为AppCompatActivity`public class MainActivity extends AppCompatActivity//ActionBarActivity`私人布尔 mStarted = false;@覆盖protected void onCreate(Bundle savedInstanceState) {...初始化气泡管理器();mStarted = true;//------------------------------------------------------------------------------------------------@覆盖受保护的无效 onResume(){Log.i("MainActivity:","onResume");super.onResume();if(mStarted) bubblesManager.showBubbles();}//------------------------------------------------------------------------------------------------@覆盖受保护的无效 onPause(){Log.i("MainActivity:","onPause");super.onPause();if(mStarted) bubblesManager.hideBubbles();}//------------------------------------------------------------------------------------------------

BubblesManager.java:

//-------------------------------------------------------------------------------------------------公共无效 showBubbles(){if(bounded && bubbleServiceConnection != null)bubblesService.showBubbles();}//显示泡泡//------------------------------------------------------------------------------------------------公共无效隐藏气泡(){if(bounded && bubbleServiceConnection != null)bubblesService.hideBubbles();}//隐藏气泡//------------------------------------------------------------------------------------------------

BubblesService.java:

//-------------------------------------------------------------------------------------------------公共无效 showBubbles(){如果(气泡.大小()> 0){for (BubbleLayout 气泡:气泡){泡泡.showBubble();}}}//显示泡泡//------------------------------------------------------------------------------------------------公共无效隐藏气泡(){如果(气泡.大小()> 0){for (BubbleLayout 气泡:气泡){泡泡.隐藏泡泡();}}}//隐藏气泡//------------------------------------------------------------------------------------------------

BubbleLayout.java:

//-------------------------------------------------------------------------------------------------公共无效 showBubble(){//View.GONE 这个视图是不可见的,它不占用任何布局空间.//View.INVISIBLE 这个视图是不可见的,但是为了布局它仍然会占用空间.getRootView().setVisibility(View.VISIBLE);}//显示泡泡//------------------------------------------------------------------------------------------------公共无效隐藏气泡(){getRootView().setVisibility(View.INVISIBLE);}//隐藏气泡//------------------------------------------------------------------------------------------------

I am attempting to add a view I am generating via a service. The code I am using is based on Facebook Chatheads which are always visible, regardless of the apps state. They are also displayed above anything else too:

I now wish to constrain the chat head to the active app. Specifically I am dealing with a Bad Token Exception whenever I change the Window.LayoutParams from TYPE_PHONE to TYPE_DRAWN_APPLICATION.

MY QUESTION: I know that I am required to pass on the correct window token to the LayoutParams but can't seem to figure out how to do this correctly. Any advice would be highly appreciated.

Here is my code:

//Main 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 - Custom 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>

//in 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;
        }
    }
}

//in 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;
        }
    }

}

///ERROR I AM RECEIVING

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$2.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)

Any advice on how I could limit the chat head to the application window without it remaining on screen when the app is on Pause?

Z

解决方案

"I now wish to constrain the chat head to the active app."

I see two options. As a simple hack (keeping the Service) use Option 1.
Option 2 means copying BubblesService.java to BubblesLocal.java and BubblesManager.java to BubblesManagerLocal.java, and hacking out all the Service code. I suggest Option 1 is what you want (much easier, and you can turn it on and off).

Option 1

Simply hide the bubbles when your application is not active.
Add the following code to your project (tested, working):

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
//------------------------------------------------------------------------------------------------

这篇关于将始终可见的聊天头的 LayoutParams 更改为不总是可见的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆