UiautomatorViewer源码(一):浅析

UiautomatorViewer是谷歌提供给Uiautomator脚本开发时查看和dump移动端页面数据的一个工具。这个工具可以对当前连接到PC上的手机屏幕进行一个快照,我们可以轻松的从dump出来的信息当前页面的层级关系和每个控件的属性。利用这些信息,我们可以轻松编写测试脚本。

但是,有时候,这个工具有些地方不尽人意。比如在一些Android版本(9.0)上并不稳定(null root node returned by UiTestAutomationBridge.)。又比如界面没有提供控件的instance序号,没有提供x-path,响应速度较慢等等。这些都是可以通过我们了解其原理后进行二次开发来解决的。

OK,扯得有点多,开始上菜。

1、去官网载个源码下来,然后在eclipse中创建一个工程,把源码拉进去,需要的一些引用包,记得也要导入。

2、打开主界面的类,

package com.android.uiautomator;

import java.io.File;

import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;

import com.android.uiautomator.actions.EasyScreenshotAction;
import com.android.uiautomator.actions.OpenFilesAction;
import com.android.uiautomator.actions.ScreenshotAction;

public class UiAutomatorViewer extends ApplicationWindow {

   private UiAutomatorView mUiAutomatorView;


   public UiAutomatorViewer() {
      super((Shell)null);
   }

   protected Control createContents(Composite parent) {//基本布局
      Composite c = new Composite(parent, 2048);
      GridLayout gridLayout = new GridLayout(1, false);
      gridLayout.marginWidth = 0;
      gridLayout.marginHeight = 0;
      gridLayout.horizontalSpacing = 0;
      gridLayout.verticalSpacing = 0;
      c.setLayout(gridLayout);
      GridData gd = new GridData(768);
      c.setLayoutData(gd);
      ToolBarManager toolBarManager = new ToolBarManager(8388608);
      toolBarManager.add(new OpenFilesAction(this));
      toolBarManager.add(new ScreenshotAction(this));
      ToolBar tb = toolBarManager.createControl(c);
      tb.setLayoutData(new GridData(768));
      this.mUiAutomatorView = new UiAutomatorView(c, 2048);
      this.mUiAutomatorView.setLayoutData(new GridData(1808));
      return parent;
   }

   public static void main(String[] args) {
      DebugBridge.init();

      try {
         UiAutomatorViewer e = new UiAutomatorViewer();
         e.setBlockOnOpen(true);
         e.open();
      } catch (Exception var5) {
         var5.printStackTrace();
      } finally {
         DebugBridge.terminate();
      }

   }

   protected void configureShell(Shell newShell) {
      super.configureShell(newShell);
      newShell.setText("UI Automator Viewer By Zekyll");//application title
   }

   protected Point getInitialSize() {
      return new Point(800, 600);
   }

   public void setModel(final UiAutomatorModel model, final File modelFile, final Image screenshot) {
      if(Display.getDefault().getThread() != Thread.currentThread()) {
         Display.getDefault().syncExec(new Runnable() {
            public void run() {
               UiAutomatorViewer.this.mUiAutomatorView.setModel(model, modelFile, screenshot);
            }
         });
      } else {
         this.mUiAutomatorView.setModel(model, modelFile, screenshot);
      }

   }
}

3、接下来,考虑程序是如何将手机与PC连接起来的。看下这个类:DebugBridge中有下面一段话:


   public static void init() {
      String adbLocation = getAdbLocation();
      if(adbLocation != null) {
         AndroidDebugBridge.init(false);
         sDebugBridge = AndroidDebugBridge.createBridge(adbLocation, false);
      }

   }

   public static void terminate() {
      if(sDebugBridge != null) {
         sDebugBridge = null;
         AndroidDebugBridge.terminate();
      }

   }

从这里可以看出,程序是使用ddmlib这个包来建立AndroidDebugBridge。

4、打开com.android.uiautomator.actions包下的ScreenshotAction这个类。我们可以看到,点击屏幕快照按钮后触发的事件代码。

public class ScreenshotAction extends Action {

   UiAutomatorViewer mViewer;


   public ScreenshotAction(UiAutomatorViewer viewer) {
      super("&Device Screenshot");
      this.mViewer = viewer;
   }

   public ImageDescriptor getImageDescriptor() {
      return ImageHelper.loadImageDescriptorFromResource("images/screenshot.png");//加载按钮图标
   }

   //点击快照按钮的事件
   public void run() {
      if(!DebugBridge.isInitialized()) {// 判断adb是否已经连接
         MessageDialog.openError(this.mViewer.getShell(), "Error obtaining Device Screenshot", "Unable to connect to adb. Check if adb is installed correctly.");
      } else {
         final IDevice device = this.pickDevice();
         if(device != null) {
            ProgressMonitorDialog dialog = new ProgressMonitorDialog(this.mViewer.getShell());

            try {
               dialog.run(true, false, new IRunnableWithProgress() {
                  public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                     UiAutomatorHelper.UiAutomatorResult result = null;

                     try {
                        result = UiAutomatorHelper.takeSnapshot(device, monitor);//截屏
                     } catch (UiAutomatorHelper.UiAutomatorException var4) {
                        monitor.done();
                        ScreenshotAction.this.showError(var4.getMessage(), var4);
                        var4.printStackTrace();
                        return;
                     }

                     ScreenshotAction.this.mViewer.setModel(result.model, result.uiHierarchy, result.screenshot);//树形结构处理
                     monitor.done();
                  }
               });
            } catch (Exception var4) {
               this.showError("Unexpected error while obtaining UI hierarchy", var4);
               var4.printStackTrace();
            }

         }
      }
   }

   private void showError(final String msg, final Throwable t) {
      this.mViewer.getShell().getDisplay().syncExec(new Runnable() {
         public void run() {
            Status s = new Status(4, "Screenshot", msg, t);
            ErrorDialog.openError(ScreenshotAction.this.mViewer.getShell(), "Error", "Error obtaining UI hierarchy", s);
         }
      });
   }

   private IDevice pickDevice() {
      List<?> devices = DebugBridge.getDevices();
      if(devices.size() == 0) {
         MessageDialog.openError(this.mViewer.getShell(), "Error obtaining Device Screenshot", "No Android devices were detected by adb.");
         return null;
      } else if(devices.size() == 1) {
         return (IDevice)devices.get(0);
      } else {
         ScreenshotAction.DevicePickerDialog dlg = new ScreenshotAction.DevicePickerDialog(this.mViewer.getShell(), devices);
         return dlg.open() != 0?null:dlg.getSelectedDevice();
      }
   }

跟踪到UiAutomatorHelper.takeSnapshot(device, monitor);这个方法,可以看到,这个方法主要做了两件事情,一个是截屏,另外一个就是dump .uix文件(当前界面数据),并且将这两个进行处理,返回一个 UiAutomatorHelper.UiAutomatorResult实例。

 public static UiAutomatorResult takeSnapshot(IDevice device, IProgressMonitor monitor) throws UiAutomatorHelper.UiAutomatorException
  {
    if (monitor == null) {
      monitor = new NullProgressMonitor();
    }

    monitor.subTask("Checking if device support UI Automator");
    if (!supportsUiAutomator(device)) {
      String msg = "UI Automator requires a device with API Level 16";

      throw new UiAutomatorException(msg, null);
    }

    monitor.subTask("Creating temporary files for uiautomator results.");
    File tmpDir = null;
    File xmlDumpFile = null;
    File screenshotFile = null;
    try {
      tmpDir = File.createTempFile("uiautomatorviewer_", "");
      tmpDir.delete();
      if (!tmpDir.mkdirs())
        throw new IOException("Failed to mkdir");
      xmlDumpFile = File.createTempFile("dump_", ".uix", tmpDir);
      screenshotFile = File.createTempFile("screenshot_", ".png", tmpDir);
    } catch (Exception e) {
      String msg = "Error while creating temporary file to save snapshot: " + e.getMessage();

      throw new UiAutomatorException(msg, e);
    }

    tmpDir.deleteOnExit();
    xmlDumpFile.deleteOnExit();
    screenshotFile.deleteOnExit();

    monitor.subTask("Obtaining UI hierarchy");
    try {
      getUiHierarchyFile(device, xmlDumpFile, monitor);
    } catch (Exception e) {
      String msg = "Error while obtaining UI hierarchy XML file: " + e.getMessage();
      throw new UiAutomatorException(msg, e);
    }
    UiAutomatorModel model;
    try
    {
      model = new UiAutomatorModel(xmlDumpFile);
    } catch (Exception e) {
      String msg = "Error while parsing UI hierarchy XML file: " + e.getMessage();
      throw new UiAutomatorException(msg, e);
    }
monitor.subTask("Obtaining device screenshot");
    RawImage rawImage;
    try {
      rawImage = device.getScreenshot();
    } catch (Exception e) {
      String msg = "Error taking device screenshot: " + e.getMessage();
      throw new UiAutomatorException(msg, e);
    }

    BasicTreeNode root = model.getXmlRootNode();
    if ((root instanceof RootWindowNode)) {
      for (int i = 0; i < ((RootWindowNode)root).getRotation(); i++) {
        rawImage = rawImage.getRotated();
      }
    }
    PaletteData palette = new PaletteData(rawImage.getRedMask(), rawImage.getGreenMask(), rawImage.getBlueMask());

    ImageData imageData = new ImageData(rawImage.width, rawImage.height, rawImage.bpp, palette, 1, rawImage.data);

    ImageLoader loader = new ImageLoader();
    loader.data = new ImageData[] { imageData };
    loader.save(screenshotFile.getAbsolutePath(), 5);
    Image screenshot = new Image(Display.getDefault(), imageData);

    return new UiAutomatorResult(xmlDumpFile, model, screenshot);
  }

5、通过dump出来的层级结构数据文件,来生成树并显示到程序界面中:UiAutomatorView类

package com.android.uiautomator;

import java.io.File;
import java.util.Iterator;

import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Tree;

import com.android.uiautomator.actions.ExpandAllAction;
import com.android.uiautomator.actions.ToggleNafAction;
import com.android.uiautomator.tree.AttributePair;
import com.android.uiautomator.tree.BasicTreeNode;
import com.android.uiautomator.tree.BasicTreeNodeContentProvider;

public class UiAutomatorView extends Composite {

   @SuppressWarnings("unused")
private static final int IMG_BORDER = 2;
   private Composite mScreenshotComposite;
   private StackLayout mStackLayout;
   private Composite mSetScreenshotComposite;
   private Canvas mScreenshotCanvas;
   private TreeViewer mTreeViewer;
   private TableViewer mTableViewer;
   private float mScale = 1.0F;
   private int mDx;
   private int mDy;
   private UiAutomatorModel mModel;
   private File mModelFile;
   private Image mScreenshot;


   public UiAutomatorView(Composite parent, int style) {
      super(parent, 0);
      this.setLayout(new FillLayout());
      SashForm baseSash = new SashForm(this, 256);
      this.mScreenshotComposite = new Composite(baseSash, 2048);
      this.mStackLayout = new StackLayout();
      this.mScreenshotComposite.setLayout(this.mStackLayout);
      this.mScreenshotCanvas = new Canvas(this.mScreenshotComposite, 2048);
      this.mStackLayout.topControl = this.mScreenshotCanvas;
      this.mScreenshotComposite.layout();
      this.mScreenshotCanvas.addMouseListener(new MouseAdapter() {
         public void mouseUp(MouseEvent e) {
            if(UiAutomatorView.this.mModel != null) {
               UiAutomatorView.this.mModel.toggleExploreMode();
               UiAutomatorView.this.redrawScreenshot();
            }

         }
      });
      this.mScreenshotCanvas.setBackground(this.getShell().getDisplay().getSystemColor(22));
      this.mScreenshotCanvas.addPaintListener(new PaintListener() {
         public void paintControl(PaintEvent e) {
            if(UiAutomatorView.this.mScreenshot != null) {
               UiAutomatorView.this.updateScreenshotTransformation();
               Transform t = new Transform(e.gc.getDevice());
               t.translate((float)UiAutomatorView.this.mDx, (float)UiAutomatorView.this.mDy);
               t.scale(UiAutomatorView.this.mScale, UiAutomatorView.this.mScale);
               e.gc.setTransform(t);
               e.gc.drawImage(UiAutomatorView.this.mScreenshot, 0, 0);
               e.gc.setTransform((Transform)null);
               if(UiAutomatorView.this.mModel.shouldShowNafNodes()) {
                  e.gc.setForeground(e.gc.getDevice().getSystemColor(7));
                  e.gc.setBackground(e.gc.getDevice().getSystemColor(7));
                  Iterator<?> rect = UiAutomatorView.this.mModel.getNafNodes().iterator();

                  while(rect.hasNext()) {
                     Rectangle r = (Rectangle)rect.next();
                     e.gc.setAlpha(50);
                     e.gc.fillRectangle(UiAutomatorView.this.mDx + UiAutomatorView.this.getScaledSize(r.x), UiAutomatorView.this.mDy + UiAutomatorView.this.getScaledSize(r.y), UiAutomatorView.this.getScaledSize(r.width), UiAutomatorView.this.getScaledSize(r.height));
                     e.gc.setAlpha(255);
                     e.gc.setLineStyle(1);
                     e.gc.setLineWidth(2);
                     e.gc.drawRectangle(UiAutomatorView.this.mDx + UiAutomatorView.this.getScaledSize(r.x), UiAutomatorView.this.mDy + UiAutomatorView.this.getScaledSize(r.y), UiAutomatorView.this.getScaledSize(r.width), UiAutomatorView.this.getScaledSize(r.height));
                  }
               }

               Rectangle rect1 = UiAutomatorView.this.mModel.getCurrentDrawingRect();
               if(rect1 != null) {
                  e.gc.setForeground(e.gc.getDevice().getSystemColor(3));
                  if(UiAutomatorView.this.mModel.isExploreMode()) {
                     e.gc.setLineStyle(2);
                     e.gc.setLineWidth(1);
                  } else {
                     e.gc.setLineStyle(1);
                     e.gc.setLineWidth(2);
                  }

                  e.gc.drawRectangle(UiAutomatorView.this.mDx + UiAutomatorView.this.getScaledSize(rect1.x), UiAutomatorView.this.mDy + UiAutomatorView.this.getScaledSize(rect1.y), UiAutomatorView.this.getScaledSize(rect1.width), UiAutomatorView.this.getScaledSize(rect1.height));
               }
            }

         }
      });
      this.mScreenshotCanvas.addMouseMoveListener(new MouseMoveListener() {
         public void mouseMove(MouseEvent e) {
            if(UiAutomatorView.this.mModel != null && UiAutomatorView.this.mModel.isExploreMode()) {
               BasicTreeNode node = UiAutomatorView.this.mModel.updateSelectionForCoordinates(UiAutomatorView.this.getInverseScaledSize(e.x - UiAutomatorView.this.mDx), UiAutomatorView.this.getInverseScaledSize(e.y - UiAutomatorView.this.mDy));
               if(node != null) {
                  UiAutomatorView.this.updateTreeSelection(node);
               }
            }

         }
      });
      this.mSetScreenshotComposite = new Composite(this.mScreenshotComposite, 0);
      this.mSetScreenshotComposite.setLayout(new GridLayout());
      final Button setScreenshotButton = new Button(this.mSetScreenshotComposite, 8);
      setScreenshotButton.setText("Specify Screenshot...");
      setScreenshotButton.addSelectionListener(new SelectionAdapter() {
         public void widgetSelected(SelectionEvent arg0) {
            FileDialog fd = new FileDialog(setScreenshotButton.getShell());
            fd.setFilterExtensions(new String[]{"*.png"});
            if(UiAutomatorView.this.mModelFile != null) {
               fd.setFilterPath(UiAutomatorView.this.mModelFile.getParent());
            }

            String screenshotPath = fd.open();
            if(screenshotPath != null) {
               ImageData[] data;
               try {
                  data = (new ImageLoader()).load(screenshotPath);
               } catch (Exception var6) {
                  return;
               }

               if(data.length >= 1) {
                  UiAutomatorView.this.mScreenshot = new Image(Display.getDefault(), data[0]);
                  UiAutomatorView.this.redrawScreenshot();
               }
            }
         }
      });
      SashForm rightSash = new SashForm(baseSash, 512);
      Composite upperRightBase = new Composite(rightSash, 2048);
      upperRightBase.setLayout(new GridLayout(1, false));
      ToolBarManager toolBarManager = new ToolBarManager(8388608);
      toolBarManager.add(new ExpandAllAction(this));
      toolBarManager.add(new ToggleNafAction(this));
      toolBarManager.createControl(upperRightBase);
      this.mTreeViewer = new TreeViewer(upperRightBase, 0);
      this.mTreeViewer.setContentProvider(new BasicTreeNodeContentProvider());
      this.mTreeViewer.setLabelProvider(new LabelProvider());
      this.mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
         public void selectionChanged(SelectionChangedEvent event) {
            BasicTreeNode selectedNode = null;
            if(event.getSelection() instanceof IStructuredSelection) {
               IStructuredSelection selection = (IStructuredSelection)event.getSelection();
               Object o = selection.getFirstElement();
               if(o instanceof BasicTreeNode) {
                  selectedNode = (BasicTreeNode)o;
               }
            }

            UiAutomatorView.this.mModel.setSelectedNode(selectedNode);
            UiAutomatorView.this.redrawScreenshot();
            if(selectedNode != null) {
               UiAutomatorView.this.loadAttributeTable();
            }

         }
      });
      Tree tree = this.mTreeViewer.getTree();
      tree.setLayoutData(new GridData(4, 4, true, true, 1, 1));
      tree.setFocus();
      Composite lowerRightBase = new Composite(rightSash, 2048);
      lowerRightBase.setLayout(new FillLayout());
      Group grpNodeDetail = new Group(lowerRightBase, 0);
      grpNodeDetail.setLayout(new FillLayout(256));
      grpNodeDetail.setText("Node Detail");
      Composite tableContainer = new Composite(grpNodeDetail, 0);
      TableColumnLayout columnLayout = new TableColumnLayout();
      tableContainer.setLayout(columnLayout);
      this.mTableViewer = new TableViewer(tableContainer, 65536);
      Table table = this.mTableViewer.getTable();
      table.setLinesVisible(true);
      this.mTableViewer.setContentProvider(new ArrayContentProvider());
      TableViewerColumn tableViewerColumnKey = new TableViewerColumn(this.mTableViewer, 0);
      TableColumn tblclmnKey = tableViewerColumnKey.getColumn();
      tableViewerColumnKey.setLabelProvider(new ColumnLabelProvider() {
         public String getText(Object element) {
            return element instanceof AttributePair?((AttributePair)element).key:super.getText(element);
         }
      });
      columnLayout.setColumnData(tblclmnKey, new ColumnWeightData(1, 20, true));
      TableViewerColumn tableViewerColumnValue = new TableViewerColumn(this.mTableViewer, 0);
      tableViewerColumnValue.setEditingSupport(new UiAutomatorView.AttributeTableEditingSupport(this.mTableViewer));
      TableColumn tblclmnValue = tableViewerColumnValue.getColumn();
      columnLayout.setColumnData(tblclmnValue, new ColumnWeightData(2, 20, true));
      tableViewerColumnValue.setLabelProvider(new ColumnLabelProvider() {
         public String getText(Object element) {
            return element instanceof AttributePair?((AttributePair)element).value:super.getText(element);
         }
      });
      baseSash.setWeights(new int[]{5, 3});
   }

   private int getScaledSize(int size) {
      return this.mScale == 1.0F?size:(new Double(Math.floor((double)((float)size * this.mScale)))).intValue();
   }

   private int getInverseScaledSize(int size) {
      return this.mScale == 1.0F?size:(new Double(Math.floor((double)((float)size / this.mScale)))).intValue();
   }

   private void updateScreenshotTransformation() {
      Rectangle canvas = this.mScreenshotCanvas.getBounds();
      Rectangle image = this.mScreenshot.getBounds();
      float scaleX = (float)(canvas.width - 4 - 1) / (float)image.width;
      float scaleY = (float)(canvas.height - 4 - 1) / (float)image.height;
      this.mScale = Math.min(scaleX, scaleY);
      this.mDx = (canvas.width - this.getScaledSize(image.width) - 4) / 2 + 2;
      this.mDy = (canvas.height - this.getScaledSize(image.height) - 4) / 2 + 2;
   }

   public void redrawScreenshot() {
      if(this.mScreenshot == null) {
         this.mStackLayout.topControl = this.mSetScreenshotComposite;
      } else {
         this.mStackLayout.topControl = this.mScreenshotCanvas;
      }

      this.mScreenshotComposite.layout();
      this.mScreenshotCanvas.redraw();
   }

   public void setInputHierarchy(Object input) {
      this.mTreeViewer.setInput(input);
   }

   public void loadAttributeTable() {
      this.mTableViewer.setInput(this.mModel.getSelectedNode().getAttributesArray());
   }

   public void expandAll() {
      this.mTreeViewer.expandAll();
   }

   public void updateTreeSelection(BasicTreeNode node) {
      this.mTreeViewer.setSelection(new StructuredSelection(node), true);
   }

   public void setModel(UiAutomatorModel model, File modelBackingFile, Image screenshot) {
      this.mModel = model;
      this.mModelFile = modelBackingFile;
      if(this.mScreenshot != null) {
         this.mScreenshot.dispose();
      }

      this.mScreenshot = screenshot;
      this.redrawScreenshot();
      BasicTreeNode wrapper = new BasicTreeNode();
      wrapper.addChild(this.mModel.getXmlRootNode());
      this.setInputHierarchy(wrapper);
      this.mTreeViewer.getTree().setFocus();
   }

   public boolean shouldShowNafNodes() {
      return this.mModel != null?this.mModel.shouldShowNafNodes():false;
   }

   public void toggleShowNaf() {
      if(this.mModel != null) {
         this.mModel.toggleShowNaf();
      }

   }

   private class AttributeTableEditingSupport extends EditingSupport {

      private TableViewer mViewer;


      public AttributeTableEditingSupport(TableViewer viewer) {
         super(viewer);
         this.mViewer = viewer;
      }

      protected boolean canEdit(Object arg0) {
         return true;
      }

      protected CellEditor getCellEditor(Object arg0) {
         return new TextCellEditor(this.mViewer.getTable());
      }

      protected Object getValue(Object o) {
         return ((AttributePair)o).value;
      }

      protected void setValue(Object arg0, Object arg1) {
      }
   }
}

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值