自定義View——聊天頁圖片展示
阿新 • • 發佈:2018-12-16
我儘量不打錯別字,用詞準確,不造成閱讀障礙。
本文是自定義聊天時的圖片和視訊顯示及傳送控制元件。
效果:
程式碼:
自定義屬性
<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();