基於Android的浮動組件,可以用於應用中的新功能展示等等。
前言
在開發Android應用時,加新功能是必不可少的,我們加入了新的功能,有的一看界面就可以看出來,但是有的新功能就比較隱蔽,也就是用戶很難知道你添加了這個新功能,這個時候就需要用戶在打開我們的應用時給出一些提示,說明我們在哪裡添加了新功能,點擊哪裡可以看到這個新功能。這時我們第一時間想到的可能是Toast,因為它用法簡單,又不影響用戶操作,但是它有個缺點,就是不能明確的指示是哪裡添加了新功能,除非你用文字描述出來。為此,我基於Toast編寫了一個小組件FloatTextToast(下面遇到的這個名字代替我寫的這個組件),他和Toast的用法一樣簡單,並且彌補了Toast的缺點,也更顯得更好看。
組件源代碼和效果圖的Demo下載
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /pub/Android源碼集錦/2011年/12月/基於Android浮動組件 可用於應用中新功能展示等/
效果圖
你可以學到
- Toast的基本用法
- Android的消息機制,如何創建自己的消息隊列
- 怎樣在Activity啟動時獲取一個View的width、height、top、left等屬性
基本思路
- 首先你要有一個處理好的9 PNG的圖片,用於自適應文字顯示,關於9 PNG處理可以參考Android Doc
- 要顯示在哪個View的下面,就要知道這個目標View的位置
- 把要顯示的文本放在一個TextView裡,使用Toast的setView方法設置Toast要顯示的View。
- 根據得到的位置,最後就是使用Toast的setGravity方法把要顯示的內容放到正確的位置顯示出來即可。
總的來說首先就是要知道目標View,根據targetView計算出要顯示提示的位置,然後根據位置使用Toast把提示的文本顯示出來。但是這裡有幾個難點,下面就一一解決
Activity加載完成時獲取targetVIew的寬高和位置屬性
我們加入了新的功能提示,自然會在用戶打開這個界面的時候就提示,但是在UI沒有渲染完成綁定倒Window上的時候,是不能獲取倒targetView的width、height和position的,那麼我們怎麼才能知道targetView的這些屬性呢?Activity的onAttachedToWindow回調方法是不能用的,況且它是在API 5加上的,以前的API中並沒有。不過我們還有一種方法,那就是在顯示提示的時候獲取targetView的屬性,如果獲取不到(為0)就一直獲取,直到獲取到為止,這其實是一個輪詢。為了達到這一目的,我們在開發者調用FloatTextToast.show()的時候使用Android的Message機制輪詢獲取一個targetView的屬性,如果獲取到,就會顯示提示文字了。在此之前先看下FloatTextToast構造函數,可以對它有個大概的了解,防止後面的代碼中出現的成員變量不認識。
- private FloatTextToast(Context context,View targetView) {
- this.mTargetView = targetView;
- this.mContext= context;
- mToast=new Toast(mContext);
- mContentView=new TextView(mContext);
- mContentView.setBackgroundResource(R.drawable.float_text_toast_bg);
- mContentView.setTextColor(Color.BLACK);
- mContentView.setTextSize(TypedValue.COMPLEX_UNIT_DIP,16);
- mToast.setView(mContentView);
-
- //初始化一個Handler線程
- mHandlerThread=new HandlerThread("FloatTextToast");
- mHandlerThread.start();
- mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());
- }
自定義自己的消息循環機制
要想在一個自定義的組件中使用Message機制,一定要有自己的Looper機制,我們不能使用Activity的Looper,因為主Looper可能會有其他的Message需要處理,這就會導致我們的show方法會延遲調用,這樣效果就不好了,所以要有一個專門的Looper來處理此Message。要聲明自己的Looper,就需要HandlerThread這個類的配合了,這可是個好東西,使用它你會很容易的創建一個自己的線程用於處理你Message。使用方法很簡單,如下代碼:
- //初始化一個Handler線程
- mHandlerThread=new HandlerThread("FloatTextToast");
- mHandlerThread.start();
- mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());
這樣就聲明了一個HandlerThread並且讓它運行,運行之後我們就可以獲取一個屬於該Thread的Looper,然後把Message發送給這個Looper,那麼這個線程就可以處理你發送的消息了。。看看我們的自定義Handler
- private class FloatTextToastHandler extends Handler{
-
- public FloatTextToastHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what){
- case WHAT_SHOW:
- showInHandler();
- }
- }
-
-
- }
它需要傳遞一個Looper作為構造參數聲明,意思就是使用這個Looper處理我發send的Message的意思。上面的代碼
- mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());
正是我們使用自己開啟的線程處理我們的Message的意思。下面看下我們的showInHandler()方法是怎麼處理的。
- /**在Handler調用的show方法,主要為了等待{@link #mTargetView}的位置*/
- private void showInHandler(){
- int[] targetPos=getTargetViewPos();
- if(targetPos[0]==0&&targetPos[1]==0){
- mHandler.sendEmptyMessageDelayed(WHAT_SHOW, 100);
- }else{
- final Rect contentPos=getContentViewPos(targetPos);
- mToast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);
- mToast.show();
- }
- }
該方法其實就是在獲取targetVIew的位置,如果獲取不到,則向自定義的Looper裡發送一個Message重新調用該函數,如果得到了位置,那麼就調用Toast的setGravity方法設置好要顯示文本的位置,然後顯示即可。