Android 는 Path 를 통 해 검색 버튼 과 시계 복잡 효 과 를 실현 합 니 다.
그러면 path 는 무엇 일 까요!
정의:path 바로 경로 입 니 다.바로 도형 의 경로 의 집합 입 니 다.그 안에 경로 안의 좌표 점 등 속성 이 포함 되 어 있 습 니 다.우 리 는 임의의 점 의 좌 표를 얻 을 수 있다.
그러면 Path 위의 모든 점 의 좌 표를 얻 으 려 면 하나의 클래스,Path Measure 가 필요 합 니 다.
PathMesure:
PathMeasure 는 Path 를 측정 하 는 종류 로 주로 다음 과 같은 방법 이 있다.
구조 방법
공공 방법
이것 은 Path 의 도구 류 와 같 습 니 다.방법 은 간단 합 니 다.그러면 우리 가 해 야 할 버튼 과 시계 개발 을 시작 하 겠 습 니 다.
(1)검색 버튼,먼저 위의 그림:
이 기능 을 실현 하려 면 먼저 그 를 분해 해서 해 야 한다.
검색 단추 의 path 경 로 를 만 들 고 외곽 에서 회전 하 는 path 를 만 듭 니 다.
public void initPath(){
mPath_search = new Path();
mPath_circle = new Path();
mMeasure = new PathMeasure();
// , 360 , ,
RectF oval1 = new RectF(-50, -50, 50, 50); //
mPath_search.addArc(oval1, 45, 359.9f);
RectF oval2 = new RectF(-100, -100, 100, 100); //
mPath_circle.addArc(oval2, 45, -359.9f);
float[] pos = new float[2];
mMeasure.setPath(mPath_circle, false); //
mMeasure.getPosTan(0, pos, null);
mPath_search.lineTo(pos[0], pos[1]); //
Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
}
우리 가 원 하 는 효 과 는 검색 단 추 를 눌 렀 을 때 부터 단 추 를 회전 시 킨 다음 검색 이 끝 난 후에 검색 단추 로 바 꾸 는 것 이다. 그래서 우 리 는 네 가지 상태 가 있다 는 것 을 확인 할 수 있다.
public enum Seach_State{
START,END,NONE,SEARCHING
}
그리고 상태 에 따라 path 를 동적 으로 그립 니 다.path 를 동적 으로 그립 니 다.PathMeasure 에서 현재 path 의 좌 표를 측정 한 다음 에 그립 니 다.
private void drawPath(Canvas c) {
c.translate(mWidth / 2, mHeight / 2);
switch (mState){
case NONE:
c.drawPath(mPath_search,mPaint);
break;
case START:
mMeasure.setPath(mPath_search,true);
Path path = new Path();
mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);
c.drawPath(path,mPaint);
break;
case SEARCHING:
mMeasure.setPath(mPath_circle,true);
Path path_search = new Path();
mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);
c.drawPath(path_search,mPaint);
break;
case END:
mMeasure.setPath(mPath_search,true);
Path path_view = new Path();
mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);
c.drawPath(path_view,mPaint);
break;
}
}
그 다음 에 속성 애니메이션 을 사용 하여 현재 그 려 진 100%를 되 돌려 주 고 이 값 을 통 해 그 려 야 할 path 를 계산 해 야 합 니 다.아래 는 전체 코드 입 니 다:
package com.duoku.platform.demo.canvaslibrary.attract.view;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* Created by chenpengfei_d on 2016/9/7.
*/
public class SearchView extends View {
private Paint mPaint;
private Context mContext;
private Path mPath_circle;
private Path mPath_search;
private PathMeasure mMeasure;
private ValueAnimator mValueAnimator_search;
private long defaultduration=3000;
private float curretnAnimationValue;
private Seach_State mState = Seach_State.SEARCHING;
public SearchView(Context context) {
super(context);
init(context);
}
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void init(Context context){
this.mContext = context;
initPaint();
initPath();
initAnimation();
}
public void initPaint(){
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);//
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
}
public void initPath(){
mPath_search = new Path();
mPath_circle = new Path();
mMeasure = new PathMeasure();
// , 360 , ,
RectF oval1 = new RectF(-50, -50, 50, 50); //
mPath_search.addArc(oval1, 45, 359.9f);
RectF oval2 = new RectF(-100, -100, 100, 100); //
mPath_circle.addArc(oval2, 45, -359.9f);
float[] pos = new float[2];
mMeasure.setPath(mPath_circle, false); //
mMeasure.getPosTan(0, pos, null);
mPath_search.lineTo(pos[0], pos[1]); //
Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
}
public void initAnimation(){
mValueAnimator_search = ValueAnimator.ofFloat(0f,1.0f).setDuration(defaultduration);
mValueAnimator_search.addUpdateListener(updateListener);
mValueAnimator_search.addListener(animationListener);
}
private ValueAnimator.AnimatorUpdateListener updateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
curretnAnimationValue = (float) animation.getAnimatedValue();
invalidate();
}
};
private Animator.AnimatorListener animationListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if(mState ==Seach_State.START){
setState(Seach_State.SEARCHING);
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawPath(canvas);
}
private int mWidth,mHeight;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
private void drawPath(Canvas c) {
c.translate(mWidth / 2, mHeight / 2);
switch (mState){
case NONE:
c.drawPath(mPath_search,mPaint);
break;
case START:
mMeasure.setPath(mPath_search,true);
Path path = new Path();
mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);
c.drawPath(path,mPaint);
break;
case SEARCHING:
mMeasure.setPath(mPath_circle,true);
Path path_search = new Path();
mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);
c.drawPath(path_search,mPaint);
break;
case END:
mMeasure.setPath(mPath_search,true);
Path path_view = new Path();
mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);
c.drawPath(path_view,mPaint);
break;
}
}
public void setState(Seach_State state){
this.mState = state;
startSearch();
}
public void startSearch(){
switch (mState){
case START:
mValueAnimator_search.setRepeatCount(0);
break;
case SEARCHING:
mValueAnimator_search.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator_search.setRepeatMode(ValueAnimator.REVERSE);
break;
case END:
mValueAnimator_search.setRepeatCount(0);
break;
}
mValueAnimator_search.start();
}
public enum Seach_State{
START,END,NONE,SEARCHING
}
}
(학습 점:path 는 조합 할 수 있 습 니 다.서로 다른 path 를 path 에 넣 고 통일 적 으로 그 릴 수 있 습 니 다) (2)시계 효과:
시계의 방향 을 말 해 보 세 요.인터넷 의 많은 시 계 는 Canvas 를 통 해 기본 적 인 도형 을 그 려 서 이 루어 집 니 다.path 를 통 해 이 루어 지지 않 았 습 니 다.path 를 사용 하여 실현 하 는 것 은 앞으로 시계의 그리 기 효 과 를 더욱 유연 하 게 제어 하기 위해 서 입 니 다.예 를 들 어 우 리 는 가장 바깥쪽 의 원 을 시계 반대 방향 으로 회전 시 켜 야 합 니 다.예 를 들 어 위 에 작은 별 을 추가 하고 path 를 사용 하면 더욱 유연 해 집 니 다.
시계의 실현 부분:
1.외곽 path 경로 만 들 기
2.눈금 path 경 로 를 만 들 고 정각 을 구분 하여 시간 점 을 그립 니 다.
3.포인 터 를 그립 니 다.(이것 은 canvas 가 그린 라인 을 사용 하고 Path 를 사용 하여 스스로 테스트 할 수 있 습 니 다)
현재 시침,분침,초침 의 각 도 를 계산 하여 그립 니 다.
전체 코드:
package com.duoku.platform.demo.canvaslibrary.attract.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import java.util.Calendar;
/**
* Created by chenpengfei_d on 2016/9/8.
*/
public class TimeView extends View {
private Paint mPaint,mPaint_time;
private Paint mPaint_h,mPaint_m,mPaint_s;
private Path mPath_Circle;
private Path mPath_Circle_h;
private Path mPath_Circle_m;
private Path mPath_h,mPath_m,mPath_s;
private Path mPath_duration;
private PathMeasure mMeasure;
private PathMeasure mMeasure_h;
private PathMeasure mMeasure_m;
private Handler mHandler = new Handler();
private Runnable clockRunnable;
private boolean isRunning;
public TimeView(Context context) {
super(context);
init();
}
public TimeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
int t = 3;
public void init(){
//
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setColor(Color.RED);
mPaint_time = new Paint();
mPaint_time.setDither(true);
mPaint_time.setAntiAlias(true);
mPaint_time.setStyle(Paint.Style.STROKE);
mPaint_time.setStrokeWidth(2);
mPaint_time.setTextSize(15);
mPaint_time.setStrokeCap(Paint.Cap.ROUND);
mPaint_time.setStrokeJoin(Paint.Join.ROUND);
mPaint_time.setColor(Color.RED);
mPaint_h = new Paint();
mPaint_h.setDither(true);
mPaint_h.setAntiAlias(true);
mPaint_h.setStyle(Paint.Style.STROKE);
mPaint_h.setStrokeWidth(6);
mPaint_h.setTextSize(15);
mPaint_h.setStrokeCap(Paint.Cap.ROUND);
mPaint_h.setStrokeJoin(Paint.Join.ROUND);
mPaint_h.setColor(Color.RED);
mPaint_m = new Paint();
mPaint_m.setDither(true);
mPaint_m.setAntiAlias(true);
mPaint_m.setStyle(Paint.Style.STROKE);
mPaint_m.setStrokeWidth(4);
mPaint_m.setTextSize(15);
mPaint_m.setStrokeCap(Paint.Cap.ROUND);
mPaint_m.setStrokeJoin(Paint.Join.ROUND);
mPaint_m.setColor(Color.RED);
mPaint_s = new Paint();
mPaint_s.setDither(true);
mPaint_s.setAntiAlias(true);
mPaint_s.setStyle(Paint.Style.STROKE);
mPaint_s.setStrokeWidth(2);
mPaint_s.setTextSize(15);
mPaint_s.setStrokeCap(Paint.Cap.ROUND);
mPaint_s.setStrokeJoin(Paint.Join.ROUND);
mPaint_s.setColor(Color.RED);
//
mPath_Circle = new Path();
mPath_Circle.addCircle(0,0,250, Path.Direction.CCW);
mPath_Circle_h = new Path();
mPath_Circle_h.addCircle(0,0,220, Path.Direction.CCW);
mPath_Circle_m = new Path();
mPath_Circle_m.addCircle(0,0,235, Path.Direction.CCW);
// PathMeasure path ,
mMeasure = new PathMeasure();
mMeasure.setPath(mPath_Circle,true);
mMeasure_h = new PathMeasure();
mMeasure_h.setPath(mPath_Circle_h,true);
mMeasure_m = new PathMeasure();
mMeasure_m.setPath(mPath_Circle_m,true);
// path
mPath_duration = new Path();
for (int i = 60; i>0 ;i --){
Path path = new Path();
float pos [] = new float[2];
float tan [] = new float[2];
float pos2 [] = new float[2];
float tan2 [] = new float[2];
float pos3 [] = new float[2];
float tan3 [] = new float[2];
mMeasure.getPosTan(mMeasure.getLength()*i/60,pos,tan);
mMeasure_h.getPosTan(mMeasure_h.getLength()*i/60,pos2,tan2);
mMeasure_m.getPosTan(mMeasure_m.getLength()*i/60,pos3,tan3);
float x = pos[0];
float y = pos[1];
float x2 = pos2[0];
float y2 = pos2[1];
float x3 = pos3[0];
float y3 = pos3[1];
path.moveTo(x , y);
if(i% 5 ==0){
path.lineTo(x2,y2);
if(t>12){
t = t-12;
}
String time = t++ +"";
Path path_time = new Path();
mMeasure_h.getPosTan(mMeasure_h.getLength()*(i-1)/60,pos2,tan2);
mPaint.getTextPath(time,0,time.length(),(x2- (x2/15)),y2-(y2/15),path_time);
path.close();
path.addPath(path_time);
}else{
path.lineTo(x3,y3);
}
mPath_duration.addPath(path);
clockRunnable = new Runnable() {// ,
@Override
public void run() {
//
postInvalidate();
mHandler.postDelayed(this, 1000);
}
};
}
mPath_h = new Path();
mPath_h.rLineTo(50,30);
mPath_m = new Path();
mPath_m.rLineTo(80,80);
mPath_s = new Path();
mPath_s.rLineTo(130,50);
}
private int mWidth,mHeight;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(!isRunning){
isRunning = true;
mHandler.postDelayed(clockRunnable,1000);
}else{
canvas.translate(mWidth/2,mHeight/2);
canvas.drawPath(mPath_Circle,mPaint);
canvas.save();
canvas.drawPath(mPath_duration,mPaint_time);
canvas.drawPoint(0,0,mPaint_time);
drawClockPoint(canvas);
}
}
private Calendar cal;
private int hour;
private int min;
private int second;
private float hourAngle,minAngle,secAngle;
/**
*
* @param canvas
*/
private void drawClockPoint(Canvas canvas) {
cal = Calendar.getInstance();
hour = cal.get(Calendar.HOUR);//Calendar.HOUR 12 ,Calendar.HOUR_OF_DAY 24
min = cal.get(Calendar.MINUTE);
second = cal.get(Calendar.SECOND);
//
hourAngle = (float)hour / 12 * 360 + (float)min / 60 * (360 / 12);//360/12
minAngle = (float)min / 60 * 360;
secAngle = (float)second / 60 * 360;
// 、 、 , canvas
canvas.save();
canvas.rotate(hourAngle,0, 0);
canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 65, mPaint_h);// 65
canvas.restore();
canvas.save();
canvas.rotate(minAngle,0, 0);
canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 90 , mPaint_m);// 90
canvas.restore();
canvas.save();
canvas.rotate(secAngle,0, 0);
canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 110 , mPaint_s);// 110
canvas.restore();
}
}
이것 은 사실 특별히 복잡 한 애니메이션 이 아 닙 니 다.좋 은 생각 이 있 으 면 Path+속성 애니메이션 을 통 해 더 좋 은 효 과 를 실현 할 수 있 습 니 다. 예 를 들 어 별빛 의 효과,예 를 들 어 동적 으로 문자+경 로 를 그립 니 다.ppt 에서 재생 되 는 특수 효 과 를 실현 합 니 다.예 를 들 어 전자 책의 자동 페이지 넘 기기 등 입 니 다.
(3)다음 에 또 하나의 지식 을 소개 하 겠 습 니 다.바로 svg 입 니 다.
svg 는 뭐 죠?
그의 학명 은 크기 조정 가능 한 벡터 그래 픽 이 라 고 하 는데 확장 가능 한 태그 언어(표준 통용 태그 언어의 부분 집합)를 바탕 으로 2 차원 벡터 그래 픽 을 묘사 하 는 도형 형식 이다.
이 형식의 그림 형식 은 Android Path 에 불 러 올 수 있 습 니 다.
Path 에 불 러 올 수 있 으 니 더 복잡 한 효 과 를 얻 을 수 있 지 않 을까요? 아래 그림 을 보 세 요.(내일 다시 쓰 겠 습 니 다)
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.