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

1,862 阅读3分钟
原文链接: blog.csdn.net

先上效果图:

这里写图片描述

之前用手机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 mItemList = new ArrayList<>(); // 存储每个Item的信息
private List mItemRectList = new ArrayList<>(); // 存储每个方块


private OnItemClickListener onItemClickListener; // Item点击接口
private int choose = -1; // 是否有Item被按下,是为Item的序号,否为-1
private int x; // 外界出入的x坐标
private int y; // 外界传入的y坐标

二、TipView的初始化

TipView的构造方法:

public TipView(Context context, ViewGroup rootView,int x,int y,List 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 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;
    }
}

具体绘制窗口代码太长不便贴出,可到源码参考。

四、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;
                    // 更新视图
                    invalidate(mItemRectList.get(i));
                }
            }
            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 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 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或其子类实例

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

参考:

github.com/JeasonWong/…