1. 程式人生 > >自定義View——聊天頁圖片展示

自定義View——聊天頁圖片展示

我儘量不打錯別字,用詞準確,不造成閱讀障礙。

本文是自定義聊天時的圖片和視訊顯示及傳送控制元件。

效果:

在這裡插入圖片描述 在這裡插入圖片描述

程式碼:

自定義屬性

 <declare-styleable name="MyIMImageView">
        <attr name="arrowTop" format="dimension|reference" />
        <attr name="arrowWidth" format="dimension|reference" />
        <attr name="arrowHeight" format="dimension|reference"
/>
<attr name="circular" format="dimension|reference" /> <attr name="direction" format="string" /> <attr name="showText" format="boolean" /> <attr name="showShadow" format="boolean" /> </declare-styleable>

View程式碼

public class MyIMImageView
extends android.support.v7.widget.AppCompatImageView { private float mAngleLength = dp2px(20); //圓弧半徑 private float mArrowTop = dp2px(40); //箭頭距離頂部的位置,有的需要箭頭在正中間 private float mArrowWidth = dp2px(20); //箭頭寬度 private float mArrowHeight = dp2px(20); //箭頭高度 private Paint mBitmapPaint;
//畫Bitmap畫筆 private Paint mTextPaint; //寫進度的Bitmap private String direction; //方向 private Bitmap mBitmap; //資源圖片 private BitmapShader mBitmapShader; //bitmap著色器 private RectF mBitmapRectF; private RectF mShadowRectF = new RectF(); private Rect mTextRect = new Rect(); private Path mPath; private boolean isShowText; //是否顯示文字 private boolean isShowShadow; //是否顯示陰影 private int mProgressPercent = 69; public MyIMImageView(Context context) { this(context, null); } public MyIMImageView(Context context, AttributeSet attrs) { super(context, attrs); initAttr(attrs); } private void initAttr(AttributeSet attrs) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyIMImageView); mArrowTop = typedArray.getDimension(R.styleable.MyIMImageView_arrowTop, mArrowTop); mArrowWidth = typedArray.getDimension(R.styleable.MyIMImageView_arrowWidth, mArrowWidth); mArrowHeight = typedArray.getDimension(R.styleable.MyIMImageView_arrowHeight, mArrowHeight); mAngleLength = typedArray.getDimension(R.styleable.MyIMImageView_circular, mAngleLength); direction = typedArray.getString(R.styleable.MyIMImageView_direction); isShowText = typedArray.getBoolean(R.styleable.MyIMImageView_showText, false); isShowShadow = typedArray.getBoolean(R.styleable.MyIMImageView_showShadow, false); typedArray.recycle(); } @Override protected void onDraw(Canvas canvas) { if (mBitmapPaint != null) { canvas.drawPath(mPath, mBitmapPaint); drawShadowAndProgress(canvas, mBitmapRectF); //畫陰影和進度 } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmapRectF = new RectF(getPaddingLeft(), getPaddingTop(), getRight() - getLeft() - getPaddingRight(), getBottom() - getTop() - getPaddingBottom()); mPath = new Path(); setBitmap(); if ("right".equals(direction)) { rightPath(mBitmapRectF, mPath); } else if ("left".equals(direction)) { leftPath(mBitmapRectF, mPath); } } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); mBitmap = bm; setBitmap(); } @Override public void setImageDrawable(@Nullable Drawable drawable) { super.setImageDrawable(drawable); mBitmap = getBitmapFromDrawable(drawable); setBitmap(); } @Override public void setImageResource(int resId) { super.setImageResource(resId); mBitmap = getBitmapFromDrawable(getDrawable()); setBitmap(); } //三角形在有右邊 private void rightPath(RectF rectF, Path path) { path.moveTo(rectF.left + mAngleLength, rectF.top); //path移動到上邊直線左點 path.lineTo(rectF.right - mAngleLength - mArrowWidth, rectF.top); //劃上邊線 //畫右上角弧線 path.arcTo(new RectF(rectF.right - mAngleLength - mArrowWidth, rectF.top, rectF.right - mArrowWidth, mAngleLength + rectF.top), 270, 90); path.lineTo(rectF.right - mArrowWidth, rectF.top + mArrowTop);//畫弧線和三角之間的直線 path.lineTo(rectF.right, rectF.top + mArrowTop + mArrowHeight / 2); //畫三角的上邊線 path.lineTo(rectF.right - mArrowWidth, rectF.top + mArrowTop + mArrowHeight); //畫三角的下邊線 path.lineTo(rectF.right - mArrowWidth, rectF.bottom - mAngleLength);//三角下邊的線 //畫右下角弧線 path.arcTo(new RectF(rectF.right - mAngleLength - mArrowWidth, rectF.bottom - mAngleLength, rectF.right - mArrowWidth, rectF.bottom), 0, 90); path.lineTo(rectF.left - mAngleLength, rectF.bottom); //畫底邊線 //畫左下角弧線 path.arcTo(new RectF(rectF.left, rectF.bottom - mAngleLength, mAngleLength + rectF.left, rectF.bottom), 90, 90); path.lineTo(rectF.left, rectF.top - mAngleLength); //畫左邊線 //畫左上角弧線 path.arcTo(new RectF(rectF.left, rectF.top, mAngleLength + rectF.left, mAngleLength + rectF.top), 180, 90); path.close(); } //三角形在左邊 private void leftPath(RectF rectF, Path path) { path.moveTo(rectF.left + mAngleLength + mArrowWidth, rectF.top);//path移動到上邊直線左點 path.lineTo(rectF.right - mAngleLength, rectF.top); //劃上邊線 //畫右上角弧線 path.arcTo(new RectF(rectF.right - mAngleLength, rectF.top, rectF.right, mAngleLength + rectF.top), 270, 90); path.lineTo(rectF.right, rectF.bottom - mAngleLength); //畫右下角弧線 path.arcTo(new RectF(rectF.right - mAngleLength, rectF.bottom - mAngleLength, rectF.right, rectF.bottom), 0, 90); path.lineTo(rectF.left - mAngleLength - mArrowWidth, rectF.bottom); //畫底邊線 //畫左下角弧線 path.arcTo(new RectF(rectF.left + mArrowWidth, rectF.bottom - mAngleLength, mAngleLength + rectF.left + mArrowWidth, rectF.bottom), 90, 90); path.lineTo(rectF.left + mArrowWidth, rectF.top + mArrowTop + mArrowHeight); //三角下邊的線 path.lineTo(rectF.left, rectF.top + mArrowTop + mArrowHeight / 2); //畫三角的下邊線 path.lineTo(rectF.left + mArrowWidth, rectF.top + mArrowTop); //畫三角的上邊線 path.lineTo(rectF.left + mArrowWidth, rectF.top + mArrowTop); //三角上邊的線 //畫左上角弧線 path.arcTo(new RectF(rectF.left + mArrowWidth, rectF.top, mAngleLength + rectF.left + mArrowWidth, mAngleLength + rectF.top), 180, 90); path.close(); } private void drawShadowAndProgress(Canvas canvas, RectF rectF) { if (isShowShadow) { mTextPaint.setAntiAlias(true); mTextPaint.setStyle(Paint.Style.FILL); mTextPaint.setColor(Color.parseColor("#70000000")); if ("right".equals(direction)) { mShadowRectF.set(rectF.left, rectF.top, rectF.right - mArrowWidth, rectF.bottom); } else { mShadowRectF.set(rectF.left + mArrowWidth, rectF.top, rectF.right, rectF.bottom); } canvas.drawRoundRect(mShadowRectF, mAngleLength / 2, mAngleLength / 2, mTextPaint); } if (isShowText) { mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(getResources().getDimension(R.dimen.shibadp)); mTextPaint.getTextBounds(mProgressPercent + "%", 0, (mProgressPercent + "%").length(), mTextRect); if ("right".equals(direction)) { canvas.drawText(mProgressPercent + "%", (rectF.right - mArrowWidth - mTextRect.width()) / 2, rectF.bottom / 2, mTextPaint); } else { canvas.drawText(mProgressPercent + "%", (rectF.left + rectF.right - mTextRect.width()) / 2, rectF.bottom / 2, mTextPaint); } } } //從Drawable中獲取bitmap private Bitmap getBitmapFromDrawable(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; if (drawable instanceof ColorDrawable) { bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); } else { bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } private void setBitmap() { mTextPaint = new Paint(); //設定著色器為拉伸,圖片很小的時候可以用 mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setShader(mBitmapShader); } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } public void setProgressPercent(int percent) { this.mProgressPercent = percent; postInvalidate(); } public void setIsShowText(boolean isShowText) { this.isShowText = isShowText; } public void setIsShowShadow(boolean isShowShadow) { this.isShowShadow = isShowShadow; } }

原理很簡單的,就是設定圖片後先獲取Bitmap,然後設定shader和paint,然後畫圖形的path,最後在onDraw方法裡,根據paint和path來畫圖。

Bug

但是這時候有bug,就是圖片大小和矩形大小不一致,圖片需要處理,基本99%的圖片都需要處理,比如圖片太大,而我們的控制元件小,那麼控制元件只能顯示圖片中,左上角位置控制元件大小的部分,其他部分放不下,就這樣:新建點陣圖影象

紅色矩形代表圖片,黑色部分為控制元件,虛線部分都看不見。解決方法也很簡單,就是縮放,這裡我們一般使用矩陣進行縮放操作,寫在setBitmap()裡。

程式碼如下:

private void setBitmap() {
        mTextPaint = new Paint();
        //設定著色器為拉伸
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mBitmapPaint = new Paint();
        mBitmapPaint.setAntiAlias(true);
        mBitmapPaint.setShader(mBitmapShader);

        mBitmapHeight = mBitmap.getHeight();
        mBitmapWidth = mBitmap.getWidth();
        setMatrix();
        invalidate();
    }

setMatrix()方法:

private void setMatrix() {
        float scaleX;
        float scaleY;
        Matrix mShaderMatrix = new Matrix();