«

微信气泡状ImageView

时间:2024-3-2 18:22     作者:韩俊     分类: Android


仅做记录,实现微信聊天界面的气泡状的ImageView,并添加了点击时的变色效果

先上效果图


























下面是代码

public class BubbleImageView extends ImageView {

    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.default_shadow_color);
        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();
            }
        }
    }

}

主题文件:

<resources>
    <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>
</resources>


布局文件
</pre><pre name="code" class="html"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f0f0f0">

    <com.demo.boucelistviewsample.BubbleImageView
        android:id="@+id/imageView"
        android:layout_width="100dip"
        android:layout_height="120dip"
        android:layout_marginLeft="20dip"
        android:scaleType="centerCrop"
        android:src="@drawable/xiaoming"
        app:direction="left"
        app:rect_radius="5dip"
        app:triangle_height="15dip"
        app:triangle_marginTop="40dip"
        app:triangle_width="12dip"></com.demo.boucelistviewsample.BubbleImageView>

    <com.demo.boucelistviewsample.BubbleImageView
        android:id="@+id/image1"
        android:layout_width="100dip"
        android:layout_height="120dip"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/imageView"
        android:layout_marginRight="20dip"
        android:scaleType="centerCrop"
        android:src="@drawable/xiaoming"
        app:direction="right"
        app:rect_radius="5dip"
        app:triangle_height="15dip"
        app:triangle_marginTop="40dip"
        app:triangle_width="12dip"></com.demo.boucelistviewsample.BubbleImageView>
</RelativeLayout>


MainActivity.java


<pre name="code" class="java">public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.imageView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "onClick", Toast.LENGTH_SHORT).show();
            }
        });
        findViewById(R.id.imageView).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                Toast.makeText(MainActivity.this, "onLongClick", Toast.LENGTH_SHORT).show();
                return false;
            }
        });
    }
}





标签: android

热门推荐