PopupWindow的使用原理以及实现卡片效果
前记:
本人菜鸟一枚,第一次写博客,写得不好。不喜勿喷,有疑问的地方可以留言回复~。
PopupWindow的使用思路:
在说明他的相关使用方法之前,我们先理清一下它的使用思路。以方便我们针对性的研究各个问题。
一· 我们大致会使用PopupWindow的场景:
1.希望点击某些东西之后,能弹出一个独立的操作区域,以承载更多的内容。
2.满足一些比较不错的设计效果。
例如下面将提示删除的地方做成一个卡片的形状,并且再赋给它一个动画,那么它会满足material design的设计风格。一个卡片进入界面时从低端滑倒中端进入,退出时从中端滑倒上端退出。
二·PopupWindow的使用方法:
1. PopupWindow的构造方法:
这是PopupWindow的第一个构造方法,传入上下文Context。API中这个构造方法里写的是this(context,null);为什么与会有一个null,那是因为这个构造方法,实质上是调用了
从这个构造方法来看,他还是调用了本类中的其他构造方法。在往后追溯呢,实质上俩个参数的构造方法又调用
了三个参数的构造方法。该方法,我就不传图片了,我直接上API里的代码:
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); }其实从这个也能看出,它是调用了四个参数的构造方法,最后发现,其实四个参数的构造方法,才是真正的构造
方法:
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes); final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground); mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0); mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false); // Preserve default behavior from Gingerbread. If the animation is // undefined or explicitly specifies the Gingerbread animation style, // use a sentinel value. if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) { final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0); if (animStyle == R.style.Animation_PopupWindow) { mAnimationStyle = ANIMATION_STYLE_DEFAULT; } else { mAnimationStyle = animStyle; } } else { mAnimationStyle = ANIMATION_STYLE_DEFAULT; } final Transition enterTransition = getTransition(a.getResourceId( R.styleable.PopupWindow_popupEnterTransition, 0)); final Transition exitTransition; if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) { exitTransition = getTransition(a.getResourceId( R.styleable.PopupWindow_popupExitTransition, 0)); } else { exitTransition = enterTransition == null ? null : enterTransition.clone(); } a.recycle(); setEnterTransition(enterTransition); setExitTransition(exitTransition); setBackgroundDrawable(bg); }这个构造方法,我就不一一解释了,因为一些默认属性的设置以及相应初始化方法的步骤。我们会在后面
一点一点的代入进来。
当然PopupWindow
还有一个无参的构造方法,他实质上是调用了三参的构造方法。那么最终也会是调用四参的构造方法:
public PopupWindow() { this(null, 0, 0); }
以上是传入Context的构造方法,PopupWindow还有一种传入View的构造方法。后面我们会详细解释这个。
2.介绍完PopupWindow的构造方法,接下来,我们应该要去了解PopupWindow的组成。
PopupWindow实质上是一个比较特殊的组件,Android中的组件基本上都是继承自View或者ViewGroup。而ViewGroup是继承自View
那么也就是说,Android中的组件都是继承自View。但是PopupWindow是什么都不继承。所以PopupWindow里面没有什么onDraw方法,也没有什么
onSizeChanged之类的方法让我们可以自定义绘制或者更改组件效果的方法。但是我们可以通过setContentView的方法来设置PopupWindow里的内容
,通过
setBackgroundDrawable
(Drawable background)方法来设置PopupWindow的背景。
实质上从java的GUI编程中去理解它,
PopupWindow是一个容器。BackgroundDrawable就是容器的形状,而ContentView
就是容器里放的内容。
三.介绍完PopupWindow的构造方法和它的组成,接下来就是介绍PopupWindow的使用了。
1.使用PopupWindow,先在创建一个PopupWindow的实例,并且将要放入PopupWindow的内容View实例化。
// 实例化popupWindow this.popupWindow = new PopupWindow(mContext);
// 将要放入PopupWindow里的View Inflate出来
contentview = mInflater.inflate(R.layout.popup_process, null);
2.接下来就是相应的参数设置了。
通过PopupWindow的setWidth()和setHeight()方法设置PopupWindow的宽高。你可以设置直接的数值,也可以
设置MATCH_PARENT或者WRAP_CONTENT;
// 通过LayoutParams的内质静态变量来设置宽高 popupWindow.setWidth(FrameLayout.LayoutParams.MATCH_PARENT); // 通过自己赋相应的值进去 popupWindow.setHeight((int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 80, mContext.getResources().getDisplayMetrics()));
当然你还需要设置
PopupWindow的contentView,这也是非常重要的。至于设置contentView的宽高,可以通过很多种
方式:
第一种是在XML文件中设置你inflate进来的布局文件的宽高
第二种是通过将inflate出来的View的Params属性修改一下
接下来就是设置
PopupWindow的外框背景了,你可以放一个ColorDrawable,或者从res文件中获取一个Drawable文件
:
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
3.接下来是contentView的一些内容:前面提到的PopupWindow的构造方法中,最后提了一句,
PopupWindow的构造方法中拥有这样一些构造方法:
public PopupWindow(View contentView) { this(contentView, 0, 0); }这里我就不要一一赘述了,因为它的构造方法虽然多,但是最后还是调用很多参数的那个构造方法。
这里我提一下,我们知道
PopupWindow的构造方法中必须含有Context,那么这种构造方法的
Context是从哪里来的呢?
其实这样的构造方法,Context是来自于contentView里的Context;
同时我们也可以一步到位创建PopupWindow,就是调用
PopupWindow(View contentView,int width,
int Hegiht);直接将
PopupWindow的内容View和PupupWindow的宽高设置好。
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); }
4.
PopupWindow里的事件监听:
前面提到过PopupWindow的使用场景了。我们在使用场景中肯定会去监听一些事件,例如监听Button,EditText
等组件的相应事件。所以这里我们看看
PopupWindow中该如何实现相应的监听
第一个,我们如果要监听ContentView中的内容,我们必须要设置
popupWindow.setFocusable(true);如果不设置这个Focusable为True,那么用户点击contentView上的相应组件,会是无效的。甚至连EditText
的焦点获取都会失效。
如果我们要监听相应的组件。必须要先将相应的组件实例化出来,一定要通过contentView(你放在PoupWindow
里的View)
去调用findViewById()方法
:
TextView delete = (TextView) contentview.findViewById(R.id.popup_delete); TextView back = (TextView) contentview.findViewById(R.id.popup_back); TextView Content = (TextView) contentview.findViewById(R.id.popup_content);接下来就是常规操作了。什么SetOnclickListener,这里就不赘述了
其次,还有一个叫做
setOutsideTouchable()方法,这个方法的意思是设置除了ContenView以外的区域是否
可以Touch。
如果说,你想用户点击其他区域时,让PopupWindow消失。必须要这样写才会生效:
popupWindow.setOutsideTouchable(true); contentview.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { popupWindow.dismiss(); return true; } return false; } });
5.PopupWindow的显示位置:
PopupWindow最大的优势是在于,PopupWindow可以在任意位置显示,这是一个比较易控制的特殊组件。
它的显示方法有一下几种:
第一个方法:showAtLocation()的意思,是在指定View的区域显示,第一个参数的意思是被指定的View,
第二个参数的意思是相对于这个View的位置,例如Gravity.CENTER的意思就是PupopWindow相对于View的中间显示
,剩下俩个参数是相对组件Gravity设置基点之后的X,Y偏移量
剩下的三个方法,可以看出来,基本上类似的。我们从API源码来看区别:
public void showAsDropDown(View anchor) { showAsDropDown(anchor, 0, 0); }
public void showAsDropDown(View anchor, int xoff, int yoff) { showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY); }
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { if (isShowing() || mContentView == null) { return; } TransitionManager.endTransitions(mDecorView); attachToAnchor(anchor, xoff, yoff, gravity); mIsShowing = true; mIsDropdown = true; final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken()); preparePopup(p); final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, p.width, p.height, gravity); updateAboveAnchor(aboveAnchor); p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1; invokePopup(p); }
通过源码API来看,最后一种才是真正的实际上去操作的方法。我们就直接解释最后一个:
showAsDropDown()方法的意思是,位于某个View的下面显示,第一个是被附着的View,第二个和第三个是X和Y
轴的偏移量。第四个是Gravity,它的意思是相对于被附着的View的位置。
四.暂时就写这么多了。过些天在写关于实现卡片效果的PopupWindow的博客。第一次写博客,感觉很多东西讲
不完,因为我要把一个东西讲细,要延伸好多其它的知识点= =。感觉自己写的不是很好,有不对的地方或者疑问的
地方,可以直接发邮件给我
我的邮箱是:[email protected]
欢迎邮件我~