如何在“辅助功能服务”中制作ActionBar Movable

问题描述 投票:3回答:2

我是主题“无障碍服务”的新手。我能够滚动按钮点击,并能够关闭手机和更多的东西,但我想让我的布局(action_bar.xml)可移动所以,任何人都可以告诉我请如何使action_bar可移动

这是我的

action_bar.xml ::

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Button
    android:id="@+id/power"
    android:layout_weight="1"
    android:text="@string/power"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<Button
    android:id="@+id/volume_up"
    android:text="@string/volume"
    android:layout_weight="1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<Button
    android:layout_weight="1"
    android:id="@+id/scroll"
    android:text="@string/scroll"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<Button
    android:id="@+id/swipe"
    android:text="@string/swipe"
    android:layout_weight="1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

和ActionBarService.Java,我在哪里使用它:

@Override
protected void onServiceConnected() {
    // Create an overlay and display the action bar
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    mLayout = new FrameLayout(this);
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
    lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
    lp.format = PixelFormat.TRANSLUCENT;
    lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.gravity = Gravity.TOP;
    LayoutInflater inflater = LayoutInflater.from(this);
    inflater.inflate(R.layout.action_bar, mLayout);
    wm.addView(mLayout, lp);  
}
java android xml accessibilityservice
2个回答
1
投票

@fishbone的答案大多是正确的,除了可访问性服务中的覆盖不一样的小部分。当然,做出这个假设可以简化事情,但最终却是错误的。可访问性服务有一些非常重要的注意事项,并且在大多数情况下,它们的行为与活动完全相同,但Accessibility Services启动叠加的方式非常重要,与大多数视图的行为方式不同,尤其是它们拦截触摸事件的方式。你必须非常小心触摸事件。最终你希望视图可以触摸,但不是,占据整个窗口。您可以使用的两种类型是:

TYPE_ACCESSIBILITY_OVERLAY

要么

TYPE_APPLICATION_OVERLAY

这两个表现相似,但不是。如果您在实际视图中遇到触摸事件,后者可能会对您有所帮助。

核心问题是,WindowManager可用的布局参数只允许您完全拦截触摸事件或根本不执行此操作。对于这部分视图,您不能这样做,而不能为其他人这样做。尝试将事件从窗口传递到您不拥有的窗口的逻辑...只是不去那里。

因此,您需要做的是没有完整的布局视图。您必须将视图添加为Touchable,但是您的视图仅占用屏幕上实际必须覆盖的部分。这应该尽可能地尽可能小(因为触摸事件将无法与这种观点相提并论)。然后,您必须执行自定义保持触摸侦听器,并执行触摸和拖动逻辑以手动移动视图。

您可以仅使用触摸/拖动视图进行更换。如同,有一个按钮,使“拖动模式”可以替换它。并且只有在将整个布局添加到窗口时,删除布局,然后添加只是工具栏视图的较小的非布局版本。

不幸的是,这里没有简单的答案。这不是Accessibility API要执行的操作。我也有义务提醒您,谷歌正在考虑的对无障碍服务的新期望几乎肯定会导致无障碍服务在Google Play商店中删除/不允许这样做。


0
投票

覆盖层的工作原理与ViewActivity上通常的Fragment相同。没有像android:movable="true"这样的魔法属性可以使叠加层移动。你应该自己做。这是我的可移动View的例子:

action_bar.xml(这里我用自定义LinearLayout替换DraggableLayout

<?xml version="1.0" encoding="utf-8"?>
<your.package.name.DraggableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Button
        android:id="@+id/power"
        android:layout_weight="1"
        android:text="Power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/volume_up"
        android:text="Volume"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_weight="1"
        android:id="@+id/scroll"
        android:text="Scroll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/swipe"
        android:text="Swipe"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</your.package.name.DraggableLayout>

DraggableLayout.class

public class DraggableLayout extends LinearLayout implements View.OnDragListener {

    GestureDetector mLongClickDetector;
    Point mPickPoint;

    public DraggableLayout(Context context) {
        this(context, null, 0);
    }

    public DraggableLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DraggableLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // When user performs a long press, we begin dragging
        mLongClickDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            public void onLongPress(MotionEvent e) {
                mPickPoint = new Point((int) e.getX(), (int) e.getY());
                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(DraggableLayout.this) {
                    @Override
                    public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
                        outShadowSize.set(getWidth(), getHeight());
                        outShadowTouchPoint.set(mPickPoint.x, mPickPoint.y);
                    }
                };
                startDrag(null, shadowBuilder, null, 0);
            }
        });
    }

    @Override
    protected void onAttachedToWindow() {
        // We should register this class as OnDragListener to parent view to catch DROP events from it
        ((ViewGroup) getParent()).setOnDragListener(this);
        super.onAttachedToWindow();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //This is also an important point: we must intercept touch events before the child elements (Buttons and so on)
        return mLongClickDetector.onTouchEvent(ev);
    }

    @Override
    public boolean onDragEvent(DragEvent event) {
        if (event.getAction() == DragEvent.ACTION_DROP) {
            // And when user performs drop we change position of view
            setX(event.getX() - mPickPoint.x);
            setY(event.getY() - mPickPoint.y);
            return true;
        }
        return false;
    }

    @Override
    public boolean onDrag(View v, DragEvent event) {
        return onDragEvent(event);
    }
}

action bar service.Java

@Override
protected void onServiceConnected() {
    // Create an overlay and display the action bar
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    mLayout = new FrameLayout(this);
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.MATCH_PARENT, // Overlay must be full screen otherwise my trick will not work
        WindowManager.LayoutParams.MATCH_PARENT,
        WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // Allow another windows to receive touch events
        PixelFormat.TRANSLUCENT);
    LayoutInflater inflater = LayoutInflater.from(this);
    inflater.inflate(R.layout.action_bar, mLayout);
    wm.addView(mLayout, lp);  
}

它似乎工作,如果不是那样,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.