Android 懸浮在Activity內的可拖動View
阿新 • • 發佈:2019-01-31
開發中經常會遇到要做一個可以懸浮在頁面內的View,以便展示活動或者提醒什麼的,跳出頁面懸浮的View就消失。一種方法是利用純程式碼後臺生成的方式,利用WindowManager和Imageview實現圖片的懸浮。但是這種方法需要SYSTEM_ALERT_WINDOW許可權,國內很多深度定製的系統,像MIUI,EMUI,Flyme等會把這個許可權關閉。因此這些系統下面就顯示不出來了,需要一直提醒使用者去開啟這個許可權,使用者體驗很不好。另一種比較簡單的方法就是直接在佈局裡面把View放置在固定的位置,這種方法有個弊端,就是不能拖動,會擋住下面的內容。
最近忙裡偷閒參考幾個技術大神寫的分散的功能,自己融合了一個可以自由拖動的懸浮窗效果,可以載入圖片,吸附到螢幕邊緣。這個只能在Activity內使用 ,App內和桌面懸浮還是建議使用系統的懸浮窗。話不多說,下面上程式碼。
首先在onMeasure中獲取可拖動區域的寬和高
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize,然後重寫onDraw方法繪製出來一個色塊,這時候是不能拖動heightSize); width = widthSize; heigh = heightSize; rect = new Rect(width-WIDTH, heigh/2-WIDTH/2, width, heigh/2+WIDTH/2);//繪製矩形的區域,靠拖動區域右邊居中顯示 }
protected void onDraw(Canvas canvas) { if (mBitmap != null){ Rect rectF = new Rect(); rectF.set(0,0,mBitmap.getWidth(),要想拖動就只能處理觸控事件了mBitmap.getHeight()); canvas.drawBitmap(mBitmap, rectF, rect, paint); }else { paint.setColor(Color.RED);//填充紅色 canvas.drawRect(rect, paint);//畫矩形 } }
public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch(event.getAction()) { case MotionEvent.ACTION_DOWN: if(!rect.contains(x, y)) { return false;//沒有在矩形上點選,不處理觸控訊息 } mStartX = x; mStartY = y; deltaX = x - rect.left; deltaY = y - rect.top; break; case MotionEvent.ACTION_MOVE: Rect old = new Rect(rect); //更新矩形的位置 rect.left = x - deltaX; if (rect.left < 0){ rect.left = 0; } rect.top = y - deltaY; if (rect.top < 0){ rect.top = 0; } rect.right = rect.left + WIDTH; if (rect.right > width){ rect.right = width; rect.left = width - WIDTH; } rect.bottom = rect.top + WIDTH; if (rect.bottom > heigh){ rect.bottom = heigh; rect.top = heigh - WIDTH; } old.union(rect);//要重新整理的區域,求新矩形區域與舊矩形區域的並集 invalidate(old);//出於效率考慮,設定髒區域,只進行區域性重新整理,不是重新整理整個view break; case MotionEvent.ACTION_UP: Rect oldl = new Rect(rect); //更新矩形的位置,吸附左右邊界處理 if (rect.left + WIDTH / 2 < width / 2){ rect.left = 0; }else { rect.left = width - WIDTH; } rect.top = y - deltaY; if (rect.top < 0){ rect.top = 0; } rect.right = rect.left + WIDTH; rect.bottom = rect.top + WIDTH; if (rect.bottom > heigh){ rect.bottom = heigh; rect.top = heigh - WIDTH; } oldl.union(rect); invalidate(oldl); if (Math.abs(mStartX - x) < 10 && Math.abs(y - mStartY) < 10) {//捕捉點選事件 if (mClickListener != null) { mClickListener.onClick(this); } } break; } return true;//處理了觸控訊息,訊息不再傳遞 }註釋中都寫了,我就不再講解了,到這裡色塊就可以拖動了,我把它做了吸附在邊界的處理,如果不需要可以把程式碼註釋掉
後面就是載入圖片的處理了,就不貼具體程式碼了,直接上完整程式碼
/** * Created by ton on 17/5/18. * 懸浮可拖拽View */ public class DragView extends View { private int WIDTH = 160;//拖動色塊的大小 private int heigh, width; private Rect rect = new Rect(0, 0, WIDTH, WIDTH);//繪製矩形的區域 private int deltaX,deltaY;//點選位置和圖形邊界的偏移量 private static Paint paint = new Paint();//畫筆 private Bitmap mBitmap = null; private String imgUrl; private OnClickListener mClickListener; private int mStartX,mStartY; public DragView(Context context, AttributeSet attrs) { super(context, attrs); WIDTH = 150; paint = new Paint(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); width = widthSize; heigh = heightSize; rect = new Rect(width-WIDTH, heigh/2-WIDTH/2, width, heigh/2+WIDTH/2);//繪製矩形的區域 } /** * Implement this to do your drawing. * * @param canvas the canvas on which the background will be drawn */ @Override protected void onDraw(Canvas canvas) { if (mBitmap != null){ Rect rectF = new Rect(); rectF.set(0,0,mBitmap.getWidth(),mBitmap.getHeight()); canvas.drawBitmap(mBitmap, rectF, rect, paint); }else { paint.setColor(Color.RED);//填充紅色 canvas.drawRect(rect, paint);//畫矩形 } } @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch(event.getAction()) { case MotionEvent.ACTION_DOWN: if(!rect.contains(x, y)) { return false;//沒有在矩形上點選,不處理觸控訊息 } mStartX = x; mStartY = y; deltaX = x - rect.left; deltaY = y - rect.top; break; case MotionEvent.ACTION_MOVE: Rect old = new Rect(rect); //更新矩形的位置 rect.left = x - deltaX; if (rect.left < 0){ rect.left = 0; } rect.top = y - deltaY; if (rect.top < 0){ rect.top = 0; } rect.right = rect.left + WIDTH; if (rect.right > width){ rect.right = width; rect.left = width - WIDTH; } rect.bottom = rect.top + WIDTH; if (rect.bottom > heigh){ rect.bottom = heigh; rect.top = heigh - WIDTH; } old.union(rect);//要重新整理的區域,求新矩形區域與舊矩形區域的並集 invalidate(old);//出於效率考慮,設定髒區域,只進行區域性重新整理,不是重新整理整個view break; case MotionEvent.ACTION_UP: Rect oldl = new Rect(rect); //更新矩形的位置 if (rect.left + WIDTH / 2 < width / 2){ rect.left = 0; }else { rect.left = width - WIDTH; } rect.top = y - deltaY; if (rect.top < 0){ rect.top = 0; } rect.right = rect.left + WIDTH; rect.bottom = rect.top + WIDTH; if (rect.bottom > heigh){ rect.bottom = heigh; rect.top = heigh - WIDTH; } oldl.union(rect); invalidate(oldl); if (Math.abs(mStartX - x) < 10 && Math.abs(y - mStartY) < 10) {//捕捉點選事件 if (mClickListener != null) { mClickListener.onClick(this); } } break; } return true;//處理了觸控訊息,訊息不再傳遞 } @Override public void setOnClickListener(OnClickListener l) { this.mClickListener = l; } /*** * 載入資源圖片 * @param resId */ public void setImageResource(int resId){ InputStream is = getContext().getResources().openRawResource(resId); Bitmap bmp = BitmapFactory.decodeStream(is); //圖片重新裁剪,原圖從中心點按顯示大小裁剪 int bw = bmp.getWidth(), bh = bmp.getHeight(); int w = WIDTH, h = WIDTH; if (bw / w >= bh / h) { mBitmap = Bitmap.createBitmap(bmp,(bw-bh)/2,0,w*bh/h,bh); } else { mBitmap = Bitmap.createBitmap(bmp,0,(bh-bw)/2,bw,h*bw/w); } invalidate(); } /**** * 使用網路圖片 * @param imageUrl */ public void setImageUrl(String imageUrl){ if (imageUrl.startsWith("http://")||imageUrl.startsWith("https://")){ imgUrl = imageUrl; }else { Log.d("DragViewDemo","image url error !"); return; } if (mBitmap == null && imgUrl.length() > 0){ ImageLoader.getInstance().loadImage(imgUrl, new ImageLoadingListener() { @Override public void onLoadingStarted(String s, View view) { } @Override public void onLoadingFailed(String s, View view, FailReason failReason) { } @Override public void onLoadingComplete(String s, View view, Bitmap bitmap) { int bw = bitmap.getWidth(), bh = bitmap.getHeight(); int w = WIDTH, h = WIDTH; if (bw / w >= bh / h) { mBitmap = Bitmap.createBitmap(bitmap,(bw-bh)/2,0,w*bh/h,bh); } else { mBitmap = Bitmap.createBitmap(bitmap,0,(bh-bw)/2,bw,h*bw/w); } invalidate(); } @Override public void onLoadingCancelled(String s, View view) { } }); } } }專案已上傳到GitHub,有需要的自己去下載Git地址。怕磚,各位輕點,多多指教~
參考資料:https://www.oschina.net/code/snippet_54100_6262
http://blog.csdn.net/tianjian4592/article/details/45031663
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); width = widthSize; heigh = heightSize; rect = new Rect(width-WIDTH, heigh/2-WIDTH/2, width, heigh/2+WIDTH/2);//繪製矩形的區域 }