1.问题产生
在PopupWindow中放置一个ListView,点击Button后希望让PopupWindow显示在Button正上方。实现ListPopWindow继承PopupWindow,位置显示要求在正上方。
public void showPopWindow(View parent) {
if (!isShowing()) {
int[] location = new int[2];
parent.getLocationOnScreen(location);
showAtLocation(parent, Gravity.NO_GRAVITY,
location[0] -getContentView().getMeasuredWidth()/2 + parent.getMeasuredWidth()/2,
location[1]- getContentView().getMeasuredHeight());
Log.v("TAG", location[1] + "");
Log.v("TAG", getContentView().getMeasuredHeight() +"");
} else {
dismiss();
}
}
这时候PopupWindow的长和宽等于ListView的长和宽。ListView的长和宽都设置为wrap_context,PopupWindow的布局也设置为:
setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
这时候发现,PopupWindow显示出来后没有显示在Butoon正上方,并且ListView没有wrap_context,每一行显示了很多留白。
说明了两个问题
1) getContentView().getMeasuredWidth()和getContentView().getMeasuredHeight()不能获得ListView的真实高度和宽度。
2) ListView和PopupWindow都设置wrap_context是无效的。
看来一些原因,但是没有看到官方的解释。如果有人知道,请给予我评论。
2.遍历ListView中的所有item计算ListView的长度和宽度
参考:http://blog.lovelyhq.com/setting-listview-height-depending-on-the-items/
初始化ListView和Adapter后,最关键的计算函数是:
**
返回ListView中所有ItemView的最大值和所有ItemView的高度
*/
public int[] dynamicMatrix(){
intmaxWidth=Integer.MIN_VALUE;//ListView中所有ItemView的最大值
int totalItemsHeight=0;//统计所有ItemView高度
View itemView=null;
int masureSpec= View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
intcount=adapter.getCount();
for (inti=0;i<count;i++){
itemView=adapter.getView(i,itemView,listView);
itemView.measure(masureSpec,masureSpec);
intitemWidth=itemView.getMeasuredWidth();
if(itemWidth>maxWidth){
maxWidth=itemWidth;
}
intitemHeight=itemView.getMeasuredHeight();
totalItemsHeight+=itemHeight;
}
maxWidth+=listView.getPaddingLeft()+listView.getPaddingRight();//添加Padding
totalItemsHeight+=listView.getDividerHeight()*(count-1);//添加Divider
int[]matrix={maxWidth,totalItemsHeight};
return matrix;
}
将ListView设置成这个高度和宽度即可。
ViewGroup.LayoutParamsparams=listView.getLayoutParams();
params.width=matrix[0];
params.height=matrix[1];
listView.setLayoutParams(params);
PopupWindow则通过updata()函数来更新显示(offsetX和offsetY即PopupWindow的长和宽)。
update(location[0]-offsetX/2+parent.getMeasuredWidth()/2,
location[1]-offsetY,matrix[0],matrix[1]);
注意,如果计算的最大宽度大于屏幕宽度,那么ListView宽度会充满屏幕,大于屏幕宽度的地方并换行。如果高度大于屏幕,ListView高度会充满屏幕并换行。所以要在这种情况下进行限制。下面的这个Demo将PopWindow显示在按钮的正上方,即判断了这种情况,如果大于屏幕尺寸,则进行了限制。
3. Demo示例
3.1PopupWindow基类
/**
* Created by CaoYanfeng on2015/10/26.
* <p>对于PoPWindow的常用属性进行设置,包括:</>
* <p>1、内置的View</>
* <p>2、int width, intheight</>
* <p>3、动画方式</>
*/
public abstract class BasePopWindow extends PopupWindow {
protected Context context;
protected View view;//PopWindow的View
BasePopWindow(Contextcontext,int layoutId){
this(context,layoutId,ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}
/**
构造函数1,宽度和高度都是WRAP_CONTENT类型
*/
BasePopWindow(Contextcontext,int layoutId,int width, int height){
this.context=context;
view=LayoutInflater.from(context).inflate(layoutId, null);
setContentView(view);
view.measure(View.MeasureSpec.UNSPECIFIED,View.MeasureSpec.UNSPECIFIED);
// 设置可以获得焦点
setFocusable(true);
// 设置弹窗内可点击
setTouchable(true);
// 设置弹窗外可点击
setOutsideTouchable(true);
setWidth(width);
setHeight(height);
setExtra();
/*Updates the state of thepopup window,if it is currently being displayed, from the currently
set state.
更新6个事件,包括动画设置
*/
update();
}
/**
设置额外属性,主要包括动画方式,背景颜色等
*/
public abstract void setExtra();
/**
通过view.findViewById()寻找PopupWindow中View view内的组件,为其添加监听事件
*/
public abstract voidinitWidgets();
public abstract voidshowPopWindow(View parent);
}
3.2ListPopWindow
/**
* Created by CaoYanfeng on2015/10/26.
*/
public abstract class ListPopWindow extends BasePopWindow{
protected ListView listView;
protected BaseAdapter adapter;
private int offsetX,offsetY;
ListPopWindow(Contextcontext,int layoutId) {
super(context, layoutId);
initWidgets();
}
/**
更新PopWindow的宽和高
*/
public void updatePopWindow(Viewparent) {
int[]matrix=dynamicMatrix();
WindowManager wm =(WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
int mScreenWidth =wm.getDefaultDisplay().getWidth();
int mScreenHeight =wm.getDefaultDisplay().getHeight();
if(matrix[0]>mScreenWidth-listView.getPaddingRight()-listView.getPaddingLeft()){
matrix[0]=mScreenWidth-listView.getPaddingRight()-listView.getPaddingLeft();
}
int[] location = new int[2];
parent.getLocationOnScreen(location);
//200像素是估算的ActionBar的高度
if(matrix[1]>location[1]-200){
matrix[1]=location[1]-200;
}
ViewGroup.LayoutParamsparams=listView.getLayoutParams();
params.width=matrix[0];
params.height=matrix[1];
listView.setLayoutParams(params);
listView.requestLayout();
offsetX=matrix[0];
offsetY=matrix[1];
update(location[0]-offsetX/2+parent.getMeasuredWidth()/2,
location[1]-offsetY,matrix[0],matrix[1]);
}
/**
设置额外属性,主要包括动画方式,背景颜色
*/
@Override
public void setExtra() {
ColorDrawable colorDrawable= new ColorDrawable(Color.WHITE);
setBackgroundDrawable(colorDrawable);
setAnimationStyle(R.style.mapLayersAnim);
}
@Override
public abstract voidinitWidgets();
@Override
public void showPopWindow(Viewparent) {
if (!isShowing()) {
int[] location = newint[2];
parent.getLocationOnScreen(location);
showAtLocation(parent,Gravity.NO_GRAVITY,
location[0]-offsetX/ 2 + parent.getMeasuredWidth()/2,
location[1]-offsetY);
Log.v("TAG",location[1] + "");
Log.v("TAG",getContentView().getMeasuredHeight() + "");
} else {
dismiss();
}
}
//获得ListView的长度和最大宽度
public int[] dynamicMatrix(){
intmaxWidth=Integer.MIN_VALUE;
int totalItemsHeight=0;
View itemView=null;
int itemType=0;
int masureSpec=View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
intcount=adapter.getCount();
for (inti=0;i<count;i++){
intpositionType=adapter.getItemViewType(i);
if(positionType!=itemType){
itemType=positionType;
itemView=null;
}
itemView=adapter.getView(i,itemView,listView);
itemView.measure(masureSpec,masureSpec);
intitemWidth=itemView.getMeasuredWidth();
if(itemWidth>maxWidth){
maxWidth=itemWidth;
}
intitemHeight=itemView.getMeasuredHeight();
totalItemsHeight+=itemHeight;
}
maxWidth+=listView.getPaddingLeft()+listView.getPaddingRight();
totalItemsHeight+=listView.getDividerHeight()*(count-1);
int[]matrix={maxWidth,totalItemsHeight};
return matrix;
}
}
4. 最后的问题
可以显示PopupWindow再update它的尺寸,就像demo中做得那样。因为在new PopupWindow的时候构造函数必须传入尺寸,而这个时候还没能计算出真实的尺寸。
mListPopWindow.showPopWindow(v);//v即按钮
mPopWindow.updatePopWindow(v);