列表界面的实现样过程可参见上一篇文章,这里先看一下MainActivity类:
主要的实现弹出框原理是在initPopWindow方法里,通过获得触摸点的X、Y的位置来实现跟随触摸位置弹出,
可以对这种列表选项的PopupWindow样式进行封装,使其更加灵活可扩展,这里只是为了暂时实现效果图中的样式而已。
package com.alter.popupwindowmenu.activity;
import android.app.Activity;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.Toast;
import com.alter.popupwindowmenu.Command.C;
import com.alter.popupwindowmenu.R;
import com.alter.popupwindowmenu.Utils.DensityUtil;
import com.alter.popupwindowmenu.adapter.ChatDetailAdapter;
import com.alter.popupwindowmenu.model.ChatMessage;
import com.alter.popupwindowmenu.model.UserInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @CreateDate: 2018/3/9
* @Author: lzsheng
* @Description:
* @Version:
*/
public class MainActivity extends Activity {
private final String TAG = MainActivity.class.getSimpleName();
private RecyclerView mRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private ChatDetailAdapter mChatDetailAdapter;
private List mChatMessages;
private PopupWindow mPopupWindow;
private View mPopContentView;
private int mPressedPos; // 被点击的位置
private float mRawX;
private float mRawY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initVariable();
initViews();
initData();
setViewsData();
}
private void initVariable() {
mChatMessages = new ArrayList<>();
}
private void initViews() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_messages);
mLinearLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
}
private void initData() {
for (int i = 'A'; i <= 'Z'; i++) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setFrom((int) (Math.random() * 2) + 1);
if (chatMessage.getFrom() == C.TYPE_MSG_RECEIVE) {
UserInfo userInfo = new UserInfo();
userInfo.setNickName("Mia");
chatMessage.setUserInfo(userInfo);
}
// int s = (int) (Math.random() * ('Z' - 'A' + 1)) + 'A';
// int e = (int) (Math.random() * ('Z' - 'A' + 1)) + 'A';
int s = (int) (Math.random() * 120) + 1;
int e = (int) (Math.random() * 120) + 1;
Log.d(TAG, "s = " + s);
Log.d(TAG, "e = " + e);
int min = 0;
int max = 0;
if (s <= e) {
min = s;
max = e;
} else {
min = e;
max = s;
}
StringBuffer buffer = new StringBuffer();
for (int j = min; j <= max; j++) {
int c = (int) (Math.random() * ('Z' - 'A' + 1)) + 'A';
Log.d(MainActivity.class.getSimpleName(), "c = " + c);
// buffer.append(String.valueOf((char) (Math.random() * max) + min));
buffer.append(String.valueOf((char) c));
}
chatMessage.setMsgContent(buffer.toString());
buffer.setLength(0);
// 或者:
// buffer.delete(0, buffer.length());
mChatMessages.add(chatMessage);
Log.d(TAG, "i = " + i + "\n===========分割线==============");
}
}
private void setViewsData() {
mChatDetailAdapter = new ChatDetailAdapter(MainActivity.this);
mChatDetailAdapter.setChatMessages(mChatMessages);
mRecyclerView.setAdapter(mChatDetailAdapter);
// mRecyclerView.smoothScrollToPosition(mChatDetailAdapter.getItemCount());
// mLinearLayoutManager.scrollToPositionWithOffset(mChatDetailAdapter.getItemCount() + 1, 0);
mRecyclerView.scrollToPosition(mChatDetailAdapter.getItemCount() - 1);
mChatDetailAdapter.setOnRecyclerViewItemLongClick(new ChatDetailAdapter.OnRecyclerViewItemLongClick() {
@Override
public void onItemLongClick(View childView, MotionEvent event, int position) {
mRawX = event.getRawX();
mRawY = event.getRawY();
mPressedPos = position;
Log.d(TAG, "e.getRawX()横坐标=" + mRawX + ", e.getRawY()纵坐标=" + mRawY);
Log.d(TAG, "position=" + position);
UserInfo userInfo = mChatMessages.get(position).getUserInfo();
ChatMessage chatMessage = mChatMessages.get(position);
int typeMsg = chatMessage.getFrom();
String msgContent = chatMessage.getMsgContent();
StringBuffer buffer = new StringBuffer();
if (typeMsg == C.TYPE_MSG_RECEIVE) {
buffer.append("收到").append(userInfo.getNickName()).append("发送过来的消息:\n").append(msgContent);
} else if (typeMsg == C.TYPE_MSG_SEND) {
buffer.append("我发出的消息:\n").append(msgContent);
}
Toast.makeText(MainActivity.this, buffer.toString(), Toast.LENGTH_SHORT).show();
initPopWindow(childView, position);
}
});
}
private void initPopWindow(final View selectedView, final int position) {
if (mPopContentView == null) {
mPopContentView = View.inflate(this, R.layout.item_list_option_pop, null);
}
LinearLayout layoutDelete = (LinearLayout) mPopContentView.findViewById(R.id.layout_delete);
// 在popupWindow还没有弹出显示之前就测量获取其宽高(单位是px像素)
int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mPopContentView.measure(w, h);
int viewWidth = mPopContentView.getMeasuredWidth();//获取测量宽度px
int viewHeight = mPopContentView.getMeasuredHeight();//获取测量高度px
final int screenWidth = DensityUtil.getScreenWidth(this.getWindow().getDecorView().getContext());
final int screenHeight = DensityUtil.getScreenHeight(this.getWindow().getDecorView().getContext());
if (mPopupWindow == null) {
mPopupWindow = new PopupWindow(mPopContentView, viewWidth, ViewGroup.LayoutParams.WRAP_CONTENT, true);
}
mPopupWindow.setOutsideTouchable(true);
// mPopupWindow.setBackgroundDrawable(drawable);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
int offX = 20; // 可以自己调整偏移
int offY = 20; // 可以自己调整偏移
float rawX = mRawX;
float rawY = mRawY;
if (mRawX <= screenWidth / 2) {
rawX = mRawX + offX;
if (mRawY < screenHeight / 3) {
rawY = mRawY;
mPopupWindow.setAnimationStyle(R.style.pop_anim_left_top); //设置动画
} else {
rawY = mRawY - viewHeight - offY;
mPopupWindow.setAnimationStyle(R.style.pop_anim_left_bottom); //设置动画
}
} else {
rawX = mRawX - viewWidth - offX;
if (mRawY < screenHeight / 3) {
rawY = mRawY;
mPopupWindow.setAnimationStyle(R.style.pop_anim_right_top); //设置动画
} else {
rawY = mRawY - viewHeight;
mPopupWindow.setAnimationStyle(R.style.pop_anim_right_bottom); //设置动画
}
}
mPopupWindow.showAtLocation(this.getWindow().getDecorView(), Gravity.NO_GRAVITY, (int) rawX, (int) rawY);
layoutDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPopupWindow.dismiss();
if (mChatMessages.size() <= 0) {
return;
} else {
mChatMessages.remove(position);
mChatDetailAdapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this, "已删除此条聊天内容", Toast.LENGTH_SHORT).show();
}
}
});
mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
selectedView.setSelected(false);
}
});
}
}
还要注意适配器里对长按事件的处理:
我给布局设置了触摸事件setOnTouchListener,并且能过长按事件将MotionEvent传递出来,这样在MainActivity中RecycleVeiw注册长按事件时,可以通过传递出来的这个MotionEvent获得触摸的位置
package com.alter.popupwindowmenu.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.alter.popupwindowmenu.Command.C;
import com.alter.popupwindowmenu.R;
import com.alter.popupwindowmenu.model.ChatMessage;
import com.alter.popupwindowmenu.model.UserInfo;
import java.util.List;
/**
* @CreateDate: 2018/1/26
* @Author: lzsheng
* @Description: 适配器,根据不同的数据类型,展示不同的UI效果
* @Version:
*/
public class ChatDetailAdapter extends RecyclerView.Adapter {
private Context mContext;
private List mChatMessages;
private OnRecyclerViewItemLongClick mOnRecyclerViewItemLongClick;
private final int TYPE_MSG_SEND = C.TYPE_MSG_SEND; //
private final int TYPE_MSG_RECEIVE = C.TYPE_MSG_RECEIVE; //
public ChatDetailAdapter(Context context) {
this.mContext = context;
}
public void setChatMessages(List chatMessages) {
mChatMessages = chatMessages;
}
public void setOnRecyclerViewItemLongClick(OnRecyclerViewItemLongClick onRecyclerViewItemLongClick) {
mOnRecyclerViewItemLongClick = onRecyclerViewItemLongClick;
}
/**
* @CreateDate: 2018/2/3
* @Author: lzsheng
* @Description: 根据数据的类型, 返回不同的ItemViewType
* @Params: [position]
* @Return: int
*/
@Override
public int getItemViewType(int position) {
if (mChatMessages != null && mChatMessages.size() > 0) {
int from = mChatMessages.get(position).getFrom();
if (from == C.TYPE_MSG_SEND) {
return TYPE_MSG_SEND;
} else if (from == C.TYPE_MSG_RECEIVE) {
return TYPE_MSG_RECEIVE;
}
}
return 0;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_MSG_SEND:
HolderChatSend holderChatSend = new HolderChatSend(
LayoutInflater.from(mContext).inflate(R.layout.rv_item_chat_msg_send, parent, false));
return holderChatSend;
case TYPE_MSG_RECEIVE:
HolderChatReceive holderChatReceive = new HolderChatReceive(
LayoutInflater.from(mContext).inflate(R.layout.rv_item_chat_msg_receive, parent, false));
return holderChatReceive;
default:
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (mChatMessages != null) {
ChatMessage chatMessage = mChatMessages.get(position);
int from = chatMessage.getFrom();
switch (from) {
case TYPE_MSG_SEND:
String msgContentSend = chatMessage.getMsgContent();
((HolderChatSend) holder).tvMsgContent.setText(msgContentSend);
break;
case TYPE_MSG_RECEIVE:
UserInfo userInfo = chatMessage.getUserInfo();
((HolderChatReceive) holder).tvNickName.setText(userInfo.getNickName());
String msgContentReceive = chatMessage.getMsgContent();
((HolderChatReceive) holder).tvMsgContent.setText(msgContentReceive);
break;
default:
break;
}
}
}
@Override
public int getItemCount() {
if (mChatMessages != null && mChatMessages.size() > 0) {
return mChatMessages.size();
}
return 0;
}
class HolderChatSend extends RecyclerView.ViewHolder implements View.OnLongClickListener {
TextView tvNickName;
TextView tvMsgContent;
LinearLayout layoutChat;
MotionEvent event;
public HolderChatSend(View itemView) {
super(itemView);
tvNickName = (TextView) itemView.findViewById(R.id.textview_nick_name);
tvMsgContent = (TextView) itemView.findViewById(R.id.textview_message);
layoutChat = (LinearLayout) itemView.findViewById(R.id.layout_message);
layoutChat.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
event = e;
break;
default:
break;
}
// 如果onTouch返回false,首先是onTouch事件的down事件发生,此时,如果长按,触发onLongClick事件;
// 然后是onTouch事件的up事件发生,up完毕,最后触发onClick事件。
return false;
}
});
layoutChat.setOnLongClickListener(this);
}
@Override
public boolean onLongClick(View v) {
if (mOnRecyclerViewItemLongClick != null) {
mOnRecyclerViewItemLongClick.onItemLongClick(v, event, getAdapterPosition());
}
return false;
}
}
class HolderChatReceive extends RecyclerView.ViewHolder implements View.OnLongClickListener {
TextView tvNickName;
TextView tvMsgContent;
LinearLayout layoutChat;
MotionEvent event;
public HolderChatReceive(View itemView) {
super(itemView);
tvNickName = (TextView) itemView.findViewById(R.id.textview_nick_name);
tvMsgContent = (TextView) itemView.findViewById(R.id.textview_message);
layoutChat = (LinearLayout) itemView.findViewById(R.id.layout_message);
layoutChat.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
event = e;
break;
default:
break;
}
// 如果onTouch返回false,首先是onTouch事件的down事件发生,此时,如果长按,触发onLongClick事件;
// 然后是onTouch事件的up事件发生,up完毕,最后触发onClick事件。
return false;
}
});
layoutChat.setOnLongClickListener(this);
}
@Override
public boolean onLongClick(View v) {
if (mOnRecyclerViewItemLongClick != null) {
mOnRecyclerViewItemLongClick.onItemLongClick(v, event, getAdapterPosition());
}
return false;
}
}
/**
* item点击接口
*/
public interface OnRecyclerViewItemLongClick {
void onItemLongClick(View childView, MotionEvent event, int position);
}
}
源码下载
GitHub地址
由于作者水平有限,语言描述及代码实现中难免有纰漏,望各位看官多提宝贵意见!
Hello , World !
感谢所有!