首页 > 代码库 > bootstrap之Click事件

bootstrap之Click事件

上一篇文章中讲了bootstrap的工作流程,这篇文章开始来研究一下bootstrap可以接受哪些指令(从源码的角度来看,由于appium的项目现在还处在持续更新中,所以有的指令已经实现,某些或许未来会实现,从源码来看的好处是你知道以后或许未来appium能做到哪些功能)。


在bootstrap项目中的io.appium.android.bootstrap.handler包中的类都是对应的相应的指令的类,里面都有execute方法来执行命令。先上上一篇文章中讲的map。


private static HashMap<String, CommandHandler> map = new HashMap<String, CommandHandler>();

  static {
    map.put("waitForIdle", new WaitForIdle());
    map.put("clear", new Clear());
    map.put("orientation", new Orientation());
    map.put("swipe", new Swipe());
    map.put("flick", new Flick());
    map.put("drag", new Drag());
    map.put("pinch", new Pinch());
    map.put("click", new Click());
    map.put("touchLongClick", new TouchLongClick());
    map.put("touchDown", new TouchDown());
    map.put("touchUp", new TouchUp());
    map.put("touchMove", new TouchMove());
    map.put("getText", new GetText());
    map.put("setText", new SetText());
    map.put("getName", new GetName());
    map.put("getAttribute", new GetAttribute());
    map.put("getDeviceSize", new GetDeviceSize());
    map.put("scrollTo", new ScrollTo());
    map.put("find", new Find());
    map.put("getLocation", new GetLocation());
    map.put("getSize", new GetSize());
    map.put("wake", new Wake());
    map.put("pressBack", new PressBack());
    map.put("dumpWindowHierarchy", new DumpWindowHierarchy());
    map.put("pressKeyCode", new PressKeyCode());
    map.put("longPressKeyCode", new LongPressKeyCode());
    map.put("takeScreenshot", new TakeScreenshot());
    map.put("updateStrings", new UpdateStrings());
    map.put("getDataDir", new GetDataDir());
    map.put("performMultiPointerGesture", new MultiPointerGesture());
    map.put("openNotification", new OpenNotification());
  }


我们就按照上面的顺序来讲。首先声明一点,事先了解一下uiautomator的api很有必要,因为这些指令中大多数都是调用uiautomator的方法去操作的,要么直接调用,要么反射调用。我的博客有很多关于这方面的文章,可以先去看看。


click


package io.appium.android.bootstrap.handler;

import com.android.uiautomator.core.UiDevice;
import com.android.uiautomator.core.UiObjectNotFoundException;
import io.appium.android.bootstrap.*;
import org.json.JSONException;

import java.util.ArrayList;
import java.util.Hashtable;

/**
 * This handler is used to click elements in the Android UI.
 * 
 * Based on the element Id, click that element.
 * 
 */
public class Click extends CommandHandler {

  /*
   * @param command The {@link AndroidCommand}
   * 
   * @return {@link AndroidCommandResult}
   * 
   * @throws JSONException
   * 
   * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android.
   * bootstrap.AndroidCommand)
   */
  @Override
  public AndroidCommandResult execute(final AndroidCommand command)
      throws JSONException {
    if (command.isElementCommand()) {
      try {
        final AndroidElement el = command.getElement();
        el.click();
        return getSuccessResult(true);
      } catch (final UiObjectNotFoundException e) {
        return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
            e.getMessage());
      } catch (final Exception e) { // handle NullPointerException
        return getErrorResult("Unknown error");
      }
    } else {
      final Hashtable<String, Object> params = command.params();
      final Double[] coords = { Double.parseDouble(params.get("x").toString()),
          Double.parseDouble(params.get("y").toString()) };
      final ArrayList<Integer> posVals = absPosFromCoords(coords);
      final boolean res = UiDevice.getInstance().click(posVals.get(0),
          posVals.get(1));
      return getSuccessResult(res);
    }
  }
}

该类中的方法就是处理点击事件,首先方法会判断你传入的命令参数是针对控件对象的还是以坐标的形式的。


控件


如果是控件的话,首先会获得命令中的控件对象,然后调用click方法,我们进入click方法看看


AndroidElement.java

public boolean click() throws UiObjectNotFoundException {
    return el.click();
  }

e1的定义为


private final UiObject el;

说明最终调用的是uiautomator中的UiObject类的click方法,这个方法的click方法就是点击该控件的中心点。然后回到Click类中继续往下看,会调用


return getSuccessResult(true);


从字面意思来看,走到这一步肯定就是告诉调用者,我只想成功了,跟你说一声。然后我们来看看这个方法里面的具体实现。


/**
   * Returns success along with the payload.
   * 
   * @param value
   * @return {@link AndroidCommandResult}
   */
  protected AndroidCommandResult getSuccessResult(final Object value) {
    return new AndroidCommandResult(WDStatus.SUCCESS, value);
  }

创建AndroidCommandResult新对象,传入WDStatus.SUCCESS,和value(我们这里传入的值为true).首先看一下SUCCESS的值,该值存放在枚举类WDStatus中。


SUCCESS (0, "The command executed successfully."), 

就是一行文本。好,下面进入AndroidCommandResult类的构造方法里。


JSONObject json;
public AndroidCommandResult(final WDStatus status, final Object val) {
    json = new JSONObject();
    try {
      json.put("status", status.code());
      json.put("value", val);
    } catch (final JSONException e) {
      Logger.error("Couldn't create android command result!");
    }
  }

构造方法里把传入的参数保存在了json对象中,以键值对的形式。好了,条件为控件的情况分析结束,下面开始分析坐标。


坐标


如果是坐标的话,程序会获得命令里的坐标参数,保存在Double数组中。


final Hashtable<String, Object> params = command.params();
final Double[] coords = { Double.parseDouble(params.get("x").toString()),
          Double.parseDouble(params.get("y").toString()) };

接下来会通过absPosFromCoords方法将Double转换为List。所以下面来看absPosFromCoords方法的实现:


/**
   * Given a position, it will return either the position based on percentage
   * (by passing in a double between 0 and 1) or absolute position based on the
   * coordinates entered.
   * 
   * @param coordVals
   * @return ArrayList<Integer>
   */
  protected static ArrayList<Integer> absPosFromCoords(final Double[] coordVals) {
    final ArrayList<Integer> retPos = new ArrayList<Integer>();
    final UiDevice d = UiDevice.getInstance();

    final Double screenX = (double) d.getDisplayWidth();
    final Double screenY = (double) d.getDisplayHeight();

    if (coordVals[0] < 1 && coordVals[1] < 1) {
      retPos.add((int) (screenX * coordVals[0]));
      retPos.add((int) (screenY * coordVals[1]));
    } else {
      retPos.add(coordVals[0].intValue());
      retPos.add(coordVals[1].intValue());
    }

    return retPos;
  }

首先会判断传入的坐标是以百分比的形式还是以坐标的形式。如果是百分比说明你传入的不是绝对坐标,而是相对坐标(可以这么理解吧,这样可以适应各个屏幕),这种情况你就需要获取屏幕的尺寸,然后和百分比做计算得到当前屏幕中你所要点击的坐标点。如果你传入的就是坐标,那就直接将Double类型的值转化为Int的值。

经过上面的一番操作以后,会得到确切坐标值保存在数组中返回。

然后程序调用UiDevice的click方法点击啦:


final boolean res = UiDevice.getInstance().click(posVals.get(0), posVals.get(1));

最后返回一个成功的AndroidCommandResult对象。