代码之家  ›  专栏  ›  技术社区  ›  Pushkar

Android显示浮动小部件覆盖不工作

  •  5
  • Pushkar  · 技术社区  · 7 年前

    我希望我的应用程序显示一个浮动气泡通知,就像在Facebook信使。跟随 https://www.androidhive.info/2016/11/android-floating-widget-like-facebook-chat-head/ 下面是我写的服务。

    import android.annotation.SuppressLint;
    import android.app.Service;
    import android.content.Intent;
    import android.graphics.PixelFormat;
    import android.os.IBinder;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.WindowManager;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    public class BubbleNotifyService extends Service {
    
        private WindowManager windowManager;
        private View BubbleView;
        private TextView bubbleTitle, bubbleData;
    
        public BubbleNotifyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @SuppressLint({"RtlHardcoded", "InflateParams"})
        @Override
        public void onCreate() {
            super.onCreate();
            //android.os.Debug.waitForDebugger();
    
            setTheme(R.style.AppTheme);
    
            BubbleView =
                    LayoutInflater.from(this).inflate(R.layout.floating_bubble, null);
    
            final WindowManager.LayoutParams params;
    
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT
            );
        } else {
            params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.TYPE_PHONE,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ,
                    PixelFormat.TRANSLUCENT
            );
        }
    
            if (BubbleView == null) {
                Log.d("Bubble", "BubbleView is null.");
            }
    
            params.gravity = Gravity.TOP | Gravity.LEFT;
            params.x = 0;
            params.y = 100;
    
            windowManager =
                    (WindowManager) getSystemService(WINDOW_SERVICE);
    
            if (windowManager != null) {
                Log.d("Bubble", "added bubble to view.");
                windowManager.addView(BubbleView, params);
            } else {
                Log.d("Bubble", "windowManager is null");
            }
    
            final View collapsedView =
                    BubbleView.findViewById(R.id.collapsed_view);
            final View expandedView =
                    BubbleView.findViewById(R.id.expanded_view);
    
            expandedView.setVisibility(View.GONE);
            collapsedView.setVisibility(View.VISIBLE);
    
    
            ImageView close_collapsed = BubbleView.findViewById(R.id.collaspsed_cancel);
            ImageView close_expanded = BubbleView.findViewById(R.id.expanded_cancel);
            Button open_act_btn = BubbleView.findViewById(R.id.open_full_btn);
            bubbleTitle = BubbleView.findViewById(R.id.bubble_title);
            bubbleMeaning = BubbleView.findViewById(R.id.bubble_data);
    
            close_collapsed.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    stopSelf();
                }
            });
    
            close_expanded.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    collapsedView.setVisibility(View.VISIBLE);
                    expandedView.setVisibility(View.GONE);
                }
            });
    
            open_act_btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
    
                    //TODO new Activity
                }
            });
    
            BubbleView.findViewById(R.id.bubble_root).setOnTouchListener(new View.OnTouchListener() {
    
                private int initialX;
                private int initialY;
                private float initialTouchX;
                private float initialTouchY;
    
                @SuppressLint("ClickableViewAccessibility")
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
    
                    switch (motionEvent.getAction()) {
    
                        case MotionEvent.ACTION_DOWN:
                            initialX = params.x;
                            initialY = params.y;
    
                            initialTouchX = motionEvent.getRawX();
                            initialTouchY = motionEvent.getRawY();
                            return true;
    
                        case MotionEvent.ACTION_UP:
                            int Xdiff = (int) (motionEvent.getRawX() - initialTouchX);
                            int Ydiff = (int) (motionEvent.getRawY() - initialTouchY);
    
                            if (Xdiff < 10 && Ydiff < 10) {
                                if (isViewCollapsed()) {
                                    collapsedView.setVisibility(View.GONE);
                                    expandedView.setVisibility(View.VISIBLE);
                                }
                            }
                            return true;
    
                        case MotionEvent.ACTION_MOVE:
                            params.x =
                                    initialX + (int) (motionEvent.getRawX() - initialTouchX);
                            params.y =
                                    initialY + (int) (motionEvent.getRawY() - initialTouchY);
    
                            windowManager.updateViewLayout(BubbleView, params);
                            return true;
                    }
                    return false;
                }
            });
    
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
    
            String title = intent.getStringExtra(IntentKeys.BUBBLE_DATA_TITLE);
            String data = intent.getStringExtra(IntentKeys.BUBBLE_DATA);
    
            bubbleTitle.setText(title);
            bubbleData.setText(data);
    
            return START_NOT_STICKY;
        }
    
        private boolean isViewCollapsed() {
            return BubbleView == null ||
                    BubbleView.findViewById(R.id.collapsed_view).getVisibility() == View.VISIBLE;
        }
    
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            if (BubbleView != null) {
                windowManager.removeView(BubbleView);
            }
        }
    }
    

    BubbleNotifyService 从另一个接收通知的服务调用为:

    Intent intent = new Intent(context, BubbleNotifyService.class);
                    intent.putExtra(IntentKeys.BUBBLE_DATA_TITLE, Results.getWord());
                    intent.putExtra(IntentKeys.BUBBLE_DATA, Results.getData());
                    context.startService(intent);
    

    意图中的所有数据都传递给 气泡通知服务 . 服务作为清单中指定的单独进程运行 android:process=":BubbleResult" ,但服务不显示任何覆盖。对应用程序授予其他应用程序权限。

    气泡布局(浮动气泡.xml)

    <?xml version="1.0" encoding="utf-8"?>
    
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/bubble_frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    
        <android.support.constraint.ConstraintLayout
            android:id="@+id/bubble_root"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
    
    
            <android.support.constraint.ConstraintLayout
                android:id="@+id/collapsed_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:visibility="visible">
    
                <ImageView
                    android:id="@+id/collapsed_icon"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    app:srcCompat="@mipmap/ic_launcher"
                    tools:ignore="ContentDescription" />
    
                <ImageView
                    android:id="@+id/collaspsed_cancel"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    app:layout_constraintStart_toEndOf="@+id/collapsed_icon"
                    app:layout_constraintTop_toTopOf="parent"
                    app:srcCompat="@drawable/ic_action_cancel"
                    tools:ignore="ContentDescription" />
    
            </android.support.constraint.ConstraintLayout>
    
            <android.support.v7.widget.CardView
                android:id="@+id/expanded_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="visible"
                app:cardCornerRadius="5dp"
                app:cardElevation="10dp"
                app:cardUseCompatPadding="true"
                app:contentPadding="5dp">
    
                <android.support.constraint.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
    
                    <TextView
                        android:id="@+id/bubble_title"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:textSize="24sp"
                        android:textStyle="bold"
                        app:layout_constraintEnd_toStartOf="@id/expanded_cancel"
                        app:layout_constraintHorizontal_bias="0.0"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent"
                        tools:text="@string/result_card_title" />
    
                    <TextView
                        android:id="@+id/bubble_data"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:textSize="18sp"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toBottomOf="@id/bubble_title"
                        tools:text="@string/result_card_data" />
    
                    <ImageView
                        android:id="@+id/expanded_cancel"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toEndOf="@id/bubble_title"
                        app:srcCompat="@drawable/ic_action_cancel"
                        tools:ignore="ContentDescription" />
    
                    <Button
                        android:id="@+id/open_full_btn"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginEnd="8dp"
                        android:layout_marginTop="8dp"
                        android:text="@string/result_card_see_more"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintTop_toBottomOf="@+id/bubble_data" />
    
    
                </android.support.constraint.ConstraintLayout>
            </android.support.v7.widget.CardView>
    
        </android.support.constraint.ConstraintLayout>
    
    
    </FrameLayout>
    

    如果你投反对票,请解释为什么投反对票。

    3 回复  |  直到 7 年前
        1
  •  2
  •   theJango    7 年前

    测试了你的代码和提到的博客文章的代码。问题似乎是因为你用过 ConstraintLayout CardView 在您的布局中。

    对于 widget behavior ,不支持这些视图类型。当我们将视图直接添加到系统窗口时,它们的行为就像 RemoteViews . 你一换就换 约束布局 卡片视图 ,您的布局变得可见和可操作。

        2
  •  0
  •   Khemraj Sharma    7 年前

    这是我的工作代码。当需要显示浮动视图时,您将启动此服务。

    浮动服务类

    import android.app.Service;
    import android.content.Intent;
    import android.graphics.PixelFormat;
    import android.os.Build;
    import android.os.IBinder;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.WindowManager;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    public class FloatingService extends Service {
    
        private WindowManager mWindowManager;
        private View mFloatingView;
    
        public FloatingService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            //Inflate the floating view layout we created
            mFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null);
            int LAYOUT_FLAG;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
            } else {
                LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
            }
            //Add the view to the window.
            final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    LAYOUT_FLAG,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT);
    
            //Specify the view position
            params.gravity = Gravity.TOP | Gravity.LEFT;        //Initially view will be added to top-left corner
            params.x = 0;
            params.y = 100;
    
            //Add the view to the window
            mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
            mWindowManager.addView(mFloatingView, params);
    
            //The root element of the collapsed view layout
            final View collapsedView = mFloatingView.findViewById(R.id.collapse_view);
            //The root element of the expanded view layout
            final View expandedView = mFloatingView.findViewById(R.id.expanded_container);
    
    
            //Set the close button
            ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn);
            closeButtonCollapsed.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //close the service and remove the from from the window
                    stopSelf();
                }
            });
    
            //Set the view while floating view is expanded.
            //Set the play button.
            ImageView playButton = (ImageView) mFloatingView.findViewById(R.id.play_btn);
            playButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(FloatingService.this, "Playing the song.", Toast.LENGTH_LONG).show();
                }
            });
    
            //Set the next button.
            ImageView nextButton = (ImageView) mFloatingView.findViewById(R.id.next_btn);
            nextButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(FloatingService.this, "Playing next song.", Toast.LENGTH_LONG).show();
                }
            });
    
            //Set the pause button.
            ImageView prevButton = (ImageView) mFloatingView.findViewById(R.id.prev_btn);
            prevButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(FloatingService.this, "Playing previous song.", Toast.LENGTH_LONG).show();
                }
            });
    
            //Set the close button
            ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button);
            closeButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    collapsedView.setVisibility(View.VISIBLE);
                    expandedView.setVisibility(View.GONE);
                }
            });
    
            //Open the application on thi button click
            ImageView openButton = (ImageView) mFloatingView.findViewById(R.id.open_button);
            openButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //Open the application  click.
                    Intent intent = new Intent(FloatingService.this, MainActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
    
                    //close the service and remove view from the view hierarchy
                    stopSelf();
                }
            });
    
            //Drag and move floating view using user's touch action.
            mFloatingView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() {
                private int initialX;
                private int initialY;
                private float initialTouchX;
                private float initialTouchY;
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
    
                            //remember the initial position.
                            initialX = params.x;
                            initialY = params.y;
    
                            //get the touch location
                            initialTouchX = event.getRawX();
                            initialTouchY = event.getRawY();
                            return true;
                        case MotionEvent.ACTION_UP:
                            int Xdiff = (int) (event.getRawX() - initialTouchX);
                            int Ydiff = (int) (event.getRawY() - initialTouchY);
    
                            //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
                            //So that is click event.
                            if (Xdiff < 10 && Ydiff < 10) {
                                if (isViewCollapsed()) {
                                    //When user clicks on the image view of the collapsed layout,
                                    //visibility of the collapsed layout will be changed to "View.GONE"
                                    //and expanded view will become visible.
                                    collapsedView.setVisibility(View.GONE);
                                    expandedView.setVisibility(View.VISIBLE);
                                }
                            }
                            return true;
                        case MotionEvent.ACTION_MOVE:
                            //Calculate the X and Y coordinates of the view.
                            params.x = initialX + (int) (event.getRawX() - initialTouchX);
                            params.y = initialY + (int) (event.getRawY() - initialTouchY);
    
                            //Update the layout with new X & Y coordinate
                            mWindowManager.updateViewLayout(mFloatingView, params);
                            return true;
                    }
                    return false;
                }
            });
        }
    
        /**
         * Detect if the floating view is collapsed or expanded.
         *
         * @return true if the floating view is collapsed.
         */
        private boolean isViewCollapsed() {
            return mFloatingView == null || mFloatingView.findViewById(R.id.collapse_view).getVisibility() == View.VISIBLE;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (mFloatingView != null) mWindowManager.removeView(mFloatingView);
        }
    }
    

    然后添加

      <service
                android:name="yourpakcage.FloatingService"
                android:enabled="true"
                android:exported="false"/>
    

    布局浮动小部件.xml

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    
        <!--Root container-->
        <RelativeLayout
            android:id="@+id/root_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:ignore="UselessParent">
    
            <!--View while view is collapsed-->
            <RelativeLayout
                android:id="@+id/collapse_view"
                android:layout_width="wrap_content"
                android:visibility="visible"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                <!--Icon of floating widget -->
                <ImageView
                    android:id="@+id/collapsed_iv"
                    android:layout_width="60dp"
                    android:layout_height="60dp"
                    android:layout_marginTop="8dp"
                    android:src="@drawable/ic_android_circle"
                    tools:ignore="ContentDescription"/>
    
                <!--Close button-->
                <ImageView
                    android:id="@+id/close_btn"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:layout_marginLeft="40dp"
                    android:src="@drawable/ic_close"
                    tools:ignore="ContentDescription"/>
            </RelativeLayout>
    
            <!--View while view is expanded-->
            <LinearLayout
                android:id="@+id/expanded_container"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="#F8BBD0"
                android:visibility="gone"
                android:orientation="horizontal"
                android:padding="8dp">
    
                <!--Album image for the song currently playing.-->
                <ImageView
                    android:layout_width="80dp"
                    android:layout_height="80dp"
                    android:src="@drawable/music_player"
                    tools:ignore="ContentDescription"/>
    
                <!--Previous button-->
                <ImageView
                    android:id="@+id/prev_btn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:src="@mipmap/ic_previous"
                    tools:ignore="ContentDescription"/>
    
                <!--Play button-->
                <ImageView
                    android:id="@+id/play_btn"
                    android:layout_width="50dp"
                    android:layout_height="50dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="10dp"
                    android:src="@mipmap/ic_play"
                    tools:ignore="ContentDescription"/>
    
                <!--Next button-->
                <ImageView
                    android:id="@+id/next_btn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="10dp"
                    android:src="@mipmap/ic_play_next"
                    tools:ignore="ContentDescription"/>
    
                <RelativeLayout
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:orientation="vertical">
    
                    <ImageView
                        android:id="@+id/close_button"
                        android:layout_width="20dp"
                        android:layout_height="20dp"
                        android:src="@drawable/ic_close"/>
    
                    <ImageView
                        android:id="@+id/open_button"
                        android:layout_width="20dp"
                        android:layout_height="20dp"
                        android:layout_alignParentBottom="true"
                        android:src="@drawable/ic_open"/>
                </RelativeLayout>
            </LinearLayout>
        </RelativeLayout>
    </FrameLayout>
    

    请添加虚拟抽屉和绳子。

    以显示浮动视图。

    startService(new Intent(passContext, FloatingViewService.class));
    
        3
  •  -1
  •   Sander    7 年前

    不幸的是,你一直遵循的指南已经过时了。当你的应用程序处于后台时,你试图启动一项服务,这会导致 IllegalArgumentException 因为 Background Execution Limits 在Android Oreo中引入。链接底部包含迁移指南。