現(xiàn)在 kotlin 用的較多的領(lǐng)域,應(yīng)該是 android 應(yīng)用開發(fā)了吧。最近開了個(gè)小差,有人買彩票不想自己預(yù)測了,也不想現(xiàn)場用彩票店的機(jī)器出號,太費(fèi)事兒。就用 kotlin 寫個(gè)手機(jī) apk 給他。要求也不高,只要隨機(jī)出數(shù)字就行。
需求搞清楚了,可以開工了。
首先要確定,他可能還要給自己的舊手機(jī)也裝上,為求萬千穩(wěn)妥,API 的最小版本選擇了 15。
之后選擇空的 Activity 就開場了。
系統(tǒng)默認(rèn)給出一個(gè)界面。
我們不理他,因?yàn)椋€計(jì)劃著以后再繼續(xù)在這個(gè)項(xiàng)目中萬一增加其他小工具呢。
所以新建一個(gè)package,取名就用漢語拼音吧。CaiPiao
然后在這個(gè) package 下新建一個(gè)空活動(dòng)(Empty Activity)
下面,我們要做的第一件事,就是讓第一個(gè)活動(dòng)(MainActivity)被應(yīng)用啟動(dòng)后,直接跳轉(zhuǎn)到我們這個(gè) CaiPiao 下的空活動(dòng)來。
打開 MainActivity ,添加代碼。先寫一個(gè)函數(shù) OpenCaiPiao ,在函數(shù)內(nèi)實(shí)現(xiàn)需要的功能。
//跳轉(zhuǎn)到彩票界面
fun OpenCaiPiao(){//我要順順的來
val intent = Intent()
intent.setClass(this, CaipiaoActivity::class.java)
startActivity(intent)
}
把它放在 class MainActivity 內(nèi),并在 onCreate 函數(shù)內(nèi)添加調(diào)用語句 OpenCaiPiao()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
OpenCaiPiao()
}
這時(shí)候,我們的注意力就可以專注到真正的業(yè)務(wù)模塊中了。
在 res>layout 中打開 activity_caipiao.xml,拖拽三個(gè) button 和 兩個(gè) textview。
一個(gè) button 對應(yīng)“雙色球”
一個(gè) button 對應(yīng)“大樂透”
一個(gè) button 對應(yīng)“清除”功能
一個(gè) textview 對應(yīng)給出的彩票號碼
一個(gè) textview 對應(yīng)給出彩票號碼的時(shí)間
現(xiàn)在流行最新的布局方式,也是默認(rèn)的ConstraintLayout。我就用這種了。
這個(gè)布局中,為了保持“隨機(jī)號碼”和“出號時(shí)間”的左對齊,只設(shè)定了上邊和左邊的距離,它們字號不同,控制了右邊距離的話,文字內(nèi)容發(fā)生改變,會(huì)有對不齊的情況發(fā)生。
雙色球和大樂透兩個(gè)按鈕,略有不同,雙色球設(shè)置了上、左、右到邊沿,而大樂透的左邊是指在雙色球按鈕的右側(cè)控鍵上。這樣,兩個(gè)按鈕的位置就相對固定了。不論橫屏還是豎屏都不會(huì)變。
相似的“清除按鈕”的上邊constraint也控制到雙色球按鈕的下側(cè)控鍵上,這樣“清除按鈕”和“雙色球按鈕”的相對位置也就保持不變了。
下面就該填寫這個(gè)Activity中的代碼了。
界面中可以直接操作就是這三個(gè)按鈕了,先在 CaiPiaoActivity.kt 的 OnCreate 中增加這三個(gè)按鈕的 OnClick 事件的監(jiān)聽。
//雙色球
btnGetShuangSeQiu.setOnClickListener {
}
//大樂透
btnDaLeTou.setOnClickListener {
}
//清除取消
btnCancel.setOnClickListener {
}
隨機(jī)出彩票號碼,那么核心功能是隨機(jī)了。先寫一個(gè)在指定的范圍內(nèi)出隨機(jī)數(shù)字的函數(shù)出來。
/**
* 取得min到max之間的隨機(jī)整數(shù)(不包含max)
* @min 最小取值
* @max 最大取值邊界(此值不取)
* */
fun cofoxRandom(min: Int = 0, max: Int): Int {
var t_min = min
var t_max = max
if (t_min > t_max) {
var temp: Int
temp = t_min
t_min = t_max
t_max = temp
}
var random = (Math.floor(Math.random() * (t_max - t_min))).toInt() + t_min
return random
}
這里要注意這個(gè)注釋的寫法
/**
*
* */
這樣標(biāo)準(zhǔn)的寫法能夠被 IDE 準(zhǔn)確的識別,才能有下面的效果。
我們要寫雙色球的隨機(jī)出號了
首先定義一個(gè)整數(shù)數(shù)組,先都賦值為 0 好了。
var ints = intArrayOf(0, 0, 0, 0, 0, 0, 0)
當(dāng)然你也可以這樣寫
var ints = Array(7,{0})
第一種寫法,是方便直接把所有的數(shù)組元素(不同值)都寫進(jìn)去。
第二種寫法,更簡潔一些,適合初始化時(shí)候都賦相同值的情況。
建立了數(shù)組,就該把隨機(jī)數(shù)字填寫進(jìn)去了。
for (i in 1..6) {
ints[i - 1] = cofoxRandom(1, 34)
}
雙色球是 6 + 1 的出球方式。我們先出 6 個(gè)紅球,所以是 1..6。
由于 cofoxRandom 函數(shù)的max值是不包含在可選范圍內(nèi)的,所以1-33個(gè)紅球號碼,我們需要的參數(shù)是(1, 34)
至于 ints[i-1],是因?yàn)椋瑪?shù)組的下標(biāo)是從 0 開始的。
這樣就可以把 6 個(gè)球號得到了。
但是,還有個(gè)問題,數(shù)字是亂的。我們需要給這 6 個(gè)數(shù)字排序。這也是一個(gè)很常見的需求。
//排序前六位,第七位當(dāng)臨時(shí)存儲(chǔ)位
for (i in ints.indices) {
for (j in ints.indices - i) {
if (j < ints.size - 1) {
if (ints[j] > ints[j + 1]) {
ints[ints.size - 1] = ints[j + 1]
ints[j + 1] = ints[j]
ints[j] = ints[ints.size - 1]
}
}
}
}
這里,我們?yōu)榱斯?jié)省內(nèi)存,就不再另外加變量了。先利用那個(gè)藍(lán)色號碼的位置,作為排序時(shí)的臨時(shí)存儲(chǔ)位。每次循環(huán),都把較大的一個(gè)數(shù)字往后移動(dòng)一位。于是,在第二層循環(huán),每次循環(huán)都可以少循環(huán)一個(gè)已經(jīng)確定了的較大數(shù)字。這就是
for (i in ints.indices) {
for (j in ints.indices - i) {
這個(gè)的來由。順便說一下,indices是取得ints下標(biāo)的意思。這樣i和j就能得到當(dāng)前循環(huán)的具體下標(biāo)了。
循環(huán)完,一個(gè)由小到大排列好的數(shù)組就出來了。
如果想完美,還有一個(gè)問題要解決,重復(fù)數(shù)字。隨機(jī)給出的數(shù)字經(jīng)常是重復(fù)的。只要再加一個(gè)邏輯,當(dāng)新的隨機(jī)數(shù)出來后,判斷一下在數(shù)組中是否存在這個(gè)數(shù)字了即可。如果存在,當(dāng)前循環(huán)內(nèi)再次取得隨機(jī)數(shù),直到取得了不在數(shù)組中的數(shù)字為止。
我們可以用 for 循環(huán)嵌套 while 循環(huán)實(shí)現(xiàn)。我們來修改剛才那個(gè) 隨機(jī)數(shù)字填寫 的 for 循環(huán)。
for (i in 1..6) {
// ints[i - 1] = cofoxRandom(1, 34)
var temp = cofoxRandom(1, 34)
while (temp in ints){
temp = cofoxRandom(1, 34)
}
ints[i - 1] = temp
}
現(xiàn)在,數(shù)組內(nèi)的號碼球數(shù)字不會(huì)重復(fù)了。
雙色球還有一個(gè)藍(lán)色號碼求哦,這個(gè)就直接出好了。
ints[ints.size-1] = cofoxRandom(1, 17)//藍(lán)色號碼球
數(shù)字都出完了。我們還可以來調(diào)一調(diào)數(shù)字格式。因?yàn)椋幸晃粩?shù)也有兩位數(shù)嘛,排列總是看起來有點(diǎn)不夠整齊。那就一位數(shù)的都在左側(cè)補(bǔ)0吧。不僅如此,每個(gè)數(shù)字之間用逗號(,)分隔開,再把紅球和藍(lán)球用短橫線(-)分隔開。
//按格式書寫
var str = "雙色球:"
for (i in ints.indices) {
if (ints[i].toString().length < 2){
str += "0"+ints[i].toString()
}else {
str += ints[i]
}
if (i == ints.size-2){
str += " - "
}else if (i == ints.size -1){
}else{
str += ", "
}
}
好了。重要的代碼都寫完了。剩下就是把 str 賦值給界面控件了吧?
還可以再多做一點(diǎn)事情。不要把這么多的代碼都放進(jìn) onCreate ,獨(dú)立出一個(gè)函數(shù)來。讓代碼更整潔。于是,我們可以得到一個(gè)“福彩雙色球隨機(jī)猜想出號”函數(shù)了。
/**
* 福彩雙色球隨機(jī)猜想出號
* */
fun cofoxShuangSeQiu(ttvw:TextView){
// var ints = intArrayOf(0, 0, 0, 0, 0, 0, 0)
var ints = Array(7,{0})
for (i in 1..6) {
// ints[i - 1] = cofoxRandom(1, 34)
var temp = cofoxRandom(1, 34)
while (temp in ints){
temp = cofoxRandom(1, 34)
}
ints[i - 1] = temp
}
//排序前六位,第七位當(dāng)臨時(shí)存儲(chǔ)位
for (i in ints.indices) {
for (j in ints.indices - i) {
if (j < ints.size - 1) {
if (ints[j] > ints[j + 1]) {
ints[ints.size - 1] = ints[j + 1]
ints[j + 1] = ints[j]
ints[j] = ints[ints.size - 1]
}
}
}
}
ints[ints.size-1] = cofoxRandom(1, 17)//藍(lán)色號碼球
//按格式書寫
var str = "雙色球:"
for (i in ints.indices) {
if (ints[i].toString().length < 2){
str += "0"+ints[i].toString()
}else {
str += ints[i]
}
if (i == ints.size-2){
str += " - "
}else if (i == ints.size -1){
}else{
str += ", "
}
}
ttvw.text = str
}
而 onCreate 中對這個(gè)函數(shù)進(jìn)行調(diào)用。
cofoxShuangSeQiu(ttvwRandomNumber)
ttvwRandomNumber 就是 textview 的 id
在界面上為了更友好一些,我們增加了出號時(shí)間。現(xiàn)在就是添加這個(gè)功能代碼的時(shí)候了。
/**
* 顯示當(dāng)前出號時(shí)間
* */
fun cofoxGetNumberTime(ttvw: TextView){
//出號時(shí)間
var ver = android.os.Build.VERSION.SDK_INT
if (ver >= 24){
ttvw.text = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())
}else{
val calendar = GregorianCalendar()
ttvw.text = GregorianCalendar().get(Calendar.YEAR).toString() +"-"+ (GregorianCalendar().get(Calendar.MONTH)+1).toString() +"-"+ GregorianCalendar().get(Calendar.DAY_OF_MONTH).toString() +" "+ GregorianCalendar().get(Calendar.HOUR).toString() +":"+ GregorianCalendar().get(Calendar.MINUTE).toString() +":"+ GregorianCalendar().get(Calendar.SECOND).toString()
}
}
好吧,這也是寫成了一個(gè)函數(shù)。并且其中用了兩種取得當(dāng)前時(shí)間的方法。因?yàn)椋覀冮_始的時(shí)候,希望這個(gè)應(yīng)用能支持到 API 15,而最簡潔最新的時(shí)間獲取的寫法是從 API 24 之后才開才是支持的。
那么先判斷一下當(dāng)前運(yùn)行的 android 系統(tǒng)的 API 是哪個(gè)版本,然后對癥下藥。
之后,我們再把調(diào)用語句寫入 onCreate 中。
//雙色球
btnGetShuangSeQiu.setOnClickListener {
cofoxShuangSeQiu(ttvwRandomNumber)
cofoxGetNumberTime(ttvwTime) //出號時(shí)間
}
雙色球的調(diào)用完成了。
大樂透只是稍有不同
大樂透的號碼球一共也是 7 個(gè),但是,大樂透的玩法是分前區(qū)和后區(qū)。是 5 + 2 的結(jié)構(gòu)。所以,在雙色球的代碼基礎(chǔ)上略微修改就可以獲得大樂透的寫法了。
/**
* 體彩大樂透隨機(jī)猜想出號
* */
fun cofoxDaLeTou(ttvw:TextView){
// var ints = intArrayOf(0, 0, 0, 0, 0, 0, 0)
var ints = Array(7,{0})
for (i in 1..5) {
// ints[i - 1] = cofoxRandom(1, 36)
var temp = cofoxRandom(1, 36)
while (temp in ints){
temp = cofoxRandom(1, 36)
}
ints[i - 1] = temp
}
//排序前5位,第6位當(dāng)臨時(shí)存儲(chǔ)位
for (i in ints.indices - 2) {
for (j in ints.indices - i) {
if (j < ints.size - 2) {
if (ints[j] > ints[j + 1]) {
ints[ints.size - 2] = ints[j + 1]
ints[j + 1] = ints[j]
ints[j] = ints[ints.size - 2]
}
}
}
}
ints[ints.size-2] = cofoxRandom(1, 13)//后區(qū)號碼球1
ints[ints.size-1] = cofoxRandom(1, 13)//后區(qū)號碼球2
//按格式書寫
var str = "大樂透:"
for (i in ints.indices) {
if (ints[i].toString().length < 2){
str += "0"+ints[i].toString()
}else {
str += ints[i]
}
if (i == ints.size-3){
str += " - "
}else if (i == ints.size -1){
}else{
str += ", "
}
}
ttvw.text = str
}
這時(shí)候,在 onCreate 中修改大樂透的調(diào)用代碼就簡單多了。
//大樂透
btnDaLeTou.setOnClickListener {
cofoxDaLeTou(ttvwRandomNumber)
cofoxGetNumberTime(ttvwTime) //出號時(shí)間
}
還記得我們的“清除”按鈕嗎?是的,也要在 onCreate 中寫上監(jiān)聽代碼。
//清除取消
btnCancel.setOnClickListener {
ttvwRandomNumber.text = resources.getString(R.string.txt_cnword_隨機(jī)號碼)
ttvwTime.text = resources.getText(R.string.txt_出號時(shí)間)
}
這里的 R.string 是什么?它們資源文件 res>values 下 strings.xml 內(nèi)的東西。
我們在這里實(shí)踐了一下編碼中對中文變量的支持。呵呵。
編譯一下