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) {
      }
   }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章