自定义实现类聊天界面的气泡状ImageView

本文介绍了一种自定义的BubbleImageView,该控件扩展了AppCompatImageView,支持设置图片圆角、阴影颜色和气泡方向。通过重写onDraw方法并使用Path来切割画布,实现了气泡状的效果。此外,还支持点击和长按事件处理,可以应用于聊天界面等场景。
摘要由CSDN通过智能技术生成

自定义实现类聊天界面的气泡状ImageView,支持图片圆角设置,阴影颜色,气泡的方向等
请添加图片描述

请添加图片描述

自定义ImageView控件实现气泡状效果

public class BubbleImageView extends androidx.appcompat.widget.AppCompatImageView {


    private static final int DEFAULT_RADIUS = 10;
    private static final int DEFAULT_TRIANGLE_WIDTH = 20;
    private static final int DEFAULT_TRIANGLE_HEIGHT = 20;
    private static final int DEFAULT_TRIANGLE_MARGIN_TOP = 20;


    private int mViewWidth = 0;//宽度
    private int mViewHeight = 0;//高度
    private int mTriangleWidth = 0;//气泡三角形的宽度
    private int mTriangleHeight = 0;//气泡三角形的高度
    private Path mBubblePath;//用于切割ImageView
    private int mRadius = 0;//ImageView四角的半径
    private int mTriangleMarginTop = 0;//气泡三角形离顶部的距离
    private int mShadowColor;//当点击时ImageView表面覆盖的颜色
    private Paint mShadowPaint;
    private boolean mTouched = false;//是否被点击


    private Rect mTouchRect;//触摸范围
    private boolean mOuter = true;
    private OnClickListener mOnClickListener;
    private OnLongClickListener mOnLongClickListener;



    private Runnable mCheckLongPressRunnable;
    private boolean mHasPerformedLongPress = false;
    private static final int LEFT = 1;
    private static final int RIGHT = 2;
    private int mDirection = LEFT;


    public BubbleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttribute(context, attrs);


    }


    /**
     * 初始化各个参数
     *
     * @param context
     * @param attrs
     */
    private void initAttribute(Context context, AttributeSet attrs) {
        TypedArray attr = getTypedArray(context, attrs, R.styleable.BubbleImageView);
        mTriangleMarginTop = attr.getDimensionPixelSize(R.styleable.BubbleImageView_triangle_marginTop, DEFAULT_TRIANGLE_MARGIN_TOP);
        mTriangleWidth = attr.getDimensionPixelSize(R.styleable.BubbleImageView_triangle_width, DEFAULT_TRIANGLE_WIDTH);
        mTriangleHeight = attr.getDimensionPixelSize(R.styleable.BubbleImageView_triangle_height, DEFAULT_TRIANGLE_HEIGHT);
        mRadius = attr.getDimensionPixelSize(R.styleable.BubbleImageView_rect_radius, DEFAULT_RADIUS);
        int mDefaultColor = context.getResources().getColor(R.color.color_666666_60);
        mShadowColor = attr.getColor(R.styleable.BubbleImageView_shadow_color, mDefaultColor);
        mDirection = attr.getInt(R.styleable.BubbleImageView_direction, LEFT);
    }


    protected TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr) {
        return context.obtainStyledAttributes(attributeSet, attr, 0, 0);
    }


    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        init();
    }


    private void initPath() {
        mBubblePath = new Path();
        switch (mDirection) {
            case LEFT:
                mBubblePath.moveTo(mTriangleWidth, mTriangleMarginTop);
                mBubblePath.lineTo(0, mTriangleMarginTop + mTriangleHeight / 2.0f);
                mBubblePath.lineTo(mTriangleWidth, mTriangleMarginTop + mTriangleHeight);
                mBubblePath.close();
                mBubblePath.addRoundRect(new RectF(mTriangleWidth, 0, mViewWidth, mViewHeight), mRadius, mRadius, Path.Direction.CW);
                break;
            case RIGHT:
                mBubblePath.moveTo(mViewWidth - mTriangleWidth, mTriangleMarginTop);
                mBubblePath.lineTo(mViewWidth, mTriangleMarginTop + mTriangleHeight / 2.0f);
                mBubblePath.lineTo(mViewWidth - mTriangleWidth, mTriangleMarginTop + mTriangleHeight);
                mBubblePath.close();
                mBubblePath.addRoundRect(new RectF(0, 0, mViewWidth - mTriangleWidth, mViewHeight), mRadius, mRadius, Path.Direction.CW);
                break;
        }
    }


    private void init() {
        mViewHeight = getMeasuredHeight();
        mViewWidth = getMeasuredWidth();
        initPath();
        //绘制一个气泡状的Path
        mShadowPaint = new Paint();
        mShadowPaint.setAntiAlias(true);
        mShadowPaint.setColor(mShadowColor);
        mTouchRect = new Rect();
        getGlobalVisibleRect(mTouchRect);//获取全局rect
    }


    public void onDraw(Canvas canvas) {
        canvas.clipPath(mBubblePath);//切割画布
        super.onDraw(canvas);
        if (mTouched)
            canvas.drawPath(mBubblePath, mShadowPaint);//当点击时,绘制表面阴影
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                onTouchDown(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                return onTouchMove(ev);
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                onTouchUp(ev);
                break;
        }
        return true;
    }


    private void onTouchDown(MotionEvent ev) {
        mOuter = false;
        mTouched = true;

        if (mCheckLongPressRunnable != null)
            getHandler().removeCallbacks(mCheckLongPressRunnable);
        invalidate();
        if (mOnLongClickListener != null) {
            postCheckForLongClick();
        }
    }


    private boolean onTouchMove(MotionEvent ev) {
        if (!mTouchRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
            mTouched = false;
            invalidate();
            mOuter = true;
            return false;
        }
        return true;
    }


    private void onTouchUp(MotionEvent ev) {
        if (mHasPerformedLongPress || mOuter)
            return;
        mTouched = false;
        invalidate();
        if (mOnClickListener != null) {
            mOnClickListener.onClick(this);
        }
    }


    private void postCheckForLongClick() {
        mHasPerformedLongPress = false;
        if (mCheckLongPressRunnable == null) {
            mCheckLongPressRunnable = new CheckForLongPress();
        }
        postDelayed(mCheckLongPressRunnable, ViewConfiguration.getLongPressTimeout());


    }




    @Override
    public void setOnClickListener(OnClickListener on) {
        mOnClickListener = on;
    }


    @Override
    public void setOnLongClickListener(OnLongClickListener on) {
        mOnLongClickListener = on;
    }


    private class CheckForLongPress implements Runnable {
        @Override
        public void run() {
            if (mTouched && mOnLongClickListener != null) {
                mOnLongClickListener.onLongClick(BubbleImageView.this);
                mHasPerformedLongPress = true;
                mTouched = false;
                postInvalidate();
            }
        }
    }
}
<declare-styleable name="BubbleImageView">
    <attr name="triangle_marginTop" format="dimension" />
    <attr name="triangle_height" format="dimension" />
    <attr name="triangle_width" format="dimension" />
    <attr name="rect_radius" format="dimension" />
    <attr name="shadow_color" format="color" />
    <attr name="direction" format="integer">
        <enum name="left" value="1"></enum>
        <enum name="right" value="2"></enum>
    </attr>
</declare-styleable>

使用

<com.qianfanyun.base.wedgit.BubbleImageView
    android:id="@+id/iv_receive_Picture"
    android:layout_width="219dp"
    android:layout_height="227dp"
    android:scaleType="centerCrop"
    app:direction="left"
    app:rect_radius="5dip"
    app:triangle_height="14dip"
    app:triangle_marginTop="15dip"
    app:triangle_width="7dip"
    app:shadow_color="@color/transparent"/>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值