Android 사용자 정의 뷰 수면 상승 효과 구현

13813 단어 androidview
실현 효 과 는 다음 과 같다.

실현 방향:
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 에 그립 니 다.수면 상승 효과 에 대한 진 도 는 여기 서 정 해 졌 다.
총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 면 댓 글 을 남 겨 주 십시오.

좋은 웹페이지 즐겨찾기