Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android -- 浮動組建

Android -- 浮動組建

編輯:關於Android編程

在開發Android應用時,加新功能是必不可少的,我們加入了新的功能,有的一看界面就可以看出來,但是有的新功能就比較隱蔽,也就是用戶很難知道你添加了這個新功能,這個時候就需要用戶在打開我們的應用時給出一些提示,說明我們在哪裡添加了新功能,點擊哪裡可以看到這個新功能。這時我們第一時間想到的可能是Toast,因為它用法簡單,又不影響用戶操作,但是它有個缺點,就是不能明確的指示是哪裡添加了新功能,除非你用文字描述出來。   基本思路                                                                                         首先你要有一個處理好的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構造函數,可以對它有個大概的了解,防止後面的代碼中出現的成員變量不認識。   復制代碼 /**      * 私有的構造函數      *       * @param context      *            上下文      * @param targetView      *            目標view      * @param content      *            內容      * @param time      *            顯示時間      */     private MyToast(Context context, View targetView, String content, int time) {         this.targetView = targetView;         this.context = context;         this.content = content;         this.time = time;         // 初始化Toast         toast = new Toast(this.context);         textView = new TextView(this.context);         textView.setTextColor(Color.BLACK);         textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);         textView.setBackgroundResource(R.drawable.float_text_toast_bg);         textView.setText(this.content);         toast.setView(textView);// 設置背景                  toast.setDuration(time);// 設置時間         // 開始handlerthread         handlerThread = new HandlerThread("MyToast");         handlerThread.start();         // 構造一個自己的looper         handler = new MyHandler(handlerThread.getLooper());     } 復制代碼 自定義自己的消息循環機制                                                                 要想在一個自定義的組件中使用Message機制,一定要有自己的Looper機制,我們不能使用Activity的Looper,因為主Looper可能會有其他的Message需要處理,這就會導致我們的show方法會延遲調用,這樣效果就不好了,所以要有一個專門的Looper來處理此Message。要聲明自己的Looper,就需要HandlerThread這個類的配合了,這可是個好東西,使用它你會很容易的創建一個自己的線程用於處理你Message。使用方法很簡單,如下代碼:   // 開始handlerthread         handlerThread = new HandlerThread("MyToast");         handlerThread.start();         // 構造一個自己的looper         handler = new MyHandler(handlerThread.getLooper()); 這樣就聲明了一個HandlerThread並且讓它運行,運行之後我們就可以獲取一個屬於該Thread的Looper,然後把Message發送給這個Looper,那麼這個線程就可以處理你發送的消息了。。看看我們的自定義Handler   復制代碼 /**      * 自定義的Handler      *       * @author sansung      *       */     class MyHandler extends Handler {           public MyHandler(Looper looper) {             super(looper);             // TODO 自動生成的構造函數存根         }           @Override         public void handleMessage(Message msg) {             // TODO 自動生成的方法存根             super.handleMessage(msg);             switch (msg.what) {             case WHAT_SHOW:                 showInHandler();                 break;             default:                 break;             }         }       } 復制代碼 它需要傳遞一個Looper作為構造參數聲明,意思就是使用這個Looper處理我發send的Message的意思。上面的代碼   handler = new MyHandler(handlerThread.getLooper()); 正是我們使用自己開啟的線程處理我們的Message的意思。下面看下我們的showInHandler()方法是怎麼處理的。   復制代碼 /**      * Handler調用的show方法,主要為了等待targetView的位置      * 如果targetView的位置沒有得到,handler looper繼續循環獲取      */     private void showInHandler() {         int[] targetPos = getTargetViewPos();         if(targetPos[0]==0&&targetPos[1]==0){             handler.sendEmptyMessageDelayed(WHAT_SHOW, 100);         }else{             final Rect contentPos=getSize(targetPos);             toast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);             toast.show();         }     } 復制代碼 該方法其實就是在獲取targetVIew的位置,如果獲取不到,則向自定義的Looper裡發送一個Message重新調用該函數,如果得到了位置,那麼就調用Toast的setGravity方法設置好要顯示文本的位置,然後顯示即可。   獲取要顯示文本的位置                                                                       要獲取顯示的位置,就要知道targetVIew的位置以及它的寬、高,這樣就能計算要顯示文本的位置了。View組件都有一個函數,可以把自己在Window裡的坐標轉換為一個數組。   復制代碼 /**      * 得到目標View的位置      * @return      */     private int[] getTargetViewPos() {         final int[] targetPos = new int[2];         targetView.getLocationInWindow(targetPos);         return targetPos;     } 復制代碼 這樣,就返回了targetView的xy坐標了。光有targetView的坐標還不夠,還要有contentView最終要顯示的位置。   復制代碼 /**      * 計算獲取浮動文本顯示的位置,把浮動文本放在targetView的中心處      *       * @param targetPos      * @return 一個包含top和left的Rect      */     private Rect getSize(int[] targetPos) {         final Rect windowVisibleRect = new Rect();         final View targetView = this.targetView;         final TextView contentView = textView;         // 狀態欄高度         targetView.getWindowVisibleDisplayFrame(windowVisibleRect);         int statusBarHeight = windowVisibleRect.top;         // 背景圖那個三角箭頭的位置         final TextPaint textPaint = contentView.getPaint();         int contentW = (int) textPaint.measureText((String) contentView                 .getText());         int arrowPos = (int) (contentW * (30.0 / 160));           final Rect rect = new Rect();         rect.left = targetPos[0] + targetView.getWidth() / 2 - arrowPos;         rect.top = targetPos[1] - statusBarHeight + targetView.getHeight();         return rect;     } 復制代碼 這個函數的功能就是讓文本顯示在targetView的下方的橫向中間的位置,也就是文本的背景尖角三角要指向targetView橫向中間的位置,這樣才好看些。為了這個才需要使用Paint測量文本的寬度,所以這也是該組件的一個缺陷,不能顯示String格式之外的字符,比如SpannableString。   完整的組件代碼                                                                               上面是對組件代碼的拆分講解,是為了說明我們當時實現這個組件的想法以及步驟,下面就整體把代碼列出來,明了的看一下。   復制代碼 public class MyToast {     private static final int WHAT_SHOW = 1;     private View targetView = null;// 目標view     private Context context = null;// Toast的上下文     private Toast toast = null;// 顯示的土司     private String content = null;// 土司顯示內容     private TextView textView = null;// 土司中的textview     private int time = 0;// 土司顯示時間     private HandlerThread handlerThread = null;// handlerThread     private static MyToast myToast = null;     private Handler handler = null;       /**      * 私有的構造函數      *       * @param context      *            上下文      * @param targetView      *            目標view      * @param content      *            內容      * @param time      *            顯示時間      */     private MyToast(Context context, View targetView, String content, int time) {         this.targetView = targetView;         this.context = context;         this.content = content;         this.time = time;         // 初始化Toast         toast = new Toast(this.context);         textView = new TextView(this.context);         textView.setTextColor(Color.BLACK);         textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);         textView.setBackgroundResource(R.drawable.float_text_toast_bg);         textView.setText(this.content);         toast.setView(textView);// 設置背景                  toast.setDuration(time);// 設置時間         // 開始handlerthread         handlerThread = new HandlerThread("MyToast");         handlerThread.start();         // 構造一個自己的looper         handler = new MyHandler(handlerThread.getLooper());     }       /**      * 顯示Toast      *       * @param context      *            上下文      * @param targetView      *            目標view      * @param content      *            內容      * @param time      *            顯示時間      */     public static MyToast makeText(Context context, View targetView,             String content, int time) {         myToast = new MyToast(context, targetView, content, time);         return myToast;       }       public void show() {         handler.sendEmptyMessage(WHAT_SHOW);     }       /**      * 計算獲取浮動文本顯示的位置,把浮動文本放在targetView的中心處      *       * @param targetPos      * @return 一個包含top和left的Rect      */     private Rect getSize(int[] targetPos) {         final Rect windowVisibleRect = new Rect();         final View targetView = this.targetView;         final TextView contentView = textView;         // 狀態欄高度         targetView.getWindowVisibleDisplayFrame(windowVisibleRect);         int statusBarHeight = windowVisibleRect.top;         // 背景圖那個三角箭頭的位置         final TextPaint textPaint = contentView.getPaint();         int contentW = (int) textPaint.measureText((String) contentView                 .getText());         int arrowPos = (int) (contentW * (30.0 / 160));           final Rect rect = new Rect();         rect.left = targetPos[0] + targetView.getWidth() / 2 - arrowPos;         rect.top = targetPos[1] - statusBarHeight + targetView.getHeight();         return rect;     }     /**      * Handler調用的show方法,主要為了等待targetView的位置      * 如果targetView的位置沒有得到,handler looper繼續循環獲取      */     private void showInHandler() {         int[] targetPos = getTargetViewPos();         if(targetPos[0]==0&&targetPos[1]==0){             handler.sendEmptyMessageDelayed(WHAT_SHOW, 100);         }else{             final Rect contentPos=getSize(targetPos);             toast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);             toast.show();         }     }     /**      * 得到目標View的位置      * @return      */     private int[] getTargetViewPos() {         final int[] targetPos = new int[2];         targetView.getLocationInWindow(targetPos);         return targetPos;     }       /**      * 自定義的Handler      *       * @author sansung      *       */     class MyHandler extends Handler {           public MyHandler(Looper looper) {             super(looper);             // TODO 自動生成的構造函數存根         }           @Override         public void handleMessage(Message msg) {             // TODO 自動生成的方法存根             super.handleMessage(msg);             switch (msg.what) {             case WHAT_SHOW:                 showInHandler();                 break;             default:                 break;             }         }       }   }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved