下面是框架,请在lua环境下运行。

require "import"
import "android.widget.*"
import "android.view.*"
import "android.os.*"
import "android.app.*"
import "android.graphics.Typeface"

活动 = activity

布局参数 =
{
  LinearLayout,
  orientation="vertical",
  layout_width="fill",
  layout_height="fill",
  background="#FFF5F5F5",
  {
    TextView,
    id="标题",
    text="不只有晴天 ‖ 测试版",
    textSize="20sp",
    layout_width="fill",
    layout_height="wrap",
    gravity="center",
    textColor="#FF4081",
    padding="8dp"
  },
  {
    LinearLayout,
    id="属性容器",
    orientation="vertical",
    layout_width="fill",
    layout_height="wrap",
    padding="8dp",
    background="#EEEEEE"
  },
  {
    ScrollView,
    layout_width="fill",
    layout_height="0dp",
    layout_weight=1,
    {
      TextView,
      id="剧情文本",
      textSize="16sp",
      layout_width="fill",
      layout_height="wrap",
      padding="16dp",
      textColor="#FF4081"
    }
  },
  {
    ScrollView,
    layout_width="fill",
    layout_height="200dp",
    {
      LinearLayout,
      id="选项容器",
      orientation="vertical",
      layout_width="fill",
      layout_height="wrap",
      padding="8dp"
    }
  }
}

活动.setContentView(loadlayout(布局参数))
标题.setTypeface(nil, Typeface.BOLD)
剧情文本.setLineSpacing(1.2, 1.0)

游戏数据 =
{
  角色列表 =
  {
    林小然 =
    {
      介绍 = "同班同学,性格开朗的阳光男孩,篮球社主力队员,喜欢电子游戏和户外运动",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们几乎没有建立任何联系,毕业后就失去了联络。",
        [30] = "结局:你们保持着普通同学关系,偶尔会在同学聚会上见面。",
        [60] = "结局:你们成为了好朋友,经常一起打球玩游戏。",
        [80] = "结局:林小然对你很有好感,但最终谁都没有表白。",
        [100] = "结局:在一个樱花盛开的日子,林小然红着脸向你表白了!"
      }
    },
    苏雨晴 =
    {
      介绍 = "文学社社长,温柔知性的学姐,喜欢阅读和写作,擅长钢琴",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们始终保持着礼貌的距离,毕业后各奔东西。",
        [30] = "结局:你们偶尔会在图书馆相遇,点头之交。",
        [60] = "结局:你们成为了可以讨论文学的朋友。",
        [80] = "结局:苏雨晴送了你一本她亲手写的诗集。",
        [100] = "结局:毕业典礼上,苏雨晴为你弹奏了一首原创钢琴曲。"
      }
    },
    陈默 =
    {
      介绍 = "沉默寡言的转学生,独来独往,但成绩优异,喜欢天文和摄影",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们几乎没有说过话,毕业后就忘记了彼此。",
        [30] = "结局:你们偶尔会讨论学习问题。",
        [60] = "结局:你们成为了可以一起看星星的朋友。",
        [80] = "结局:陈默送了你一张他拍的星空照片。",
        [100] = "结局:在一个流星雨的夜晚,陈默终于向你敞开心扉。"
      }
    },
    赵明远 =
    {
      介绍 = "学生会主席,品学兼优的学长,擅长演讲和辩论,目标明确",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们始终保持着公事公办的关系。",
        [30] = "结局:你们偶尔会讨论学生会工作。",
        [60] = "结局:你们成为了可以互相提建议的朋友。",
        [80] = "结局:赵明远在毕业演讲中提到了你。",
        [100] = "结局:赵明远放弃了名校offer,选择和你同校。"
      }
    },
    周子轩 =
    {
      介绍 = "音乐才子,乐队主唱,性格张扬不羁,有很多粉丝",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们始终是偶像和粉丝的关系。",
        [30] = "结局:你们偶尔会在音乐教室遇到。",
        [60] = "结局:你们成为了可以讨论音乐的朋友。",
        [80] = "结局:周子轩为你写了一首歌。",
        [100] = "结局:在毕业演唱会上,周子轩当众向你表白。"
      }
    },
    夏小柔 =
    {
      介绍 = "邻家女孩类型,活泼可爱,喜欢烘焙和小动物",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们只是普通的邻居关系。",
        [30] = "结局:你们偶尔会一起上学。",
        [60] = "结局:你们成为了可以分享点心的小伙伴。",
        [80] = "结局:夏小柔每天都会给你带自制点心。",
        [100] = "结局:在一个雨天,夏小柔红着脸递给你心形饼干。"
      }
    },
    叶清 =
    {
      介绍 = "美术特长生,气质清冷,喜欢独处写生,擅长国画",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们几乎没有交集。",
        [30] = "结局:你们偶尔会在美术室遇到。",
        [60] = "结局:你们成为了可以讨论艺术的朋友。",
        [80] = "结局:叶清送了你一幅她画的肖像。",
        [100] = "结局:在毕业画展上,叶清的作品全是你的身影。"
      }
    },
    韩雪 =
    {
      介绍 = "学霸型女生,冷静理性,目标是医学院,喜欢解谜游戏",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们只是普通的同学关系。",
        [30] = "结局:你们偶尔会讨论学习问题。",
        [60] = "结局:你们成为了可以一起解题的伙伴。",
        [80] = "结局:韩雪为你设计了一个专属谜题。",
        [100] = "结局:在毕业典礼上,韩雪解开了自己设下的心结。"
      }
    },
    林初夏 =
    {
      介绍 = "体育特长生,田径队主力,性格爽朗,像个假小子",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们几乎没有接触。",
        [30] = "结局:你们偶尔会在操场遇到。",
        [60] = "结局:你们成为了可以一起跑步的朋友。",
        [80] = "结局:林初夏在比赛后把奖牌送给了你。",
        [100] = "结局:在终点线前,林初夏停下脚步牵起了你的手。"
      }
    },
    陆远 =
    {
      介绍 = "校篮球队队长,阳光帅气,有很多追求者,但很专一",
      属性 =
      {
        好感度 = 0,
        了解度 = 0,
        亲密度 = 0,
        心动值 = 0,
        依赖度 = 0,
        信任度 = 0,
        默契度 = 0,
        回忆度 = 0,
        专属度 = 0
      },
      结局 =
      {
        [0] = "结局:你们只是普通的校友关系。",
        [30] = "结局:你们偶尔会在篮球场遇到。",
        [60] = "结局:你们成为了可以一起打球的朋友。",
        [80] = "结局:陆远在比赛中专门为你投进了一个三分球。",
        [100] = "结局:在夺冠的瞬间,陆远当众向你表白。"
      }
    }
  },
  基础属性 =
  {
    体力 = 100,
    精力 = 100,
    心情 = 80,
    零花钱 = 1000,
    学业 = 60,
    魅力 = 40,
    气质 = 30,
    勇气 = 30,
    智慧 = 50
  },
  当前角色 = nil,
  选择记录 = {},
  场景顺序 = {},
  当前场景 = nil,
  日期 = 1
}

剧情配置=
{
  初始化=
  {
    文本="欢迎来到不只有晴天,请选择你要攻略的角色。",
    选项=
    {
      {
        文本="林小然-阳光开朗的同班同学-男性",
        跳转="林小然_开始",
        效果=function()
          游戏数据.角色列表.林小然.属性.了解度=5
          游戏数据.当前角色 = "林小然"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略林小然,一个阳光开朗的同班同学"
      },
      {
        文本="苏雨晴-温柔知性的学姐-女性",
        跳转="苏雨晴_开始",
        效果=function()
          游戏数据.角色列表.苏雨晴.属性.了解度=5
          游戏数据.当前角色 = "苏雨晴"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略苏雨晴,一个温柔知性的学姐"
      },
      {
        文本="陈默-沉默寡言的转学生-男性",
        跳转="陈默_开始",
        效果=function()
          游戏数据.角色列表.陈默.属性.了解度=5
          游戏数据.当前角色 = "陈默"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略陈默,一个沉默寡言的转学生"
      },
      {
        文本="赵明远-学生会主席学长-男性",
        跳转="赵明远_开始",
        效果=function()
          游戏数据.角色列表.赵明远.属性.了解度=5
          游戏数据.当前角色 = "赵明远"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略赵明远,学生会主席学长"
      },
      {
        文本="周子轩-音乐才子-男性",
        跳转="周子轩_开始",
        效果=function()
          游戏数据.角色列表.周子轩.属性.了解度=5
          游戏数据.当前角色 = "周子轩"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略周子轩,音乐才子"
      },
      {
        文本="夏小柔-活泼可爱的邻家女孩-女性",
        跳转="夏小柔_开始",
        效果=function()
          游戏数据.角色列表.夏小柔.属性.了解度=5
          游戏数据.当前角色 = "夏小柔"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略夏小柔,活泼可爱的邻家女孩"
      },
      {
        文本="叶清-美术特长生-女性",
        跳转="叶清_开始",
        效果=function()
          游戏数据.角色列表.叶清.属性.了解度=5
          游戏数据.当前角色 = "叶清"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略叶清,美术特长生"
      },
      {
        文本="韩雪-学霸型女生-女性",
        跳转="韩雪_开始",
        效果=function()
          游戏数据.角色列表.韩雪.属性.了解度=5
          游戏数据.当前角色 = "韩雪"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略韩雪,学霸型女生"
      },
      {
        文本="林初夏-体育特长生-女性",
        跳转="林初夏_开始",
        效果=function()
          游戏数据.角色列表.林初夏.属性.了解度=5
          游戏数据.当前角色 = "林初夏"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略林初夏,体育特长生"
      },
      {
        文本="陆远-校篮球队队长-男性",
        跳转="陆远_开始",
        效果=function()
          游戏数据.角色列表.陆远.属性.了解度=5
          游戏数据.当前角色 = "陆远"
          table.insert(游戏数据.场景顺序, "初始化")
        end,
        分析="你决定攻略陆远,校篮球队队长"
      }
    }
  },
}

当前剧情 = nil
剧情索引 = 1
剧情字数 = 0
剧情句子 = ""
剧情处理器 = Handler()
选项按钮 = {}

function 显示结局报告()
  if not 游戏数据.当前角色 or not 游戏数据.角色列表[游戏数据.当前角色] then
    local 提示 = AlertDialog.Builder(活动)
    提示.setTitle("提示")
    提示.setMessage("游戏数据异常,请重新开始")
    提示.setPositiveButton("确定",nil)
    提示.show()
    return
  end
  local 角色名 = 游戏数据.当前角色
  local 角色数据 = 游戏数据.角色列表[角色名]
  local 好感度 = 角色数据.属性.好感度 or 0
  local 评价等级 = math.min(100, math.floor(好感度/20)*20)
  local 对话框 = AlertDialog.Builder(活动)
  local 视图 = LinearLayout(活动)
  视图.setOrientation(LinearLayout.VERTICAL)
  视图.setPadding(32,32,32,32)
  local 标题 = TextView(活动)
  标题.setText("【"..角色名.."路线完整分析报告】")
  标题.setTextSize(18)
  标题.setTextColor(0xFFFF4081)
  标题.setTypeface(nil, Typeface.BOLD)
  标题.setGravity(Gravity.CENTER)
  视图.addView(标题)
  local 滚动视图 = ScrollView(活动)
  滚动视图.setLayoutParams(LinearLayout.LayoutParams(
  ViewGroup.LayoutParams.MATCH_PARENT,
  ViewGroup.LayoutParams.WRAP_CONTENT))
  local 内容布局 = LinearLayout(活动)
  内容布局.setOrientation(LinearLayout.VERTICAL)
  local 属性文本 = TextView(活动)
  属性文本.setText("\n最终属性值:\n")
  属性文本.setTextSize(14)
  属性文本.setTypeface(nil, Typeface.BOLD)
  内容布局.addView(属性文本)
  for 属性名, 值 in pairs(角色数据.属性) do
    local 属性行 = TextView(活动)
    属性行.setText(属性名..": "..值)
    属性行.setTextSize(12)
    属性行.setPadding(0,0,0,4)
    内容布局.addView(属性行)
  end
  local 结局文本 = TextView(活动)
  结局文本.setText("\n\n最终结局:\n"..(角色数据.结局[评价等级] or "你们的关系发展到了一个特别的阶段。"))
  结局文本.setTextSize(14)
  内容布局.addView(结局文本)
  local 分析标题 = TextView(活动)
  分析标题.setText("\n\n详细选择分析:")
  分析标题.setTextSize(14)
  分析标题.setTypeface(nil, Typeface.BOLD)
  内容布局.addView(分析标题)
  for _, 场景名 in ipairs(游戏数据.场景顺序) do
    if 游戏数据.选择记录[场景名] then
      local 场景 = 剧情配置[场景名]
      if 场景 and 场景.选项 then
        for _, 选项 in ipairs(场景.选项) do
          if 选项.文本 == 游戏数据.选择记录[场景名] and 选项.分析 then
            local 分析文本 = TextView(活动)
            分析文本.setText("\n"..场景名..": "..选项.文本.."\n"..选项.分析)
            分析文本.setTextSize(12)
            分析文本.setPadding(0,0,0,16)
            内容布局.addView(分析文本)
          end
        end
      end
    end
  end
  滚动视图.addView(内容布局)
  视图.addView(滚动视图)
  对话框.setView(视图)
  对话框.setPositiveButton("重新开始",{
    onClick=function()
      游戏数据.当前角色 = nil
      游戏数据.选择记录 = {}
      游戏数据.场景顺序 = {}
      游戏数据.日期 = 1
      游戏数据.基础属性 =
      {
        体力 = 100,
        精力 = 100,
        心情 = 80,
        零花钱 = 1000,
        学业 = 60,
        魅力 = 40,
        气质 = 30,
        勇气 = 30,
        智慧 = 50
      }
      for 角色名, 角色 in pairs(游戏数据.角色列表) do
        角色.属性.好感度 = 0
        角色.属性.了解度 = 0
        角色.属性.亲密度 = 0
        角色.属性.心动值 = 0
        角色.属性.依赖度 = 0
        角色.属性.信任度 = 0
        角色.属性.默契度 = 0
        角色.属性.回忆度 = 0
        角色.属性.专属度 = 0
      end
      显示剧情("初始化")
  end})
  对话框.setNegativeButton("退出",{
    onClick=function()
      活动.finish()
  end})
  对话框.show()
end

function 更新属性显示()
  属性容器.removeAllViews()
  if not 游戏数据.当前角色 then return end
  local 基础布局 = LinearLayout(活动)
  基础布局.setOrientation(LinearLayout.VERTICAL)
  基础布局.setLayoutParams(LinearLayout.LayoutParams(
  ViewGroup.LayoutParams.MATCH_PARENT,
  ViewGroup.LayoutParams.WRAP_CONTENT))
  local 基础标题 = TextView(活动)
  基础标题.setText("【基础属性】")
  基础标题.setTextSize(14)
  基础标题.setTypeface(nil, Typeface.BOLD)
  基础标题.setPadding(0,0,0,8)
  基础布局.addView(基础标题)
  local 基础行布局 = LinearLayout(活动)
  基础行布局.setOrientation(LinearLayout.HORIZONTAL)
  local 基础计数 = 0
  for 属性名, 值 in pairs(游戏数据.基础属性) do
    local 属性视图 = TextView(活动)
    属性视图.setText(属性名..": "..值)
    属性视图.setTextSize(12)
    属性视图.setPadding(8,0,8,8)
    属性视图.setTextColor(0xFF3399FF)
    属性视图.setLayoutParams(LinearLayout.LayoutParams(
    ViewGroup.LayoutParams.WRAP_CONTENT,
    ViewGroup.LayoutParams.WRAP_CONTENT,1))
    基础行布局.addView(属性视图)
    基础计数 = 基础计数 + 1
    if 基础计数 % 3 == 0 then
      基础布局.addView(基础行布局)
      基础行布局 = LinearLayout(活动)
      基础行布局.setOrientation(LinearLayout.HORIZONTAL)
    end
  end
  if 基础计数 % 3 ~= 0 then
    基础布局.addView(基础行布局)
  end
  属性容器.addView(基础布局)
  local 角色布局 = LinearLayout(活动)
  角色布局.setOrientation(LinearLayout.VERTICAL)
  角色布局.setLayoutParams(LinearLayout.LayoutParams(
  ViewGroup.LayoutParams.MATCH_PARENT,
  ViewGroup.LayoutParams.WRAP_CONTENT))
  角色布局.setPadding(0,16,0,0)
  local 角色标题 = TextView(活动)
  角色标题.setText("【"..游戏数据.当前角色.."】")
  角色标题.setTextSize(14)
  角色标题.setTypeface(nil, Typeface.BOLD)
  角色标题.setPadding(0,0,0,8)
  角色布局.addView(角色标题)
  local 角色行布局 = LinearLayout(活动)
  角色行布局.setOrientation(LinearLayout.HORIZONTAL)
  local 角色计数 = 0
  for 属性名, 值 in pairs(游戏数据.角色列表[游戏数据.当前角色].属性) do
    local 属性视图 = TextView(活动)
    属性视图.setText(属性名..": "..值)
    属性视图.setTextSize(12)
    属性视图.setPadding(8,0,8,8)
    属性视图.setTextColor(0xFFFF99CC)
    属性视图.setLayoutParams(LinearLayout.LayoutParams(
    ViewGroup.LayoutParams.WRAP_CONTENT,
    ViewGroup.LayoutParams.WRAP_CONTENT,1))
    角色行布局.addView(属性视图)
    角色计数 = 角色计数 + 1
    if 角色计数 % 3 == 0 then
      角色布局.addView(角色行布局)
      角色行布局 = LinearLayout(活动)
      角色行布局.setOrientation(LinearLayout.HORIZONTAL)
    end
  end
  if 角色计数 % 3 ~= 0 then
    角色布局.addView(角色行布局)
  end
  属性容器.addView(角色布局)
end

function 随机排序选项(选项表)
  local 随机选项 = {}
  for i,v in ipairs(选项表) do
    随机选项[i] = v
  end
  for i = #随机选项, 2, -1 do
    local j = math.random(i)
    随机选项[i], 随机选项[j] = 随机选项[j], 随机选项[i]
  end
  return 随机选项
end

function 显示剧情(场景名)
  if type(场景名) == "function" then
    场景名 = 场景名()
  end
  游戏数据.当前场景 = 场景名
  当前剧情 = 剧情配置[场景名]
  if not 当前剧情 then
    print("错误:找不到场景 "..tostring(场景名))
    return
  end
  if type(当前剧情.文本) == "function" then
    剧情句子 = 当前剧情.文本()
   else
    剧情句子 = 当前剧情.文本 or ""
  end
  剧情索引 = 1
  剧情字数 = utf8.len(剧情句子)
  剧情文本.setText("")
  local 随机选项 = 随机排序选项(当前剧情.选项 or {})
  更新选项(随机选项)
  if 剧情任务 then
    剧情处理器.removeCallbacks(剧情任务)
  end
  剧情任务 = function()
    if 剧情索引 <= 剧情字数 then
      剧情文本.append(utf8.sub(剧情句子, 剧情索引, 剧情索引))
      剧情索引 = 剧情索引 + 1
      剧情处理器.postDelayed(剧情任务, 40)
    end
  end
  剧情处理器.post(剧情任务)
  更新属性显示()
end

function 更新选项(选项表)
  选项容器.removeAllViews()
  选项按钮 = {}
  for i, 选项 in ipairs(选项表) do
    local 按钮 = Button(活动)
    按钮.setLayoutParams(LinearLayout.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.WRAP_CONTENT))
    按钮.setText(选项.文本 or "选项")
    按钮.setTextSize(14)
    按钮.setPadding(16,16,16,16)
    按钮.setBackgroundResource(android.R.drawable.btn_default)
    按钮.setTextColor(0xFF333333)
    按钮.onClick = function()
      if 选项.效果 and type(选项.效果) == "function" then
        选项.效果()
      end
      if 游戏数据.当前场景 then
        游戏数据.选择记录[游戏数据.当前场景] = 选项.文本
        table.insert(游戏数据.场景顺序, 游戏数据.当前场景)
      end
      if 选项.跳转 then
        if 选项.跳转 == "显示结局报告" then
          显示结局报告()
         else
          显示剧情(选项.跳转)
        end
       else
        活动.finish()
      end
    end
    选项容器.addView(按钮)
    选项按钮[i] = 按钮
  end
end

显示剧情("初始化")