最近無意間看到了涉及到跑馬燈效果的代碼,於是在網上查閱了很多資料,在這里對自己看的一些文章進行一下總結,順便加上自己的一些體會。
讓我們一步步逐漸向下。
首先我們要實現走馬燈這樣一個效果,通常來說都是在TextView這個控件中來實現的,而且其中的文字一定是單行顯示,如果多行顯示,那走馬燈效果
也就失去了存在的意義。另外,在EditText中使用走馬燈沒有必要,也不合理,實際上對於EditText來說android:ellipsize這個屬性只有對於設置在android:hint中的文字
的時候是有用的,而且android:ellipsize="marquee"這個用法不能用在EditText控件上。對於在EditText用戶輸入的文字,android:ellipsize這個屬性沒有用處。關於EditText
設置android:ellipsize的相關用法以后再講,在這里也算留個標記,以防自己忘了。
在TextView中實現我們的走馬燈效果,需要兩個屬性android:singleLine="true",以及android:ellipsize="marquee",我們來看下面的代碼:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"/>
- </LinearLayout>
運行這段代碼之后,我們會發現走馬燈效果並沒有顯示出來,顯示出的文字是不動的,實際效果如下:
這其中的原因在於跑馬燈效果需要TextVIew獲得當前的焦點(focus)。然而對於TextView這個控件來說,他的默認的Clickable,LongClickable,Focusable,
FocusableInTouchMode這四個屬性的值都是false,所以跑馬燈效果也就不會出來了,即使你用手觸摸TextView或者按下手機上的導航按鍵(現在的手機沒這
個東東了都。。。)也是無法顯示跑馬燈的效果的。
解決這個問題我們就需要讓我們的TextView得到焦點,這里主要涉及android:focusable和android:focusableInTouchMode這兩個屬性,簡單來說把這兩個屬性都設置成
true,那么在運行程序以后跑馬燈效果就顯示出來了,這里就不再貼這兩行代碼了。
但是細細品味這兩個屬性之后發現其中其實還是有一些玄機的:
1.。如果這兩個屬性設置成android:focusable="true"以及android:focusableInTouchMode="false",那么會發現程序運行之后,走馬燈效果沒有出現,
這個時候需要用戶按下手機或者模擬器上的上下導航鍵,才能讓走馬燈的效果出現,這說明android:focusable是針對於手機按鍵有效的,然而根據api的解釋,
android:focusableInTouchMode是根據屏幕觸摸決定的。
2。如果這兩個屬性設置成android:focusable="false"與android:focusableInTouchMode="true",那么無論如何走馬燈都出現不了了,就算加上android:clickable="true"
也不行,這說明 android:focusable="true"是android:focusableInTouchMode="true"能有效的先決條件,我推測可能是在源碼實現中,android:focusableInTouchMode
的邏輯是嵌套在android:focusable中的,這個有待於以后進一步的研究,路漫漫其修遠兮。。。
3。在把這兩個屬性都設置成true以后,會發現程序運行之后,走馬燈效果自動就顯現了出來,這說明應用在運行后,會自動地按照某種順序(在這里應該是自上而下),
尋找第一個android:focusableInTouchMode="true"這個屬性有效的第一個控件,當然要使這個屬性有效按照前面的討論android:focusable="true"也必須具備。根據測試,
LinearLayout的Clickable,LongClickable,Focusable,FocusableInTouchMode這四個屬性默認也都是false,因此,在上面的例子中TextView就率先獲得了焦點,
走馬燈也就走了起來了。
這里我們做一下驗證,首先將代碼修改為:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:focusable="true">
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:focusable="true"
- android:focusableInTouchMode="true"
- ></TextView>
- </LinearLayout>
也就是為LinearLayout加上了android:focusable="true"然后運行應用,會發現TextView的走馬燈照走不誤:
然后我們為LinearLayout加上android:focusableInTouchMode="true"然后再運行,會發現走馬燈失效了。
這里也就驗證了我們總結的第三條結論。
但是稍等,我們在這里又發現問題了!現在無論我們怎么點擊導航按鈕,又或是點擊屏幕上的TextView走馬燈死活都走不出來了。這是怎么回事呢?
讓我們理順一下思路,按照我們前面的總結,走馬燈要想有效,TextView必須要得到焦點,現在走馬燈出不來,說明TextView沒有得到焦點。
這里有兩個情況,一是導航按鈕無法讓TextView或得焦點,二是屏幕點擊無法讓TextView獲得焦點。
先看第一種情況,第一種情況的原因在於使用導航按鈕切換焦點默認的的方式會跳過內部控件,說白了,上面例子里面的TextView在LinearLayout里面,現在
LinearLayout有焦點,如果你按導航按鈕上下按鍵,焦點只會在LinearLayout同層次的控件之間切換,不會進入到Linearlayout內部,為了驗證這個結論,我們使用
下面的代碼:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="100dip"
- android:focusable="true"
- android:focusableInTouchMode="true"/>
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:focusable="true"
- android:focusableInTouchMode="true"
- ></TextView>
- </LinearLayout>
然后我們運行程序,會發現開始的時候走馬燈沒有自動運行,因為這時候焦點在代碼里面的第二個LinearLayout那里,然后我們按下導航下按鍵,會發現走馬燈效果出來了,
這里我就不貼圖了。
但是稍等,再重新運行應用,不要按導航按鍵,然后我們這個時候用手指或者說模擬器里的鼠標繼續點擊TextView,會發現走馬燈還是沒有出現。
這個時候我們來到了第二種情況了。
這里的問題可以總結為,除了應用第一次開啟的時候,應用自動尋找到android:focusableInTouchMode="true"屬性有效的控件冰賦予焦點,我們要如何
自行通過點擊屏幕的方式使一個控件獲得焦點,在這種情況之下控件想要獲得焦點的流程是什么。
這方面的資料我沒有查到,所以只能自己做一些試驗,然后總結。有盲人摸象的嫌疑,但是我認為在某種程度上是管用的,繼續。
首先我們將代碼修改為如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="100dip"
- android:focusable="true"
- android:focusableInTouchMode="true"
- />
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- ></TextView>
- </LinearLayout>
也就是給TextView加上了一個android:clickable="true"屬性,然后運行之后發現,現在通過觸摸的方式點擊TextView可以讓走馬燈初顯也就是可以讓
TextView獲得焦點了。
看起來問題解決了,但是仔細想想其中還是有一些值得思考的地方:
android:clickable與android:focusableInTouchMode之間是一種什么關系?是說一個空間如果要想能獲得焦點就必須可點擊嗎?又或者說一個空間只要可以點擊
就一定可以獲得焦點?這二者之間是充要條件還是充分條件還是必要條件?
我們來做一下驗證:
首先運行如下代碼:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="100dip"
- android:clickable="true"
- />
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- ></TextView>
- </LinearLayout>
運行后會發現,應用開始以后跑馬燈馬上出現,鼠標點擊TextView上方的位置也就是第二個LinearLayout的區域,跑馬燈不停止,這說明:
android:clickable="true"不一定就能獲得焦點,也就是一個空間能點擊不一定就能獲得焦點。
我們來看下一段代碼:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="100dip"
- android:clickable="false"
- android:focusable="true"
- android:focusableInTouchMode="true"
- />
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- ></TextView>
- </LinearLayout>
這段代碼運行之后,首先代碼中的第二個LinearLayout自動獲得焦點,然后我們點擊TextView。跑馬燈出現,TextView獲得焦點,然后我們點擊TextView上方區域,
跑馬燈不停止。這說明如果一個空間能獲得觸摸模式焦點但卻不能點擊,那么在觸摸模式下無論怎么觸摸也還是不能獲得焦點的。
好的我們來看最后一段代碼:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="100dip"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- />
- <TextView
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="走馬燈效果的演示"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- ></TextView>
- </LinearLayout>
這段代碼運行之后,首先走馬燈不出現,代碼中的第二個LinearLayout獲得焦點,然后我們點擊第二個TextView,走馬燈出現,然后我們點擊TextView上方的區域,
走馬燈效果消失,說明焦點轉移到代碼中的第二個LinearLayout。
好的,總結一下也就是說,在觸摸模式下android:clickable="true"是(android:focusable="true",android:focusableInTouchMode="true")能獲得焦點的必要條件,
就是說一個控件想要在觸摸模式下獲得焦點就一定要可點擊,上面三個屬性都要有。