谷歌电视应用程序如何在由D-pad控制的WEBVIEW导航上实现鼠标指针?

谷歌电视应用程序如何在由D-pad控制的WEBVIEW导航上实现鼠标指针?,webview,d-pad,Webview,D Pad,为Android TV实现基于Webview的应用程序,无需在视频页面上登录到网站的链接。由于网页是桌面,使用D-Pad键导航非常困难。我想实现由D-Pad控制的鼠标光标导航。对可用示例源代码的任何帮助都会有所帮助。在这里尝试做同样的事情 基本方法: 创建绘制、移动和设置光标动画的自定义视图 在框架布局中,将此自定义光标视图添加到webview顶部 当用户单击(键:DPAD center)时,通过模拟触摸事件模拟对光标位置的单击 当光标位于边缘时,按相应按钮滚动WebView 在执行此操

为Android TV实现基于Webview的应用程序,无需在视频页面上登录到网站的链接。由于网页是桌面,使用D-Pad键导航非常困难。我想实现由D-Pad控制的鼠标光标导航。对可用示例源代码的任何帮助都会有所帮助。

在这里尝试做同样的事情


基本方法:

  • 创建绘制、移动和设置光标动画的自定义视图
  • 在框架布局中,将此自定义光标视图添加到webview顶部
  • 当用户单击(键:DPAD center)时,通过模拟触摸事件模拟对光标位置的单击
  • 当光标位于边缘时,按相应按钮滚动WebView

在执行此操作时,焦点处理有点像PITA:

当webview有焦点时,它会做各种奇怪的事情(滚动、突出显示等等)。所以我试着让我的光标视图聚焦。工作非常好,除非点击文本输入字段->键盘在WebView未聚焦时无法显示/工作

因此,使用getHitTestResult()可以确定单击是否会命中输入字段,并使WebView在单击之前具有焦点。这很好,但我还没有找到一种可靠的方法,当用户输入完文本后,将焦点移回光标视图


我尝试过的一件事是在IME连接上设置一个钩子,但我无法使这种方法稳定到足以在公共应用程序中使用。

通过创建自定义指针布局,在android tv webview中启用光标指针

public class CursorLayout extends FrameLayout {
public static final int CURSOR_DISAPPEAR_TIMEOUT = 5000;
public static int CURSOR_RADIUS = 0;
public static float CURSOR_STROKE_WIDTH = 0.0f;
public static float MAX_CURSOR_SPEED = 0.0f;
public static int SCROLL_START_PADDING = 100;
public static final int UNCHANGED = -100;
public int EFFECT_DIAMETER;
public int EFFECT_RADIUS;
private Callback callback;
/* access modifiers changed from: private */
public Point cursorDirection = new Point(0, 0);
/* access modifiers changed from: private */
public Runnable cursorHideRunnable = new Runnable() {
    public void run() {
        CursorLayout.this.invalidate();
    }
};
/* access modifiers changed from: private */
public PointF cursorPosition = new PointF(0.0f, 0.0f);
/* access modifiers changed from: private */
public PointF cursorSpeed = new PointF(0.0f, 0.0f);
private Runnable cursorUpdateRunnable = new Runnable() {
    public void run() {
        if (CursorLayout.this.getHandler() != null) {
            CursorLayout.this.getHandler().removeCallbacks(CursorLayout.this.cursorHideRunnable);
        }
        long currentTimeMillis = System.currentTimeMillis();
        long access$100 = currentTimeMillis - CursorLayout.this.lastCursorUpdate;
        CursorLayout.this.lastCursorUpdate = currentTimeMillis;
        float f = ((float) access$100) * 0.05f;
        PointF access$200 = CursorLayout.this.cursorSpeed;
        CursorLayout cursorLayout = CursorLayout.this;
        float f2 = cursorLayout.cursorSpeed.x;
        CursorLayout cursorLayout2 = CursorLayout.this;
        float access$400 = cursorLayout.bound(f2 + (cursorLayout2.bound((float) cursorLayout2.cursorDirection.x, 1.0f) * f), CursorLayout.MAX_CURSOR_SPEED);
        CursorLayout cursorLayout3 = CursorLayout.this;
        float f3 = cursorLayout3.cursorSpeed.y;
        CursorLayout cursorLayout4 = CursorLayout.this;
        access$200.set(access$400, cursorLayout3.bound(f3 + (cursorLayout4.bound((float) cursorLayout4.cursorDirection.y, 1.0f) * f), CursorLayout.MAX_CURSOR_SPEED));
        if (Math.abs(CursorLayout.this.cursorSpeed.x) < 0.1f) {
            CursorLayout.this.cursorSpeed.x = 0.0f;
        }
        if (Math.abs(CursorLayout.this.cursorSpeed.y) < 0.1f) {
            CursorLayout.this.cursorSpeed.y = 0.0f;
        }
        if (CursorLayout.this.cursorDirection.x == 0 && CursorLayout.this.cursorDirection.y == 0 && CursorLayout.this.cursorSpeed.x == 0.0f && CursorLayout.this.cursorSpeed.y == 0.0f) {
            if (CursorLayout.this.getHandler() != null) {
                CursorLayout.this.getHandler().postDelayed(CursorLayout.this.cursorHideRunnable, 5000);
            }
            return;
        }
        CursorLayout.this.tmpPointF.set(CursorLayout.this.cursorPosition);
        CursorLayout.this.cursorPosition.offset(CursorLayout.this.cursorSpeed.x, CursorLayout.this.cursorSpeed.y);
        Log.d("cursor1234_xxxx", String.valueOf(CursorLayout.this.cursorPosition.x));
        Log.d("cursor1234_yyyy", String.valueOf(CursorLayout.this.cursorPosition.y));
        if (CursorLayout.this.cursorPosition.x < 0.0f) {
            CursorLayout.this.cursorPosition.x = 0.0f;
        } else if (CursorLayout.this.cursorPosition.x > ((float) (CursorLayout.this.getWidth() - 1))) {
            CursorLayout.this.cursorPosition.x = (float) (CursorLayout.this.getWidth() - 1);
        }
        if (CursorLayout.this.cursorPosition.y < 0.0f) {
            CursorLayout.this.cursorPosition.y = 0.0f;
        } else if (CursorLayout.this.cursorPosition.y > ((float) (CursorLayout.this.getHeight() - 1))) {
            CursorLayout.this.cursorPosition.y = (float) (CursorLayout.this.getHeight() - 1);
        }
        if (!CursorLayout.this.tmpPointF.equals(CursorLayout.this.cursorPosition) && CursorLayout.this.dpadCenterPressed) {
            CursorLayout cursorLayout5 = CursorLayout.this;
            cursorLayout5.dispatchMotionEvent(cursorLayout5.cursorPosition.x, CursorLayout.this.cursorPosition.y, 2);
        }
        View childAt = CursorLayout.this.getChildAt(0);
        if (childAt != null) {
            if (CursorLayout.this.cursorPosition.y > ((float) (CursorLayout.this.getHeight() - CursorLayout.SCROLL_START_PADDING))) {
                if (CursorLayout.this.cursorSpeed.y > 0.0f && childAt.canScrollVertically((int) CursorLayout.this.cursorSpeed.y)) {
                    childAt.scrollTo(childAt.getScrollX(), childAt.getScrollY() + ((int) CursorLayout.this.cursorSpeed.y));
                }
            } else if (CursorLayout.this.cursorPosition.y < ((float) CursorLayout.SCROLL_START_PADDING) && CursorLayout.this.cursorSpeed.y < 0.0f && childAt.canScrollVertically((int) CursorLayout.this.cursorSpeed.y)) {
                childAt.scrollTo(childAt.getScrollX(), childAt.getScrollY() + ((int) CursorLayout.this.cursorSpeed.y));
            }
            if (CursorLayout.this.cursorPosition.x > ((float) (CursorLayout.this.getWidth() - CursorLayout.SCROLL_START_PADDING))) {
                if (CursorLayout.this.cursorSpeed.x > 0.0f && childAt.canScrollHorizontally((int) CursorLayout.this.cursorSpeed.x)) {
                    childAt.scrollTo(childAt.getScrollX() + ((int) CursorLayout.this.cursorSpeed.x), childAt.getScrollY());
                }
            } else if (CursorLayout.this.cursorPosition.x < ((float) CursorLayout.SCROLL_START_PADDING) && CursorLayout.this.cursorSpeed.x < 0.0f && childAt.canScrollHorizontally((int) CursorLayout.this.cursorSpeed.x)) {
                childAt.scrollTo(childAt.getScrollX() + ((int) CursorLayout.this.cursorSpeed.x), childAt.getScrollY());
            }
        }
        CursorLayout.this.invalidate();
        if (CursorLayout.this.getHandler() != null) {
            CursorLayout.this.getHandler().post(this);
        }
    }
};
/* access modifiers changed from: private */
public boolean dpadCenterPressed = false;
/* access modifiers changed from: private */
public long lastCursorUpdate = System.currentTimeMillis();
private Paint paint = new Paint();
PointF tmpPointF = new PointF();

public interface Callback {
    void onUserInteraction();
}

/* access modifiers changed from: private */
public float bound(float f, float f2) {
    if (f > f2) {
        return f2;
    }
    float f3 = -f2;
    return f < f3 ? f3 : f;
}

public CursorLayout(Context context) {
    super(context);
    init();
}

public CursorLayout(Context context, AttributeSet attributeSet) {
    super(context, attributeSet);
    init();
}

private void init() {
    if (!isInEditMode()) {
        this.paint.setAntiAlias(true);
        setWillNotDraw(false);
        Display defaultDisplay = ((WindowManager) getContext().getSystemService(getContext().WINDOW_SERVICE)).getDefaultDisplay();
        Point point = new Point();
        defaultDisplay.getSize(point);
        this.EFFECT_RADIUS = point.x / 20;
        this.EFFECT_DIAMETER = this.EFFECT_RADIUS * 2;
        CURSOR_STROKE_WIDTH = (float) (point.x / 400);
        CURSOR_RADIUS = point.x / 110;
        MAX_CURSOR_SPEED = (float) (point.x / 25);
        SCROLL_START_PADDING = point.x / 15;
    }
}

public void setCallback(Callback callback2) {
    this.callback = callback2;
}

public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
    Callback callback2 = this.callback;
    if (callback2 != null) {
        callback2.onUserInteraction();
    }
    return super.onInterceptTouchEvent(motionEvent);
}

/* access modifiers changed from: protected */
public void onSizeChanged(int i, int i2, int i3, int i4) {
    super.onSizeChanged(i, i2, i3, i4);
    UtilMethods.LogMethod("cursorView123_", "onSizeChanged");
    if (!isInEditMode()) {
        this.cursorPosition.set(((float) i) / 2.0f, ((float) i2) / 2.0f);
        if (getHandler() != null) {
            getHandler().postDelayed(this.cursorHideRunnable, 5000);
        }
    }
}

public boolean dispatchKeyEvent(KeyEvent keyEvent) {
    UtilMethods.LogMethod("cursorView123_", "dispatchKeyEvent");
    Callback callback2 = this.callback;
    if (callback2 != null) {
        callback2.onUserInteraction();
    }
    int keyCode = keyEvent.getKeyCode();
    if (!(keyCode == 66 || keyCode == 160)) {
        switch (keyCode) {
            case 19:
                if (keyEvent.getAction() == 0) {
                    if (this.cursorPosition.y <= 0.0f) {
                        return super.dispatchKeyEvent(keyEvent);
                    }
                    handleDirectionKeyEvent(keyEvent, -100, -1, true);
                } else if (keyEvent.getAction() == 1) {
                    handleDirectionKeyEvent(keyEvent, -100, 0, false);
                }
                return true;
            case 20:
                if (keyEvent.getAction() == 0) {
                    if (this.cursorPosition.y >= ((float) getHeight())) {
                        return super.dispatchKeyEvent(keyEvent);
                    }
                    handleDirectionKeyEvent(keyEvent, -100, 1, true);
                } else if (keyEvent.getAction() == 1) {
                    handleDirectionKeyEvent(keyEvent, -100, 0, false);
                }
                return true;
            case 21:
                if (keyEvent.getAction() == 0) {
                    if (this.cursorPosition.x <= 0.0f) {
                        return super.dispatchKeyEvent(keyEvent);
                    }
                    handleDirectionKeyEvent(keyEvent, -1, -100, true);
                } else if (keyEvent.getAction() == 1) {
                    handleDirectionKeyEvent(keyEvent, 0, -100, false);
                }
                return true;
            case 22:
                if (keyEvent.getAction() == 0) {
                    if (this.cursorPosition.x >= ((float) getWidth())) {
                        return super.dispatchKeyEvent(keyEvent);
                    }
                    handleDirectionKeyEvent(keyEvent, 1, -100, true);
                } else if (keyEvent.getAction() == 1) {
                    handleDirectionKeyEvent(keyEvent, 0, -100, false);
                }
                return true;
            case 23:
                break;
            default:
                switch (keyCode) {
                    case 268:
                        if (keyEvent.getAction() == 0) {
                            handleDirectionKeyEvent(keyEvent, -1, -1, true);
                        } else if (keyEvent.getAction() == 1) {
                            handleDirectionKeyEvent(keyEvent, 0, 0, false);
                        }
                        return true;
                    case 269:
                        if (keyEvent.getAction() == 0) {
                            handleDirectionKeyEvent(keyEvent, -1, 1, true);
                        } else if (keyEvent.getAction() == 1) {
                            handleDirectionKeyEvent(keyEvent, 0, 0, false);
                        }
                        return true;
                    case 270:
                        if (keyEvent.getAction() == 0) {
                            handleDirectionKeyEvent(keyEvent, 1, -1, true);
                        } else if (keyEvent.getAction() == 1) {
                            handleDirectionKeyEvent(keyEvent, 0, 0, false);
                        }
                        return true;
                    case 271:
                        if (keyEvent.getAction() == 0) {
                            handleDirectionKeyEvent(keyEvent, 1, 1, true);
                        } else if (keyEvent.getAction() == 1) {
                            handleDirectionKeyEvent(keyEvent, 0, 0, false);
                        }
                        return true;
                }
        }
    }
    if (!isCursorDissappear()) {
        if (keyEvent.getAction() == 0 && !getKeyDispatcherState().isTracking(keyEvent)) {
            getKeyDispatcherState().startTracking(keyEvent, this);
            this.dpadCenterPressed = true;
            dispatchMotionEvent(this.cursorPosition.x, this.cursorPosition.y, 0);
        } else if (keyEvent.getAction() == 1) {
            getKeyDispatcherState().handleUpEvent(keyEvent);
            dispatchMotionEvent(this.cursorPosition.x, this.cursorPosition.y, 1);
            this.dpadCenterPressed = false;
        }
        return true;
    }
    return super.dispatchKeyEvent(keyEvent);
}

/* access modifiers changed from: private */
public void dispatchMotionEvent(float f, float f2, int i) {
    UtilMethods.LogMethod("cursorView123_", "dispatchMotionEvent");
    long uptimeMillis = SystemClock.uptimeMillis();
    long uptimeMillis2 = SystemClock.uptimeMillis();
    PointerProperties pointerProperties = new PointerProperties();
    pointerProperties.id = 0;
    pointerProperties.toolType = 1;
    PointerProperties[] pointerPropertiesArr = {pointerProperties};
    PointerCoords pointerCoords = new PointerCoords();
    pointerCoords.x = f;
    pointerCoords.y = f2;
    pointerCoords.pressure = 1.0f;
    pointerCoords.size = 1.0f;
    dispatchTouchEvent(MotionEvent.obtain(uptimeMillis, uptimeMillis2, i, 1, pointerPropertiesArr, new PointerCoords[]{pointerCoords}, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0));
}

private void handleDirectionKeyEvent(KeyEvent keyEvent, int i, int i2, boolean z) {
    this.lastCursorUpdate = System.currentTimeMillis();
    if (!z) {
        getKeyDispatcherState().handleUpEvent(keyEvent);
        this.cursorSpeed.set(0.0f, 0.0f);
    } else if (!getKeyDispatcherState().isTracking(keyEvent)) {
        Handler handler = getHandler();
        handler.removeCallbacks(this.cursorUpdateRunnable);
        handler.post(this.cursorUpdateRunnable);
        getKeyDispatcherState().startTracking(keyEvent, this);
    } else {
        return;
    }
    Point point = this.cursorDirection;
    if (i == -100) {
        i = point.x;
    }
    if (i2 == -100) {
        i2 = this.cursorDirection.y;
    }
    point.set(i, i2);
}

/* access modifiers changed from: protected */
public void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    UtilMethods.LogMethod("cursorView123_", "dispatchDraw");
    if (!isInEditMode() && !isCursorDissappear()) {
        float f = this.cursorPosition.x;
        float f2 = this.cursorPosition.y;
        this.paint.setColor(Color.argb(128, 255, 255, 255));
        this.paint.setStyle(Style.FILL);
        canvas.drawCircle(f, f2, (float) CURSOR_RADIUS, this.paint);
        this.paint.setColor(-7829368);
        this.paint.setStrokeWidth(CURSOR_STROKE_WIDTH);
        this.paint.setStyle(Style.STROKE);
        canvas.drawCircle(f, f2, (float) CURSOR_RADIUS, this.paint);
    }
}

private boolean isCursorDissappear() {
    return System.currentTimeMillis() - this.lastCursorUpdate > 5000;
}

/* access modifiers changed from: protected */
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
}}
public类CursorLayout扩展了FrameLayout{
公共静态最终整数游标\u消失\u超时=5000;
公共静态int游标_半径=0;
公共静态浮动光标\u笔划\u宽度=0.0f;
公共静态浮动最大光标速度=0.0f;
公共静态int SCROLL\u START\u PADDING=100;
公共静态最终整数不变=-100;
公共int效应_直径;
公共int效应_半径;
私有回调;
/*访问修饰符已从:private更改*/
公共点光标方向=新点(0,0);
/*访问修饰符已从:private更改*/
public Runnable cursorHideRunnable=new Runnable(){
公开募捐{
CursorLayout.this.invalidate();
}
};
/*访问修饰符已从:private更改*/
公共点F光标位置=新点F(0.0f,0.0f);
/*访问修饰符已从:private更改*/
公共点F游标速度=新点F(0.0f,0.0f);
private Runnable cursorUpdateRunnable=new Runnable(){
公开募捐{
if(CursorLayout.this.getHandler()!=null){
CursorLayout.this.getHandler().removeCallbacks(CursorLayout.this.cursorHiderUnable);
}
long currentTimeMillis=System.currentTimeMillis();
长访问$100=currentTimeMillis-CursorLayout.this.lastCursorUpdate;
CursorLayout.this.lastCursorUpdate=currentTimeMillis;
浮动f=((浮动)存取$100)*0.05f;
PointF access$200=CursorLayout.this.cursorSpeed;
CursorLayout CursorLayout=CursorLayout.this;
float f2=游标布局.cursorSpeed.x;
CursorLayout cursorLayout2=CursorLayout.this;
浮动访问$400=cursorLayout.bound(f2+(cursorLayout2.bound((float)cursorLayout2.cursorDirection.x,1.0f)*f),cursorLayout.MAX\u CURSOR\u速度);
CursorLayout cursorLayout3=CursorLayout.this;
float f3=光标布局3.cursorSpeed.y;
CursorLayout cursorLayout4=CursorLayout.this;
访问$200.set(访问$400,游标布局3.bound(f3+(游标布局4.bound((浮动)游标布局4.cursorDirection.y,1.0f)*f),游标布局最大游标速度);
if(Math.abs(CursorLayout.this.cursorSpeed.x)<0.1f){
CursorLayout.this.cursorSpeed.x=0.0f;
}
if(Math.abs(CursorLayout.this.cursorSpeed.y)<0.1f){
CursorLayout.this.cursorSpeed.y=0.0f;
}
if(CursorLayout.this.cursorDirection.x==0&&CursorLayout.this.cursorDirection.y==0&&CursorLayout.this.cursorSpeed.x==0.0f&&CursorLayout.this.cursorSpeed.y==0.0f){
if(CursorLayout.this.getHandler()!=null){
CursorLayout.this.getHandler().postDelayed(CursorLayout.this.cursorhiderunable,5000);
}
返回;
}
CursorLayout.this.tmpPointF.set(CursorLayout.this.cursorPosition);
CursorLayout.this.cursorPosition.offset(CursorLayout.this.cursorSpeed.x,CursorLayout.this.cursorSpeed.y);
Log.d(“cursor1234_xxxx”,String.valueOf(CursorLayout.this.cursorPosition.x));
Log.d(“cursor1234_yyy”,String.valueOf(CursorLayout.this.cursorPosition.y));
if(CursorLayout.this.cursorPosition.x<0.0f){
CursorLayout.this.cursorPosition.x=0.0f;
}else if(CursorLayout.this.cursorPosition.x>((float)(CursorLayout.this.getWidth()-1))){
CursorLayout.this.cursorPosition.x=(float)(CursorLayout.this.getWidth()-1);
}
if(CursorLayout.this.cursorPosition.y<0.0f){
CursorLayout.this.cursorPosition.y=0.0f;
}else if(CursorLayout.this.cursorPosition.y>((float)(CursorLayout.this.getHeight()-1))){
CursorLayout.this.cursorPosition.y=(float)(CursorLayout.this.getHeight()-1);
}
如果(!CursorLayout.this.tmpPointF.equals(CursorLayout.this.cursorPosition)&&CursorLayout.this.dpadcenter按下){
CursorLayout cursorLayout5=CursorLayout.this;
cursorLayout5.dispatchMotionEvent(cursorLayout5.cursorPosition.x,CursorLayout.this.cursorPosition.y,2);
}
View childAt=CursorLayout.this.getChildAt(0);
if(childAt!=null){
如果(CursorLayout.this.cursorPosition.y>((float)(CursorLayout.this.getHeight()-CursorLayout.SCROLL\u启动_
<com.example.webviewtvapp.CursorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/cursorLayout">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</com.example.webviewtvapp.CursorLayout>