實作對話框(可以點擊的 ScrollView) - Android

Dinah avatar
By Dinah
at 2009-12-18T19:25

Table of Contents

說實話,這是寫 Android 寫到現在被搞最久的一次了。之前大部份都還
滿順手的,遇到的問題 Google 一下也大部份都有人問過,或是提供了解
答的線索。

但這次就不同了,被搞了超久,追了半天才找到問題所在,終於做出我想
要的東西出來了。

簡單來講,為了要實作出理想的 Maidroid 系列,我必須要有一個元件是
用來當做對話框的,就像 AVG 遊戲裡的對話框一樣,是用來讓程式裡的
妺斗們與主人互動的。

在實作上來講,就是要有一個區塊能夠顯示對話,而當使用者按下去時,
能夠顯示下一句的對話。

看起來好像很簡單,用 TextView 就可以做到了,但實際上還有另外一樣
需求,就是當文字超出範圍時,要能夠出現捲軸,讓使用者捲動。

合理的選擇是用 ScrollView 把 TextView 包起來。因為如果單純地使用
TextView 的話,會有很麻煩的效果出現,而且我不知道為什麼,雖然可
以捲動,但 Scroll Bar 就硬是不給我出現。

但這一包,問題就一大堆了。

包話 View.OnClickListener 完全不能用,所以必需自己用其他方式來判
斷是否是 Click 的動作。

本來以為用 OnTouchEventListener 就好了,沒想到用下去,只有當對話
框出現捲軸時才有用,追了好幾天,試了好幾種方法,才終於找到一個非
常不起眼的原因--在 ScrollView 的實作裡頭,如果這個 View 沒有出
現捲軸,他會把除了 ACTION_DOWN 之外的 MotionEvent 全部給吃光光!

就為了這個奇怪的行為,浪費了我好幾天啊!我可以理解如果沒有捲軸你
就不需要處理,可是也沒必要把 Event 吃掉吧!?(淚)

所以,以下終於是正確可動的版本了,另附轉換特效,以及給其他人在最
後一個訊息時可以做出反應的 Inject Method。

順道一題,這是 Scala 的版本,Java 的話請自行轉換吧,反正整個流
程和概念是一樣的。

不過話說回來,我還是不確定這是不是最恰當的實作方式就是了(特別是
在是否為 Click 的判斷上),但至少它可以動。XD

[CODE]

import android.content.Context

import android.view.animation.Animation
import android.view.animation.Animation.AnimationListener
import android.view.animation.AnimationUtils._

import android.view.View
import android.view.MotionEvent

import android.widget.ScrollView
import android.widget.TextView

import android.util.AttributeSet
import android.util.Log

class ViewMessageBox (context: Context, attrs: AttributeSet) extends
ScrollView (context, attrs)
{
// Call Initialzation when construct
init ()

/*=================================================================
* Private Fields
*===============================================================*/
private lazy val MAX_OFFSET = 1
private lazy val textView = new TextView (context)
private var messageList: List[String] = Nil

/*=================================================================
* Initialization Routine
*===============================================================*/
private def init ()
{
textView.setTextSize (20)
addView (textView)

this.setOnTouchListener (onTouchListener)
}

private def switchToMessage (message: String, next: List[String])
{
def updateTextView (message: String)
{
val ARROW = "\u25bc"
val DIAMOND = "\u25c6"
val endSymbol = if (next == Nil) DIAMOND else ARROW

textView.setText (message + endSymbol)
}

def prepareAnimation (): Animation =
{
val fadeIn = loadAnimation (context, android.R.anim.fade_in)
val fadeOut = loadAnimation (context, android.R.anim.fade_out)

fadeOut.setAnimationListener (new AnimationListener () {
def onAnimationEnd (animation: Animation) {
updateTextView (message)
textView.startAnimation (fadeIn)
}

def onAnimationRepeat (animation: Animation) {}
def onAnimationStart (animation: Animation) {}
})

fadeOut
}

val animation = prepareAnimation ()
textView.startAnimation (animation)
}

/*=================================================================
* Private Methods
*===============================================================*/

/**
* 用來檢查是否有 ScrollBar 出現,測試的標準是當 TextView 比
* ScrollView 來的大時,就當做有 ScrollBar。
*/
private def hasScrollBar = textView.getHeight > this.getHeight

/*=================================================================
* Callbacks
*===============================================================*/

/**
* 重點在這裡。
*
* Click 的定義:壓下與放開的點誤差不超過 1
*/
private lazy val onTouchListener = new View.OnTouchListener ()
{
// 壓下起始座標
var startX = 0.0
var startY = 0.0

def onTouch (view: View, event: MotionEvent): Boolean = {

// 如果沒有 ScrollBar 出現,除了 ACTION_DOWN 之外
// 的 Event 都會被吃掉,所以直接當做 Click 處理。
if (!hasScrollBar) {
showNextMessage ()
return false
}

// 如果有 ScrollBar,先記錄起始下壓點在哪,如果按下
// 與放下的點 X 與 Y 軸均不超過 MAX_OFFSET ,就當做
// 是 Click 事件。
event.getAction match {
case MotionEvent.ACTION_DOWN =>
startX = event.getX
startY = event.getY

case MotionEvent.ACTION_UP =>
if (Math.abs(startX - event.getX) < MAX_OFFSET &&
Math.abs(startY - event.getY) < MAX_OFFSET) {
showNextMessage ()
}

case _ =>
}

return false
}
}

/*=================================================================
* Public Field / Method
*===============================================================*/

// 當最後一個訊息被顯示時會呼叫這個函式
var injectWhenLast: () => Unit = () => {}

def setMessageList (message:List[String])
{
messageList = message
showNextMessage ()
}

def showNextMessage ()
{
// 將 TextView 設成 List 裡的第一個字串
messageList match {
case message :: next =>
switchToMessage (message, next)

if (next == Nil) {
injectWhenLast ()
}

messageList = next

case _ =>
}
}
}
[/CODE]

--
~ 白馬帶著她一步步地回到中原。白馬已經老了,只能慢慢地走,
'v' Brian Hsu 但終是能回到中原的。江南有楊柳、桃花,有燕子、金魚……
// \\ ( 墳 墓 )
/( )\ 但這個美麗的姑娘就像古高昌國人那樣固執。 【白馬嘯西風】
^`~'^
http://bone.twbbs.org.tw/blog 『那都是很好很好的,可我偏不喜歡。』

--
Tags: Android

All Comments

Elvira avatar
By Elvira
at 2009-12-21T09:49
有心推
Suhail Hany avatar
By Suhail Hany
at 2009-12-23T08:12
看不懂推
Hazel avatar
By Hazel
at 2009-12-24T01:56
moe~(咦

請教 Android 版本更新...

Tracy avatar
By Tracy
at 2009-12-10T10:27
借題發揮 我今天打開HERO他跟我說要更新,軟體大小約3.6MB 還以為要昇2.0了XDand#34;... 不過好像只是修正瀏覽器的BIG5顯示,水平校正之類的 http://www.htc.com/tw/support.aspx ※ 引述《bananashot (香蕉球)》之銘言: : 不好 ...

Dolphin Browser 最好用的瀏覽器

Franklin avatar
By Franklin
at 2009-11-25T03:05
Dolphin Browser 最好用的瀏覽器 這麼說一點都不過分 http://tinyurl.com/yck5a2a 上面網址有QR碼 也有影片的介紹 我在這裡簡單說一下 這瀏覽器的功能挺強大的 首先 光是支援滑鼠手勢 這點就方便太多了 無論你要叫出書籤 或是把正在瀏覽的網頁加入書籤都很方便 ...

AttachEmail: 從SD卡中加檔案到Email附件

Lily avatar
By Lily
at 2009-11-18T00:43
(不知道為什麼系統沒直接提供這功能) 晚上花了幾個鐘頭做了一版出來, 大家有需要可以到 Market 下載. 搜尋 AttachEmail - ...

EBAndroid - Android 使用的 EPWing 格式字典

Rosalind avatar
By Rosalind
at 2009-11-10T11:21
這是一篇廣告文... ================= 由 Dell Axim 51V 開始我便使用 Windows Mobile 系統作個人記事系統。那時候剛好開 始學日文,千辛萬苦的找來了一個可用在 WM 的日文辭典軟體 -- EBPocket。 前些日子跳了進 Android 的陷阱,卻翻身不能 ...

Android SDK 2.0 發佈了

Doris avatar
By Doris
at 2009-10-29T17:43
今天上Android官網發現2.0的SDK已經發佈了 有新增新的功能與API,給有需要開發的同好 我個人更期待搭配Android SDK 2.0的智慧型手機 可能年底才會推出 附上網址給各位參考 http://developer.android.com/intl/zh-TW/sdk/android- ...