android - 将总是可见的聊天头的 LayoutParams 更改为不总是可见

标签 android layout service token android-layoutparams

我正在尝试添加通过服务生成的 View 。我使用的代码基于 Facebook Chatheads无论应用程序状态如何,它们始终可见。它们也显示在其他任何东西之上:

enter image description here Android Chat Head

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

我的问题: 我知道我需要将正确的窗口 token 传递给 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 - 自定义气泡布局

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

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

Z

最佳答案

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

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

选项 1

当您的应用程序未处于 Activity 状态时,隐藏气泡即可。
将以下代码添加到您的项目(已测试,有效):

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

关于android - 将总是可见的聊天头的 LayoutParams 更改为不总是可见,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47244548/

相关文章:

angular - 无法将来自服务方法的数据显示到组件中

android - Android Studio 添加依赖报错

java - 如何使用一个类让不同的 Activity 从中读取数据?

android - Crashlytics : Android Studio Gradle Error related to XML

android - 自动缩放 TextView 文本以适应 4.0 中的边界

php - 文件未加载到 php 文件中

java - Swing GUI 组件过于紧凑

Android:无法更改 v7 支持工具栏的 NavigationIcon

python - 在 C 中进行连接管理有哪些好方法?

delphi - 实际上在 services.msc 打开时**删除**服务