安卓开发之自定义控件TipView仿QQ长按后的提示窗口
2016-09-27 23:22
330 查看
先上效果图:
![](http://img.blog.csdn.net/20160928101600252)
之前用手机QQ时,一直很觉得这个窗口提示挺不错的,今天将它大概地实现了一遍。
首先是:提示窗口的三角下标是可以改变位置的,然后窗口中有很多小的item,item被点击时会显示出不同的颜色,
同时三角下标的颜色也随着改变。
然后是:提示窗口的item们会根据传入的坐标实现向上显示或向下显示。
initView()的具体实现:主要对临界情况的一些值得处理
具体绘制各个窗口方块的代码太长不便贴出,可参考源码及其注释。
大致的流程就是
a. 绘制最左方块、最右方块和中间方块
b. 根据方块是否被点击而更换不同的颜色来绘制方块
c. 绘制分隔线
覆写TipView的onTouchEvent方法:
使用注意:传给rootView的参数需要FrameLayout / RelativeLayout或其子类实例,这样TipView对象才可以悬浮地显示出来。
之前用手机QQ时,一直很觉得这个窗口提示挺不错的,今天将它大概地实现了一遍。
首先是:提示窗口的三角下标是可以改变位置的,然后窗口中有很多小的item,item被点击时会显示出不同的颜色,
同时三角下标的颜色也随着改变。
然后是:提示窗口的item们会根据传入的坐标实现向上显示或向下显示。
一、对TipView定义一些成员变量
// 一些状态变量 private static final int STATUS_DOWN = 1; private static final int STATUS_UP = 2; // 初始窗口的Item显示在上方 private int mStatus = STATUS_UP; // Item之间的分隔线的颜色 private int mSeparateLineColor ; // 窗口边上两个方块的边角半径大小 private int mCorner = dip2px(6); private Paint mPaint; // 画方块和文字的画笔 private Paint doPaint; // 画三角下标的画笔 private Path mPath; // 绘制的路径 private int mBorderMargin = dip2px(5); // 提示窗口与屏幕(根布局)的最小距离 private int mItemWidth = dip2px(50); // 窗口Item的宽度 private int mItemHeight = dip2px(48); // 窗口Item的高度 private int mTriangleTop = dip2px(50); // 三角下标的顶点 private int mHalfTriangleWidth = dip2px(6); // 三角小标的半宽 private int mItemBorder; // 三角小标与窗口Item的临界 private int realLeft; // 窗口的left值 private List<TipItem> mItemList = new ArrayList<>(); // 存储每个Item的信息 private List<Rect> mItemRectList = new ArrayList<>(); // 存储每个方块的信息 private OnItemClickListener onItemClickListener; // Item点击接口 private int choose = -1; // 是否有Item被按下,是为Item的序号,否为-1 // 外界传入的点击坐标x、y private int x; private int y;
二、TipView的初始化
TipView的构造方法:public TipView(Context context, ViewGroup rootView,int x,int y,List<TipItem> mItemList) { super(context); this.x = x; // 设置传入过来的x轴坐标 this.y = y; // 设置传入过来的y轴坐标 // x和y决定了三角下标的位置 initPaint(); // 初始化画笔 setTipItemList(mItemList); // 初始化Item集合,并对Item的字符串长度进行处理 //【当字符串过长时,使用后端省略处理:“xxx...”】 addView(rootView); // 将TipView实例添加到传入的rootView中。 // 注意:rootView需要FrameLayout/RelativeLayout或其子类实例 initView(); // 根据传入的点击坐标x、y进行状态判断和最左位置的处理 }
initView()的具体实现:主要对临界情况的一些值得处理
private void initView() { // 获取屏幕宽度 int mScreenWidth = getResources().getDisplayMetrics().widthPixels; // 当点击事件的纵坐标y比较小时(点击的位置比较上面) if (y/2<mItemHeight) { mStatus = STATUS_DOWN; // 令窗口在下方显示 mTriangleTop = y + dip2px(5); // 设置三角下标的顶点 mItemBorder = mTriangleTop + dip2px(6); // 设置三角下标和窗口方块的交界处 } else { mStatus = STATUS_UP; // 同理如上 mTriangleTop = y - dip2px(5); mItemBorder = mTriangleTop - dip2px(6); } // 获取当Item数量两边对称时,窗口的left值: realLeft = x - (mItemWidth * mItemList.size()) / 2; if (realLeft < 0) { // 跑出屏幕左边的话,令窗口的left值为距离值 realLeft = mBorderMargin; // 防止三角下标与方块分离 if(x-mCorner<=realLeft) x = realLeft+mCorner*2; } else if (realLeft + (mItemWidth*mItemList.size()) > mScreenWidth) { // 跑出屏幕右边的话,则减去溢出的宽度,再减去距离值 realLeft -= realLeft + (mItemWidth*mItemList.size())-mScreenWidth+mBorderMargin; // 防止三角下标与方块分离 if(x+mCorner>=realLeft+mItemWidth*mItemList.size()) x = realLeft+mItemWidth*mItemList.size()-mCorner*2; } // 没有对窗口左右边都超出的情况做处理,大家有没有好的解决方案? }
三、TipView的绘制
首先绘制图层为透明,再根据判断得来的状态绘制窗口。@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawBackground(canvas); switch (mStatus) { case STATUS_DOWN: drawItemDown(canvas); break; case STATUS_UP: drawItemUp(canvas); break; default: break; } }
具体绘制各个窗口方块的代码太长不便贴出,可参考源码及其注释。
大致的流程就是
a. 绘制最左方块、最右方块和中间方块
b. 根据方块是否被点击而更换不同的颜色来绘制方块
c. 绘制分隔线
四、TipView中的Item点击事件的监听
设计一个监听器接口:public interface OnItemClickListener { // 设计两个参数 void onItemClick(String name,int position); void dismiss(); }
覆写TipView的onTouchEvent方法:
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: for (int i = 0; i < mItemRectList.size(); i++) { if (onItemClickListener != null && isPointInRect(new PointF(event.getX(), event.getY()), mItemRectList.get(i))) { // 被按下时,choose值为当前方块Item序号 choose = i; // 更新视图 postInvalidate(mItemRectList.get(i).left,mItemRectList.get(i).top,mItemRectList.get(i).right,mItemRectList.get(i).bottom); } } return true; case MotionEvent.ACTION_UP: for (int i = 0; i < mItemRectList.size(); i++) { if (onItemClickListener != null && isPointInRect(new PointF(event.getX(), event.getY()), mItemRectList.get(i))) { // 触发方法,传入两个参数 onItemClickListener.onItemClick(mItemList.get(i).getTitle(),i); choose = -1; } } if (onItemClickListener != null) { onItemClickListener.dismiss(); // 触发方法 } removeView(); // 移除TipView return true; } return true; }
五、以Build模式为设计模式:
public static class Builder { // TipView中一些重要的成员变量 private OnItemClickListener onItemClickListener; private Context mContext; private ViewGroup mRootView; private List<TipItem> mTipItemList = new ArrayList<>(); private int mSeparateLineColor = Color.WHITE; private int x ,y; public Builder(Context context, ViewGroup rootView,int x,int y) { mContext = context; mRootView = rootView; this.x = x; this.y = y; } public Builder addItem(TipItem tipItem) { mTipItemList.add(tipItem); return this; } public Builder addItems(List<TipItem> list) { mTipItemList.addAll(list); return this; } public Builder setSeparateLineColor(int color) { mSeparateLineColor = color; return this; } public Builder setOnItemClickListener(OnItemClickListener onItemClickListener){ this.onItemClickListener = onItemClickListener; return this; } // 创建TipView实例 public TipView create() { TipView flipShare = new TipView(mContext, mRootView,x,y,mTipItemList); flipShare.setOnItemClickListener(onItemClickListener); flipShare.setSeparateLineColor(mSeparateLineColor); return flipShare; } }
六、使用示例:
创建TipView实例时,需要传入控件被点击或长按时的位置坐标。使用注意:传给rootView的参数需要FrameLayout / RelativeLayout或其子类实例,这样TipView对象才可以悬浮地显示出来。
public class MainActivity extends AppCompatActivity { private Button button0; private RelativeLayout rl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button0 = (Button) findViewById(R.id.button0); rl = (RelativeLayout) findViewById(R.id.activity_main); button0.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int x = 0;int y = 0; if(event.getAction()==MotionEvent.ACTION_UP){ // 获取点击位置 x= (int) event.getX();y = (int) event.getY(); Log.i("zz",x+" "+y); // 传入视图上的坐标 TipView share = new TipView.Builder(MainActivity.this,rl,x+v.getLeft(),y+v.getTop()) .addItem(new TipItem("复制复制")) .addItem(new TipItem("粘贴")) .addItem(new TipItem("删除")) .addItem(new TipItem("收藏")) .addItem(new TipItem("转发")) .addItem(new TipItem("更多")) .setOnItemClickListener(new TipView.OnItemClickListener() { @Override public void onItemClick(String str,int a) { Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show(); } @Override public void dismiss() { } }) .create(); } return true; } }); } }
效果图Demo:Github
参考:
https://github.com/JeasonWong/FlipShare相关文章推荐
- QQ风格淡入淡出消息提示窗口的实现(WinForm)
- 像MSN,QQ,迅雷类似效果的提示窗口
- 关于安卓开发通过Toast显示消息提示框
- Cocos2d-x游戏开发之弹出式全屏提示窗口
- 用VC++制作MSN、QQ 的消息提示窗口
- 模仿QQ、MSN短消息提示窗口
- 【安卓手游开发经验】解决安卓调试经常提示内存不足问题
- 【吐血写此博】Fedora14下eclipse进行android开发,ibus提示没有输入窗口的 解决办法
- 类似QQ消息提示的任务栏窗口图标的高亮显示方法
- ios开发第一篇 简单的qq登录窗口
- 类似qq新闻提示窗口样码(cpy)
- 摘录的WinForm Control 开发5,WinForm 开发:TaskbarNotifierDemo ,类似qq弹出窗口
- QQ/MSN右下角弹出提示窗口
- Word二次开发--用户编辑时自动显示提示窗口
- 仿QQ、MSN消息提示窗口的实现(C#)
- 用VC++制作MSN、QQ 的消息提示窗口
- 制作 MSN、QQ 的消息提示窗口
- WinForm:实现类似QQ消息框一样的右下角消息提示窗口
- winforms实现类似QQ消息框一样的右下角消息提示窗口
- 实现类似QQ单一账户登录,在另一个地方登录后在原登录窗口提示下线