Eclipse Plug-in - How to use GC to build graphics on TableViewer column

這邊要提到的是如何在TableViewer column 以圖形的方式顯示,如以長條圖加上文字來表達!

在此將要利用到的是Graphics Context(GC class)來繪製!

首先要先從如何建置TableViewer來開始,它是在View下面作呈現的

主程式 - TableDisplayView.java
public class TableDisplayView extends ViewPart{
 private TableViewer viewer;
 private String title[] = {"Name", "Price", "Percent Graphics Bar"};
 private int width[] = {150, 150, 600};
 @Override
 public void createPartControl(Composite parent) {
  // TODO Auto-generated method stub
  viewer = new TableViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
  viewer.setContentProvider(new ArrayContentProvider());
  Table table = viewer.getTable();
  
  createColumns();
  
  table.setHeaderVisible(true);
  table.setLinesVisible(true);
  
  ShoppingGenerator generator = new ShoppingGenerator(); 
  viewer.setInput(generator.getProvider());
 }
}

因為是在Eclipse's View下面呈現,因此需繼承相關的類別ViewPart

plugin.xml
<extension point="org.eclipse.ui.views">
    <view
        category="org.eclipse.ui"
        class="com.bin.practice.ui.TableDisplayView"
        id="com.bin.practice.ui.TableDisplayView"
        name="TableDisplayView"
        restorable="true">
    </view>
</extension>


再者,定義要建置的主畫面TableViewer容器

一、資料格式

若setContentProvider內利用ArrayContentProvider,對應到setInput傳入的Object將會是

Array type elements,在此為自訂的bean - Shopping.java及產生器ShoppingGenerator.java

public class Shopping {
 private String name;
 private int price;
 private float percent;
 
 public Shopping(String name, int price) {
  super();
  this.name = name;
  this.price = price;
 }
 public String getName() {
  return name;
 }
 public int getPrice() {
  return price;
 }
 public float getPercent() {
  return percent;
 }
 public void setPercent(float percent) {
  this.percent = percent;
 }
}
public class ShoppingGenerator {
 Shopping shoppings[];
 
 public Shopping[] getProvider(){
  String names[] = {"shoes", "watch", "clothes", "book", "bike"};
  int prices[]   = {1300, 2500, 780, 350, 3600};
  shoppings = new Shopping[names.length];
  
  for(int i = 0 ; i < shoppings.length ; i++){
   shoppings[i] = new Shopping(names[i], prices[i]);
  }
  
  calculatePercent();
  return shoppings;
 }
 
 private void calculatePercent(){
  int total = Stream.of(shoppings).mapToInt(s -> s.getPrice()).sum();
  
  Stream.of(shoppings).forEach(s -> {
   s.setPercent(((float)s.getPrice()/(float)total)*100);
  });
 }
}

二、定義column格式
private void createColumns(){
  for(int i = 0 ; i < title.length ; i++){
   TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
   TableColumn column = viewerColumn.getColumn();
   column.setText(title[i]);
   column.setWidth(width[i]);
   if(i == title.length - 1)
    viewerColumn.setLabelProvider(new DrawGraphicsProvider());
   else
    viewerColumn.setLabelProvider(new TableLabelProvider(title[i]));
  } 
 }

在這邊要注意的是,每個column都有它需要定義的setLabelProvider,要表達的就是這個

column內的資料要以什麼資料來呈現?! 在這邊就要定義清楚!

若是一般的資料請見 - TableLabelProvider.java

若是該欄要呈現圖形資料請見 - DrawGraphicsProvider.java

PS. 以上兩個類別皆為自訂類別,但繼承相關需被衍生的類別

文字或數字資料 - TableLabelProvider.java
public class TableLabelProvider extends ColumnLabelProvider{
 private String title;
 
 public TableLabelProvider(String title){
  this.title = title;
 }
 
 @Override
 public String getText(Object element) {
  if(element instanceof Shopping){
   try {
    Method method = Shopping.class.getDeclaredMethod(getGetterMethod(title));
    Object value = method.invoke(element);    
    return value+"";
   } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
  return "";
 }
 
 public String getGetterMethod(String name){
  return "get"+name.substring(0);
 }
}

在此繼承ColumnLabelProvider,在覆寫getText時傳進來的element即為setInput傳進去的某一

筆Array資料,這個資料在此為Shopping.java;再利用Java Reflect以傳進來的title name來呼叫

Shopping相關method,並以此得到的資料作為getText()回傳!

圖形呈現 - DrawGraphicsProvider.java

這邊要展示的為本篇的重頭戲,如何畫出圖形
public class DrawGraphicsProvider extends OwnerDrawLabelProvider{

 @Override
 protected void measure(Event event, Object element) {
  // TODO Auto-generated method stub
 }
        @Override
 protected void paint(Event event, Object element) {
  // TODO Auto-generated method stub
  if(!(element instanceof Shopping)){
   return;
  }
  
  Shopping node = (Shopping) element;
  
  Table tree = (Table) event.widget;
  //Display display = tree.getDisplay();
  //Color bg = new Color(display.getCurrent(), 218, 224, 222); //background color
  GC gc = event.gc;
  
  int widthcol = tree.getColumn(event.index).getWidth();
  int totalProgBarLength = (int)(widthcol * 0.8); //This makes it grow/shrink dynamically.
  if (totalProgBarLength > 0) {
   //---- Shared elements
   int oriX = event.x;
   int oriY = event.y;
   int barHeight = (int) (event.height * 0.8);
  
   float percent = node.getPercent();
   
   gc.setBackground(getColor(event, SWT.COLOR_DARK_GREEN));
                        gc.fillRectangle(oriX, oriY, totalProgBarLength, barHeight);
   
   //---- Draw dark red bar for price percent
   int progBarRedWidth = getWithByScale(totalProgBarLength, percent);
   gc.setBackground(getColor(event, SWT.COLOR_DARK_RED));
            gc.fillRectangle(oriX, oriY, progBarRedWidth, barHeight);
            
            //---- Draw percent text
            gc.setForeground(getColor(event, SWT.COLOR_BLACK));
            int offsetX = oriX + totalProgBarLength + (int)(widthcol * 0.05);
            int offsetY = oriY;
            gc.drawText(String.valueOf(percent), offsetX, offsetY, true);
            
  }
  //bg.dispose();
 }
 
 private Color getColor(Event event, int color){
  Display display = event.widget.getDisplay();
  return display.getSystemColor(color);
 }
 
 /**
  * add 0.5 is for rounding because convert to int
  * @param originalWith
  * @param percent
  * @return
  */
 private int getWithByScale(int originalWith, float percent){
  return (int) ((originalWith * (percent * 0.01)) + 0.5);
 }
}

繼承OwnerDrawLabelProvider.java後,重新定義paint method

由它傳進來的event可取得Display、GC、Widget等resource,另element即為Shopping

先針對column取得其width

1. 畫取底圖 - 深綠色長條圖

自訂底圖顏色
gc.setBackground(getColor(event, SWT.COLOR_DARK_GREEN));
畫取填滿的長方形,前兩個參數為左上角的起點,再延伸其長度、高度
gc.fillRectangle(oriX, oriY, totalProgBarLength, barHeight);

2. 畫取比例圖 - 深紅色長條圖

底圖畫好後,接下來要畫的就是算出來的percent在此底圖上共佔了多少比例

3. 畫取純文字

文字的部分無設定background表示無底圖顏色
gc.setForeground(getColor(event, SWT.COLOR_BLACK));
第四個參數為true表示底圖是透明的
gc.drawText(String.valueOf(percent), offsetX, offsetY, true);

4. 自訂Color

若要自訂顏色,請自行new Color,但別忘了需自己dispose掉!
Display display = tree.getDisplay();
Color bg = new Color(display.getCurrent(), 218, 224, 222); //background color

三、DEMO

簡單的利用GC來繪製TableViewer上的column圖形在此也算是大功告成!

留言