Android 사용자 정의 뷰 수면 상승 효과 구현
실현 방향:
1.원 중 수면 상승 효 과 를 어떻게 실현 하 는 지:Paint 의 setXfermode 속성 을 이용 하여 Porter Duff.Mode.SRCIN 진도 가 있 는 사각형 과 원 의 교 집합 을 그립 니 다.
2.어떻게 물결 무늬 효과:베 어 셀 곡선 을 이용 하여 파 의 피크 수 치 를 동태 적 으로 바 꾸 고'진도 가 증가 함 에 따라 물결 무늬 가 점점 작 아 지 는 효과'를 실현 합 니까?
말 이 많 지 않 으 니 코드 를 보 세 요.
우선 사용자 정의 속성 값 입 니 다.어떤 사용자 정의 속성 값 이 있 습 니까?
원 배경 색:circlecolor,진도 색상:progresscolor,진행 표시 텍스트 색상:textcolor,진행 텍스트 크기:textsize,그리고 마지막:파문 최대 높이:rippletopheight
<declare-styleable name="WaterProgressView">
<attr name="circle_color" format="color"/><!-- -->
<attr name="progress_color" format="color"/><!-- -->
<attr name="text_color" format="color"/><!-- -->
<attr name="text_size" format="dimension"/><!-- -->
<attr name="ripple_topheight" format="dimension"/><!-- -->
</declare-styleable>
다음은 사용자 정의 View:WaterProgressView 의 일부 코드 입 니 다.구성원 변수
public class WaterProgressView extends ProgressBar {
//
public static final int DEFAULT_CIRCLE_COLOR = 0xff00cccc;
//
public static final int DEFAULT_PROGRESS_COLOR = 0xff00CC66;
//
public static final int DEFAULT_TEXT_COLOR = 0xffffffff;
//
public static final int DEFAULT_TEXT_SIZE = 18;
//
public static final int DEFAULT_RIPPLE_TOPHEIGHT = 10;
private Context mContext;
private Canvas mPaintCanvas;
private Bitmap mBitmap;
//
private Paint mCirclePaint;
//
private int mCircleColor;
//
private Paint mProgressPaint;
//
private int mProgressColor ;
// path
private Path mProgressPath;
//
private int mRippleTop = 10;
//
private Paint mTextPaint;
//
private int mTextColor;
private int mTextSize = 18;
// , ,
private int mTargetProgress = 50;
//
private GestureDetector mGestureDetector;
}
사용자 정의 속성 값 가 져 오기:
private void getAttrValue(AttributeSet attrs) {
TypedArray ta = mContext.obtainStyledAttributes(attrs, R.styleable.WaterProgressView);
mCircleColor = ta.getColor(R.styleable.WaterProgressView_circle_color,DEFAULT_CIRCLE_COLOR);
mProgressColor = ta.getColor(R.styleable.WaterProgressView_progress_color,DEFAULT_PROGRESS_COLOR);
mTextColor = ta.getColor(R.styleable.WaterProgressView_text_color,DEFAULT_TEXT_COLOR);
mTextSize = (int) ta.getDimension(R.styleable.WaterProgressView_text_size, DesityUtils.sp2px(mContext,DEFAULT_TEXT_SIZE));
mRippleTop = (int)ta.getDimension(R.styleable.WaterProgressView_ripple_topheight,DesityUtils.dp2px(mContext,DEFAULT_RIPPLE_TOPHEIGHT));
ta.recycle();
}
구조 함수 정의,주의mProgressPaint.setXfermode
// new
public WaterProgressView(Context context) {
this(context,null);
}
// xml View
public WaterProgressView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public WaterProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
getAttrValue(attrs);
//
initPaint();
mProgressPath = new Path();
}
private void initPaint() {
// paint
mCirclePaint = new Paint();
mCirclePaint.setColor(mCircleColor);
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePaint.setAntiAlias(true);
mCirclePaint.setDither(true);
// paint
mProgressPaint = new Paint();
mProgressPaint.setColor(mProgressColor);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setDither(true);
mProgressPaint.setStyle(Paint.Style.FILL);
// mProgressPaint , xfermode PorterDuff.Mode.SRC_IN ,
mProgressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//
mTextPaint = new Paint();
mTextPaint.setColor(mTextColor);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setAntiAlias(true);
mTextPaint.setDither(true);
mTextPaint.setTextSize(mTextSize);
}
onMeasure()
방법 코드:
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// , View , MeasureSpec.EXACTLY
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width,height);
// Bitmap, drawCircle,drawPath,drawText draw bitmap canvas , bitmap onDraw canvas ,
// bitmap width,height left,top,right,bottom padding
mBitmap = Bitmap.createBitmap(width-getPaddingLeft()-getPaddingRight(),height- getPaddingTop()-getPaddingBottom(), Bitmap.Config.ARGB_8888);
mPaintCanvas = new Canvas(mBitmap);
}
다음은 핵심 부분,onDraw 의 코드 입 니 다.우 리 는 먼저 Circle,진도 바,진도 문 자 를 사용자 정의 canvas 의 bitmap 에 그립 니 다.그리고 이 bitmap 를 onDraw 방법 중의 canvas 에 그립 니 다.drawCircle 과 drawText 는 어렵 지 않 을 것 입 니 다.관건 은 진도 표를 그 리 는 것 입 니 다.어떻게 그 리 는 것 입 니까?물결 무늬 효과 가 있 고 곡선 이 있 으 니 drawPath 를 사용 하 겠 습 니 다.drawPath 의 절 차 는 다음 과 같 습 니 다.
그 중에서 ratio 의 코드 는 다음 과 같다.즉,ratio 는 현재 진도 가 전체 진도 에서 차지 하 는 백분율 이다.
float ratio = getProgress()*1.0f/getMax();
좌 표 는 B 점 에서 아래로,오른쪽으로 정방 향 으로 연장 되 기 때문에 A 점 의 좌 표 는(width,(1-ratio)*height)이 고 그 중에서 width 는 bitmap 의 너비 이 며 height 는 bitmap 의 높이 이다.우 리 는 먼저 mProgressPath.moveto 를 A 점 까지 간 다음 에 A 점 에서 시계 방향 으로 path 의 각 관건 점 을 확인한다.그림 과 같이 코드 는 다음 과 같다.
int rightTop = (int) ((1-ratio)*height);
mProgressPath.moveTo(width,rightTop);
mProgressPath.lineTo(width,height);
mProgressPath.lineTo(0,height);
mProgressPath.lineTo(0,rightTop);
이렇게 mProgressPath 는 이미 lineto 가 C 점 에 이 르 렀 기 때문에 A 점 과 C 점 사이 에 물결 무늬 효 과 를 형성 하려 면 A 점 과 C 점 사이 에 베 어 셀 곡선 을 그 려 야 한다.우 리 는 파 봉 의 최고점 을 10 으로 설정 하면 한 단락 의 파장 은 40 이 고
width*1.0f/40
단락 의 이런 곡선 을 그 려 야 한다 면 곡선 을 그 리 는 코드 는 다음 과 같다.
int count = (int) Math.ceil(width*1.0f/(10 *4));
for(int i=0; i<count; i++) {
mProgressPath.rQuadTo(10,10,2* 10,0);
mProgressPath.rQuadTo(10,-10,2* 10,0);
}
mProgressPath.close();
mPaintCanvas.drawPath(mProgressPath,mProgressPaint);
이렇게 하면 수면 이 오 르 고 파문 효과 가 있 는 진 도 를 그 릴 수 있다.그러나 우 리 는 수면 이 상승 함 에 따라 목표 진도 에 가 까 워 질 수록 수면 의 파문 이 점점 작 아 져 야 한다.10 추출 을 변수 로 mRippleTop 등 초기 시 파 봉 의 최대 치 로 정의 한 다음 에 top 을 진도 에 따라 목표 진도 에 계속 가 까 워 질 때 곡선의 실시 간 파 봉 치 로 정의 해 야 한다.그 중에서 mTargetProgress 는 목표 progress 이다.목표 진도 가 있어 야 현재 진도 가 목표 진도 에 계속 가 까 워 지 는 과정 에서 수면 이 점점 평면 으로 향 하 는 효 과 를 실현 할 수 있다.
float top = (mTargetProgress-getProgress())*1.0f/mTargetProgress* mRippleTop;
그래서 drawPath 의 코드 업 데 이 트 는 다음 과 같 습 니 다.
float top = (mTargetProgress-getProgress())*1.0f/mTargetProgress* mRippleTop;
for(int i=0; i<count; i++) {
mProgressPath.rQuadTo(mRippleTop,top,2* mRippleTop,0);
mProgressPath.rQuadTo(mRippleTop,-top,2* mRippleTop,0);
}
이렇게 하면 수면 상승의 진 도 를 진정 으로 실현 할 수 있다.그러나 그림 에서 더 블 클릭 할 때 수면 이 0%에서 목표 진도 로 올 라 가 고 클릭 할 때 수면 이 목표 진도 에서 계속 움 직 이 는 효 과 를 어떻게 실현 합 니까?
먼저 더 블 클릭 효과 의 실현 을 말 합 니 다.이것 은 간단 합 니 다.Handler 를 정의 합 니 다.더 블 클릭 할 때
handler.postDelayed(runnable,time)
일정 시간progress+1
마다 runnable 에서invalidate()
진 도 를 계속 업데이트 하고 현재 progress 가 mTarget Progress 에 도착 할 때 까지 계속 업데이트 합 니 다.코드 는 다음 과 같다.
/**
*
*/
private void startDoubleTapAnimation() {
setProgress(0);
doubleTapHandler.postDelayed(doubleTapRunnable,60);
}
private Handler doubleTapHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
// , 60ms
private Runnable doubleTapRunnable = new Runnable() {
@Override
public void run() {
if(getProgress() < mTargetProgress) {
invalidate();
setProgress(getProgress()+1);
doubleTapHandler.postDelayed(doubleTapRunnable,60);
} else {
doubleTapHandler.removeCallbacks(doubleTapRunnable);
}
}
};
더 블 클릭 효과 가 실현 되 었 습 니 다.그러면 어떻게 클릭 효 과 를 실현 합 니까?클릭 할 때 수면 이 일정 시간 동안 끊임없이 솟 아 오 르 고 수면 의 파문 이 점점 작 아 지면 서 수면 이 평평 해 져 야 한다.mSingleTap AnimationCount 변 수 를 수면 에서 밀 려 오 는 횟수 로 정의 한 다음 에 두 번 눌 렀 을 때의 처리 처럼 Handler 가 일정 시간 간격 으로 업데이트 인터페이스의 message 를 보 내 는 것 을 정의 할 수 있 습 니 다.mSingleTapAnimationCount--
그리고 우 리 는 초기 파 봉 을 한 번 에 플러스 로 바 꾸 면 수면 에서 밀 려 오 는 효 과 를 실현 할 수 있 습 니 다.핵심 코드 는 다음 과 같 습 니 다.
private void startSingleTapAnimation() {
isSingleTapAnimation = true;
singleTapHandler.postDelayed(singleTapRunnable,200);
}
private Handler singleTapHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
// , 200ms
private Runnable singleTapRunnable = new Runnable() {
@Override
public void run() {
if(mSingleTapAnimationCount > 0) {
invalidate();
mSingleTapAnimationCount--;
singleTapHandler.postDelayed(singleTapRunnable,200);
} else {
singleTapHandler.removeCallbacks(singleTapRunnable);
//
isSingleTapAnimation = false;
// 50
mSingleTapAnimationCount = 50;
}
}
};
onDraw 의 코드 를 변경 합 니 다.두 번 눌 렀 을 때 drawPath 의 곡선 부분 을 그 리 는 논리 와 다 르 기 때문에 변 수 를 정의 합 니 다.isSingleTap Animation 의 차 이 는 애니메이션 을 누 르 고 있 는 지,두 번 눌 러 서 애니메이션 을 하고 있 는 지 입 니 다.변 경 된 코드 는 다음 과 같 습 니 다.
//
mProgressPath.reset();
// draw path
int rightTop = (int) ((1-ratio)*height);
mProgressPath.moveTo(width,rightTop);
mProgressPath.lineTo(width,height);
mProgressPath.lineTo(0,height);
mProgressPath.lineTo(0,rightTop);
// ,
int count = (int) Math.ceil(width*1.0f/(mRippleTop *4));
// animation
if(!isSingleTapAnimation&&getProgress()>0) {
float top = (mTargetProgress-getProgress())*1.0f/mTargetProgress* mRippleTop;
for(int i=0; i<count; i++) {
mProgressPath.rQuadTo(mRippleTop,-top,2* mRippleTop,0);
mProgressPath.rQuadTo(mRippleTop,top,2* mRippleTop,0);
}
} else {
// animation , , mRippleTop 2
// ,
float top = (mSingleTapAnimationCount*1.0f/50)*10;
//
if(mSingleTapAnimationCount%2==0) {
for(int i=0; i<count; i++) {
mProgressPath.rQuadTo(mRippleTop *2,top*2,2* mRippleTop,0);
mProgressPath.rQuadTo(mRippleTop *2,-top*2,2* mRippleTop,0);
}
} else {
for(int i=0; i<count; i++) {
mProgressPath.rQuadTo(mRippleTop *2,-top*2,2* mRippleTop,0);
mProgressPath.rQuadTo(mRippleTop *2,top*2,2* mRippleTop,0);
}
}
}
mProgressPath.close();
mPaintCanvas.drawPath(mProgressPath,mProgressPaint);
기본적으로 중요 한 코드 와 핵심 논리 와 코드 가 위 에 있 습 니 다.주의 점:
1.drawCircle 에서 padding 을 고려 하려 면 circle 의 너비 와 높이 는 getWidth 와 getHeight 에서 padding 값 을 빼 고 코드 는 다음 과 같 습 니 다.
// bitmap
int width = getWidth()-getPaddingLeft()-getPaddingRight();
int height = getHeight()-getPaddingTop()-getPaddingBottom();
//
mPaintCanvas.drawCircle(width/2,height/2,height/2,mCirclePaint);
2.drawText 일 때 text 의 height 중간 에 draw 를 시작 하 는 것 이 아니 라 baseline 에서 draw 를 시작 합 니 다.그럼 baseline 의 height 좌 표를 어떻게 얻 습 니까?
Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
// ascent baseline , ascent 。descent+ascent ,
float baseLine = height*1.0f/2 - (metrics.descent+metrics.ascent)/2;
drawText 의 모든 코드 는 다음 과 같 습 니 다.
//
String text = ((int)(ratio*100))+"%";
//
float textWidth = mTextPaint.measureText(text);
Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
//descent+ascent ,
float baseLine = height*1.0f/2 - (metrics.descent+metrics.ascent)/2;
mPaintCanvas.drawText(text,width/2-textWidth/2,baseLine,mTextPaint);
3.padding 을 고려 해 야 하기 때문에 onDraw 의 canvas 를(getPaddingLeft(),getPaddingTop())
에 번역 하 는 것 을 기억 하 세 요.
canvas.translate(getPaddingLeft(),getPaddingTop());
canvas.drawBitmap(mBitmap,0,0,null);
마지막 으로 사용자 정의 bitmap draw 를 onDraw 의 canvas 에 그립 니 다.수면 상승 효과 에 대한 진 도 는 여기 서 정 해 졌 다.총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 면 댓 글 을 남 겨 주 십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.