1. 程式人生 > >Appium原始碼分析(四)-swipe

Appium原始碼分析(四)-swipe

 @Override
  public AndroidCommandResult execute(final AndroidCommand command)
      throws JSONException {
    final Hashtable<String, Object> params = command.params();
    final Point start = new Point(params.get("startX"), params.get("startY"));
    final Point end = new Point(params.get("endX"
), params.get("endY")); final Integer steps = (Integer) params.get("steps"); final UiDevice device = UiDevice.getInstance(); Point absStartPos = new Point(); Point absEndPos = new Point(); try { if (command.isElementCommand()) { final AndroidElement el = command.getElement(); absStartPos = el.getAbsolutePosition(start); absEndPos = el.getAbsolutePosition(end); } else
{ absStartPos = PositionHelper.getDeviceAbsPos(start); absEndPos = PositionHelper.getDeviceAbsPos(end); } } catch (final UiObjectNotFoundException e) { return getErrorResult(e.getMessage()); } catch (final InvalidCoordinatesException e) { return getErrorResult(e.getMessage()); } catch
(final Exception e) { // handle NullPointerException return getErrorResult("Unknown error"); } Logger.debug("Swiping from " + absStartPos.toString() + " to " + absEndPos.toString() + " with steps: " + steps.toString()); final boolean rv = device.swipe(absStartPos.x.intValue(), absStartPos.y.intValue(), absEndPos.x.intValue(), absEndPos.y.intValue(), steps); if (!rv) { return getErrorResult("The swipe did not complete successfully"); } return getSuccessResult(rv); }

Swipe的程式碼其實很少,但是引數實際上不太少,param引數中包括了swipe的起始座標以及結束座標,還有一個步驟數,就是你從起始座標到結束座標需要滑動多少次。我們到UiAutomator裡面去看看 這個如何根據傳入的step來進行滑動呢

public boolean swipe(int downX, int downY, int upX, int upY, int steps, boolean drag) {
        boolean ret = false;
        int swipeSteps = steps;
        double xStep = 0;
        double yStep = 0;

        // avoid a divide by zero
        if(swipeSteps == 0)
            swipeSteps = 1;

        xStep = ((double)(upX - downX)) / swipeSteps;
        yStep = ((double)(upY - downY)) / swipeSteps;

        // first touch starts exactly at the point requested
        ret = touchDown(downX, downY);
        if (drag)
            SystemClock.sleep(mUiAutomatorBridge.getSystemLongPressTime());
        for(int i = 1; i < swipeSteps; i++) {
            ret &= touchMove(downX + (int)(xStep * i), downY + (int)(yStep * i));
            if(ret == false)
                break;
            // set some known constant delay between steps as without it this
            // become completely dependent on the speed of the system and results
            // may vary on different devices. This guarantees at minimum we have
            // a preset delay.
            SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
        }
        if (drag)
            SystemClock.sleep(REGULAR_CLICK_LENGTH);
        ret &= touchUp(upX, upY);
        return(ret);
    }

挺清楚的了,原來swipe到最後是呼叫了touchDwon以及touchUp方法,通過結束的X/Y座標減去開始的X/Y座標,再除以一個步驟數,就得到了每次Swipe需要多長的距離了

再繼續前面Swipe的分析獲取到傳入的引數以後,首先判斷這個命令是針對於元素的滑動還是針對於裝置的滑動

  • 如果是針對於元素進行滑動的話,這裡就要進行一個座標的轉換,將其轉換成相對於元素的座標。我們看看實際的程式碼
public static Point getAbsolutePosition(final Point point, final Rect displayRect, 
                                          final Point offsets, final boolean shouldCheckBounds)
      throws UiObjectNotFoundException, InvalidCoordinatesException {
    final Point absolutePosition = new Point();

    absolutePosition.x = translateCoordinate(point.x, displayRect.width(), offsets.x);
    absolutePosition.y = translateCoordinate(point.y, displayRect.height(), offsets.y);

    if (shouldCheckBounds &&
        !displayRect.contains(absolutePosition.x.intValue(), absolutePosition.y.intValue())) {
      throw new InvalidCoordinatesException("Coordinate " + absolutePosition.toString() +
          " is outside of element rect: " + displayRect.toShortString());
    }

    return absolutePosition;
  }

這裡是分別對x,y座標進行轉換,分別傳入元素的寬高以及左頂點座標 看看實際轉換的程式碼

 private static double translateCoordinate(double pointCoord, double length, double offset) {
    double translatedCoord = 0;

    if (pointCoord == 0) {
      translatedCoord = length * 0.5;
    } else if (Math.abs(pointCoord) > 0 && Math.abs(pointCoord) < 1) {
      translatedCoord = length * pointCoord;
    } else {
      translatedCoord = pointCoord;
    }

    return translatedCoord + offset;
  }
  • 首先判斷傳入的座標值是不是0,如果是0的話就是從元素的中點開始
  • 再來判斷傳入的座標是不是小數,也就是百分比,如果是的話就進行相應的轉換
  • 最後如果前兩者都是那麼傳入的就是一個真實的座標,就直接取那個值就行
    -
    換算的值加上我們元素的一個偏移值,這個偏移值就是如果是X座標就是元素Rect的left,Y座標的話就是元素Rect的top了。
    換算出來的值最後呼叫uiautomator的swipe方法就OK了。

下來我們再來看看針對於裝置的Swipe又是怎麼樣的呢

public static Point getDeviceAbsPos(final Point point)
      throws UiObjectNotFoundException, InvalidCoordinatesException {
    final UiDevice d = UiDevice.getInstance();
    final Rect displayRect = new Rect(0, 0, d.getDisplayWidth(), d.getDisplayHeight());

    Logger.debug("Display bounds: " + displayRect.toShortString());

    return getAbsolutePosition(point, displayRect, new Point(), true);
  }

改變的地方就是之前傳參傳的是元素的矩陣的座標,而這次傳入的是裝置的寬高。其他沒有什麼區別了。