2017-11-12 21 views
5

Próbuję dodać widok generuję za pośrednictwem usługi. Kod, którego używam, jest oparty na Facebook Chatheads, które są zawsze widoczne, niezależnie od stanu aplikacji. Są one również wyświetlane powyżej cokolwiek innego TOO:Zmiana układuParams zawsze widocznych Heads Chat nie zawsze widoczne

enter image description here Android Chat Head

I teraz chcą ograniczyć głowę czat do aktywnej aplikacji. W szczególności mam do czynienia z wyjątkiem Bad Token, gdy tylko zmieniam Window.LayoutParams z TYPE_PHONE na TYPE_DRAWN_APPLICATION.

MOJE PYTANIE: Wiem, że jestem zobowiązany przekazać prawidłowy token okna do LayoutParams, ale nie mogę wymyślić, jak to zrobić poprawnie. Każda rada byłaby wysoko ceniona.

Oto mój kod:

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

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

// w bąbelkach Usługa

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 Otrzymuję

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) 

Wszelkie porady, w jaki sposób mogę ograniczyć czat głowę do okna aplikacji bez pozostała ona na ekranie, gdy aplikacja jest włączona pauza?

Z

+0

Dlaczego rozszerzasz 'Service'? –

+0

Dobre pytanie. Wydaje mi się, że rozszerzenie usługi pozwala nam uruchomić BubblesService jako usługę, którą możemy powiązać z oknem? – Z101

+1

Nice Guess. W tym celu należy uruchomić 'BubblesService' i powiązać składnik interfejsu użytkownika przed uzyskaniem dostępu do' addBubble' –

Odpowiedz

2

"I teraz chcą ograniczyć głowę czat do aktywnej aplikacji."

Widzę dwie opcje:. Jako prosty hack (utrzymanie usługi) użyj opcji Opcja 1.
Opcja 2 środki kopiowanie BubblesService.java do BubblesLocal.java i BubblesManager.java do BubblesManagerLocal.java, i włamania się cały kod Service. Sugeruję Opcja 1 jest tym, czego potrzebujesz (o wiele łatwiej, możesz go włączać i wyłączać). here is an image

Wariant 1

Wystarczy ukryjpęcherzyki gdy aplikacja nie jest aktywna.
Dodaj następujący kod do swojego projektu (przetestowany, działający):

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 
//------------------------------------------------------------------------------------------------ 
Powiązane problemy