popuwindow

PopuWindow

前言

我只是想学习一下

PopuWindow

实现popuwindow的语法。
1. 首先唤醒一个布局
View contentView = LayoutInflater.from(this).inflate(R.layout.popuwindow_layout, null);
2. 然后创建一个popuwindow
PopupWindow popupWindow = new PopupWindow(contentView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);

第一个参数为popuwindow设置一个布局因为popuwindow没有自带的布局,二三 参数是设置宽高,第四个是设置是否取到焦点。
当然popuwindow的构造函数可是很多的

比如下面

显示popuwindow
  1. 唤醒了布局,创建了popuwindow最后就是显示了。 显示的方法都在这里

解释一下
//相对某个控件的位置(正左下方),无偏移
showAsDropDown(View anchor):

相对某个控件的位置,有偏移;xoff表示x轴的偏移,正值表示向左,负值表示向右;yoff表示相对y轴的偏移,正值是向下,负值是向上;
showAsDropDown(View anchor, int xoff, int yoff, int gravity)

相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移
showAtLocation(View parent, int gravity, int x, int y):

// 显示在v这个空间的下方
popupWindow.showAsDropDown(v);

// 可以显示在mCoor布局的任何地方

popupWindow.showAtLocation(mCoor, Gravity.BOTTOM, 0, 0);

这样的话可以成功显示了但是这里有一个bug就是不设置背景的时候会出现一个问题就是只可以点popuwindow其他地方都不可以点,包括后退键。

所以我们应该设置背景
popupWindow.setBackgroundDrawable(new BitmapDrawable(getResources(), BitmapFactory.decodeFile(null)));

至于为什么出现这种情况 我们需要看一下源码 那个后一点再说

为popuwindow加动画

首先定义一下显示动画

一个简单的透明度显示

出现时候的动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <alpha
        android:duration="5000"
        android:fromAlpha="0"
        android:toAlpha="1"/>
</set>  

消失时候的动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
   <alpha
       android:duration="5000"
       android:fromAlpha="1"
       android:toAlpha="0"/>
</set>

然后在style中定义

<style name="contextMenuAnim" parent="@android:style/Animation.Activity">
    <item name="android:windowEnterAnimation">@anim/anim</item>
    <item name="android:windowExitAnimation">@anim/anim2</item>
</style>

然后在文件中设置
popupWindow.setAnimationStyle(R.style.contextMenuAnim);

在这里说明一下为什么要为popuwindow添加背景

源码

点进去 setBackgroundDrawable 源码太长了 只显示一小部分

 public void setBackgroundDrawable(Drawable background) {
        mBackground = background;
}

然后我们看看在显示的时候有没有用到mBackground

popuWindow在显示的时候调用preparePopup(p);

public void showAtLocation(IBinder token, int gravity, int x, int y) {
    if (isShowing() || mContentView == null) {
        return;
    }

    TransitionManager.endTransitions(mDecorView);

    unregisterForScrollChanged();

    mIsShowing = true;
    mIsDropdown = false;

    final WindowManager.LayoutParams p = createPopupLayoutParams(token);
    preparePopup(p);

    // Only override the default if some gravity was specified.
    if (gravity != Gravity.NO_GRAVITY) {
        p.gravity = gravity;
    }

    p.x = x;
    p.y = y;

    invokePopup(p);
}

我们跟着点进去(其实是搜索mBackground得到的)

    private void preparePopup(WindowManager.LayoutParams p) {
        if (mContentView == null || mContext == null || mWindowManager == null) {
            throw new IllegalStateException("You must specify a valid content view by "
                    + "calling setContentView() before attempting to show the popup.");
        }

        // The old decor view may be transitioning out. Make sure it finishes
        // and cleans up before we try to create another one.
        if (mDecorView != null) {
            mDecorView.cancelTransitions();
        }

        // When a background is available, we embed the content view within
        // another view that owns the background drawable.
        if (mBackground != null) {
            mBackgroundView = createBackgroundView(mContentView);
            mBackgroundView.setBackground(mBackground);
        } else {
            mBackgroundView = mContentView;
        }

        mDecorView = createDecorView(mBackgroundView);

        // The background owner should be elevated so that it casts a shadow.
        mBackgroundView.setElevation(mElevation);

        // We may wrap that in another view, so we'll need to manually specify
        // the surface insets.
        final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
        p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
        p.hasManualSurfaceInsets = true;

        mPopupViewInitialLayoutDirectionInherited =
                (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
        mPopupWidth = p.width;
        mPopupHeight = p.height;
    }

看看这一个

if (mBackground != null) {
        mBackgroundView = createBackgroundView(mContentView);
        mBackgroundView.setBackground(mBackground);
    } else {
        mBackgroundView = mContentView;
    }

当背景为空的时候mBackgroundView = mContentView;

mContentView什么 先看看
我找到了它在这里赋值

 public void setContentView(View contentView) {
    if (isShowing()) {
        return;
    }

    mContentView = contentView;
        。
        。
        。
    省      略

}

然后这个方法在这里调用

public PopupWindow(View contentView, int width, int height, boolean focusable) {
    if (contentView != null) {
        mContext = contentView.getContext();
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    }

    setContentView(contentView);
    setWidth(width);
    setHeight(height);
    setFocusable(focusable);
}

可以看到mContentView就是popuwindow的布局,那么说背景为空时就会套上一层布局

然后我们看看这层布局干了什么

一步步去查找

用背景创建了什么

mDecorView = createDecorView(mBackgroundView);

继续点击

 private PopupDecorView createDecorView(View contentView) {
    final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
    final int height;
    if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
        height = ViewGroup.LayoutParams.WRAP_CONTENT;
    } else {
        height = ViewGroup.LayoutParams.MATCH_PARENT;
    }

    final PopupDecorView decorView = new PopupDecorView(mContext);
    decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
    decorView.setClipChildren(false);
    decorView.setClipToPadding(false);

    return decorView;
}

返回decorView 而这个又干了些什么继续点final PopupDecorView decorView = new PopupDecorView(mContext);

好了最终大BOSS出现了

 private class PopupDecorView extends FrameLayout {
    private TransitionListenerAdapter mPendingExitListener;

    public PopupDecorView(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            if (getKeyDispatcherState() == null) {
                return super.dispatchKeyEvent(event);
            }

            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
                final KeyEvent.DispatcherState state = getKeyDispatcherState();
                if (state != null) {
                    state.startTracking(event, this);
                }
                return true;
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                final KeyEvent.DispatcherState state = getKeyDispatcherState();
                if (state != null && state.isTracking(event) && !event.isCanceled()) {
                    dismiss();
                    return true;
                }
            }
            return super.dispatchKeyEvent(event);
        } else {
            return super.dispatchKeyEvent(event);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int x = (int) event.getX();
        final int y = (int) event.getY();

        if ((event.getAction() == MotionEvent.ACTION_DOWN)
                && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
            dismiss();
            return true;
        } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
            dismiss();
            return true;
        } else {
            return super.onTouchEvent(event);
        }
    }

详细分析请看http://blog.csdn.net/harvic880925/article/details/49278705

看了之后我的总结是没有设置setOutsideTouchable(false)将生效

参考http://blog.csdn.net/harvic880925/article/details/49278705

Demo下载地址
https://github.com/justafreshman/Popuwindow_Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值