編輯:關於Android編程
當我們在手機上安裝360安全衛士時,手機屏幕上時刻都會出現一個小浮動窗口,點擊該浮動窗口可跳轉到安全衛士的操作界面,而且該浮動窗口不受其他activity的覆蓋影響仍然可見(多米音樂也有相關的和主界面交互的懸浮小窗口)。它能懸浮在手機桌面,且不受Activity界面的影響,說明該懸浮窗口是不隸屬於Activity界面的,也就是說,他是隸屬於啟動它的應用程序所在進程。如360App所在的應用進程,當殺掉它所在的應用進程時,它才會消失。懸浮窗口的實現涉及到WindowManager(基於4.0源碼分析),它是一個接口,實現類有WindowManagerImpl,CompatModeWrapper(WindowManagerImpl的內部類),LocalWindowManager(Window的內部類)。
通過WindowManager的addView()方法,並設置WindowManager.LayoutParams的相關屬性,就可以往WindowManager中加入所需要的View,而根據WindowManager.LayoutParams屬性不同,也就能實現不同的效果。比如創建系統頂級窗口,實現懸浮窗口效果。如果需要將View從WindowManager中移除,只需要調用removeView()即可。
1、代碼實現主界面為一個Button按鈕點擊跳轉到小懸浮窗口,然後關閉本窗口。
package com.example.suspend; import android.os.Bundle; import android.app.Activity; import android.content.Intent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private Button suspend; private TextView text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initUI(); } private void initUI() { // TODO Auto-generated method stub // WindowService wind = new WindowService(); suspend = (Button)findViewById(R.id.suspend); suspend.setOnClickListener(new suspendListener()); text = (TextView)findViewById(R.id.text); text.setText(MyWindowManager.getUsedPercentValue(getApplicationContext())); } public class suspendListener implements OnClickListener{ @Override public void onClick(View arg0) { // TODO Auto-generated method stub //啟動懸浮窗口關閉本窗口 Intent intent = new Intent(MainActivity.this,WindowService.class); startService(intent); finish(); } } }2、WindowService 中使用了一個定時器,定時為500ms,在定時器裡創建小窗口,在啟動前先判斷是否在桌面
/** * 判斷當前界面是否桌面 */ private boolean isHome(){ ActivityManager mactivityManager =(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); List rti = mactivityManager.getRunningTasks(1); return getHomes().contains(rti.get(0).topActivity.getPackageName()); } /** * 獲得屬於桌面的應用的應用包名稱 * @return 返回包含所有包名的字符串列表 */ private List完整代碼為:getHomes(){ List names = new ArrayList (); PackageManager packageManager = this.getPackageManager(); Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); List resolveInfo = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo ri : resolveInfo){ names.add(ri.activityInfo.packageName); System.out.println("packageName" + names); Log.d(TAG,"tag:"+names); } return names; }
package com.example.suspend; import android.app.ActivityManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Handler; import android.os.IBinder; import android.util.Log; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; public class WindowService extends Service { private static final String TAG = "PACKAGENAME"; //用於線程中創建或移除懸浮窗。 private Handler handler = new Handler(); //定時器,定時進行檢測當前應該創建還是移除懸浮 private Timer timer; @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return null; } /** * 啟動service的時候,onCreate方法只有第一次會調用,onStartCommand和onStart每次都被調用。 * onStartCommand會告訴系統如何重啟服務,如判斷是否異常終止後重新啟動,在何種情況下異常終止 * 這個整形可以有四個返回值:start_sticky、start_no_sticky、START_REDELIVER_INTENT、START_STICKY_COMPATIBILITY。 */ @Override public int onStartCommand(Intent intent, int flags, int startId) { /** * 開啟定時器,每隔500ms刷新一次 */ if (timer == null){ timer = new Timer(); timer.scheduleAtFixedRate(new RefreshTask(),0,500); } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); //Service 被終止的同時也停止定時器繼續運行 timer.cancel(); timer = null; } class RefreshTask extends TimerTask{ @Override public void run() { //判斷當前界面是桌面,且沒有懸浮顯示,則創建懸浮窗 if (isHome() && !MyWindowManager.isWindowShowing()){ handler.post(new Runnable() { @Override public void run() { MyWindowManager.createSmallWindow(getApplicationContext()); } }); } //當前界面不是桌面,且有懸浮窗口顯示,則移除懸浮窗口 else if (!isHome() && MyWindowManager.isWindowShowing()){ handler.post(new Runnable() { @Override public void run() { MyWindowManager.removeSmallWindow(getApplicationContext()); MyWindowManager.removeBigWindow(getApplicationContext()); } }) ; } // 當前界面是桌面,且有懸浮窗顯示,則更新內存數據。 else if (isHome() && MyWindowManager.isWindowShowing()){ handler.post(new Runnable() { @Override public void run() { MyWindowManager.updateUsedPercent(getApplicationContext()); } }); } } } /** * 判斷當前界面是否桌面 */ private boolean isHome(){ ActivityManager mactivityManager =(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); List rti = mactivityManager.getRunningTasks(1); return getHomes().contains(rti.get(0).topActivity.getPackageName()); } /** * 獲得屬於桌面的應用的應用包名稱 * @return 返回包含所有包名的字符串列表 */ private List3、創建窗口方法具體代碼有寫getHomes(){ List names = new ArrayList (); PackageManager packageManager = this.getPackageManager(); Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); List resolveInfo = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo ri : resolveInfo){ names.add(ri.activityInfo.packageName); System.out.println("packageName" + names); Log.d(TAG,"tag:"+names); } return names; } }
public static void createSmallWindow(Context context){ //WindowManager基本用到:addView,removeView,updateViewLayout WindowManager windowManager = getWindowManager(context); //獲取屏幕寬高 abstract Display getDefaultDisplay(); //獲取默認顯示的 Display 對象 int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); //設置小懸浮窗口的位置以及相關參數 if (smallWindowActivity == null) { smallWindowActivity = new SmallWindowActivity(context); if (smallWindowParams == null) { smallWindowParams = new LayoutParams();// smallWindowParams.type = LayoutParams.TYPE_PHONE;//設置窗口的window type smallWindowParams.format = PixelFormat.RGBA_8888;//設置圖片格式,效果為背景透明 smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;//下面的flags屬性的效果形同“鎖定”。 懸浮窗不可觸摸,不接受任何事件,同時不影響後面的事件響應。 smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;//調整懸浮窗口位置在左邊中間 smallWindowParams.width = SmallWindowActivity.viewWidth;//設置懸浮窗口的寬高 smallWindowParams.height = SmallWindowActivity.viewHeight; smallWindowParams.x = screenWidth;//設置懸浮窗口位置 smallWindowParams.y = screenHeight / 2; } smallWindowActivity.setParams(smallWindowParams); windowManager.addView(smallWindowActivity, smallWindowParams);//將需要加到懸浮窗口中的View加入到窗口中 } }要移除窗口可使用 windowManager.removeView(smallWindowActivity);
小窗口實現的是顯示手機內存百分比,下面為計算內存百分比的方法:
/** * 計算已使用內存的百分比,並返回。 * * @param context * 可傳入應用程序上下文。 * @return 已使用內存的百分比,以字符串形式返回。 */ public static String getUsedPercentValue(Context context) { String dir = "/proc/meminfo"; try { FileReader fr = new FileReader(dir); BufferedReader br = new BufferedReader(fr, 2048); String memoryLine = br.readLine(); String subMemoryLine = memoryLine.substring(memoryLine.indexOf("MemTotal:")); br.close(); long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll("\\D+", "")); long availableSize = getAvailableMemory(context) / 1024; int percent = (int) ((totalMemorySize - availableSize) / (float) totalMemorySize * 100); return percent + "%"; } catch (IOException e) { e.printStackTrace(); } return "懸浮窗"; } /** * 更新小懸浮窗的TextView上的數據,顯示內存使用的百分比。 * * @param context * 可傳入應用程序上下文。 */ public static void updateUsedPercent(Context context) { if (smallWindowActivity != null) { TextView percentView = (TextView) smallWindowActivity.findViewById(R.id.percent); percentView.setText(getUsedPercentValue(context)); } } /** * 獲取當前可用內存,返回數據以字節為單位。 * * @param context * 可傳入應用程序上下文。 * @return 當前可用內存。 */ private static long getAvailableMemory(Context context) { ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); getActivityManager(context).getMemoryInfo(mi); return mi.availMem; } /** * 如果ActivityManager還未創建,則創建一個新的ActivityManager返回。否則返回當前已創建的ActivityManager。 * * @param context * 可傳入應用程序上下文。 * @return ActivityManager的實例,用於獲取手機可用內存。 */ private static ActivityManager getActivityManager(Context context) { if (mactivityManager == null) { mactivityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); } return mactivityManager; }完整的MyWindowManager類代碼為
package com.example.suspend; import android.app.ActivityManager; import android.content.Context; import android.graphics.PixelFormat; import android.view.Gravity; import android.view.WindowManager.LayoutParams; import android.view.WindowManager; import android.widget.TextView; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class MyWindowManager{ //小懸浮窗View的實例 private static SmallWindowActivity smallWindowActivity; //大懸浮窗View的實例 private static BigWindowActivity bigWindowActivity; //小懸浮View的參數 private static LayoutParams smallWindowParams; //大懸浮View的參數 private static LayoutParams bigWindowParams; //用於控制在屏幕上添加或移除懸浮窗 private static WindowManager mWindowManager; //用於獲取手機可用內存 private static ActivityManager mactivityManager; public static void createSmallWindow(Context context){ //WindowManager基本用到:addView,removeView,updateViewLayout WindowManager windowManager = getWindowManager(context); //獲取屏幕寬高 abstract Display getDefaultDisplay(); //獲取默認顯示的 Display 對象 int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); //設置小懸浮窗口的位置以及相關參數 if (smallWindowActivity == null) { smallWindowActivity = new SmallWindowActivity(context); if (smallWindowParams == null) { smallWindowParams = new LayoutParams();// smallWindowParams.type = LayoutParams.TYPE_PHONE;//設置窗口的window type smallWindowParams.format = PixelFormat.RGBA_8888;//設置圖片格式,效果為背景透明 smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;//下面的flags屬性的效果形同“鎖定”。 懸浮窗不可觸摸,不接受任何事件,同時不影響後面的事件響應。 smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;//調整懸浮窗口位置在左邊中間 smallWindowParams.width = SmallWindowActivity.viewWidth;//設置懸浮窗口的寬高 smallWindowParams.height = SmallWindowActivity.viewHeight; smallWindowParams.x = screenWidth;//設置懸浮窗口位置 smallWindowParams.y = screenHeight / 2; } smallWindowActivity.setParams(smallWindowParams); windowManager.addView(smallWindowActivity, smallWindowParams);//將需要加到懸浮窗口中的View加入到窗口中 } } /** * 創建一個大懸浮窗。位置為屏幕正中間。 * * @param context * 必須為應用程序的Context. */ // @SuppressWarnings("deprecation") public static void createBigWindow(Context context) { WindowManager windowManager = getWindowManager(context); int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); if (bigWindowActivity == null) { bigWindowActivity = new BigWindowActivity(context); if (bigWindowParams == null) { bigWindowParams = new LayoutParams(); bigWindowParams.x = screenWidth / 3 - BigWindowActivity.viewWidth / 3; bigWindowParams.y = screenHeight / 3 - BigWindowActivity.viewHeight / 3; bigWindowParams.type = LayoutParams.TYPE_PHONE; bigWindowParams.format = PixelFormat.RGBA_8888; bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP; bigWindowParams.width = BigWindowActivity.viewWidth; bigWindowParams.height = BigWindowActivity.viewHeight; } windowManager.addView(bigWindowActivity, bigWindowParams); } } /** * 將小懸浮窗從屏幕上移除。 * abstract void removeViewImmediate(View view);//是removeView(View) 的一個特殊擴展, * 在方法返回前能夠立即調用該視圖層次的View.onDetachedFromWindow() 方法。 * @param context * 必須為應用程序的Context. */ public static void removeSmallWindow(Context context) { if (smallWindowActivity != null) { WindowManager windowManager = getWindowManager(context); windowManager.removeView(smallWindowActivity);//移除懸浮窗口 smallWindowActivity = null; } } /** * 將大懸浮窗從屏幕上移除。 * * @param context * 必須為應用程序的Context. */ public static void removeBigWindow(Context context) { if (bigWindowActivity != null) { WindowManager windowManager = getWindowManager(context); windowManager.removeView(bigWindowActivity); bigWindowActivity = null; } } /** * 是否有懸浮窗(包括小懸浮窗和大懸浮窗)顯示在屏幕上。 * * @return 有懸浮窗顯示在桌面上返回true,沒有的話返回false。 */ public static boolean isWindowShowing() { return smallWindowActivity != null || bigWindowActivity != null; //return smallWindowActivity != null; } /** * 如果WindowManager還未創建,則創建一個新的WindowManager返回。否則返回當前已創建的WindowManager。 * * @param context * 必須為應用程序的Context. * @return WindowManager的實例,用於控制在屏幕上添加或移除懸浮窗。 */ private static WindowManager getWindowManager(Context context) { if (mWindowManager == null) { mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } return mWindowManager; } /** * 計算已使用內存的百分比,並返回。 * * @param context * 可傳入應用程序上下文。 * @return 已使用內存的百分比,以字符串形式返回。 */ public static String getUsedPercentValue(Context context) { String dir = "/proc/meminfo"; try { FileReader fr = new FileReader(dir); BufferedReader br = new BufferedReader(fr, 2048); String memoryLine = br.readLine(); String subMemoryLine = memoryLine.substring(memoryLine.indexOf("MemTotal:")); br.close(); long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll("\\D+", "")); long availableSize = getAvailableMemory(context) / 1024; int percent = (int) ((totalMemorySize - availableSize) / (float) totalMemorySize * 100); return percent + "%"; } catch (IOException e) { e.printStackTrace(); } return "懸浮窗"; } /** * 更新小懸浮窗的TextView上的數據,顯示內存使用的百分比。 * * @param context * 可傳入應用程序上下文。 */ public static void updateUsedPercent(Context context) { if (smallWindowActivity != null) { TextView percentView = (TextView) smallWindowActivity.findViewById(R.id.percent); percentView.setText(getUsedPercentValue(context)); } } /** * 獲取當前可用內存,返回數據以字節為單位。 * * @param context * 可傳入應用程序上下文。 * @return 當前可用內存。 */ private static long getAvailableMemory(Context context) { ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); getActivityManager(context).getMemoryInfo(mi); return mi.availMem; } /** * 如果ActivityManager還未創建,則創建一個新的ActivityManager返回。否則返回當前已創建的ActivityManager。 * * @param context * 可傳入應用程序上下文。 * @return ActivityManager的實例,用於獲取手機可用內存。 */ private static ActivityManager getActivityManager(Context context) { if (mactivityManager == null) { mactivityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); } return mactivityManager; } }4、小窗口的代碼繼承了LinearLayout,可實現手動觸摸移動,分別設置了手指點擊下、移動和離開的處理。實現代碼為:
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //手指按下時記錄必要數據,縱坐標的值都需要減去狀態欄高度 xInView = event.getX(); yInView = event.getY(); xDownInScreen = event.getRawX(); yDownInScreen = event.getRawY() - getStatusBarHeight(); xInScreen = event.getRawX(); yInScreen = event.getRawY() - getStatusBarHeight(); break; case MotionEvent.ACTION_MOVE: xInScreen = event.getRawX(); yInScreen = event.getRawY()-getStatusBarHeight(); //手指一動的時候就更新懸浮窗位置 updateViewPosition(); break; case MotionEvent.ACTION_UP: //如果手指離開屏幕時,xDownInScreen和xInScreen相等,且yDownInScreen == yInScreen //則視為觸發 if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) { MyWindowManager.createBigWindow(getContext());//創建大窗口 MyWindowManager.removeSmallWindow(getContext());//移除小窗口 Toast.makeText(getContext(), "手指離開屏幕!", Toast.LENGTH_SHORT).show(); } break; default: break; } return true; }完整代碼:
package com.example.suspend; import android.content.Context; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import android.view.WindowManager; import java.lang.reflect.Field; public class SmallWindowActivity extends LinearLayout { public static int viewWidth;//小懸浮寬度 public static int viewHeight;//小懸浮高度 private static int statusBarHeight;//狀態欄高度 private WindowManager windowManager;//更新小懸浮的位置 private WindowManager.LayoutParams mParams;//小懸浮高度 private float xInScreen;//記錄當前手指位置在屏幕上的橫坐標值 private float yInScreen;//記錄當前手指位置在屏幕上的縱坐標值 private float xDownInScreen;//記錄手指按下時在屏幕上的橫坐標的值 private float yDownInScreen;//記錄手指按下時在屏幕上的縱坐標的值 private float xInView;//記錄手指按下時在小懸浮窗的View上的橫坐標的值 private float yInView;//記錄手指按下時在小懸浮窗的View上的縱坐標的值 /** * * @param context */ public SmallWindowActivity(Context context) { super(context); windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.activity_small_window,this); View view = findViewById(R.id.small_window_layout); //獲取手機屏幕寬高 viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; //顯示手機內存空間百分比 TextView percentView = (TextView)findViewById(R.id.percent); percentView.setText(MyWindowManager.getUsedPercentValue(context)); } /** * 手指觸摸屏幕處理 */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //手指按下時記錄必要數據,縱坐標的值都需要減去狀態欄高度 xInView = event.getX(); yInView = event.getY(); xDownInScreen = event.getRawX(); yDownInScreen = event.getRawY() - getStatusBarHeight(); xInScreen = event.getRawX(); yInScreen = event.getRawY() - getStatusBarHeight(); break; case MotionEvent.ACTION_MOVE: xInScreen = event.getRawX(); yInScreen = event.getRawY()-getStatusBarHeight(); //手指一動的時候就更新懸浮窗位置 updateViewPosition(); break; case MotionEvent.ACTION_UP: //如果手指離開屏幕時,xDownInScreen和xInScreen相等,且yDownInScreen == yInScreen //則視為觸發 if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) { MyWindowManager.createBigWindow(getContext());//創建大窗口 MyWindowManager.removeSmallWindow(getContext());//移除小窗口 Toast.makeText(getContext(), "手指離開屏幕!", Toast.LENGTH_SHORT).show(); } break; default: break; } return true; } //將懸浮窗的參數傳入,用於更新小懸浮窗的位置 public void setParams(WindowManager.LayoutParams params) { mParams = params; } //更新小懸浮窗在屏幕中的位置 private void updateViewPosition() { mParams.x = (int) (xInScreen - xInView); mParams.y = (int) (yInScreen - yInView); windowManager.updateViewLayout(this, mParams); } /** * 用於獲取狀態欄高度 * @return 返回狀態欄高度的像素值 */ private int getStatusBarHeight() { if (statusBarHeight == 0) { try { Class c = Class.forName("com.android.internal.R$dimen"); Object o = c.newInstance(); Field field = c.getField("status_bar_height"); int x = (Integer) field.get(o); statusBarHeight = getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } } return statusBarHeight; } }
小窗口僅實現了顯示內存百分比,添加了一個TextView,小窗口的布局代碼為:
5、當點擊小窗口的時候添加了監聽就是當手指離開的時候啟動創建一個另一個窗口然後關閉本窗口。
大窗口也是實現點擊可以隨意移動,還添加了兩個按鈕的功能,發送短信和返回。實現代碼為:
package com.example.suspend; import android.content.Context; import android.content.Intent; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; public class BigWindowActivity extends LinearLayout { //記錄大懸浮窗的寬度 public static int viewWidth; //記錄大懸浮窗高度 public static int viewHeight; private Button phone,sms,app,music; int screenWidth,screenHeight; int lastX,lastY;//記錄移動的最後的位置 int dx,dy; public BigWindowActivity(final Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.big_window, this); View view = findViewById(R.id.big_window_layout); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; initUI(); } private void initUI() { // TODO Auto-generated method stub //獲取屏幕的分辨率 DisplayMetrics dm = getResources().getDisplayMetrics(); screenWidth = dm.widthPixels; screenHeight = dm.heightPixels-50; Button back = (Button)findViewById(R.id.fanhui); //添加觸摸監聽 back.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub //獲取Action int ea = event.getAction(); Log.i("TAG","Touch:"+ea); switch (ea){ case MotionEvent.ACTION_DOWN: lastX = (int)event.getRawX(); lastY = (int)event.getRawY(); break; case MotionEvent.ACTION_MOVE: //移動中動態設置位置 mobilesetting(v,event); break; case MotionEvent.ACTION_UP://當手指離開的時候執行 if (dx==dy){ comeback(getContext()); } break; } return false; } }); sms = (Button)findViewById(R.id.SMS); sms.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub int ea = event.getAction(); Log.i("TAG","Touch:"+ea); switch (ea){ case MotionEvent.ACTION_DOWN: lastX = (int)event.getRawX(); lastY = (int)event.getRawY(); break; case MotionEvent.ACTION_MOVE: //移動中動態設置位置 mobilesetting(v,event); break; case MotionEvent.ACTION_UP://當手指離開的時候執行 if (dx==dy){ sms(); } break; } return false; } }); } /** * 點擊返回移除大窗口創建小窗口 * @param context */ public void comeback(Context context){ // 點擊返回的時候,移除大懸浮窗,創建小懸浮窗 MyWindowManager.removeBigWindow(context); MyWindowManager.createSmallWindow(context); } //發短信 public void sms(){ Intent it = new Intent(Intent.ACTION_VIEW); it.putExtra("sms_body", "The SMS text"); it.setType("vnd.android-dir/mms-sms"); it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(it); comeback(getContext()); } /** * 移動控件設置位置 * @param v * @param event */ public void mobilesetting(View v,MotionEvent event){ //移動中動態設置位置 dx = (int)event.getRawX()-lastX;//移動中x當前位置 dy = (int)event.getRawY()-lastY; int left = v.getLeft()+dx; int top = v.getTop()+dy; int right = v.getRight()+dx; int bottom = v.getBottom()+dy; if(left<0){ left=0; right = left+v.getWidth();//0 } if (right>screenWidth){ right = screenWidth; left = right - v.getWidth();//max } if(top < 0){ top = 0; bottom = top + v.getHeight(); } if(bottom > screenHeight){ bottom = screenHeight; top = bottom - v.getHeight(); } v.layout(left, top, right, bottom); //將當前的位置再次設置 lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); } // @Override // public boolean onTouchEvent(MotionEvent event) { // // TODO Auto-generated method stub // //點擊屏幕彈出的框會消失 // //popupWindow.dismiss(); // return super.onTouchEvent(event); // } }布局代碼為四個按鈕控件:
控件的實現效果是在xml裡面添加了shape.xml,設置了控件的顏色跟形狀。
6、使用到的權限為:
實現的效果圖:
這幾天對Android中實現畫圓弧及圓弧效果中所實現的效果進行了修改,改為進度圓心進度條,效果如圖所示TasksCompletedView.java 代碼如下import
幾個月沒有碰Android Studio了,打開時卻突然出現了這樣的錯誤:我可是百事不得其解啊!我最後一次使用的時候都好好的,現在居然說我的Java環境變量有問題。我一看
用了微信sdk各種痛苦,感覺比qq sdk調用麻煩多了,回調過於麻煩,還必須要在指定包名下的actvity進行回調,所以我在這裡寫一篇博客,有這個需求的朋友可以借鑒一下,
這裡記錄一個比較方便的方式來解決Textview設置不同顏色的字體的方法。可能第一反應是布局的嵌套,這個方法肯定可以啊,但是肯定不推薦啊,布局要盡量減少布局的嵌套,其次,