将始终可见的聊天头的LayoutParams更改为不总是可见

时间:2017-11-12 01:24:01

标签: android layout service token android-layoutparams

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

enter image description here Android Chat Head

我现在希望将聊天头限制在活动应用中。具体来说,每当我将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);
}

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

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

ž

1 个答案:

答案 0 :(得分:2)

  

“我现在希望将聊天头限制在活动应用中。”

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

选项1

当您的应用程序未处于活动状态时,只需隐藏 bubbles 将以下代码添加到您的项目中(已测试,正在运行):

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