用過新版本android 360手機小幫手都人都對 360中只在案頭顯示一個小小懸浮視窗羨慕不已吧?
其實實現這種功能,主要有兩步:
1.判斷當前顯示的是為案頭。這個內容我在前面的文章裡面已經有過介紹,如果還沒看過的趕快穩步看一下哦。
【Kris專題】判斷當前顯示是否為案頭
2.使用windowManager往最頂層添加一個View .這個知識點就是為本文主要講解的內容哦。在本文的講解中,我們還會講到下面的知識點:
a.如果擷取到狀態列的高度
b.懸浮視窗的拖動
c.懸浮視窗的點擊事件
有開始之前,我們先來看一下:
接下來我們來看看FloatView的代碼:
/** * * @author <a href="mailto:kris@krislq.com">Kris.lee</a> * @website <a href="\"http://www.krislq.com\"" target="\"_blank\"">www.krislq.com</a> * @date Nov 29, 2012 * @version 1.0.0 * */public class FloatView extends ImageView{ private float mTouchX; private float mTouchY; private float x; private float y; private float mStartX; private float mStartY; private OnClickListener mClickListener; private WindowManager windowManager = (WindowManager) getContext() .getApplicationContext().getSystemService(Context.WINDOW_SERVICE); // 此windowManagerParams變數為擷取的全域變數,用以儲存懸浮視窗的屬性 private WindowManager.LayoutParams windowManagerParams = ((FloatApplication) getContext() .getApplicationContext()).getWindowParams(); public FloatView(Context context) { super(context); } @Override public boolean onTouchEvent(MotionEvent event) { //擷取到狀態列的高度 Rect frame = new Rect(); getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; System.out.println("statusBarHeight:"+statusBarHeight); // 擷取相對螢幕的座標,即以螢幕左上方為原點 x = event.getRawX(); y = event.getRawY() - statusBarHeight; // statusBarHeight是系統狀態列的高度 Log.i("tag", "currX" + x + "====currY" + y); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 捕獲手指觸摸按下動作 // 擷取相對View的座標,即以此View左上方為原點 mTouchX = event.getX(); mTouchY = event.getY(); mStartX = x; mStartY = y; Log.i("tag", "startX" + mTouchX + "====startY" + mTouchY); break; case MotionEvent.ACTION_MOVE: // 捕獲手指觸摸移動動作 updateViewPosition(); break; case MotionEvent.ACTION_UP: // 捕獲手指觸摸離開動作 updateViewPosition(); mTouchX = mTouchY = 0; if ((x - mStartX) < 5 && (y - mStartY) < 5) { if(mClickListener!=null) { mClickListener.onClick(this); } } break; } return true; } @Override public void setOnClickListener(OnClickListener l) { this.mClickListener = l; } private void updateViewPosition() { // 更新浮動視窗位置參數 windowManagerParams.x = (int) (x - mTouchX); windowManagerParams.y = (int) (y - mTouchY); windowManager.updateViewLayout(this, windowManagerParams); // 重新整理顯示 }}
代碼解釋:
自訂application的目的是為了儲存windowParams的值 ,因為我們在拖動懸浮視窗的時候,如果每次都重新new一個layoutParams的話,在update 的時候會在異常發現。
windowParams的值也不一定非得在自訂application裡面來儲存,只要是全域的都行。
最後我們再來看看Activity中的實現。
/** * * @author <a href="mailto:kris@krislq.com">Kris.lee</a> * @website <a href="\"http://www.krislq.com\"" target="\"_blank\"">www.krislq.com</a> * @date Nov 29, 2012 * @version 1.0.0 * */public class MainActivity extends Activity implements OnClickListener{ private WindowManager windowManager = null; private WindowManager.LayoutParams windowManagerParams = null; private FloatView floatView = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);//取消標題列 getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN , WindowManager.LayoutParams. FLAG_FULLSCREEN);//全屏 setContentView(R.layout.activity_main); createView(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public void onDestroy() { super.onDestroy(); // 在程式退出(Activity銷毀)時銷毀懸浮視窗 windowManager.removeView(floatView); } private void createView() { floatView = new FloatView(getApplicationContext()); floatView.setOnClickListener(this); floatView.setImageResource(R.drawable.ic_launcher); // 這裡簡單的用內建的icon來做示範 // 擷取WindowManager windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); // 設定LayoutParams(全域變數)相關參數 windowManagerParams = ((FloatApplication) getApplication()).getWindowParams(); windowManagerParams.type = LayoutParams.TYPE_PHONE; // 設定window type windowManagerParams.format = PixelFormat.RGBA_8888; // 設定圖片格式,效果為背景透明 // 設定Window flag windowManagerParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; /* * 注意,flag的值可以為: * LayoutParams.FLAG_NOT_TOUCH_MODAL 不影響後面的事件 * LayoutParams.FLAG_NOT_FOCUSABLE 不可聚焦 * LayoutParams.FLAG_NOT_TOUCHABLE 不可觸摸 */ // 調整懸浮視窗至左上方,便於調整座標 windowManagerParams.gravity = Gravity.LEFT | Gravity.TOP; // 以螢幕左上方為原點,設定x、y初始值 windowManagerParams.x = 0; windowManagerParams.y = 0; // 設定懸浮視窗長寬資料 windowManagerParams.width = LayoutParams.WRAP_CONTENT; windowManagerParams.height = LayoutParams.WRAP_CONTENT; // 顯示myFloatView映像 windowManager.addView(floatView, windowManagerParams); } public void onClick(View v) { Toast.makeText(this, "Clicked", Toast.LENGTH_SHORT).show(); }}
代碼解釋:
在activity中我們主要是添加懸浮窗,並且設定他的位置。另外需要注意flags的應用:
LayoutParams.FLAG_NOT_TOUCH_MODAL 不影響後面的事件
LayoutParams.FLAG_NOT_FOCUSABLE 不可聚焦
LayoutParams.FLAG_NOT_TOUCHABLE 不可觸摸
最後我們在onDestroy()中移除到懸浮視窗。所以,我們測試的時候,記得按Home鍵來切換到案頭。
最後千萬記得,在androidManifest.xml中來申明我們需要用到的android.permission.SYSTEM_ALERT_WINDOW許可權
並且記得申明我們自訂的application哦。
AndroidManifest.xml代碼如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.krislq.floating" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:name="FloatApplication"> <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
原文:http://www.eoeandroid.com/thread-233373-1-1.html