博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
仿Via浏览器在长按位置显示置功能菜单栏
阅读量:4090 次
发布时间:2019-05-25

本文共 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) { }			}		}	}

基础版本的完整代码贴一下(完全用手写布局)

  1. 布局文件 test_menu_links.xml

用到的样式, styles.xml

		
  1. 关键代码,复制到你的 MainActivity.java
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;		}	}
  1. 背景图Drawable, frame_pop_menu.xml:
  1. 测试代码 MainActivity.java
public class MainActivity extends Activity {	@Override	protected void onCreate(@Nullable Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.test_activity);		testPopupView();	}}
  1. 测试布局 test_activity.xml

listview的子项布局随意, 此处是 bookmark_item.xml。

转载地址:http://cmbii.baihongyu.com/

你可能感兴趣的文章
Winform多线程
查看>>
Spring AOP + Redis + 注解实现redis 分布式锁
查看>>
poj 1976 A Mini Locomotive (dp 二维01背包)
查看>>
《计算机网络》第五章 运输层 ——TCP和UDP 可靠传输原理 TCP流量控制 拥塞控制 连接管理
查看>>
《PostgreSQL技术内幕:查询优化深度探索》养成记
查看>>
剑指_复杂链表的复制
查看>>
FTP 常见问题
查看>>
shell 快捷键
查看>>
MODULE_DEVICE_TABLE的理解
查看>>
编译Android4.0源码时常见错误及解决办法
查看>>
Android 源码编译make的错误处理
查看>>
启用SELinux时遇到的问题
查看>>
No devices detected. Fatal server error: no screens found
查看>>
db db2 base / instance database tablespace container
查看>>
db db2_monitorTool IBM Rational Performace Tester
查看>>
Android 解决TextView设置文本和富文本SpannableString自动换行留空白问题
查看>>
Android自定义View实现商品评价星星评分控件
查看>>
postgresql监控工具pgstatspack的安装及使用
查看>>
postgresql减少wal日志生成量的方法
查看>>
swift中单例的创建及销毁
查看>>