iT邦幫忙

第 12 屆 iThome 鐵人賽

0
Software Development

30天 Lua重拾筆記系列 第 33

30天Lua重拾筆記33】Java + Lua計算機

這是我前幾年作為學習/練習的例子。

看過與C交互後,接著來看看一個更實際應用的例子。不過不用C,來用Java。

為甚麼呢?Java自帶一個跨平台的視窗開發套組,本身也有豐富的函式庫可用,第三方函式庫也眾多,作為宿主語言是蠻好的標的。不過不直接使用原本的Lua,需要使用LuaJ

其實最初只是因為Android App使用Java開發。而當時Android Studio編譯APK實在太慢,才會有為啥不能先用其他方式寫邏輯、開發,為啥不先嵌入一個腳本語言?才去做的一個練習,之後我使用的套件也確實有用於Android App開發上。
是的,如果你是正在開發Android,不管是以前主要用Java還是後來的Kotlin,今天應該都還是可以使用LuaJ。

LuaJ已經停滯好一段時間了,本次範例的為GitHub上另一個分支。基本上遵循Lua 5.2的規則,也就是本系列有部份是Lua 5.3、5.4的語法並不能使用於之。

目標

本次範例主要目標:一個含有GUI的簡易計算機。其包含:

  • Java提供GUI界面,和部份功能。
  • Lua處理邏輯。
  • 一個擴展方法,能夠改變成品行為。

使用Lua撰寫邏輯

使用Lua的好處之一,在不確定要以和總平台開發前,是可以先使Lua撰寫邏輯,最後在嵌入到某個平台或框架。

使用的全局變數

first_number = 0 -- 累積數字(上個結果)
input_number = 0 -- 當前將處理數字
op           = "" -- 運算操作子(+-*/)

處理數值輸入

最簡單的處理數值輸入的方式,就是當前數字乘十,在加上輸入數字:

function on_number_click(number)
   input_number = input_number*10 + number
end

運算處理

function on_op_click(set_op)
   ------------------------
   -- 處理運算輸入,保存狀態
   ------------------------
   op = set_op  -- 設置運算處理
   first_number = input_number -- 保留數字
   input_number = 0  -- 等待新數字
end

function on_EQ_click()
   ------------------
   -- 處理實際運算
   -- 可以處理 +, -, *, /
   ------------------
   if(op == "+")then
      input_number = first_number + input_number
   elseif(op == "-") then
      input_number = first_number - input_number
   elseif(op == "*") then
      input_number = first_number * input_number
   elseif(op == "/") then
      input_number = first_number / input_number
   else
      error("op is "..op..[[, but want get "*", "+", "-" or "/"]])
   end

   first_number = 0
   op = ""
end

提供擴展性

pcall(dofile,"extend.txt")

Java界面

※ 注意!本文程式內容是拆分說明的,你可能無法分段執行,你可以參考最後附上的完整示例。

GUI.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GUI{
    public static void main(String[] args){
        cal c = new cal();
        c.main();
    }
}

class cal {
    JFrame demo = new JFrame("計算機");

    // JButton exitBtn = new JButton("Exit");
    JPanel panel = new JPanel(new FlowLayout());
    JLabel display = new JLabel("顯示器", SwingConstants.RIGHT);
    JButton[] number = {
        new JButton("1"),
        new JButton("2"),
        new JButton("3"),
        new JButton("4"),
        new JButton("5"),
        new JButton("6"),
        new JButton("7"),
        new JButton("8"),
        new JButton("9"),
        new JButton("0")
    };

    JButton eqBtn   = new JButton("=");
    JButton cBtn    = new JButton("C");
    JButton acBtn   = new JButton("AC");
    JButton addBtn  = new JButton("+");
    JButton minBtn  = new JButton("-");
    JButton divBtn  = new JButton("/");
    JButton douBtn  = new JButton("*");
    JButton backBtn = new JButton("←");
    JButton[] opBtn = {addBtn, minBtn, divBtn, douBtn};

    public void main() {
        demo.setSize(400, 300);
        demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GridBagLayout GBLayout = new GridBagLayout();
        panel.setLayout(new GridBagLayout());

        demo.getContentPane().add(BorderLayout.NORTH, display);
        demo.add(panel);
        int x = 0,y = 1;
        for(JButton btn:number){
            int width = 1,height = 1;
            if (btn.getText().equals("0")){
                x++;
            }
            GridBagConstraints c = new GridBagConstraints();
            c.gridx = x;
            c.gridy = y;
            c.gridwidth = width;
            c.gridheight = height;
            c.weightx = 0.003;
            c.weighty = 0.003;
            c.fill = GridBagConstraints.NONE;
            c.anchor = GridBagConstraints.CENTER;
            panel.add(btn, c);
            x++;
            if (x%3 == 0){
                x = 0;
                y++;
            }
        }
        x = 4;
        y = 1;
        for(JButton btn:opBtn){
            int width = 1,height = 1;
            GridBagConstraints c = new GridBagConstraints();
            c.gridx = x;
            c.gridy = y;
            c.gridwidth = width;
            c.gridheight = height;
            c.weightx = 0.003;
            c.weighty = 0.003;
            c.fill = GridBagConstraints.NONE;
            c.anchor = GridBagConstraints.CENTER;
            panel.add(btn, c);
            y++;
        }
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 2;
        c.gridy = 4;
        c.gridwidth = 1;
        c.gridheight = 1;
        c.weightx = 0.003;
        c.weighty = 0.003;
        c.fill = GridBagConstraints.NONE;
        c.anchor = GridBagConstraints.CENTER;
        panel.add(eqBtn,c);
        c.gridx = 3;
        c.gridy = 0;
        panel.add(cBtn,c);
        c.gridx = 4;
        c.gridy = 0;
        panel.add(acBtn,c);
        c.gridx = 0;
        c.gridy = 4;
        panel.add(backBtn,c);

        //
        demo.setVisible(true);
    }
}

在Java載入LuaJ並綁定事件處理

/****************
  import LuaJ
******************/
import org.luaj.vm2.*;
import org.luaj.vm2.lib.jse.*;

/*******************************/

public void main() {
    /*Loading lua evn*/
    _G.get("require").call(LuaValue.valueOf("cal"));

    for(JButton btn:number){
        final String num = btn.getText();
        btn.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent e){
                    _G.get("on_number_click").call(LuaValue.valueOf(num));
                    updateScreen();
                }
            });
    }

    for(JButton btn:opBtn){
        final String op = btn.getText();
        btn.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent e){
                    _G.get("on_op_click").call(LuaValue.valueOf(op));
                    updateScreen();
                }
            });
    }

    eqBtn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                _G.get("on_EQ_click").call();
                updateScreen();
            }
        });

    acBtn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                _G.get("on_AC_click").call();
                updateScreen();
            }
        });

    cBtn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                _G.get("on_C_click").call();
                updateScreen();
            }
        });

    backBtn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                _G.get("on_back_click").call();
                //JOptionPane.showMessageDialog(null, "test");
                updateScreen();
            }
        });
}

void updateScreen(){
    LuaValue result = _G.get("input_number");
    display.setText(result.toString());
}

完整示例

JAVA-calculator可以看到完整示例,你也可以直接從Release頁面下載來使用看看。

完整示例包含一些開發測試比較方便的函數,還有讓Lua呼叫Java函數的能力的示例。

在後來,我將Lua邏輯移植到Kotlin+JavaFX裡,可以於我的GitHub頁面看到。

本文同步發表於個人網站


上一篇
【30天Lua重拾筆記32】進階議題: LuaRocks & LuaDist
下一篇
【30天Lua重拾筆記34】番外篇: Fengari - 一個JS實現的Lua,運行Lua在瀏覽器內吧!
系列文
30天 Lua重拾筆記36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言