Android 사용자 정의 view 그림 판 구현 방법

13403 단어 view그림 판
효과 보기:중간 에 있 는 그림 판 위의 작은 컨트롤 러 는 실시 간 으로 그 려 진 그림 아래 의 작은 컨트롤 러 를 표시 하여 그림 을 그 리 는 제어 2 개의 작은 컨트롤 러 를 모두 이동 할 수 있 습 니 다.

스 크 래 치 효과 도 있 습 니 다.매개 변 수 를 바 꾸 기만 하면 됩 니 다.

사용자 정의 view 는 우선 속성 을 사용자 정의 해 야 합 니 다:
values 아래 attrs.xml 만 들 기:

 <!--   -->
  <declare-styleable name="DrawImg">
    <attr name="PaintColor" />      //    
    <attr name="PaintWidth" />      //     
    <attr name="CanvasImg" />      //    
  </declare-styleable>

  <!--    -->
  <attr name="PaintColor" format="color" />    
  <attr name="PaintWidth" format="dimension" />     
  <attr name="CanvasImg" format="reference" />
아래 세 줄 의 지정 단위 의 코드 를 풀 어 여러 사용자 정의 view 를 사용 할 수 있 습 니 다.
다음 에 사용자 정의 view 클래스 계승 view 를 새로 만 들 고 앞의 세 가지 구조 방법 을 다시 씁 니 다.
빨 간 선 표 시 는 안 드 로 이 드 studio 3.0.0 매개 변수 알림 에 대한 새로운 기능 입 니 다.

이 를 통 해 앞의 두 구조 방법 을 모두 세 개의 매개 변수의 구조 방법 을 실현 하도록 한다.
구조 방법 을 간단히 말 하 다.하나의 매개 변수의 구조 방법 은 코드 에서 new 를 사용 할 때 사용 되 며,2 개의 매개 변수의 구조 방법 은 레이아웃 xml 에서 사용 되 며,3 개의 매개 변 수 는 기본적으로 사용자 정의 view 류 에서 사용 되 며,대략 이렇다.
다음은 attrs.xml 에서 TypedArray 를 통 해 사용자 정의 속성 을 추출 합 니 다.

// attrs         
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DrawImg, defStyleAttr, 0);
    for (int i = 0; i < a.getIndexCount(); i++) {
      int attr = a.getIndex(i);
      switch (attr) {
        case R.styleable.DrawImg_PaintWidth:    //    
          paintWidth = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
              TypedValue.COMPLEX_UNIT_DIP, -1, getResources().getDisplayMetrics()));
          break;
        case R.styleable.DrawImg_PaintColor:    //    
          paintColor = a.getColor(attr, Color.GREEN);
          break;
        case R.styleable.DrawImg_CanvasImg:     //    
          hasCanvasImg = a.getResourceId(attr, -1);
          break;
      }
    }
    //        
    if (paintWidth == -1) {
      paintWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, context.getResources().getDisplayMetrics());
    }
    //  bitmap
    if (hasCanvasImg != -1) {
      bitmap = BitmapFactory.decodeResource(getResources(), hasCanvasImg);
    }
    //onMeasure     ,onDraw               new   
    path = new Path();
레이아웃 에 사용자 정의 속성 을 사용 하지 않 아 오류 가 발생 하지 않도록 기본 값 을 설정 해 야 합 니 다.
사용자 정의 view 키 방법 onMeasure(),onDraw()를 다시 씁 니 다.onMeasure()는 이 사용자 정의 view 의 크기 를 지정 합 니 다.onDraw()는 실시 간 으로 그림 을 그 리 는 데 사 용 됩 니 다.
가장 중요 한 3 가지:캔버스,붓 페인트,경로 Path
코드 가 약간 길 지만 주석 이 완전 하 니 주의해 야 할 것 을 제시 하 세 요.
new Paint()방법 중 paint 는 setXfermode()방법 이 있 습 니 다.이 는 도형 혼합 방식 을 나타 내 며 18 가지 가 있 습 니 다~(아래 그림 보다 ADD 와 OVERLAY 가 많 습 니 다)~그림 한 장 보 여 주세요.여기 서 우 리 는 2 가지 SRC 를 사용한다.IN 과 DSTOUT。
SRC_IN:두 겹 의 교차 부분 을 취하 여 상층 부 를 표시 합 니 다.
DST_OUT:두 층 의 비 교 집합 부분 을 취하 여 아래쪽 을 표시 합 니 다.
솔직히 이렇게 말 해도 이해 하기 어 려 우 니 직접 해 봐 야 겠 어 요.하지만 여기 서 알 면:
SRC 사용IN 이 가 보드 를 그 리 는 효과 가 있어 요.
DST 사용OUT 는 스 크 래 치 효과 가 있어 요.

/**
   * onMeasure    
   * 1) getChildCount():   View   ;
   * 2) getChildAt(i):   i    ;
   * 3) subView.getLayoutParams().width/height:            ;
   * 4) measureChild(child, widthMeasureSpec, heightMeasureSpec):   View   ;
   * 5) child.getMeasuredHeight/width():   measureChild()               View    ;
   * 6) getPaddingLeft/Right/Top/Bottom():          ;
   * 7) setMeasuredDimension(width, height):         。        ,     
   * “super. onMeasure(widthMeasureSpec, heightMeasureSpec);”    。
   */
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    /**
     * getMode      (  3 )   getSize     
     *
     * EXACTLY:             , 100dp、match_parent ,     size      ;
     * AT_MOST:       wrap_content   ,     size           ;
     * UNSPECIFIED:           (   )。
     *
     * */
    //    _ 
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    //    _ 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    //  
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    //  
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    //  view  
    //             ,      ,         view  
    if (widthMode == MeasureSpec.EXACTLY) {
      width = widthSize;
    } else {
      if (hasCanvasImg != -1) {
        //       ,     
        width = bitmap.getWidth();
      } else {
        //              view           
        width = 500;
      }
    }
    //  view    
    if (heightMode == MeasureSpec.EXACTLY) {
      height = heightSize;
    } else {
      if (hasCanvasImg != -1) {
        height = bitmap.getHeight();
      } else {
        height = 500;
      }
    }
    //    view   
    setMeasuredDimension(width, height);

    //        
    newPaint();
  }
  private void newPaint() {
    //          bitmap             
    newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    //  bitmap          
    bmPixels = new int[newBitmap.getWidth() * newBitmap.getHeight()];
    //new   Canvas,   bitmap       createBitmap  ;
    //     :IllegalStateException : Immutable bitmap passed to Canvas constructor
    canvas = new Canvas(newBitmap);
    if (hasCanvasImg == -1) {
      //        ,        
      canvas.drawColor(Color.GRAY);
    } else {
      //         view  
      bitmap = zoomBitmap(this.bitmap, width, height);
      canvas.drawBitmap(bitmap, 0, 0, null);
    }
    //            
    paint = new Paint();
    paint.setColor(paintColor);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(paintWidth);
    //           ,            ,          
    paint.setAntiAlias(true);
    //            ,            ,    
    paint.setDither(true);
    //        STROKE FILL_OR_STROKE ,         
    paint.setStrokeCap(Paint.Cap.ROUND);
    //             
    paint.setStrokeJoin(Paint.Join.ROUND);
    //            
    /**
     * SRC_IN:       。    
     */
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
  }

  //  onDraw        ,                    
  @Override
  protected void onDraw(Canvas canvas) {
    canvas.drawBitmap(newBitmap, 0, 0, null);
    super.onDraw(canvas);
  }

  //            ,      Bitmap  
  public static Bitmap zoomBitmap(Bitmap bm, int newWidth, int newHeight) {
    //        
    int width = bm.getWidth();
    int height = bm.getHeight();
    //       
    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;
    //        matrix  
    Matrix matrix = new Matrix();
    matrix.postScale(scaleWidth, scaleHeight);
    //       
    return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
  }
이것 은 이 view 에 있어 서 비교적 복잡 한 코드 이지 만 기능 은 매우 간단 하 다.우 리 는 두 가지 일 을 했다.
1.MeasureSpec.getMode(측정 모드)를 통 해 전체 컨트롤 의 너비 와 높이 를 계산 합 니 다.
2.canvas.drawBitmap 를 통 해 캔버스 에 bitmap 를 그 리 는 동시에 new 는 붓 Paint 를 통 해 색상,굵기 등 속성 을 설정 합 니 다.
주의:
1.onDraw()방법 은 invalidate()를 호출 하거나 보기 가 변 할 때마다 다시 가기 때문에 안에 new 물건 을 넣 을 수 없습니다.
2.int[]유형의 배열 bmPixels 가 있 습 니 다.여기 서 무슨 뜻 인지 대충 말씀 드 리 겠 습 니 다.구체 적 인 설명 은 Bitmap 류 getPixels 와 createBitmap 방법 에 대한 상세 한 설명 에서 말 했 습 니 다.
bmPixels:우 리 는 bitmap 의 너비 에 높이 를 곱 하면 int[]형식의 배열 에 도착 할 수 있 습 니 다.이 배열 은 bitmap 를 구성 하 는 모든 픽 셀 점 입 니 다.특정한 픽 셀 점 이 0 일 때 그 가 색깔 이 없다 는 것 을 설명 합 니 다!0 은 색깔 이 있다 는 뜻 이다.
그림 을 그 리 는 이상 손가락 이동 을 감청 해 야 합 니 다.onTouch Event()방법:

@Override
  public boolean onTouchEvent(MotionEvent event) {
    int currX = (int) event.getX();
    int currY = (int) event.getY();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //   ,            
        path.moveTo(currX, currY);
        break;
      case MotionEvent.ACTION_MOVE:
        //   ,    
        path.lineTo(currX, currY);
        break;
      case MotionEvent.ACTION_UP:
    }
    //     ,        
    canvas.drawPath(path, paint);
    //  View     , draw()  ,            ,    layout()  。
    invalidate();
    return true;
  }
이것 은 간단 합 니 다.손가락 을 눌 렀 을 때 위 치 를 기록 합 니 다.path.moveto 는 path 에 시작 점 위 치 를 설정 하고 이동 할 때 path.lineTo()방법 으로 경 로 를 기록 하 는 동시에 canvas.drawPath(path,paint)를 사용 하여 직접 그립 니 다.invalidate()는 보기 업 데 이 트 를 알 립 니 다.
여기에 쓰 세 요.xml 레이아웃 에서 이 view 를 사용 하면 그림 을 그 릴 수 있 습 니 다.
우리 의 붓 Paint 류 는 색깔,굵기,패턴 등 을 지정 할 수 있 습 니 다.그러면 우 리 는 공개 적 인 방법 을 써 서 이 속성 들 을 동태 적 으로 설정 하여 붓 을 더욱 다양성 있 게 할 수 있 습 니 다.

//      
  public void setPaintColor(int color) {
    //path = new Path();
      path.reset();
    paint.setColor(color);
  }

  //      
  public void setPaintMode(int style) {
    //path = new Path();
      path.reset();
    /**
     * SRC_IN:       ,    
     * DST_OUT:        ,    
     */
    if (style == 1) {
      paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    } else {
      paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
    }
    resetCanvaas();
  }

  //      
  public void resetCanvaas() {
    //path = new Path();
      path.reset();
    canvas.drawBitmap(bitmap, 0, 0, null);
    invalidate();
    listener.bitmapChangeListener(bitmap);
  }
위의 코드 는 붓 색깔 을 설정 하고,붓 종류 와 캔버스 리 셋 을 설정 합 니 다.왜 모두 new Path 를 사용 해 야 합 니까?새로 경 로 를 열 어 붓 에 게 주지 않 으 면,새로운 색깔 을 설정 하고,예전 의 Path 를 사용 하면,붓 은 원래 의 색깔 을 유지 하 는 것 이 아니 라 예전 의 Path 도 새로운 색깔 로 다시 설정 하기 때 문 입 니 다.
이렇게 하면 문제 가 발생 할 수 있 습 니 다.매번 new Path,new 에서 한 번 만 들 고 한 번 의 메모리 를 차지 하 며 피 하 는 방법 을 생각 하지만 본 고 는 그림 을 그 리 는 것 이 중점 이 아니 라 논술 하지 않 습 니 다.path.reset()로 변경 됨
효과 의 오른쪽 상단 은 float 형식의 수 를 보 여 줍 니 다.이것 은 스 크 래 치 모드 에서 부분 이 bitmap 에서 차지 하 는 비율 을 지 웠 습 니 다.onMeasure()방법 에는 int[]유형의 배열 bmPixels 가 있 습 니 다.이때 우 리 는 이 배열 을 이용 하여 이 예 를 들 어야 합 니 다.

onTouchEvent()방법의 case MotionEvent.ACTIONUP 에 코드 추가:

@Override
  public boolean onTouchEvent(MotionEvent event) {
    int currX = (int) event.getX();
    int currY = (int) event.getY();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //   ,            
        path.moveTo(currX, currY);
        break;
      case MotionEvent.ACTION_MOVE:
        //   ,    
        path.lineTo(currX, currY);
        //    ,   bitmap    
        listener.bitmapChangeListener(newBitmap);
        break;
      case MotionEvent.ACTION_UP:
        //     ,         
        int nullPixel = 0;
        newBitmap.getPixels(bmPixels, 0, width, 0, 0, width, height);
        for (int i = 0; i < bmPixels.length; i++) {
          //                 0,   0   
          if (bmPixels[i] == 0) {
            nullPixel++;
          }
        }
        //            
        listener.showBitmapClear((float) nullPixel / (float) bmPixels.length);
        break;
    }
    //     ,        
    canvas.drawPath(path, paint);
    //  View     , draw()  ,            ,    layout()  。
    invalidate();
    return true;
  }
new Bitmap.getPixels(bmPixels,0,width,0,0,width,height)가 있 습 니 다.getPixels 방법 에 대한 상세 한 설명 은 new Bitmap 의 모든 픽 셀 점 을 모두 꺼 내 방법 중의 첫 번 째 매개 변수 인 bmPixels 에 넣 는 것 입 니 다.이때,우 리 는 for 순환 을 통 해 bmPixels 배열 을 옮 겨 다 녔 다.0 과 같은 설명 은 색 이 지 워 지지 않 았 다 는 것 이다.그들의 수량 을 통계 하고 그들 이 차지 하 는 비율 을 계산 하면 지 워 진 비율 을 계산 할 수 있다.마찬가지 로 우 리 는 0 과 같은 판단 조건 을 바 꾸 어 그 를 다른 색깔 과 같 게 할 수 있다.그러면 다른 색깔 이 차지 하 는 비례 를 계산 할 수 있다.
리 셋 인 터 페 이 스 를 써 서 코드 에서 꺼 내 면 OK 입 니 다.

//    
  public interface bitmapListener {
    //       bitmap   imageview  
    void bitmapChangeListener(Bitmap bitmap);
    //      
    void showBitmapClear(float clear);
  }

  public void addBitmapListener(bitmapListener bitmapListener) {
    this.listener = bitmapListener;
  }
2 개의 인터페이스 가 있 고,하 나 는 실시 간 으로 비트 맵 을 보 여 주 며,하 나 는 지우 기 비율 을 보 여 줍 니 다.

좋은 웹페이지 즐겨찾기