本文共 7520 字,大约阅读时间需要 25 分钟。
首先先感谢Andy提供的原始代码 ,省去了不少麻烦,不必面对枯燥的文档。
首先感谢 popfisher 提供了一些思路
诶,人类的本质果然是复读机,就连本文也是使用Chrome扩展:“一键复制与自动滚动”,复制了原文的一些[标题](地址)
与 Markdown 源码。有需要的可以前往Edge的扩展市场下载。
主要还有一个问题,不知道微信怎么做到的弹出pop之后限制不滑动listview。
我这边就简单的判断了下popwindow弹出来之后不截取触摸事件。
这个问题其实很好解决。模拟一个触摸停止的事件即可,但注意需传给布局容器,而且是传ACTION_CANCEL而非ACTION_UP:
MotionEvent evt = MotionEvent.obtain(0, 0,MotionEvent.ACTION_CANCEL, -100, -100, 0);root.dispatchTouchEvent(evt);evt.recycle();
如果传 ACTION_UP,会触发点击事件,如果传给 recyclerview 本身,则没有效果。
Via 浏览器的长按菜单栏更像是桌面应用的右键菜单。仔细观察,发现它的菜单周围有一个margin区域,点击不会使pop消失,相当于一个防止误点击的margin区域,可以直接在菜单布局中用padding实现,不过相应地要修改弹出位置的计算方式。
测试
如果要大量运用这种弹出菜单,可以将这一切用代码封装起来,代码一样可以堆出和布局文件相当的效果。封装完毕,调用代码如下:
private void showPopupWindow(View anchorView) { int[] texts = new int[] { R.string.duoxuanmoshi ,R.string.houtaidakai ,R.string.xinbiaoqianyedaikai ,R.string.sheweimorenye ,R.string.fuzhilianjie ,R.layout.edit_and_delete ,R.string.fenxiang }; PopupMenuHelper.PopupMenuListener listener = (popupMenuHelper, v, isLongClick) -> { boolean ret=true; boolean dismiss = !isLongClick; switch (v.getId()) { case R.string.duoxuanmoshi: showT("进入多选模式啦"); break; } if (dismiss) { popupMenuHelper.dismiss(); } return ret; }; new PopupMenuHelper(this, texts, listener).show(anchorView, x, y); }
可以看到代码非常简洁,而且重要部分非常醒目了——菜单项条目、点击事件的监听,一目了然,不再有浮云遮眼。
代码第一行定义的int数组,很重要,可以说一人分饰了三个角色。此数组里中定义的都是资源ID,同时资源ID又会被当成菜单项子视图的ID,而用到的资源ID又可分为字符串ID、和布局文件ID,此三种ID即为三个不同的角色。
要实现不同的弹出菜单,只需提供不同的资源ID数组,以及事件监听器即可,其他完全一样,故而可被封装。
其中的资源数组兼容字符串与布局资源,所以封装的 PopupMenuHelper,虽然仅仅一百多行,但内容丰富,不仅仅能够显示一行行的文本菜单,还可以适量加入自定义布局的内容,十分灵活。
由于有所耦合,故封装版的代码就不放上来了。
实际使用效果:
增加了动画,配合用 RecyclerView 打造的树形列表(主页书签导航列表)
自定义布局实现的菜单子项“编辑”的旁边有个小的删除按钮,橙色代表会有确认对话框,红色代表直接删除。
private void initLayout() { int padding = (int) (11* GlobalOptions.density); int padding1 = (int) (32.8*GlobalOptions.density); for (int i = 0; i < texts.length; i++) { int resId = texts[i]; // 判断是否是字符串ID //context.getResources().getResourceTypeName() if(resId>=0x7f110000) { TextView tv = new TextView(context); tv.setText(resId); tv.setId(resId); tv.setPadding(padding1, padding, padding1, padding); tv.setGravity(Gravity.CENTER_VERTICAL); tv.setTextColor(Color.BLACK); tv.setSingleLine(true); tv.setClickable(true); tv.setOnClickListener(this); tv.setBackground(Utils.getThemeDrawable(context, R.attr.listChoiceBackgroundIndicator)); lv.addView(tv); } else { try { View view = LayoutInflater.from(context).inflate(resId, lv, false); if (view.isClickable()) { view.setOnClickListener(this); } if (view.isLongClickable()) { view.setOnLongClickListener(this); } if (view instanceof ViewGroup) { Utils.setOnClickListenersOneDepth((ViewGroup) view, this, 999, null); } lv.addView(view); } catch (Exception ignored) { } } } }
用到的样式, styles.xml
import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.PopupWindow; private PopupWindow mPopupWindow; private ListView mListView; private void testPopupView() { mListView = (ListView) findViewById(R.id.listview); mListView.setAdapter(new CustomAdapter()); mListView.setVisibility(View.VISIBLE); } private View getPopupWindowContentView() { // 一个自定义的布局,作为显示的内容 int layoutId = R.layout.test_menu_links; // 布局ID View contentView = LayoutInflater.from(this).inflate(layoutId, null); return contentView; } private void showPopupWindow(View anchorView) { View contentView = getPopupWindowContentView(); mPopupWindow = new PopupWindow(contentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); // 如果不设置PopupWindow的背景,有些版本就会出现一个问题:无论是点击外部区域还是Back键都无法dismiss弹框 mPopupWindow.setBackgroundDrawable(new ColorDrawable()); // 设置好参数之后再show int windowPos[] = PopupWindowUtil.calculatePopWindowPos(anchorView, contentView, contentView.findViewById(R.id.test_child_view), x, y); mPopupWindow.showAtLocation(anchorView, Gravity.TOP | Gravity.START, windowPos[0], windowPos[1]); } class CustomAdapter extends BaseAdapter { @Override public int getCount() { return 20; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder viewHolder; final View finalConvertView; if (convertView == null) { convertView = LayoutInflater.from(getBaseContext()).inflate(R.layout.bookmark_item, null); viewHolder = new ViewHolder(); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } finalConvertView = convertView; convertView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { showPopupWindow(finalConvertView); return false; } }); convertView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { x = (int) event.getRawX(); y = (int) event.getRawY(); return false; } }); convertView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //点击事件 } }); return convertView; } } int x; int y; class ViewHolder { View moreRoot; View moreImgv; } static class PopupWindowUtil { public static int[] calculatePopWindowPos(View anchorView , View popView, View itemTestView , int anchorX, int anchroY) { final int[] windowPos = new int[2]; final int screenHeight = getScreenHeight(anchorView.getContext()); final int screenWidth = getScreenWidth(anchorView.getContext()); // 测量popView 弹出框 popView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); int padding = popView.getPaddingTop(); int popHeight = popView.getMeasuredHeight() - padding*2; int popWidth = popView.getMeasuredWidth() - padding*2; int itemHeight = itemTestView.getHeight() + padding; final boolean showUp = screenHeight + itemHeight - anchroY < popHeight; final boolean showLeft = screenWidth - anchorX < popWidth; if (showUp) { windowPos[1] = anchroY - popHeight - padding; } else { windowPos[1] = anchroY - padding; } if (showLeft) { windowPos[0] = anchorX - popWidth - padding; } else { windowPos[0] = anchorX - padding; } return windowPos; } public static int getScreenHeight(Context context) { return context.getResources().getDisplayMetrics().heightPixels; } /** * 获取屏幕宽度(px) */ public static int getScreenWidth(Context context) { return context.getResources().getDisplayMetrics().widthPixels; } }
public class MainActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_activity); testPopupView(); }}
listview的子项布局随意, 此处是 bookmark_item.xml。
转载地址:http://cmbii.baihongyu.com/