概述
我们在使用手机QQ时,点击菜单键,会弹出如本案例说演示的效果图似的菜单选项。
实现方式有很多种,在这里我们来演示下如何通过自定义PopupWindow的方式一步一步的实现如上效果。
关于PopupWindow的基本知识点请查看 PopupWindow
分析
UI部分
- shape的使用-corners solid等
- 中间的使用View分割
- 背景的处理
- ……
功能点
- 响应点击事件–通过接口回调的方式
- 点击外部,PopupWindow可消失
- ……
实现
自定义PopupWindow
UI编写
- 位于父布局的底部
- 距边框有一定的距离,根布局使用layout_margin即可
- ListView(四个圆角+白色背景) + View分割(透明色) +底部文字(圆角+白色背景)
- 蓝色字体 居中显示(ListView中的 在Item设置即可,底部文字设置gravity即可)
- …….
布局文件如下所示:
list_popupwindow.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/id_rl_relativeLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" android:padding="16dp"> <ListView android:id="@+id/id_lv_popupWindowListView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/view" android:background="@drawable/bg_menu" /> <View android:id="@+id/view" android:layout_width="match_parent" android:layout_height="20dp" android:layout_above="@+id/id_tv_bottom" android:background="@color/transparent" /> <TextView android:id="@+id/id_tv_bottom" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentBottom="true" android:background="@drawable/bg_menu" android:gravity="center" android:text="取消" android:textColor="@color/skyblue" android:textSize="20dp" /> </RelativeLayout>
其中用到的几个背景xml如下,都在drawable目录下
bg_menu.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!--stroke: 设置描边,可描成实线或虚线。当然了也可以不设置stroke属性--> <stroke android:color="@color/transparent" android:width="2dp"/> <!--设置形状填充的颜色,只有android:color一个属性--> <solid android:color="@color/bg_white" /> <!-- 设置圆角,只适用于rectangle类型,可分别设置四个角不同半径的圆角,当设置的圆角半径很大时,比如200dp,就可变成弧形边了--> <corners android:radius="10dp" /> </shape>
自定义PopupWindow编写 +接口回调+监听OnTouch事件实现点击外部消失
加载自定义的xml文件,然后获取ListView组件,设置adapter 即可显示UI布局。代码中的注释已经非常详细了,再此就不多涉及了。
import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; import android.widget.PopupWindow; import android.widget.TextView; import com.turing.base.R; import com.turing.nutritiousSerial.listPopupWindow.adapter.CustomPopupWindowAdpater; import com.turing.nutritiousSerial.listPopupWindow.beans.PopupItemBean; import java.util.List; /** * MyApp * * @author Mr.Yang on 2016-04-25 10:12. * @version 1.0 * @desc */ public class ListPopupWindow extends PopupWindow { //上下文 private Context context; //父视图 private View parentView; //item数据源 private List<PopupItemBean> dataList; //适配器 private CustomPopupWindowAdpater adapter; //声明接口对象 private OnPopubItemClickListener popupItemListener; private OnBottomTextViewClickListener bottomTextViewListener; /** * 定义接口用于PopupItem回调点击事件处理 */ public interface OnPopubItemClickListener { void onPopupItemClick(View view, int postion); } /** * 定义接口用于底部TextView回调点击事件处理 */ public interface OnBottomTextViewClickListener { void onBottomClick(); } /** * 构造函数 */ public ListPopupWindow(Context context, List<PopupItemBean> dataList, View parentView, OnPopubItemClickListener popupItemListener, OnBottomTextViewClickListener bottomTextViewListener) { this.context = context; this.dataList = dataList; this.parentView = parentView; this.popupItemListener = popupItemListener; this.bottomTextViewListener = bottomTextViewListener; initCustomPopupWindow(); } /** * 初始化自定义的PopupWindow */ private void initCustomPopupWindow() { // 加载自定义布局文件,转化为组件 parentView = LayoutInflater.from(context).inflate(R.layout.list_popupwindow, null); // 设置显示的view setContentView(parentView); // 初始化控件 ListView lv = (ListView) parentView.findViewById(R.id.id_lv_popupWindowListView); TextView tv = (TextView) parentView.findViewById(R.id.id_tv_bottom); // 设置弹出窗体的高 this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT); this.setHeight(ViewGroup.LayoutParams.MATCH_PARENT); // 设置弹出窗体可点击 this.setFocusable(true); // 设置SelectPicPopupWindow弹出窗体的背景 this.setBackgroundDrawable(new ColorDrawable(0xb0000000)); // view添加OnTouchListener监听判断获取触屏位置如果在布局外面则销毁弹出框 parentView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // getTop View自身的顶边到其父布局顶边的距离,因为是根目录所以为0 int height = parentView.findViewById(R.id.id_rl_relativeLayout).getTop(); // getY 点击事件距离控件顶边的举例 int y = (int) event.getY(); // 当抬起 并且 y>height时(也就是 只要不点击ListView范围内),dismiss popupWindow if (event.getAction() == MotionEvent.ACTION_UP) { if (y > height) { dismiss(); } } return true; } }); // 更新位置和大小(不加这行代码也行) update(); // 实例化适配器 adapter = new CustomPopupWindowAdpater(context, dataList); // 设置适配器 lv.setAdapter(adapter); // ListView设置点击事件 lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // 回调onPopupItemClick popupItemListener.onPopupItemClick(view, position); } }); // TextView设置点击事件 tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 回调onBottomClick bottomTextViewListener.onBottomClick(); } }); } }
CustomPopupWindowAdpater.java
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import com.turing.base.R; import com.turing.nutritiousSerial.listPopupWindow.beans.PopupItemBean; import java.util.List; /** * MyApp * * @author Mr.Yang on 2016-04-25 11:04. * @version 1.0 * @desc */ public class CustomPopupWindowAdpater extends BaseAdapter { private Context context; private List<PopupItemBean> datas; private LayoutInflater layoutInflater; /** * 构造函数 */ public CustomPopupWindowAdpater(Context context, List<PopupItemBean> datas) { this.context = context; this.datas = datas ; layoutInflater = LayoutInflater.from(context); } @Override public int getCount() { return datas.size(); } @Override public Object getItem(int position) { return datas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // 声明ViewHolder ViewHolder viewHolder; if (convertView == null) { // 加载Item布局,转换为View布局 convertView = layoutInflater.inflate(R.layout.popup_item, parent, false); // 实例化ViewHolder viewHolder = new ViewHolder(); // 查找组件赋值给ViewHolder viewHolder.textView = (TextView) convertView.findViewById(R.id.id_tv_popupItemText); // 设置TAG convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } // 设置Item中的值 viewHolder.textView.setText(datas.get(position).getText() ); return convertView; } /** * 对应Item布局中的组件 */ class ViewHolder { private TextView textView; } }
调用
因为PopupWindow需要依赖父组件来显示,所以实例话PopupWindow的时候,传入根布局View,实现接口回调Activity类实现自定义的两个接口,并传入到PopupWindow中。
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.Gravity; import android.view.View; import android.widget.RelativeLayout; import com.turing.base.R; import com.turing.base.utils.AlertUtil; import com.turing.nutritiousSerial.listPopupWindow.beans.PopupItemBean; import com.turing.nutritiousSerial.listPopupWindow.customPopupWindow.ListPopupWindow; import java.util.ArrayList; import java.util.List; public class ListPopupWindowDemoActivity extends AppCompatActivity implements ListPopupWindow.OnPopubItemClickListener, ListPopupWindow.OnBottomTextViewClickListener { // 定义父View即PopupWindow依赖浮动的View private RelativeLayout relativeLayout; private ListPopupWindow popWindow; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list_popup_window_demo); // 父View relativeLayout = (RelativeLayout) findViewById(R.id.id_rl_relativeLayout); } /** * 按钮监听事件 * * @param view */ public void showCustomListPopupWindow(View view) { // 展示数据集合 List<PopupItemBean> datalists = new ArrayList<>(); // List集合中的数据 PopupItemBean itemBean1 = new PopupItemBean("版本更新"); PopupItemBean itemBean2 = new PopupItemBean("反馈"); PopupItemBean itemBean3 = new PopupItemBean("退出QQ"); // 添加到List集合中 datalists.add(itemBean1); datalists.add(itemBean2); datalists.add(itemBean3); // 实例化自定义ListPopupWindow popWindow = new ListPopupWindow(ListPopupWindowDemoActivity.this, datalists, relativeLayout, this, this); // 居中并且靠底部显示 popWindow.showAtLocation(relativeLayout, Gravity.CENTER | Gravity.BOTTOM, 0, 0); } @Override public void onBottomClick() { popWindow.dismiss(); AlertUtil.showToastShort(ListPopupWindowDemoActivity.this, "点击取消"); } @Override public void onPopupItemClick(View view, int postion) { switch (postion) { case 0: AlertUtil.showToastShort(ListPopupWindowDemoActivity.this, String.valueOf(postion) + " 被点击"); break; case 1: AlertUtil.showToastShort(ListPopupWindowDemoActivity.this, String.valueOf(postion) + " 被点击"); break; case 2: AlertUtil.showToastShort(ListPopupWindowDemoActivity.this, String.valueOf(postion) + " 被点击"); break; default: break; } } }
代码已提交Github: