Android 는 Path 를 사용 하여 동적 으로 애니메이션 을 불 러 오 는 검색 효 과 를 실현 합 니 다.

오늘 동적 으로 데 이 터 를 불 러 오 는 애니메이션 효 과 를 검색 하려 면 먼저 효 과 를 보 세 요.문자 로 딱딱 하 게 묘사 하고 그림 을 보면 모든 것 을 알 수 있 습 니 다.

이것 을 실현 하 는 것 은 Path 의 getSegment()를 사용 하여 세 션 을 캡 처 한 start 와 stop 을 계속 바 꾸 고 애니메이션 과 결합 하여 오늘 절차 에 따라 이 를 실현 하 는 것 입 니 다.보고 나 면 어렵 지 않 을 것 입 니 다.다만 이렇게 실현 할 줄 몰 랐 을 뿐 입 니 다.시야 가 당신 의 높이 를 결정 하 는 지,아니면 제 가 블 로 그 를 쓰 는 습관 을 이 어 가 는 지 한 걸음 한 걸음 분석 하 는 것 입 니 다.첫 번 째 단 계 는 다음 과 같은 그림 을 그 리 는 것 입 니 다.

단순히 이 그림 을 그리 면 간단 하고 간단 합 니 다.원 을 그리고 선 을 하나 더 그리 면 됩 니 다.하지만 여기 의 효 과 를 고려 해 야 합 니 다.이렇게 하면 안 됩 니 다.위의 gif 그림 을 보면 알 수 있 습 니 다.사실은 이것 은 2 개의 동심원 입 니 다.그리고 앞의 path 의 출발점 과 뒤의 path 의 출발점 이 연결 되면 직선 이 됩 니 다.그런데 path 의 도형 내용 이 바로 이 원 입 니 다.어떻게 그 려 졌 나 요?원 을 그 리 는 것 이 라면 위의 선 출발점 과 종점 위 치 를 어떻게 계산 하 느 냐 가 문제 입 니 다.그러나 우리 가 원 을 그 리 는 것 은 타원 을 그 리 는 형식 으로 도 원 을 그 릴 수 있 습 니 다.45 도 에서 원 을 그 릴 수 있 습 니 다.이 선의 출발점 이 맞 는 지 분석 도 는 다음 과 같 습 니 다.

좋 습 니 다.위의 분석 에 따라 코드 를 작성 하여 정적 검색 그림 을 그립 니 다.

package com.tuya;
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.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private Paint paint;
 private int width;//view   
 private int height;//view   
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//             
 private PathMeasure pathMeasure;
 private float[] pos;
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(3);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  *    path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//   
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);
  searchPath.addArc(searchRect,45,360);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-360);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(width/2,height/2);//       view        
  canvas.drawPath(searchPath,paint);
  canvas.drawPath(circlePath,paint);
 }
}
효과 그림:

원래 이 바깥 원 은 그 릴 필요 가 없 었 다.나 는 여기 서 그 려 서 이 두 원 이 일정한 관 계 를 가지 고 있다 는 것 을 알려 주 었 을 뿐 인 데 왜 이 선 이 이 렇 습 니까?우리 가 이 원 을 그 릴 때 45 도 에서 360 도 를 그 리 는 것 이 딱 일주일 이 었 다.원 을 만 들 었 다.지금 테스트 를 할 때 360 도 말고 330 도 를 쓰 면 효 과 는 다음 과 같다.

이때 너 는 이 선 이 옳다 는 것 을 알 게 될 것 이다.문 제 를 야기 하 는 것 은 사실은 이렇다.그림 분석 과 같다.

타원 을 그 리 는 키 코드:searchPath.addArc(searchRect,45,358); circlePath.addArc(circleRect,45,-358);360 으로 쓰 지 말고 358 로 바 꿔 보 세 요.효과 도:

이 선 이 정상 인지 아 닌 지 알 게 되 었 습 니 다.바깥 에 있 는 원 에 약간의 결함 이 있 습 니 다.첫째,358 을 359 로 바 꾸 면 괜 찮 을 것 입 니 다.그리고 우리 의 실제 효 과 는 이 바깥 의 원 이 필요 하지 않 기 때문에 고치 지 않 아 도 괜 찮 습 니 다.그러면 좋 습 니 다.첫 번 째 단 계 는 완성 한 셈 입 니 다.지금 두 번 째 단계 가 어떻게 실현 되 는 지 생각 하고 있 습 니 다.먼저 두 번 째 단계 의 효 과 를 gif 로 보 여 주세요.그렇지 않 으 면 생각 만 하고 생각 만 하지 않 습네가 미녀 를 보 는 것 처럼 첫눈 에 그것 을 보 았 지,그렇지,더 이상 말 하지 않 겠 다!화면 감 있 게...

아니면 캔버스 분석:

어디 에 우리 가 startD 라 는 시작 점 에서 의 위치 값 을 바 꾸 면 ok 입 니 다.물론 여러 가지 방법 이 있 습 니 다.그러나 Android 에 서 는 기본적으로 값 애니메이션 을 사용 합 니 다.ok.이 사고 에 따라 이 두 번 째 논 리 를 실현 합 니 다.

package com.tuya;
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.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private Paint paint;
 private int width;//view   
 private int height;//view   
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//             
 private PathMeasure pathMeasure;
 private float[] pos;
 private float animPercent;//
 private ValueAnimator serchStartAnim;
 private long animDuration = 2000;//    
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  initPaint();
  initAnim();
  initAnimListener();
  startAnim();
 }
 /**
  *       
  */
 private void startAnim() {
  serchStartAnim.start();
 }
 /**
  *     
  */
 private void initAnimListener() {
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
     //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
 }
 /**
  *      
  */
 private void initAnim() {
  serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
 }
 /**
  *      
  */
 private void initPaint() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(3);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  *    path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//   
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);

  searchPath.addArc(searchRect,45,358);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-358);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
   canvas.translate(width/2,height/2);//       view        
   drawSearch(canvas);
 }
 private void drawSearch(Canvas canvas) {
  Path dst = new Path();
  pathMeasure.setPath(searchPath,false);
  pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true);
  canvas.drawPath(searchPath,paint);
 }
}
효과:

지금 은 우리 의 효과 가 아직 바깥 원 의 큰 원 의 효과 가 부족 하 다.그러면 큰 원 은 작은 원 애니메이션 이 실 행 된 후에 회전 효 과 를 하 는 것 이다.좋아,우 리 는 애니메이션 만 감청 하면 돼.그림 을 그 려.

package com.tuya;
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.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private static final String TAG = "DynamicSearchView2";
 private Paint paint;
 private int width;//view   
 private int height;//view   
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//             
 private PathMeasure pathMeasure;
 private float[] pos;
 private float animPercent;//
 private ValueAnimator serchStartAnim;
 private ValueAnimator bigCircleAnim;//         
 private long animDuration = 2000;//    
 private int drawTag = 1;//             
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  initPaint();
  initAnim();
  initAnimListener();
  startAnim();
 }
 /**
  *       
  */
 private void startAnim() {
  drawTag = 1;
  serchStartAnim.start();
  invalidate();
 }
 /**
  *         
  */
 public void startBigCirCleAnim(){
  serchStartAnim.removeAllUpdateListeners();//                  bug
  bigCircleAnim.start();
  drawTag = 2;
 }
 /**
  *     
  */
 private void initAnimListener() {
  serchStartAnim.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animator) {
   }
   @Override
   public void onAnimationEnd(Animator animator) {
    startBigCirCleAnim();
   }
   @Override
   public void onAnimationCancel(Animator animator) {
   }
   @Override
   public void onAnimationRepeat(Animator animator) {
   }
  });
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
 }
 /**
  *      
  */
 private void initAnim() {
  bigCircleAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
  serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
 }
 /**
  *      
  */
 private void initPaint() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(3);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  *    path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//   
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);

  searchPath.addArc(searchRect,45,358);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-358);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(width/2,height/2);//       view        
  drawSearch(canvas);
 }
 private void drawSearch(Canvas canvas) {
  if(drawTag==1){
   drawSearchGraph(canvas);
  }else if(drawTag==2){
   drawBigCircleGraph(canvas);
  }
 }
 /**
  *       
  * @param canvas
  */
 private void drawBigCircleGraph(Canvas canvas) {
  pathMeasure.setPath(circlePath, false);
  Path dst2 = new Path();
  float stop = pathMeasure.getLength() * animPercent;
  float start = (float) (stop - ((0.5 - Math.abs(animPercent - 0.5)) * 200f));
  pathMeasure.getSegment(start, stop, dst2, true);
  canvas.drawPath(dst2, paint);
 }
 /**
  *      
  * @param canvas
  */
 private void drawSearchGraph(Canvas canvas) {
  pathMeasure.setPath(searchPath,false);
  Path dst = new Path();
  pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true);
  canvas.drawPath(dst,paint);
 }
}
효과:

한 바퀴 돌 면 끝 이 야.특정한 수요 가 있 으 면 전체 바퀴 의 동 그 라 미 를 통제 해 야 해.인터넷 로 딩 이 라면 인터넷 이 특별히 좋 지 않 으 면 상관 하지 않 아.이따가 주 보 를 써 야 하 니까 고 통 스 러 워.
아직 마지막 단 계 는 대원 의 운동 이 끝 난 후에 검색 상 자 를 그 리 는 것 입 니 다.사실 이것 은 첫 번 째 효과 와 딱 관련 이 있 습 니 다.

package com.tuya;
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.view.View;
/**
 * Created by admin on 2016/12/17.
 */
public class DynamicSearchView2 extends View {
 private static final String TAG = "DynamicSearchView2";
 private Paint paint;
 private int width;//view   
 private int height;//view   
 private Path searchPath;
 private Path circlePath;
 private float BigCircleRectWidth;//             
 private PathMeasure pathMeasure;
 private float[] pos;
 private float animPercent;//
 private ValueAnimator serchStartAnim;
 private ValueAnimator bigCircleAnim;//         
 private ValueAnimator startDrawSearchAnim;//         
 private long animDuration = 2000;//    
 private int drawTag = 1;//             
 public DynamicSearchView2(Context context) {
  this(context,null);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs) {
  this(context, attrs,0);
 }
 public DynamicSearchView2(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  initPaint();
  initAnim();
  initAnimListener();
  startAnim();
 }
 /**
  *       
  */
 private void startAnim() {
  drawTag = 1;
  serchStartAnim.start();
  invalidate();
 }
 /**
  *         
  */
 public void startBigCirCleAnim(){
  serchStartAnim.removeAllUpdateListeners();//                  bug
  bigCircleAnim.start();
  drawTag = 2;
 }
 /**
  *           
  */
 public void drawSearchAanim(){
  bigCircleAnim.removeAllUpdateListeners();//                  bug
  startDrawSearchAnim.start();
  drawTag = 3;
 }
 /**
  *     
  */
 private void initAnimListener() {
  bigCircleAnim.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animator) {
   }
   @Override
   public void onAnimationEnd(Animator animator) {
    drawSearchAanim();
   }
   @Override
   public void onAnimationCancel(Animator animator) {
   }
   @Override
   public void onAnimationRepeat(Animator animator) {
   }
  });
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  serchStartAnim.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animator) {
   }
   @Override
   public void onAnimationEnd(Animator animator) {
    startBigCirCleAnim();
   }
   @Override
   public void onAnimationCancel(Animator animator) {
   }
   @Override
   public void onAnimationRepeat(Animator animator) {
   }
  });
  serchStartAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  bigCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
  startDrawSearchAnim .addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
   @Override
   public void onAnimationUpdate(ValueAnimator valueAnimator) {
    //          ,      
    animPercent = (float) valueAnimator.getAnimatedValue();
    invalidate();
   }
  });
 }
 /**
  *      
  */
 private void initAnim() {
  bigCircleAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
  serchStartAnim = ValueAnimator.ofFloat(0,1).setDuration(animDuration);
  startDrawSearchAnim = ValueAnimator.ofFloat(1,0).setDuration(animDuration);
 }
 /**
  *      
  */
 private void initPaint() {
  paint = new Paint();
  paint.setAntiAlias(true);
  paint.setStrokeWidth(6);
  paint.setColor(Color.WHITE);
  paint.setStyle(Paint.Style.STROKE);
 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = w;
  height = h;
  initPath();
 }
 /**
  *    path
  */
 private void initPath() {
  searchPath = new Path();
  circlePath = new Path();
  if(width>height){//   
   BigCircleRectWidth = height;
  }else if(width<height){
   BigCircleRectWidth = width;
  }else{
   BigCircleRectWidth = width;
  }
  float smallbordWidth =BigCircleRectWidth/8;
  RectF searchRect = new RectF(-smallbordWidth,-smallbordWidth,smallbordWidth,smallbordWidth);
  searchPath.addArc(searchRect,45,358);
  float bigBordWidth = smallbordWidth*2;
  RectF circleRect = new RectF(-bigBordWidth,-bigBordWidth,bigBordWidth,bigBordWidth);
  circlePath.addArc(circleRect,45,-358);
  pathMeasure = new PathMeasure(circlePath,false);
  pos = new float[2];
  pathMeasure.getPosTan(0,pos,null);
  searchPath.lineTo(pos[0],pos[1]);
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(width/2,height/2);//       view        
  drawSearch(canvas);
 }
 private void drawSearch(Canvas canvas) {
  if(drawTag==1){
   drawSearchGraph(canvas);
  }else if(drawTag==2){
   drawBigCircleGraph(canvas);
  }else if(drawTag==3){
   drawSearchBox(canvas);
  }
 }
 /**
  *                 
  * @param canvas
  */
 private void drawSearchBox(Canvas canvas) {
  pathMeasure.setPath(searchPath, false);
  Path dst3 = new Path();
  pathMeasure.getSegment(pathMeasure.getLength() * animPercent, pathMeasure.getLength(), dst3, true);
  canvas.drawPath(dst3, paint);
 }
 /**
  *       
  * @param canvas
  */
 private void drawBigCircleGraph(Canvas canvas) {
  pathMeasure.setPath(circlePath, false);
  Path dst2 = new Path();
  float stop = pathMeasure.getLength() * animPercent;
  float start = (float) (stop - ((0.5 - Math.abs(animPercent - 0.5)) * 200f));
  pathMeasure.getSegment(start, stop, dst2, true);
  canvas.drawPath(dst2, paint);
 }
 /**
  *      
  * @param canvas
  */
 private void drawSearchGraph(Canvas canvas) {
  pathMeasure.setPath(searchPath,false);
  Path dst = new Path();
  pathMeasure.getSegment(pathMeasure.getLength()*animPercent,pathMeasure.getLength(),dst,true);
  canvas.drawPath(dst,paint);
 }
}
효과:

github: https://github.com/zhouguizhi/PathSearch
총결산
위 에서 말 한 것 은 소 편 이 여러분 에 게 소개 한 안 드 로 이 드 가 Path 를 사용 하여 동적 으로 애니메이션 을 불 러 오 는 효 과 를 실현 하 는 것 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 면 저 에 게 메 시 지 를 남 겨 주세요.소 편 은 신속하게 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기