亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

基于Android的浮動組件,可以用于應用中的新功

系統 2912 0

前言


在開發Android應用時,加新功能是必不可少的,我們加入了新的功能,有的一看界面就可以看出來,但是有的新功能就比較隱蔽,也就是用戶很難知道你添加了這個新功能,這個時候就需要用戶在打開我們的應用時給出一些提示,說明我們在哪里添加了新功能,點擊哪里可以看到這個新功能。這時我們第一時間想到的可能是Toast,因為它用法簡單,又不影響用戶操作,但是它有個缺點,就是不能明確的指示是哪里添加了新功能,除非你用文字描述出來。為此,我基于Toast編寫了一個小組件FloatTextToast(下面遇到的這個名字代替我寫的這個組件),他和Toast的用法一樣簡單,并且彌補了Toast的缺點,也更顯得更好看。

效果圖

你可以學到


  1. Toast的基本用法
  2. Android的消息機制,如何創建自己的消息隊列
  3. 怎樣在Activity啟動時獲取一個View的width、height、top、left等屬性

基本思路


  1. 首先你要有一個處理好的9 PNG的圖片,用于自適應文字顯示,關于9 PNG處理可以參考Android Doc
  2. 要顯示在哪個View的下面,就要知道這個目標View的位置
  3. 把要顯示的文本放在一個TextView里,使用Toast的setView方法設置Toast要顯示的View。
  4. 根據得到的位置,最后就是使用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方法設置好要顯示文本的位置,然后顯示即可。

獲取要顯示文本的位置

要獲取顯示的位置,就要知道targetVIew的位置以及它的寬、高,這樣就能計算要顯示文本的位置了。View組件都有一個函數,可以把自己在Window里的坐標轉換為一個數組。
        private int[] getTargetViewPos(){
		final int[] targetPos=new int[2];
		mTargetView.getLocationInWindow(targetPos);
		return targetPos;
	}
      
這樣,就返回了targetView的xy坐標了。光有targetView的坐標還不夠,還要有contentView最終要顯示的位置。
        /**
	 * 計算獲取浮動文本顯示的位置,把浮動文本放在targetView的中心處
	 * @return 一個包含top和left的Rect
	 */
	private  Rect getContentViewPos(int[] targetPos){
		final Rect windowVisibleRect=new Rect();
		final View targetView=mTargetView;
		final TextView contentView=mContentView;
		//狀態欄高度
		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。

完整的組件代碼


上面是對組件代碼的拆分講解,是為了說明我們當時實現這個組件的想法以及步驟,下面就整體把代碼列出來,明了的看一下。
        /**
 * 浮動的文本顯示。根據一個提供的View,可以把文本顯示到該View的下面.
 * 可以設置顯示的時間,多了該時間后自動消失。目前只支持純文本{@link String}類型的顯示
 * 因為要計算顯示文本的寬度。
 * @author michael_li(飛雪無情)
 * @since 2011-12-10 下午04:57:36
 */
public class FloatTextToast {
	public static final int LENGTH_LONG=Toast.LENGTH_LONG;
	public static final int LENGTH_SHORT=Toast.LENGTH_SHORT;
	private static final int WHAT_SHOW=1;
	
	private Context mContext;
	private View mTargetView;
	private Toast mToast;
	private  TextView mContentView;
	
	private HandlerThread mHandlerThread;
	private FloatTextToastHandler mHandler;
	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());
	}
	/**
	 * 生成一個FloatTextToast
	 * @param context Activity 上下文
	 * @param targetView  目標View,浮動文本要顯示在哪個View下面
	 * @param text 要顯示的文本
	 * @param duration 浮動文本顯示的時間 {@link #LENGTH_LONG} {@link #LENGTH_SHORT}
	 * @return 一個FloatTextToast,可以調用{@link #show()}顯示
	 */
	public static FloatTextToast makeText(Context context,View targetView, String text, int duration) {
		final FloatTextToast floatToast=new FloatTextToast(context,targetView);
		final TextView contentView=floatToast.mContentView;
		contentView.setText(text);
		floatToast.mToast.setDuration(duration);
		return floatToast;
	}
	/**
	 * 顯示浮動文本
	 */
	public void show(){
		mHandler.sendEmptyMessage(WHAT_SHOW);
	}
	/**在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();
		}
	}
	private int[] getTargetViewPos(){
		final int[] targetPos=new int[2];
		mTargetView.getLocationInWindow(targetPos);
		return targetPos;
	}
	/**
	 * 計算獲取浮動文本顯示的位置,把浮動文本放在targetView的中心處
	 * @return 一個包含top和left的Rect
	 */
	private  Rect getContentViewPos(int[] targetPos){
		final Rect windowVisibleRect=new Rect();
		final View targetView=mTargetView;
		final TextView contentView=mContentView;
		//狀態欄高度
		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;
	}
	private class FloatTextToastHandler extends Handler{

		public FloatTextToastHandler(Looper looper) {
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			switch(msg.what){
			case WHAT_SHOW:
				showInHandler();
			}
		}
		
		
	}
}
      

此組件和Toast的實現方法一樣,所以上手不難,只需使用makeText靜態方法生成一個即可
        FloatTextToast.makeText(Context context, View targetView, String text, int duration).show()
      
就這么簡單,傳進去幾個參數,show出即可,和Toast一樣好用。

小結


這里主要是通過類之間的組合編寫一個一個FloatTextToast組件,便于在應用中提示一些信息,不光局限于新功能的提示,還有其他的點擊查看個人信息等等,就如上面的效果圖一樣。這里主要的難點就在于Activity啟動獲取targetView的狀態,這里采用了不受影響的自定義的消息機制,能及時的獲取targetView的狀態。這里也采用的Toast的隊列機制,這樣就能夠更好的一個個的提示,讓用戶看完一個再顯示另外一個,不至于一下子全顯示出來,而用戶沒有時間看。這里還采用了Paint用于測量文本的真實寬度,所以也有了一些缺陷,如果哪位有更好的方法,也可以留言告知我,不勝感激。

附上組件源代碼和效果圖的Demo下載 http://download.csdn.net/detail/michael__li/3904636

基于Android的浮動組件,可以用于應用中的新功能展示等等。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。。?/p>

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久久免费精品一区二区 | 在线亚洲精品国产波多野结衣 | 手机看高清特黄a大片 | 久草在在线视频 | 国产欧美在线播放 | 午夜精品久久久久久 | 一级毛片人与动免费观看 | 四虎影视4hu4虎成人 | 深夜影院老司机69影院 | 国产成人丝袜网站在线观看 | 国内自拍 在线播放 网红 | 欧美日韩精选 | 欧美成人一区二区三区不卡视频 | 久久九九亚洲精品 | 欧美成人亚洲欧美成人 | 亚洲国产成+人+综合 | 一本伊大人香蕉久久网手机 | 中文字幕日韩精品亚洲七区 | 免费国产成人午夜私人影视 | 日本高清不卡免费 | 99久久国产免费福利 | 一级毛片日韩 | 久久欧美久久欧美精品 | 精品一二区| 手机看片国产免费久久网 | 九月丁香婷婷亚洲综合色 | 日本精品久久久久中文字幕2 | 欧美亚洲国产精品久久第一页 | 四虎影视国产精品 | 狠狠色丁香久久婷婷 | 欧美福利在线视频 | 曰本亚洲欧洲色a在线 | 久热免费在线观看 | 亚洲精品中文字幕乱码三区一二 | 青娱乐91在线 | 中文字幕在线一区二区在线 | 免费网站成人亚洲 | 天天天天天天天操 | 亚洲自拍中文 | 亚洲国产精品乱码在线观看97 | 91国内精品久久久久影院优播 |