2017-11-12 25 views
5

서비스를 통해 생성하는보기를 추가하려고합니다. 사용중인 코드는 앱 상태에 관계없이 항상 표시되는 Facebook Chatheads을 기반으로합니다. 그들은 또한 너무 다른 것 위에 표시됩니다 :항상 보이는 채팅 헤드의 LayoutParams가 항상 표시되지는 않습니다.

enter image description here Android Chat Head

내가 지금 활성 응용 프로그램에 채팅 헤드를 제한하고 싶습니다. 특히 Window.LayoutParams를 TYPE_PHONE에서 TYPE_DRAWN_APPLICATION으로 변경할 때마다 Bad Token Exception을 처리하고 있습니다.

MY QUESTION : 올바른 창 토큰을 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> 

//

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

을 bubblesManager

에서 // in bubbles 서비스

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 내가

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) 

I 앱이 일시 정지에 때 화면에 남아있는없이 응용 프로그램 창에 채팅 헤드를 제한 할 수 방법에 대한 조언을받는 건가요?

Z

+0

왜 '서비스'를 연장하겠습니까? –

+0

좋은 질문입니다. 서비스를 확장하면 BubblesService를 우리가 창에 바인딩 할 수있는 서비스로 실행할 수 있다고 생각하십니까? – Z101

+1

좋은 추측. 이를 위해'BubblesService'를 시작하고'addBubble'에 액세스하기 전에 UI 컴포넌트를 바인딩해야합니다. –

답변

2

"지금은 활성 응용 프로그램에 채팅 헤드를 제한하고 싶습니다."

두 개의 옵션을 참조하십시오. 간단한 해킹 (서비스 유지)은 옵션 1을 사용하십시오. BubblesService.java 모든 Service 코드를 해킹 BubblesManagerLocal.java, 및 -BubblesLocal.java에와 BubblesManager.java을 복사
옵션 2 의미합니다. 나는 옵션 1을 원합니다 (훨씬 쉽게, 당신은 그것을 켜고 끌 수 있습니다). here is an image

옵션 응용 프로그램이 활성화되지 않은 경우 거품 1 개

단순히 숨기기.
다음 코드를 프로젝트에 추가하십시오 (테스트 된 작동 중).

MainActivity.자바 :

//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 
//------------------------------------------------------------------------------------------------ 
관련 문제