1. 程式人生 > >Android 懸浮在Activity內的可拖動View

Android 懸浮在Activity內的可拖動View

開發中經常會遇到要做一個可以懸浮在頁面內的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, 
heightSize); width = widthSize; heigh = heightSize; rect = new Rect(width-WIDTH, heigh/2-WIDTH/2, width, heigh/2+WIDTH/2);//繪製矩形的區域,靠拖動區域右邊居中顯示 }
然後重寫onDraw方法繪製出來一個色塊,這時候是不能拖動
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);//繪製矩形的區域
}