Android 사용자 정의 AvatarImageView 프로필 이미지 표시 효과 구현

효과 도 보기:
这里写图片描述
우리 프로젝트 의 이미지 디 스 플레이 는 일반적으로 원형 이지 만 가끔 은 여러 가지 스타일(규칙 적 인 모양 은 아 닙 니 다)을 제외 하지 않 습 니 다.예 를 들 어 지난번 UI 가 저 에 게 원형 아래 에 한 조각 이 빠 졌 습 니 다.저 희 는 일반적으로 사용자 정의 모양 을 실현 하 는 도형 은 세 가지 방식 이 있 습 니 다.Porter DuffXfermode,BitmapShader,ClipPath 입 니 다.다음은 제 가 여기 서 사용 하 는 첫 번 째 방식(실현 은 비교적 간단 합 니 다)을 설명 하 겠 습 니 다.
1.PorterDuffXfermode
이것 은 Tomas Proter 와 Tom Duff 가 명명 한 이미지 변환 모드 로 16 개의 매 거 진 값 으로 Canvas 상하 두 개의 그림 층 의 상호작용 을 제어 합 니 다(먼저 그린 그림 층 은 하층 에 있 습 니 다).
这里写图片描述
(파란색 은 윗 층 에 있다)
1.Porter Duff.Mode.CLEAR 가 그린 그림 은 캔버스 에 제출 되 지 않 습 니 다.
2.PorterDuff.Mode.SRC 상단 그림 표시
3.PorterDuff.Mode.DST 아래 그림 보이 기
4.PorterDuff.Mode.SRC_오 버 는 정상적으로 디 스 플레이 를 그립 니 다.상하 부 는 폴 더 캡 을 그립 니 다.
5.PorterDuff.Mode.DST_오 버,위아래 다 나 와.아래쪽 이 위 에 표 시 됩 니 다.
6.PorterDuff.Mode.SRC_IN 은 두 겹 으로 교 집합 을 그립 니 다.상층 부 를 드러내다.
7.PorterDuff.Mode.DST_IN 은 두 겹 으로 교 집합 을 그립 니 다.아래쪽 보이 기.
8.PorterDuff.Mode.SRC_OUT 는 상부 에서 비 교 집합 부분 을 그립 니 다.
9.PorterDuff.Mode.DST_OUT 는 아래쪽 에서 비 교 집합 부분 을 그립 니 다.
10.PorterDuff.Mode.SRC_ATOP 는 하층부 비 교 집합 부분 과 상층 부 교 집합 부분 을 취한 다.
11.PorterDuff.Mode.DST_ATOP 는 상층 의 비 교 집합 부분 과 하층 의 교 집합 부분 을 취한 다.
12.Porter Duff.Mode.XOR 이상 또는:두 그림 의 교차 부분 제거
13.PorterDuff.Mode.DARKEN 은 두 개의 그림 을 가 져 와 모든 영역 을 만 들 고 교차 부분 색상 이 진 화 됩 니 다.
14.Porter Duff.Mode.LIGHTEN 은 두 개의 그림 을 모두 취하 고 교차 부분 색상 을 밝 게 합 니 다.
15.Porter Duff.Mode.MULTIPLY 두 그림 의 교차 부분 을 중첩 한 후 색상
16.Porter Duff.Mode.Screen 은 두 그림 의 모든 영역 을 취하 고 교차 부분 은 투명 색 으로 변 합 니 다.
1.1 사고방식
Ps 를 할 줄 아 는 친 구 는 두 개의 그림 이 있다 면 위의 그림 을 아래 그림 의 모양 으로 자 르 려 면 아래 그림 의 선택 영역 을 바 꾸 고 위의 그림 을 선택 하면 됩 니 다.
그러면 저 희 는 Porter Duff.Mode 의 SRC 를 이용 할 수 있 습 니 다.IN 또는 DSTIN 은 두 개의 그림 의 교 집합 을 얻어 서 우리 가 원 하 는 여러 가지 스타일 로 그림 을 자 릅 니 다.우 리 는 형상 도 층 과 표시 도 층 이 필요 하 다.또한 그림 층 을 전면적으로 덮어 쓰 려 고 합 니 다.
1.2 실현
ImageView 를 계승 하여 imageview 의 네 가지 setImage 방법(더 좋 은 호환성 을 위해)을 복사 하여 setImageDrawable 방법 에서 전경 그림 을 얻 었 습 니 다.

 @Override
 public void setImageBitmap(Bitmap bm) {
 super.setImageBitmap(bm);
 mBitmap = bm;
 setBitmaps();
 }
 /**
 *               src
 */
 @Override
 public void setImageDrawable(Drawable drawable) {
 super.setImageDrawable(drawable);
 mBitmap = getBitmapFromDrawable(drawable);
 setBitmaps();
 }

 @Override
 public void setImageResource(int resId) {
 super.setImageResource(resId);
 mBitmap = getBitmapFromDrawable(getDrawable());
 setBitmaps();
 }

 @Override
 public void setImageURI(Uri uri) {
 super.setImageURI(uri);
 mBitmap = getBitmapFromDrawable(getDrawable());
 setBitmaps();
 }
배경 그림 을 가 져 오 면 invalidate()는 onDraw 방법 으로 다시 그립 니 다.

private void setBitmaps(){
 if(null==getBackground()){
  throw new IllegalArgumentException(String.format("background is null."));
 }else{
  backgroundBitmap = getBitmapFromDrawable(getBackground());
  invalidate();
 }
 }
물론 onMeasure 에서 view 의 높이 와 너 비 를 가 져 와 두 그림 의 크기 를 조정 해 야 합 니 다.

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec,heightMeasureSpec);
 /**
  *        ,  MeasureSpec.EXACTLY ( match_parent , accurate )
  *                    
  */
 viewWidth = MeasureSpec.getSize(widthMeasureSpec);
 viewHeight = MeasureSpec.getSize(heightMeasureSpec);
 }
그 다음 에 Porter Duff Xfermode 를 사용 하여 두 그림 의 상호작용 을 최종 Bitmap 로 만 듭 니 다.

private Bitmap createImage()
 {
 Paint paint = new Paint();
 paint.setAntiAlias(true);
 Bitmap finalBmp = Bitmap.createBitmap(viewWidth,viewHeight, Bitmap.Config.ARGB_8888);
 /**
  *            
  */
 Canvas canvas = new Canvas(finalBmp);
 /**
  *       
  */
 canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
 /**
  *   SRC_IN,       ,    。
  */
 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
 /**
  *       
  */
 canvas.drawBitmap(mBitmap, 0, 0, paint);
 return finalBmp;
 }
다시 그리 기 시작 합 니 다.

 @Override
 protected void onDraw(Canvas canvas) {
 if(mBitmap!=null && backgroundBitmap!=null){
  /**
  *         
  */
  int min = Math.min(viewWidth, viewHeight);
  backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, min, min, false);
  mBitmap = Bitmap.createScaledBitmap(mBitmap, min, min, false);
  /**
  *     bitmap   
  */
  canvas.drawBitmap(createImage(), 0, 0, null);
 }
 }
도구 함수 붙 이기

 /**
 * Drawable Bitmap
 */
 private Bitmap getBitmapFromDrawable(Drawable drawable) {
 super.setScaleType(ScaleType.CENTER_CROP);
 if (drawable == null) {
  return null;
 }
 if (drawable instanceof BitmapDrawable) {
  return ((BitmapDrawable) drawable).getBitmap();
 }
 try {
  Bitmap bitmap;
  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;
 } catch (OutOfMemoryError e) {
  return null;
 }
 }
간단 하지 않 습 니까?그리고 사 용 했 습 니 다.
1.3 사용
속성 값 이 추가 되 지 않 았 습 니 다.레이아웃 에서 다음 과 같이 사용 합 니 다.

<cn.fanrunqi.avatarimageview.AvatarImageView
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="@drawable/oval_shape"
  <!--android:background="@drawable/bg_a"-->
  android:src="@drawable/c"
  />
①、여기 안 드 로 이 드:background 는 우리 의 모양 그림 층 을 정의 합 니 다.xxx 일 수 있 습 니 다.shape.xml 의 레이아웃 파일,예 를 들 어.

<?xml version="1.0" encoding="utf-8"?>
<shape
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="oval"
 android:useLevel="false">
 <solid android:color="#fff"/>
 <size android:width="100dp"
  android:height="100dp"/>
</shape>
그림 일 수도 있 습 니 다.
这里写图片描述
②、android:src 는 우리 가 표시 할 프로필 사진 을 정의 합 니 다.
원본 주소
2.BitmapShader
우 리 는 착색 기 라 고 할 수 있 는데,아래 코드 를 통 해 착색 기 를 얻 을 수 있다.
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
매개 변수
① mBitmap:당신 이 그 릴 그림
② emun Shader.TileMode 는 세 가지 착색 모드 를 정의 합 니 다.
CLAMP 스 트 레 칭
반복 반복
MIRROR 미 러
해상도 와 컴퓨터 화면 이 다른 그림 을 벽지 로 설정 할 때 선택 한 세 가지 방식 과 같다.
우 리 는 붓 에 착색 기 를 설치 할 수 있 습 니 다.그러면 붓 은 canvas 의 해당 모양 에 우리 의 그림 mBitmap 를 그 릴 수 있 습 니 다.

mBitmapPaint.setShader(mBitmapShader);

canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
물론 우리 가 일반적으로 설정 하 는 모드 는 CLAMP 스 트 레 칭(그림 mBitmap 의 너비 가 View 보다 작 을 때 스 트 레 칭)이지 만 우 리 는 일반적으로 스 트 레 칭(변형)하지 않 기 때문에 착색 기 에 matrix 를 설정 하여 그림 을 적당 하 게 확대 하거나 축소 해 야 한다.

mBitmapShader.setLocalMatrix(mShaderMatrix);


2.1 CircleImageView 소스 코드 분석
유명한 프로젝트 CircleImageView 는 착색 기로 이 루어 진 것 으로 실현 사고방식 에 이미 말 했 듯 이 코드 에 상세 한 주석 이 있어 이해 하기 에는 문제 가 없 을 것 이다.

public class CircleImageView extends ImageView {
 //    
 private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
 private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
 private static final int COLORDRAWABLE_DIMENSION = 2;
 //       
 private static final int DEFAULT_BORDER_WIDTH = 0;
 //       
 private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
 private static final boolean DEFAULT_BORDER_OVERLAY = false;

 private final RectF mDrawableRect = new RectF();
 private final RectF mBorderRect = new RectF();

 private final Matrix mShaderMatrix = new Matrix();
 //            mBitmapShader  canvas             (mBitmapShader       bitmap )
 private final Paint mBitmapPaint = new Paint();
 //    ,       bitmap      ,
 private final Paint mBorderPaint = new Paint();
 //                  
 private int mBorderColor = DEFAULT_BORDER_COLOR;
 private int mBorderWidth = DEFAULT_BORDER_WIDTH;

 private Bitmap mBitmap;
 private BitmapShader mBitmapShader; //     
 private int mBitmapWidth; //     
 private int mBitmapHeight; //     

 private float mDrawableRadius;//     
 private float mBorderRadius;//          

 private ColorFilter mColorFilter;
 //  false
 private boolean mReady;
 private boolean mSetupPending;
 private boolean mBorderOverlay;
 //    
 public CircleImageView(Context context) {
 super(context);
 init();
 }
 //    
 public CircleImageView(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
 }
 /**
 *     
 */
 public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
 //  TypedArray        getXXXX     xml       ;
 //        
 mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
 //        
 mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
 mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_border_overlay, DEFAULT_BORDER_OVERLAY);
 //   recycle()   TypedArray,      
 a.recycle();
 init();
 }
 /**
 *            setup                    
 */
 private void init() {
 //   ScaleType      CENTER_CROP,           ,    。
 super.setScaleType(SCALE_TYPE);
 mReady = true;

 if (mSetupPending) {
  setup();
  mSetupPending = false;
 }
 }

 @Override
 public ScaleType getScaleType() {
 return SCALE_TYPE;
 }
 /**
 *          imageview    CENTER_CROP      
 *
 * @param scaleType
 */
 @Override
 public void setScaleType(ScaleType scaleType) {
 if (scaleType != SCALE_TYPE) {
  throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
 }
 }

 @Override
 public void setAdjustViewBounds(boolean adjustViewBounds) {
 if (adjustViewBounds) {
  throw new IllegalArgumentException("adjustViewBounds not supported.");
 }
 }

 @Override
 protected void onDraw(Canvas canvas) {
 //          
 if (getDrawable() == null) {
  return;
 }
 //            mBitmapPaint
 canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
 //           0                    mBorderPaint
 if (mBorderWidth != 0) {
  canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
 }
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);
 setup();
 }

 public int getBorderColor() {
 return mBorderColor;
 }

 public void setBorderColor(int borderColor) {
 if (borderColor == mBorderColor) {
  return;
 }

 mBorderColor = borderColor;
 mBorderPaint.setColor(mBorderColor);
 invalidate();
 }

 public void setBorderColorResource( int borderColorRes) {
 setBorderColor(getContext().getResources().getColor(borderColorRes));
 }

 public int getBorderWidth() {
 return mBorderWidth;
 }

 public void setBorderWidth(int borderWidth) {
 if (borderWidth == mBorderWidth) {
  return;
 }

 mBorderWidth = borderWidth;
 setup();
 }

 public boolean isBorderOverlay() {
 return mBorderOverlay;
 }

 public void setBorderOverlay(boolean borderOverlay) {
 if (borderOverlay == mBorderOverlay) {
  return;
 }

 mBorderOverlay = borderOverlay;
 setup();
 }

 /**
 *         
 *   ImageView setImageXxx()  
 *                   
 * @param bm
 */
 @Override
 public void setImageBitmap(Bitmap bm) {
 super.setImageBitmap(bm);
 mBitmap = bm;
 setup();
 }

 @Override
 public void setImageDrawable(Drawable drawable) {
 super.setImageDrawable(drawable);
 mBitmap = getBitmapFromDrawable(drawable);
 setup();
 }

 @Override
 public void setImageResource( int resId) {
 super.setImageResource(resId);
 mBitmap = getBitmapFromDrawable(getDrawable());
 setup();
 }

 @Override
 public void setImageURI(Uri uri) {
 super.setImageURI(uri);
 mBitmap = getBitmapFromDrawable(getDrawable());
 setup();
 }

 @Override
 public void setColorFilter(ColorFilter cf) {
 if (cf == mColorFilter) {
  return;
 }

 mColorFilter = cf;
 mBitmapPaint.setColorFilter(mColorFilter);
 invalidate();
 }
 /**
 * Drawable Bitmap
 * @param drawable
 * @return
 */
 private Bitmap getBitmapFromDrawable(Drawable drawable) {
 if (drawable == null) {
  return null;
 }

 if (drawable instanceof BitmapDrawable) {
  //                     。           bitmap
  return ((BitmapDrawable) drawable).getBitmap();
 }

 try {
  Bitmap bitmap;

  if (drawable instanceof ColorDrawable) {
  bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
  } else {
  bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
  }

  Canvas canvas = new Canvas(bitmap);
  drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
  drawable.draw(canvas);
  return bitmap;
 } catch (OutOfMemoryError e) {
  return null;
 }
 }
 /**
 *        ,          (Paint)         :
 *      BitmapShader Bitmap       ,              ,
 *     updateShaderMatrix()    invalidate()  ;
 */
 private void setup() {
 //  mReady    false,             if          
 //  mSetupPending true      ,          。
 if (!mReady) {
  mSetupPending = true;
  return;
 }
 //       
 if (mBitmap == null) {
  return;
 }
 //      , mBitmap         ,                   
 mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
 //          
 mBitmapPaint.setAntiAlias(true);
 //          
 mBitmapPaint.setShader(mBitmapShader);
 //         
 mBorderPaint.setStyle(Paint.Style.STROKE);//      
 mBorderPaint.setAntiAlias(true);
 mBorderPaint.setColor(mBorderColor); //    
 mBorderPaint.setStrokeWidth(mBorderWidth);//      
 //             
 mBitmapHeight = mBitmap.getHeight();
 mBitmapWidth = mBitmap.getWidth();
 //          ,   CircleImageView       ,   
 mBorderRect.set(0, 0, getWidth(), getHeight());
 //          (  )     , mBorderRect                  
 mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);
 //          mBorderRect(CircleImageView       )
 mDrawableRect.set(mBorderRect);
 if (!mBorderOverlay) {
  //demo     
  //  inset             mBorderRect                 
  mDrawableRect.inset(mBorderWidth, mBorderWidth);
 }
 //             ,           
 mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);
 //             mBitmap         
 updateShaderMatrix();
 //    ondraw()          
 invalidate();
 }
 /**
 *        BitmapShader Matrix  ,        ,    。
 *   :                       
 */
 private void updateShaderMatrix() {
 float scale;
 float dx = 0;
 float dy = 0;

 mShaderMatrix.set(null);
 //                (mBitmapWidth / mDrawableRect.width()) > (mBitmapHeight / mDrawableRect.height())
 //        
 if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
  //y    x         y                 (mDrawableRect)  )
  scale = mDrawableRect.height() / (float) mBitmapHeight;
  dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
 } else {
  //x    y         x                 (mDrawableRect)  )
  scale = mDrawableRect.width() / (float) mBitmapWidth;
  dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
 }
 // shaeder     ,              。
 mShaderMatrix.setScale(scale, scale);
 //   
 mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
 //       
 mBitmapShader.setLocalMatrix(mShaderMatrix);
 }

}
3.ClipPath
우 리 는 다음 과 같이 경 로 를 새로 만 든 다음 path.addxxx()방법 으로 경 로 를 얻 을 수 있다 는 것 을 알 고 있 습 니 다.

Path path = new Path(); 
그 다음 에 canvas 를 우리 경로 구역 의 모양 으로 자 를 수 있 습 니 다.바로 캔버스 모양 이 확정 되 었 고 마지막 으로 도형 만 그리 면 됩 니 다.
다음 코드 는 도형 bitmap 를 원 에 그 려 서 원형 두상 을 얻 을 수 있 습 니 다.

@Override
protected void onDraw(Canvas canvas) {
 Path path = new Path(); 
 //            
 path.addCircle(float x, float y, mRadius, Direction.CCW);
 //  canvas  
 canvas.save();
 // canvas          
 canvas.clipPath(path);
 //    Bitmap
 canvas.drawBitmap(Bitmap,float left, float top, mPaint);
 //  Canvas
 canvas.restore();
}
이런 방식 은 분명히 가장 간단 하 다.너 는 좌표 점 을 하나씩 추가 하여 하나의 경 로 를 형성 할 수 있다.하지만 모양 이 복잡 한 상황 에 서 는 첫 번 째 실현 이 편리 하 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기