您的位置:首页 > 其它

安卓开发之自定义控件TipView仿QQ长按后的提示窗口

2016-09-27 23:22 330 查看
先上效果图:



之前用手机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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息