PopWindow的简单使用
网上看到很多popwindow的使用,但是每次去看了都需要自行归纳总结和修改完善,今天在此自我总结一下。
popwindow多的我就不介绍了,使用场景通常有以下:标题栏弹窗(功能按钮);底部弹窗(拍照/选取照片)等。然后就直接上手吧,以下介绍三个使用场景:
1.标题栏弹窗功能按钮
效果图如下(此为整理,由于在项目里面,所以就马了):
实现方式其实不难,既然是popwindow那肯定要自定义写一个了,以下为主要类,多的就不说了,也有注释,可以直接拷贝过去用:
public abstract class TitlePopupWindow extends PopupWindow { public Context mContext; // 列表弹窗的间隔 public final int LIST_PADDING = 10; // 实例化一个矩形 public Rect mRect = new Rect(); // 坐标的位置(x、y) public final int[] mLocation = new int[2]; // 屏幕的宽度和高度 public int mScreenWidth, mScreenHeight; // 判断是否需要添加或更新列表子类项 public boolean mIsDirty; // 位置不在中心 public int popupGravity = Gravity.NO_GRAVITY; // 弹窗子类项选中时的监听 public OnItemOnClickListener mItemOnClickListener; // 定义列表对象 public ListView mListView; // 定义弹窗子类项列表 public ArrayList<ActionItem> mActionItems = new ArrayList<ActionItem>(); public TitlePopupWindow(Context context) { // 设置布局的参数 this(context, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @SuppressLint("InflateParams") @SuppressWarnings("deprecation") public TitlePopupWindow(Context context, int width, int height) { this.mContext = context; // 设置可以获得焦点 setFocusable(true); // 设置弹窗内可点击 setTouchable(true); // 设置弹窗外可点击 setOutsideTouchable(true); // 获得屏幕的宽度和高度 mScreenWidth = Util.getScreenWidth(mContext); mScreenHeight = Util.getScreenHeight(mContext); // 设置弹窗的宽度和高度 setWidth(width); setHeight(height); setBackgroundDrawable(new BitmapDrawable()); // 设置弹窗的布局界面 setContentView(mContext); getListView(); initUI(mListView, mItemOnClickListener, mActionItems); } /** * item的listView布局文件 */ public abstract void getListView(); /** * 设置弹窗的布局界面(添加弹窗布局) */ @SuppressLint("InflateParams") public void setContentView(Context mContext) { // 设置弹窗的布局界面 setContentView(LayoutInflater.from(mContext).inflate( R.layout.title_popup, null)); } /** * 初始化弹窗列表(添加监听) */ public abstract void initUI(ListView mListView, OnItemOnClickListener mItemOnClickListener, ArrayList<ActionItem> mActionItems); /** * 显示弹窗列表界面 */ public void show(View view) { // 获得点击屏幕的位置坐标 view.getLocationOnScreen(mLocation); // 设置矩形的大小 mRect.set(mLocation[0], mLocation[1], mLocation[0] + view.getWidth(), mLocation[1] + 2 * view.getHeight()); // 判断是否需要添加或更新列表子类项 if (mIsDirty) { populateActions(); } // 显示弹窗的位置 showAtLocation(view,mRect,popupGravity); } /** * 显示弹窗的位置 * @param view * @param mRect 绘制的矩形 * @param popupGravity 显示的位置(可自定义) */ public void showAtLocation(View view, Rect mRect,int popupGravity){ showAtLocation(view, popupGravity, mRect.right, mRect.top + view.getHeight()); } /** * 设置弹窗列表子项 */ public void populateActions() { mIsDirty = false; // 设置列表的适配器 mListView.setAdapter(new BaseAdapter() { @Override public View getView(int position, View convertView, ViewGroup parent) { TextView textView = null; if (convertView == null) { textView = new TextView(mContext); textView.setTextColor(mContext.getResources().getColor( android.R.color.white)); textView.setTextSize(14); // 设置文本居中 textView.setGravity(Gravity.CENTER); // 设置文本域的范围 textView.setPadding(0, 10, 0, 10); // 设置文本在一行内显示(不换行) textView.setSingleLine(true); } else { textView = (TextView) convertView; } ActionItem item = mActionItems.get(position); setItemText(textView,item); return textView; } @Override public long getItemId(int position) { return position; } @Override public Object getItem(int position) { return mActionItems.get(position); } @Override public int getCount() { return mActionItems.size(); } }); } /** * 设置item格式 * @param textView item控件 * @param item 弹窗内部子类项(绘制标题和图标) */ public void setItemText(TextView textView,ActionItem item){ // 设置文本文字 textView.setText(item.mTitle); // 设置文字与图标的间隔 // textView.setCompoundDrawablePadding(10); // // 设置在文字的左边放一个图标 // textView.setCompoundDrawablesWithIntrinsicBounds( // item.mDrawable, null, null, null); } /** * 添加子类项 */ public void addAction(ActionItem action) { if (action != null) { mActionItems.add(action); mIsDirty = true; } } /** * 清除子类项 */ public void cleanAction() { if (mActionItems.isEmpty()) { mActionItems.clear(); mIsDirty = true; } } /** * 根据位置得到子类项 */ public ActionItem getAction(int position) { if (position < 0 || position > mActionItems.size()) return null; return mActionItems.get(position); } /** * 设置监听事件 */ public void setItemOnClickListener( OnItemOnClickListener onItemOnClickListener) { this.mItemOnClickListener = onItemOnClickListener; } /** * 功能描述:弹窗子类项按钮监听事件 */ public static interface OnItemOnClickListener { public void onItemClick(ActionItem item, int position); } }
下面为使用的传入的对象(本次demo没有使用图片,所以注释了图片设置,需要的可以根据实际情况jiashan)
public class ActionItem { //定义图片对象 // private Drawable mDrawable; //定义文本对象 public CharSequence mTitle; public ActionItem(Drawable drawable, CharSequence title){ // this.mDrawable = drawable; this.mTitle = title; } public ActionItem(Context context, int titleId, int drawableId){ this.mTitle = context.getResources().getText(titleId); // this.mDrawable = context.getResources().getDrawable(drawableId); } public ActionItem(Context context, CharSequence title) { this.mTitle = title; // this.mDrawable = context.getResources().getDrawable(drawableId); } }
然后下面是布局(什么背景线条这些可以根据自己的实际情况设置我这儿就不贴出来了):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/title_function_bg" android:orientation="vertical" > <ListView android:id="@+id/title_list" android:layout_width="80dp" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@mipmap/mm_title_functionframe_line" android:listSelector="@drawable/title_list_selector" android:padding="3dp" android:scrollingCache="false" /> </LinearLayout>最后是使用(只贴主要代码):
mtitlePopup = new TitlePopupWindow(this, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) { @Override public void setItemText(TextView textView, ActionItem item) { super.setItemText(textView, item); } @Override public void initUI(ListView mListView, OnItemOnClickListener mItemOnClickListener, ArrayList<ActionItem> mActionItems) { mItemOnClickListenerto = mItemOnClickListener; mActionItemsto = mActionItems; mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int index, long arg3) { // 点击子类项后,弹窗消失 dismiss(); if (index == 0) { // 跳转到接收消息界面 } else if (index == 1) { // 跳转到发送消息界面 } if (mItemOnClickListenerto != null) mItemOnClickListenerto.onItemClick( mActionItemsto.get(index), index); } }); } @Override public void setContentView(Context mContext) { super.setContentView(mContext); } @Override public void showAtLocation(View view, Rect mRect, int popupGravity) { super.showAtLocation(view, mRect, popupGravity); } @Override public void getListView() { mListView = (ListView) getContentView().findViewById( R.id.title_list); } }; mtitlePopup.addAction(new ActionItem(this, "接收消息")); mtitlePopup.addAction(new ActionItem(this, "发送消息"));
以上就是简单的使用,希望各位给予建议修正。
2.底部弹窗框(此处以照片选择为例)
具体效果图见下:
以下是布局(使用注意见注释):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/window_mask"> <!-- 以上的background是遮罩层颜色,具体颜色可以自己设置,在此就不贴出来了--> <!-- 以下的background才是底部弹窗的颜色,此处我新增了两个按钮,当点击已经选取的图片时提供删除和查看 --> <LinearLayout android:background="@color/white" android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/id_pop_layout" android:orientation="vertical" > <Button android:id="@+id/choos_photo_popup_ck" android:layout_width="match_parent" android:layout_height="40dp" android:textColor="@color/white" android:background="@drawable/set_button_stely_blue_noradius" android:gravity="center" android:text="查看" android:textSize="14sp" /> <TextView android:layout_width="match_parent" android:layout_height="2dp" /> <Button android:id="@+id/choos_photo_popup_pz" android:layout_width="match_parent" android:layout_height="40dp" android:textColor="@color/white" android:background="@drawable/set_button_stely_blue_noradius" android:gravity="center" android:text="拍照" android:textSize="14sp" /> <TextView android:layout_width="match_parent" android:layout_height="2dp" /> <Button android:id="@+id/choos_photo_popup_xc" android:layout_width="match_parent" android:layout_height="40dp" android:textColor="@color/white" android:background="@drawable/set_button_stely_blue_noradius" android:gravity="center" android:text="从相册选择" android:textSize="14sp" /> <TextView android:layout_width="match_parent" android:layout_height="2dp"/> <Button android:id="@+id/choos_photo_popup_sc" android:layout_width="match_parent" android:layout_height="40dp" android:textColor="@color/white" android:background="@drawable/set_button_stely_blue_noradius" android:gravity="center" android:text="删除" android:textSize="14sp" /> <TextView android:layout_width="match_parent" android:layout_height="6dp" /> <Button android:id="@+id/choos_photo_popup_qx" android:layout_width="match_parent" android:layout_height="40dp" android:background="@drawable/set_button_stely_blue_noradius" android:gravity="center" android:textColor="@color/white" android:text="取消" android:textSize="14sp" /> </LinearLayout> </RelativeLayout>
主要类:
public class ChoosPhotoPopupUtils extends PopupWindow implements OnClickListener { private Button btnTakePhoto, btnSelect, btnCancel, btnDel, btnLook; private View mPopView; private OnItemClickListener mListener; // 点击的是已添加的图片或者是新增加“+”图片的标识来控制是否显示删除和查看按钮 private int code; // 传入的图片地址 private String path; private Context mContext; public ChoosPhotoPopupUtils(Context context,int code, String path) { super(context); this.code = code; this.path = path; this.mContext = context; init(context); setPopupWindow(); btnTakePhoto.setOnClickListener(this); btnSelect.setOnClickListener(this); btnCancel.setOnClickListener(this); btnDel.setOnClickListener(this); btnLook.setOnClickListener(this); } /** * 初始化 * * @param context context */ @SuppressLint("InflateParams") private void init(Context context) { LayoutInflater inflater = LayoutInflater.from(context); // 绑定布局 mPopView = inflater.inflate(R.layout.choos_photo_popup, null); btnTakePhoto = (Button) mPopView .findViewById(R.id.choos_photo_popup_pz); btnSelect = (Button) mPopView.findViewById(R.id.choos_photo_popup_xc); btnCancel = (Button) mPopView.findViewById(R.id.choos_photo_popup_qx); btnDel = (Button) mPopView.findViewById(R.id.choos_photo_popup_sc); btnLook = (Button) mPopView.findViewById(R.id.choos_photo_popup_ck); if (code == 0) { btnDel.setVisibility(View.GONE); btnLook.setVisibility(View.GONE); } else { btnDel.setVisibility(View.VISIBLE); btnLook.setVisibility(View.VISIBLE); } } /** * 设置窗口的相关属性 */ @SuppressLint("InlinedApi") private void setPopupWindow() { this.setContentView(mPopView);// 设置View this.setWidth(LayoutParams.MATCH_PARENT);// 设置弹出窗口的宽 this.setHeight(Util.getScreenHeight(mContext));// 设置弹出窗口的高 this.setFocusable(true);// 设置弹出窗口可 this.setAnimationStyle(R.style.mypopwindow_anim_style);// 设置动画 this.setBackgroundDrawable(new ColorDrawable(0x00000000));// 设置背景透明 mPopView.setOnTouchListener(new OnTouchListener() {// 如果触摸位置在窗口外面则销毁 @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { // id是你需要点击的控件id之上的地方,来实现点击外围扩散的效果 int height = mPopView.findViewById(R.id.id_pop_layout).getTop(); int y = (int) event.getY(); if (event.getAction() == MotionEvent.ACTION_UP) { if (y < height) { dismiss(); } } return true; } }); } /** * 定义一个接口,公布出去 在Activity中操作按钮的单击事件 */ public interface OnItemClickListener { void setOnItemClick(View v, int code, String path); } public void setOnItemClickListener(OnItemClickListener listener) { this.mListener = listener; } @Override public void onClick(View v) { if (mListener != null) { mListener.setOnItemClick(v, code, path); } } }
上面的类用到的动画:
<!-- 照片选择弹窗时的动画 --> <style name="mypopwindow_anim_style"> <item name="android:windowEnterAnimation">@anim/push_bottom_in</item> <!-- 指定显示的动画xml --> <item name="android:windowExitAnimation">@anim/push_bottom_out</item> <!-- 指定消失的动画xml --> </style>
<?xml version="1.0" encoding="utf-8"?> <!-- res/anim文件夹下定义 --> <!-- 名为push_bottom_in.xml --> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="400" android:fromYDelta="100%p" android:toYDelta="0" /> </set>
<?xml version="1.0" encoding="utf-8"?> <!-- push_buttom_out.xml--> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="400" android:fromYDelta="0" android:toYDelta="100%p" /> </set>
以下是使用:
mPop = new ChoosPhotoPopupUtils(PatrolMsgSendActivity.this, position, ""); mPop.setSoftInputMode(PopupWindow.INPUT_METHOD_NEEDED); mPop.setClippingEnabled(false); mPop.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); mPop.setOnItemClickListener(PatrolMsgSendActivity.this); // 设置PopupWindow中的位置 mPop.showAtLocation(需要显示弹窗的界面, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, Util.getStatusHeight(PatrolMsgSendActivity.this)*2);//此处是具体展示的位置和界面的设置,在此偷个懒,具体自行百度咯。
最后一个参数只是为了防止遮罩层无法挡住手机状态栏状态栏而设置的获取屏幕状态栏的高度
监听回调(以下的代码我没有补充,因为不属于弹窗类型的,具体的可以参考我之前整理的照片选择的代码,传送门:demo):
/** * 照片选择 * * @param v 点击控件 * @param code 位置 * @param path 路径 */ @Override public void setOnItemClick(View v, int code, String path) { switch (v.getId()) { case R.id.choos_photo_popup_ck: // 查看 mPop.dismiss(); break; case R.id.choos_photo_popup_pz:// 拍照 mPop.dismiss(); break; case R.id.choos_photo_popup_xc:// 相册 mPop.dismiss(); break; case R.id.choos_photo_popup_qx:// 取消 mPop.dismiss(); break; case R.id.choos_photo_popup_sc:// 删除 mPop.dismiss(); break; } }
以上就为底部弹窗。
3.其他(好看的筛选)
效果图如下:
具体实现类:
public class SelectSpinnerPopWindow extends PopupWindow implements AdapterView.OnItemClickListener { private Context mContext; private View mClickView; private View view; private MaxListView mListView; private SpinnerAdapter mAdapter; private SpinnerAdapter.IOnItemSelectListener mItemSelectListener; public SelectSpinnerPopWindow(Context context, View clickView) { super(context); mContext = context; mClickView = clickView; init(); } public void setItemListener(SpinnerAdapter.IOnItemSelectListener listener){ mItemSelectListener = listener; } public void setAdapter(SpinnerAdapter adapter){ mAdapter = adapter; mListView.setAdapter(mAdapter); } private void init() { view = LayoutInflater.from(mContext).inflate(R.layout.select_spinner_window_layout, null); mListView = (MaxListView) view.findViewById(R.id.select_spinner_window_listView); mListView.setOnItemClickListener(this); mListView.setListViewHeight(400); setPopupWindow(); } public void refreshData(List<String> list, int selIndex) { if (list != null && selIndex != -1) { if (mAdapter != null){ mAdapter.refreshData(list, selIndex); } } } @Override public void onItemClick(AdapterView<?> arg0, View view, int pos, long arg3) { dismiss(); if (mItemSelectListener != null){ mItemSelectListener.onItemClick(mClickView,pos); } } /** * 设置窗口的相关属性 */ @SuppressLint("InlinedApi") private void setPopupWindow() { this.setContentView(view);// 设置View this.setWidth(ActionBar.LayoutParams.MATCH_PARENT);// 设置弹出窗口的宽 this.setFocusable(true);// 设置弹出窗口可 //this.setAnimationStyle(R.style.mypopwindow_anim_style);// 设置动画 this.setBackgroundDrawable(new ColorDrawable(0x00000000));// 设置背景透明 view.setOnTouchListener(new View.OnTouchListener() {// 如果触摸位置在窗口外面则销毁 @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { int height = view.findViewById(R.id.select_spinner_window_listView).getBottom(); int y = (int) event.getY(); if (event.getAction() == MotionEvent.ACTION_UP) { if (y > height) { dismiss(); } } return true; } }); } public void setWindowHeight(int height) { this.setHeight(height);// 设置弹出窗口的高 此处是为了防止选项过多而导致列表过长而影响美观。所以设置最大高度 } }
筛选适配类(布局就不贴了,比较简单,就普通的listview和item,也可以自己扩展添加图片):
public class SpinnerAdapter extends BaseAdapter{ public static interface IOnItemSelectListener{ public void onItemClick(View view, int pos); } private List<String> mObjects; private LayoutInflater mInflater; public SpinnerAdapter(Context context, List<String> mObjects){ this.mObjects = mObjects; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void refreshData(List<String> objects, int selIndex){ mObjects = objects; if (selIndex < 0){ selIndex = 0; } if (selIndex >= mObjects.size()){ selIndex = mObjects.size() - 1; } } @Override public int getCount() { return mObjects.size(); } @Override public Object getItem(int pos) { return mObjects.get(pos).toString(); } @Override public long getItemId(int pos) { return pos; } @Override public View getView(int pos, View convertView, ViewGroup arg2) { ViewHolder viewHolder; if (convertView == null) { convertView = mInflater.inflate(R.layout.spinner_item_layout, null); viewHolder = new ViewHolder(); viewHolder.mTextView = (TextView) convertView.findViewById(R.id.spinner_item_textView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } //Object item = getItem(pos); viewHolder.mTextView.setText(mObjects.get(pos)); return convertView; } public static class ViewHolder { TextView mTextView; } }
可以设置最大高度的listview:
public class MaxListView extends ListView { /** * ListView高度 */ private int listViewHeight; public int getListViewHeight() { return listViewHeight; } public void setListViewHeight(int listViewHeight) { this.listViewHeight = listViewHeight; } public MaxListView(Context context) { super(context); } public MaxListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MaxListView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (listViewHeight > -1) { heightMeasureSpec = MeasureSpec.makeMeasureSpec(listViewHeight, MeasureSpec.AT_MOST); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
弹窗的listview(布局的背景色也为遮罩层的颜色):
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/window_mask" > <包名.MaxListView android:background="@color/white" android:paddingTop="5dp" android:paddingRight="5dp" android:paddingLeft="5dp" android:id="@+id/select_spinner_window_listView" android:layout_width="match_parent" android:layout_height="wrap_content"></包名.MaxListView> </RelativeLayout>
使用:
首先数据初始化
// ==上下班状态== // 初始化数据 mWorkStatusListType.add("全部");// 1 mWorkStatusListType.add("上班");// 2 mWorkStatusListType.add("上班离线");// 3 mWorkStatusListType.add("上班越界");// 3 mWorkStatusListType.add("下班");// 4 SpinnerAdapter mWorkStatusAdapter = new SpinnerAdapter(this, mWorkStatusListType); mWorkStatusAdapter.refreshData(mWorkStatusListType, 0); // 显示第一条数据 // patrolSearchType.setText(mWorkStatusListType.get(0)); // 初始化PopWindow mWorkStatusSpinnerPopWindow = new SelectSpinnerPopWindow(this, patrolPipListWorkStatusRl); mWorkStatusSpinnerPopWindow.setAdapter(mWorkStatusAdapter); mWorkStatusSpinnerPopWindow.setItemListener(this);
然后调用显示:
/** * 设置PopWindow 传入上面初始化的PopWindow */ @SuppressLint("WrongConstant") private void showSpinWindow(SelectSpinnerPopWindow mSpinnerPopWindow) { if (mSpinnerPopWindow != null) { // 设置PopupWindow中的位置 以下是计算弹窗显示的位置,显示在点击的viwe控件正下面并设置遮罩层等 int[] location = new int[2]; patrolPipListSearch.getLocationOnScreen(location); int y = location[1] + patrolPipListSearch.getHeight(); mSpinnerPopWindow.setWindowHeight(Util.getScreenHeight(this) - y); mSpinnerPopWindow.showAtLocation(this.findViewById(R.id.patrol_pip_list_xml), Gravity.START | Gravity.TOP, 0, y); mSpinnerPopWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { setSearchBg(); // 此处是监听弹窗消失的时候的ui变化,比如展开的符号和点击控件字体颜色的改变(展开收缩符号切换,白色橙色字体颜色更换等) } }); } else { LogUtils.showToast(this, "获取下拉数据中。。。"); } }
最后当然是点击监听了(点击的view的id,根据id判断数据操作-list数据.getPosition(pos)):
@Override public void onItemClick(View view, int pos) { setSearchBg(); // 点击选项的时候将ui的颜色等(同上)还原成初始化状态 switch (view.getId()) { // 执行筛选 case R.id.patrol_pip_list_type: String value = typeList.getPosition(pos); break; case R.id.patrol_pip_list_area: String areaStr = areaList.getPosition(pos); break; } }
以上只是一些简单使用,具体的使用还是要看具体的场景,总的来说,主要是把握好弹窗弹出的具体位置,这个位置还是需要我们自己去慢慢熟练。还有就是监听回调的使用以获取点击的结果等。
demo的话只有后面补充了。谅解。