Android 사용자 정의 컨트롤 의 원형/원 각 구현 코드

1.문 제 는 어디 에 있 습 니까?
문 제 는 app 개발 에서 흔히 볼 수 있 는 장면 인 사용자 프로필 사진 을 원 으로 보 여 주 는 데 서 비롯 된다.

 어떻게
슬기 로 운 저 는 첫 번 째 생각 은 중간 에 원형 이 투명 하고 사방 이 바탕색 과 같 으 며 사이즈 가 두상 과 같은 몽 판 그림 을 자 르 면 두상 에 덮 으 면 끝 이 잖 아 요.하하 하!
배경 이 순수한 전제 에서 이것 은 확실히 문 제 를 간단하게 해결 할 수 있 지만 배경 이 이렇게 간단 하지 않다 면?

이런 불규칙 한 배경 에서 두 가지 문제 가 있다.
1)배경 그림 은 핸드폰 너비 의 크기 조정 에 적응 되 고 두상 의 사 이 즈 는 너비 가 넓 고 DP 가 고정 되 어 있 기 때문에 고정된 몽 판 그림 은 서로 다른 기종 에서 배경 그림 과 일치 하 는 것 을 보장 할 수 없다.
2)、이런 순수한 색 이 아 닌 배경 에서 어느 날 이미지 위 치 를 조정 하려 면 다시 그림 을 바 꿔 야 합 니 다.유지 하기 가 너무 어렵 습 니 다..............................................
그래서 프로필 사진 이 네모 난 것 이 분명 하 다 면 ImageView 를 둥 글 게 합 시다.
일 을 시작 하 다
기본 적 인 사 고 는 ImageView 를 사용자 정의 하고 onDraw 를 다시 쓰 는 방법 으로 동 그 란 그림 을 그 리 는 것 입 니 다.

public class ImageViewPlus extends ImageView{
 private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG);
 private Bitmap mRawBitmap;
 private BitmapShader mShader;
 private Matrix mMatrix = new Matrix();
 
 public ImageViewPlus(Context context, AttributeSet attrs) {
 super(context, attrs);
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 Bitmap rawBitmap = getBitmap(getDrawable());
 if (rawBitmap != null){
  int viewWidth = getWidth();
  int viewHeight = getHeight();
  int viewMinSize = Math.min(viewWidth, viewHeight);
  float dstWidth = viewMinSize;
  float dstHeight = viewMinSize;
  if (mShader == null || !rawBitmap.equals(mRawBitmap)){
  mRawBitmap = rawBitmap;
  mShader = new BitmapShader(mRawBitmap, TileMode.CLAMP, TileMode.CLAMP);
  }
  if (mShader != null){
  mMatrix.setScale(dstWidth / rawBitmap.getWidth(), dstHeight / rawBitmap.getHeight());
  mShader.setLocalMatrix(mMatrix);
  }
  mPaintBitmap.setShader(mShader);
  float radius = viewMinSize / 2.0f;
  canvas.drawCircle(radius, radius, radius, mPaintBitmap);
 } else {
  super.onDraw(canvas);
 }
 }

 private Bitmap getBitmap(Drawable drawable){
 if (drawable instanceof BitmapDrawable){
  return ((BitmapDrawable)drawable).getBitmap();
 } else if (drawable instanceof ColorDrawable){
  Rect rect = drawable.getBounds();
  int width = rect.right - rect.left;
  int height = rect.bottom - rect.top;
  int color = ((ColorDrawable)drawable).getColor();
  Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);
  canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));
  return bitmap;
 } else {
  return null;
 }
 }
}
코드 분석:
 canvas.draw Circle 은 그 려 진 모양 이 원형 이 고 동 그 란 내용 은 mPaintBitmap.setShader 를 통 해 이 루어 졌 다.
그 중에서 BitmapShader 는 Bitmap 가 ImageView 를 채 우 는 방식(CLAMP:스 트 레 칭 가장자리,MIRROR:미 러,REPEAT:전체 그림 중복)을 설정 해 야 합 니 다.
이것 은 사실 무엇 을 설정 하 는 지 중요 하지 않 습 니 다.왜냐하면 우리 가 실제 필요 로 하 는 것 은 Bitmap 를 미리 설정 한 세 가지 효과 가 아니 라 비례 에 따라 크기 를 조정 하 는 것 입 니 다.
그 러 니 mMatrix.setScale 과 mShader.setLocalMatrix 를 함께 사용 하여 그림 크기 를 조정 하 는 것 을 잊 지 마 세 요.
4.더 많은 게임 방법-테두리 지원
아래 효과 도 를 보 세 요.동 그 란 두상 에 테 두 리 를 두 르 려 면 어떻게 해 야 하나 요?

public class ImageViewPlus extends ImageView{
 private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG);
 private Paint mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
 private Bitmap mRawBitmap;
 private BitmapShader mShader;
 private Matrix mMatrix = new Matrix();
 private float mBorderWidth = dip2px(15);
 private int mBorderColor = 0xFF0080FF;
 
 public ImageViewPlus(Context context, AttributeSet attrs) {
 super(context, attrs);
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 Bitmap rawBitmap = getBitmap(getDrawable());
 if (rawBitmap != null){
  int viewWidth = getWidth();
  int viewHeight = getHeight();
  int viewMinSize = Math.min(viewWidth, viewHeight);
  float dstWidth = viewMinSize;
  float dstHeight = viewMinSize;
  if (mShader == null || !rawBitmap.equals(mRawBitmap)){
  mRawBitmap = rawBitmap;
  mShader = new BitmapShader(mRawBitmap, TileMode.CLAMP, TileMode.CLAMP);
  }
  if (mShader != null){
  mMatrix.setScale((dstWidth - mBorderWidth * 2) / rawBitmap.getWidth(), (dstHeight - mBorderWidth * 2) / rawBitmap.getHeight());
  mShader.setLocalMatrix(mMatrix);
  }
  mPaintBitmap.setShader(mShader);
  mPaintBorder.setStyle(Paint.Style.STROKE);
  mPaintBorder.setStrokeWidth(mBorderWidth);
  mPaintBorder.setColor(mBorderColor);
  float radius = viewMinSize / 2.0f;
  canvas.drawCircle(radius, radius, radius - mBorderWidth / 2.0f, mPaintBorder);
  canvas.translate(mBorderWidth, mBorderWidth);
  canvas.drawCircle(radius - mBorderWidth, radius - mBorderWidth, radius - mBorderWidth, mPaintBitmap);
 } else {
  super.onDraw(canvas);
 }
 }

 private Bitmap getBitmap(Drawable drawable){
 if (drawable instanceof BitmapDrawable){
  return ((BitmapDrawable)drawable).getBitmap();
 } else if (drawable instanceof ColorDrawable){
  Rect rect = drawable.getBounds();
  int width = rect.right - rect.left;
  int height = rect.bottom - rect.top;
  int color = ((ColorDrawable)drawable).getColor();
  Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);
  canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));
  return bitmap;
 } else {
  return null;
 }
 }
 
 private int dip2px(int dipVal)
 {
 float scale = getResources().getDisplayMetrics().density;
 return (int)(dipVal * scale + 0.5f);
 }
}
코드 를 보면 테 두 리 를 넣 으 면 실제 적 으로 실심 단색 Paint 로 동 그 란 테 두 리 를 그 렸 고 이 를 바탕 으로 원래 의 두상 을 그리 면 된다.
주의해 야 할 점 은 세 가지 가 있다.
1)원 틀 의 반지름 은 radius 가 아니 라 radius-mBorder Width/2.0f 여야 합 니 다.펜 을 들 고 선 을 그 리 는 것 을 상상 하 라.선 은 사실 오른쪽 그림 에 있 는 흰색 원 의 위 치 를 그 리 는 것 이지 만 매우 굵 을 뿐이다.
2)、ImageView 크기 가 변 하지 않 는 토대 에서 두상 의 실제 크기 는 테두리 가 없 을 때 보다 작 기 때문에 mMatrix.setScale 일 때 테두리 의 너 비 를 제거 해 야 합 니 다.
3)、비트 맵 을 그 릴 때 canvas.draw Circle(radius,radius,radius-mBorder Width,mPaintBitmap)을 직접 그 릴 수 없습니다.그러면 두상 의 오른쪽 과 아래쪽 가장자리 가 늘 어 난 것 을 발견 할 수 있 습 니 다(오른쪽 그림)
   왜 일 까요?Paint 는 기본적으로 왼쪽 상단 을 기준 으로 그리 기 시 작 했 기 때문에 이때 두상 의 실제 영역 은 오른쪽 그림 에 있 는 빨간색 상자 이 고 빨간색 상자 가 넘 는 부분(원형의 오른쪽 과 아래쪽)은 자 연 스 럽 게 TileMode.ClaMP 효과 에 의 해 가장자리 로 늘 어 났 습 니 다.
   따라서 좌표계 의 위 치 를 옮 기 고 원심 을 조정 해 야 두상 을 정확 한 구역(오른쪽 그림 녹색 상자)에 그 릴 수 있다.
5.더 많은 게임 방법--xml 설정 지원
테두리 가 생 겼 으 니 테두리 의 너비 와 색 을 설정 하려 면 어떻게 해 야 합 니까?
기본적으로 두 가지 사고방식:
1)ImageViewPlus 에 set 인 터 페 이 스 를 추가 하고 설정 이 완료 되면 invalidate()를 통과 합 니 다.다시 그리 면 됩 니 다.
2)xml 에서 사용자 정의 속성 을 설정 하 는 것 을 지원 합 니 다.이렇게 사용 하면 매우 편리 합 니 다.
xml 설정 사용자 정의 속성 을 지원 합 니 다.
사용자 정의 컨트롤 이 xml 설정 을 지원 하려 면 먼저\res\\values 에서 속성 을 정의 해 야 합 니 다.

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
 <attr name="borderColor" format="color" />
 <attr name="borderWidth" format="dimension" />

 <declare-styleable name="ImageViewPlus"> 
 <attr name="borderColor" />
 <attr name="borderWidth" />
 </declare-styleable> 
</resources> 
View attrs_imageviewplus.xml
 그리고 ImageView Plus 의 구조 함수 에서 사용자 정의 속성 을 읽 습 니 다.

private static final int DEFAULT_BORDER_COLOR = Color.TRANSPARENT;
 private static final int DEFAULT_BORDER_WIDTH = 0;
 
 public ImageViewPlus(Context context, AttributeSet attrs) {
 super(context, attrs);
 // xml        
 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageViewPlus);
 mBorderColor = ta.getColor(R.styleable.ImageViewPlus_borderColor, DEFAULT_BORDER_COLOR);
 mBorderWidth = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_borderWidth, dip2px(DEFAULT_BORDER_WIDTH));
 ta.recycle();
 }
 xml 레이아웃 에서 사용자 정의 속성 사용 하기:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:snser="http://schemas.android.com/apk/res/cc.snser.imageviewplus"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/wallpaper"
 android:orientation="vertical"
 tools:context="${relativePackage}.${activityClass}" >
 
 <cc.snser.imageviewplus.ImageViewPlus
 android:id="@+id/imgplus"
 android:layout_width="200dp"
 android:layout_height="300dp"
 android:layout_marginBottom="50dp"
 android:layout_centerHorizontal="true"
 android:layout_alignParentBottom="true"
 android:src="@drawable/img_square"
 snser:borderColor="#FF0080FF"
 snser:borderWidth="15dp" />
 
</RelativeLayout>


6.더 많은 게임 방법--원 각 이미지 뷰
원형 ImageView 와 대응 하 는 테 두 리 를 만 들 었 습 니 다.아래 의 원 각 ImageView 는 어떻게 실현 합 니까?
 
사실 원리 적 으로 canvas.draw Circle 대응 을 canvas.draw RoundRect 로 바 꾸 면 됩 니 다.코드 를 직접 붙 입 시다.

public class ImageViewPlus extends ImageView{
 /**
 * android.widget.ImageView
 */
 public static final int TYPE_NONE = 0;
 /**
 *   
 */
 public static final int TYPE_CIRCLE = 1;
 /**
 *     
 */
 public static final int TYPE_ROUNDED_RECT = 2; 
 
 private static final int DEFAULT_TYPE = TYPE_NONE;
 private static final int DEFAULT_BORDER_COLOR = Color.TRANSPARENT;
 private static final int DEFAULT_BORDER_WIDTH = 0;
 private static final int DEFAULT_RECT_ROUND_RADIUS = 0;
 
 private int mType;
 private int mBorderColor;
 private int mBorderWidth;
 private int mRectRoundRadius;
 
 private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG);
 private Paint mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
 
 private RectF mRectBorder = new RectF();
 private RectF mRectBitmap = new RectF();
 
 private Bitmap mRawBitmap;
 private BitmapShader mShader;
 private Matrix mMatrix = new Matrix();
 
 public ImageViewPlus(Context context, AttributeSet attrs) {
 super(context, attrs);
 // xml        
 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageViewPlus);
 mType = ta.getInt(R.styleable.ImageViewPlus_type, DEFAULT_TYPE);
 mBorderColor = ta.getColor(R.styleable.ImageViewPlus_borderColor, DEFAULT_BORDER_COLOR);
 mBorderWidth = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_borderWidth, dip2px(DEFAULT_BORDER_WIDTH));
 mRectRoundRadius = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_rectRoundRadius, dip2px(DEFAULT_RECT_ROUND_RADIUS));
 ta.recycle();
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 Bitmap rawBitmap = getBitmap(getDrawable());
 
 if (rawBitmap != null && mType != TYPE_NONE){
  int viewWidth = getWidth();
  int viewHeight = getHeight();
  int viewMinSize = Math.min(viewWidth, viewHeight);
  float dstWidth = mType == TYPE_CIRCLE ? viewMinSize : viewWidth;
  float dstHeight = mType == TYPE_CIRCLE ? viewMinSize : viewHeight;
  float halfBorderWidth = mBorderWidth / 2.0f;
  float doubleBorderWidth = mBorderWidth * 2;
  
  if (mShader == null || !rawBitmap.equals(mRawBitmap)){
  mRawBitmap = rawBitmap;
  mShader = new BitmapShader(mRawBitmap, TileMode.CLAMP, TileMode.CLAMP);
  }
  if (mShader != null){
  mMatrix.setScale((dstWidth - doubleBorderWidth) / rawBitmap.getWidth(), (dstHeight - doubleBorderWidth) / rawBitmap.getHeight());
  mShader.setLocalMatrix(mMatrix);
  }
  
  mPaintBitmap.setShader(mShader);
  mPaintBorder.setStyle(Paint.Style.STROKE);
  mPaintBorder.setStrokeWidth(mBorderWidth);
  mPaintBorder.setColor(mBorderWidth > 0 ? mBorderColor : Color.TRANSPARENT);
  
  if (mType == TYPE_CIRCLE){
  float radius = viewMinSize / 2.0f;
  canvas.drawCircle(radius, radius, radius - halfBorderWidth, mPaintBorder);
  canvas.translate(mBorderWidth, mBorderWidth);
  canvas.drawCircle(radius - mBorderWidth, radius - mBorderWidth, radius - mBorderWidth, mPaintBitmap);
  } else if (mType == TYPE_ROUNDED_RECT){
  mRectBorder.set(halfBorderWidth, halfBorderWidth, dstWidth - halfBorderWidth, dstHeight - halfBorderWidth);
  mRectBitmap.set(0.0f, 0.0f, dstWidth - doubleBorderWidth, dstHeight - doubleBorderWidth);
  float borderRadius = mRectRoundRadius - halfBorderWidth > 0.0f ? mRectRoundRadius - halfBorderWidth : 0.0f;
  float bitmapRadius = mRectRoundRadius - mBorderWidth > 0.0f ? mRectRoundRadius - mBorderWidth : 0.0f;
  canvas.drawRoundRect(mRectBorder, borderRadius, borderRadius, mPaintBorder);
  canvas.translate(mBorderWidth, mBorderWidth);
  canvas.drawRoundRect(mRectBitmap, bitmapRadius, bitmapRadius, mPaintBitmap);
  }
 } else {
  super.onDraw(canvas);
 }
 }

 private int dip2px(int dipVal)
 {
 float scale = getResources().getDisplayMetrics().density;
 return (int)(dipVal * scale + 0.5f);
 }
 
 private Bitmap getBitmap(Drawable drawable){
 if (drawable instanceof BitmapDrawable){
  return ((BitmapDrawable)drawable).getBitmap();
 } else if (drawable instanceof ColorDrawable){
  Rect rect = drawable.getBounds();
  int width = rect.right - rect.left;
  int height = rect.bottom - rect.top;
  int color = ((ColorDrawable)drawable).getColor();
  Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);
  canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));
  return bitmap;
 } else {
  return null;
 }
 }
}
View ImageViewPlus.java 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:snser="http://schemas.android.com/apk/res/cc.snser.imageviewplus"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/wallpaper"
 android:orientation="vertical"
 tools:context="${relativePackage}.${activityClass}" >
 
 <cc.snser.imageviewplus.ImageViewPlus
 android:id="@+id/imgplus"
 android:layout_width="200dp"
 android:layout_height="300dp"
 android:layout_marginBottom="50dp"
 android:layout_centerHorizontal="true"
 android:layout_alignParentBottom="true"
 android:src="@drawable/img_rectangle"
 snser:type="rounded_rect"
 snser:borderColor="#FF0080FF"
 snser:borderWidth="10dp"
 snser:rectRoundRadius="30dp" />
 
</RelativeLayout>

View layout 



<?xml version="1.0" encoding="utf-8"?> 
<resources> 
 <attr name="type"> 
 <enum name="none" value="0" /> 
 <enum name="circle" value="1" /> 
 <enum name="rounded_rect" value="2" />
 </attr>
 <attr name="borderColor" format="color" />
 <attr name="borderWidth" format="dimension" />
 <attr name="rectRoundRadius" format="dimension" />

 <declare-styleable name="ImageViewPlus"> 
 <attr name="type" />
 <attr name="borderColor" />
 <attr name="borderWidth" />
 <attr name="rectRoundRadius" />
 </declare-styleable>
</resources> 

View attrs_imageviewplus.xml

이상 은 본 고의 모든 내용 입 니 다.여러분 이 안 드 로 이 드 소프트웨어 프로 그래 밍 을 배 우 는 데 도움 이 되 기 를 바 랍 니 다.

좋은 웹페이지 즐겨찾기