21

Mục tiêu:Tạo một cái nhìn hình ảnh drawable & Zoomable trong android

Tạo một ImageView đó là drawable và Zoomable,

Điều đó có nghĩa khi tôi bấm một nút để vào, nó là drawable,

hoặc khi tôi tắt, có thể thu phóng.

* Chú ý vẽ nên zoom phù hợp với các ImageView

================================ ===============================

Gần đây tôi đã viết một cái nhìn hình ảnh tùy chỉnh drawable như thế này:

public class DrawView extends ImageView { 

     private int color = Color.BLACK; 
     private float width = 4f; 
     private List<Holder> holderList = new ArrayList<Holder>(); 

     private class Holder {  
      Path path; 
      Paint paint; 

      Holder(int color, float width) { 
       path = new Path(); 
       paint = new Paint(); 
       paint.setAntiAlias(true); 
       paint.setStrokeWidth(width); 
       paint.setColor(color); 
       paint.setStyle(Paint.Style.STROKE); 
       paint.setStrokeJoin(Paint.Join.ROUND); 
       paint.setStrokeCap(Paint.Cap.ROUND); 
      } 
     } 

     public DrawView(Context context) { 
      super(context); 
      init(); 
     } 

     public DrawView(Context context, AttributeSet attrs) { 
      super(context, attrs); 
      init(); 
     } 

     public DrawView(Context context, AttributeSet attrs, int defStyle) { 
      super(context, attrs, defStyle); 
      init(); 
     } 

     private void init() { 
      holderList.add(new Holder(color, width)); 
     } 

     @Override 
     protected void onDraw(Canvas canvas) { 
      super.onDraw(canvas); 
      for (Holder holder : holderList) { 
       canvas.drawPath(holder.path, holder.paint); 
      } 
     } 

     @Override 
     public boolean onTouchEvent(MotionEvent event) { 
      float eventX = event.getX(); 
      float eventY = event.getY(); 

      switch (event.getAction()) { 
       case MotionEvent.ACTION_DOWN: 
        holderList.add(new Holder(color,width)); 
        holderList.get(holderList.size() - 1).path.moveTo(eventX, eventY); 
        return true; 
       case MotionEvent.ACTION_MOVE: 
        holderList.get(holderList.size() - 1).path.lineTo(eventX, eventY); 
        break; 
       case MotionEvent.ACTION_UP: 
        break; 
       default: 
        return false; 
      } 

      invalidate(); 
      return true; 
     } 

     public void resetPaths() { 
      for (Holder holder : holderList) { 
       holder.path.reset(); 
      } 
      invalidate(); 
     } 

     public void setBrushColor(int color) { 
      this.color = color; 
     } 

     public void setWidth(float width) { 
      this.width = width; 
     } 
    } 

Và XML là:

<com.example.tool.DrawView 
    android:id="@+id/draw" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:adjustViewBounds="true" /> 

các prob lem là, cách làm cho nó có thể zoom cũng như? lưu ý rằng các bản vẽ phải phù hợp với số lần xem ảnh khi phóng to.

Cố gắng sử dụng một số thư viện hình ảnh tùy chỉnh nhưng không có may mắn.

ví dụ: Khi tôi sử dụng PhotoView nó có thể zoom nhưng các bản vẽ không sắp xếp, và mức độ phóng sẽ thiết lập lại sau khi tôi bật/tắt các phóng to https://github.com/chrisbanes/PhotoView

Ngoài ra, tìm thấy một số thư viện khác như thế nhưng không phù hợp với trường hợp giao diện tùy chỉnh https://github.com/matabii/scale-imageview-android

enter image description here

Update1: bản demo

Đề xuất để tham khảo ứng dụng này, các chức năng vẽ thực sự là giống như những gì tôi là đấu tranh để đạt được, nhưng tôi không thể tìm ra cách họ làm cho nó thực hiện

https://play.google.com/store/apps/details?id=com.zentertain.photoeditor&hl=en

Cảm ơn

Update2: Từ Sandeep Maram Nguồn

Thanks a lot Sandeep Maram, sau khi kiểm tra mã, mọi thứ hoạt động tốt, điều duy nhất còn lại là bản vẽ không thẳng hàng với chế độ xem thu phóng. Xin hãy nhìn vào ảnh chụp màn hình

Trước: enter image description here

Sau:

enter image description here

Vòng tròn không được mở rộng lên/xuống khi zoom, sẽ thực sự đẹp nếu khắc phục điều đó. cũng không quan trọng nếu hình ảnh chồng chéo lên nút.

+1

cách thu phóng áp dụng cho chế độ xem, hiển thị cho tôi mã? –

+1

dễ dàng của nó: không sử dụng tùy chỉnh ** ImageView **, sử dụng tùy chỉnh ** Drawable **, sau đó vượt qua Drawable đó để 'ImageView.setImageDrawable()' – pskink

+0

được cập nhật, cảm ơn – user3538235

Trả lời

5

trả lời được cập nhật với zoom bật/tắt và drawableview

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
tools:context=".MainActivity" > 
<Button 
    android:id="@+id/enable_zoom" 
    android:layout_height="wrap_content" 
    android:layout_width="wrap_content" 
    android:text="disable zoom"/> 

<com.rbt.zoomdraw.CustomImageView 
    android:id="@+id/zoom_iv" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:src="@drawable/ic_launcher" 
    android:layout_below="@+id/enable_zoom" /> 

<com.rbt.zoomdraw.DrawableView 
    android:id="@+id/drawble_view" 
    android:layout_width="wrap_content" 
    android:layout_height="match_parent" 
    android:layout_alignBottom="@+id/zoom_iv" 
    android:layout_alignTop="@+id/zoom_iv" /> 

MainActivity.java

public class MainActivity extends Activity implements OnClickListener { 

private Button enableZoomBtn; 
private DrawableView drawbleView; 
private CustomImageView touchImageView; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    drawbleView = (DrawableView) findViewById(R.id.drawble_view); 
    enableZoomBtn = (Button) findViewById(R.id.enable_zoom); 
    touchImageView = (CustomImageView) findViewById(R.id.zoom_iv); 
    enableZoomBtn.setOnClickListener(this); 
    drawbleView.setDrawingEnabled(false); 
} 
@Override 
public void onClick(View v) { 
    int id = v.getId(); 
    switch (id) { 
    case R.id.enable_zoom: 
     if(enableZoomBtn.getText().equals("disable zoom")){ 
      touchImageView.setZoomEnable(false); 
      drawbleView.setDrawingEnabled(true); 
      enableZoomBtn.setText("enable zoom"); 
     } else{ 
      touchImageView.setZoomEnable(true); 
      drawbleView.setDrawingEnabled(false); 
      enableZoomBtn.setText("disable zoom"); 
     } 
     break; 

    default: 
     break; 
    } 
    } 
} 

DrawableView.java

public class DrawableView extends View { 
public int width; 
public int height; 
private boolean isEditable; 
private Path drawPath; 
private Paint drawPaint; 
private Paint canvasPaint; 
private Canvas drawCanvas; 
private Bitmap canvasBitmap; 
private int paintColor = Color.RED; 
public DrawableView(Context context) { 
    super(context); 
} 
public DrawableView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    this.canvasPaint = new Paint(Paint.DITHER_FLAG); 
    setupDrawing(); 
} 
public DrawableView(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
} 
@Override 
protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
    super.onSizeChanged(w, h, oldw, oldh); 
    this.height = h; 
    this.width = w; 
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 
    drawCanvas = new Canvas(canvasBitmap); 
} 
private void setupDrawing() { 
    drawPath = new Path(); 
    drawPaint = new Paint(); 
    drawPaint.setColor(paintColor); 
    drawPaint.setAntiAlias(true); 
    drawPaint.setDither(true); 
    drawPaint.setStyle(Paint.Style.STROKE); 
    drawPaint.setStrokeJoin(Paint.Join.ROUND); 
    drawPaint.setStrokeCap(Paint.Cap.ROUND); 
    drawPaint.setStrokeWidth(10); 
} 
public void setDrawingEnabled(boolean isEditable){ 
    this.isEditable = isEditable; 
} 
@Override 
protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 
    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint); 
    canvas.drawPath(drawPath, drawPaint); 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    if(isEditable){ 
     float touchX = event.getX(); 
     float touchY = event.getY(); 
     switch (event.getAction()) { 
     case MotionEvent.ACTION_DOWN: 
      drawPath.moveTo(touchX, touchY); 
      break; 
     case MotionEvent.ACTION_MOVE: 
      drawPath.lineTo(touchX, touchY); 
      break; 
     case MotionEvent.ACTION_UP: 
      drawPath.lineTo(touchX, touchY); 
      drawCanvas.drawPath(drawPath, drawPaint); 
      drawPath = new Path(); 
      break; 
     default: 
      return false; 
     } 
    } else{ 
     return false; 
    } 
    invalidate(); 
    return true; 
    } 
} 

CustomImageView

public class CustomImageView extends ImageView { 
Matrix matrix; 
// We can be in one of these 3 states 
static final int NONE = 0; 
static final int DRAG = 1; 
static final int ZOOM = 2; 
int mode = NONE; 
private boolean zoomEnable= true; 
// Remember some things for zooming 
PointF last = new PointF(); 
PointF start = new PointF(); 
float minScale = 1f; 
float maxScale = 5f; 
float[] m; 

int viewWidth, viewHeight; 
static final int CLICK = 3; 
float saveScale = 1f; 
protected float origWidth, origHeight; 
int oldMeasuredWidth, oldMeasuredHeight; 
ScaleGestureDetector mScaleDetector; 
Context context; 
public CustomImageView(Context context) { 
    super(context); 
    sharedConstructing(context); 
} 
public void setZoomEnable(boolean status){ 
    zoomEnable = status; 
} 
public CustomImageView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    sharedConstructing(context); 
} 

private void sharedConstructing(Context context) { 
    super.setClickable(true); 
    this.context = context; 
    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    matrix = new Matrix(); 
    m = new float[9]; 
    setImageMatrix(matrix); 
    setScaleType(ScaleType.MATRIX); 

    setOnTouchListener(new OnTouchListener() { 
     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      if(zoomEnable){ 
       mScaleDetector.onTouchEvent(event); 
       PointF curr = new PointF(event.getX(), event.getY()); 

       switch (event.getAction()) { 
       case MotionEvent.ACTION_DOWN: 
        last.set(curr); 
        start.set(last); 
        mode = DRAG; 
        break; 

       case MotionEvent.ACTION_MOVE: 
        if (mode == DRAG) { 
         float deltaX = curr.x - last.x; 
         float deltaY = curr.y - last.y; 
         float fixTransX = getFixDragTrans(deltaX, viewWidth, 
           origWidth * saveScale); 
         float fixTransY = getFixDragTrans(deltaY, viewHeight, 
           origHeight * saveScale); 
         matrix.postTranslate(fixTransX, fixTransY); 
         fixTrans(); 
         last.set(curr.x, curr.y); 
        } 
        break; 

       case MotionEvent.ACTION_UP: 
        mode = NONE; 
        int xDiff = (int) Math.abs(curr.x - start.x); 
        int yDiff = (int) Math.abs(curr.y - start.y); 
        if (xDiff < CLICK && yDiff < CLICK) 
         performClick(); 
        break; 

       case MotionEvent.ACTION_POINTER_UP: 
        mode = NONE; 
        break; 
       } 

       setImageMatrix(matrix); 
       invalidate(); 
       return true; // indicate event was handled 

      } else{ 
       return false; 
      } 
     } 

    }); 
} 

public void setMaxZoom(float x) { 
    maxScale = x; 
} 

private class ScaleListener extends 
ScaleGestureDetector.SimpleOnScaleGestureListener { 
    @Override 
    public boolean onScaleBegin(ScaleGestureDetector detector) { 
     mode = ZOOM; 
     return true; 
    } 

    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     float mScaleFactor = detector.getScaleFactor(); 
     float origScale = saveScale; 
     saveScale *= mScaleFactor; 
     if (saveScale > maxScale) { 
      saveScale = maxScale; 
      mScaleFactor = maxScale/origScale; 
     } else if (saveScale < minScale) { 
      saveScale = minScale; 
      mScaleFactor = minScale/origScale; 
     } 

     if (origWidth * saveScale <= viewWidth 
       || origHeight * saveScale <= viewHeight) 
      matrix.postScale(mScaleFactor, mScaleFactor, viewWidth/2, 
        viewHeight/2); 
     else 
      matrix.postScale(mScaleFactor, mScaleFactor, 
        detector.getFocusX(), detector.getFocusY()); 

     fixTrans(); 
     return true; 
    } 
} 

void fixTrans() { 
    matrix.getValues(m); 
    float transX = m[Matrix.MTRANS_X]; 
    float transY = m[Matrix.MTRANS_Y]; 

    float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale); 
    float fixTransY = getFixTrans(transY, viewHeight, origHeight 
      * saveScale); 

    if (fixTransX != 0 || fixTransY != 0) 
     matrix.postTranslate(fixTransX, fixTransY); 
} 

float getFixTrans(float trans, float viewSize, float contentSize) { 
    float minTrans, maxTrans; 

    if (contentSize <= viewSize) { 
     minTrans = 0; 
     maxTrans = viewSize - contentSize; 
    } else { 
     minTrans = viewSize - contentSize; 
     maxTrans = 0; 
    } 

    if (trans < minTrans) 
     return -trans + minTrans; 
    if (trans > maxTrans) 
     return -trans + maxTrans; 
    return 0; 
} 

float getFixDragTrans(float delta, float viewSize, float contentSize) { 
    if (contentSize <= viewSize) { 
     return 0; 
    } 
    return delta; 
} 

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    viewWidth = MeasureSpec.getSize(widthMeasureSpec); 
    viewHeight = MeasureSpec.getSize(heightMeasureSpec); 

    // 
    // Rescales image on rotation 
    // 
    if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight 
      || viewWidth == 0 || viewHeight == 0) 
     return; 
    oldMeasuredHeight = viewHeight; 
    oldMeasuredWidth = viewWidth; 

    if (saveScale == 1) { 
     // Fit to screen. 
     float scale; 

     Drawable drawable = getDrawable(); 
     if (drawable == null || drawable.getIntrinsicWidth() == 0 
       || drawable.getIntrinsicHeight() == 0) 
      return; 
     int bmWidth = drawable.getIntrinsicWidth(); 
     int bmHeight = drawable.getIntrinsicHeight(); 

     Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight); 

     float scaleX = (float) viewWidth/(float) bmWidth; 
     float scaleY = (float) viewHeight/(float) bmHeight; 
     scale = Math.min(scaleX, scaleY); 
     matrix.setScale(scale, scale); 

     // Center the image 
     float redundantYSpace = (float) viewHeight 
       - (scale * (float) bmHeight); 
     float redundantXSpace = (float) viewWidth 
       - (scale * (float) bmWidth); 
     redundantYSpace /= (float) 2; 
     redundantXSpace /= (float) 2; 

     matrix.postTranslate(redundantXSpace, redundantYSpace); 

     origWidth = viewWidth - 2 * redundantXSpace; 
     origHeight = viewHeight - 2 * redundantYSpace; 
     setImageMatrix(matrix); 
    } 
    fixTrans(); 
    } 
} 

Tôi hy vọng nó sẽ làm việc cho bạn.

+0

bạn sẽ nhớ giải thích ngắn gọn về cách sử dụng nó? dường như không có chức năng nào để tắt/bật thu phóng. và để vẽ lên tất cả những gì tôi cần làm là sử dụng mã được triển khai của tôi? Cảm ơn bạn đã giúp đỡ. – user3538235

+0

Đề xuất của tôi là có thêm một lớp tùy chỉnh mở rộng chế độ xem. Thực hiện các thao tác có thể vẽ trên chế độ xem này. Đặt chế độ xem này là aligntop và alignbottom thành chế độ xem hình ảnh có thể thu phóng. Tôi nghĩ rằng đó là tập luyện cho bạn. – sandeepmaaram

+0

Đã cố gắng sử dụng chế độ xem và có vẻ như đối với phần thu phóng. Điều đó có nghĩa là có hai lớp, một cho vẽ và một cho zoom? bạn sẽ có một số mã puseduo cho lời giải thích? Cảm ơn rất nhiều – user3538235

2

Tôi không có giải pháp hoàn chỉnh. Tuy nhiên tôi có thể đề nghị bạn hãy xem PhotoView có triển khai thu phóng và tùy chỉnh CanvasView mà tôi đã sử dụng trong một trong các dự án cũ. Nó cho phép vẽ bằng công cụ "bút" và nhập văn bản trên đó. Tính năng văn bản không cần thiết cho bạn và có thể quá cụ thể cho phần đó để bạn có thể bỏ qua nó. Có lẽ nó sẽ giúp bạn. Nó hỗ trợ xoay màn hình dẫn đến kích thước mới có thể kéo và cả văn bản và đường cong được chia tỷ lệ trong trường hợp này.

public class CanvasView extends View { 

    // ----------------------------------------------------------------------- 
    // 
    // Constants 
    // 
    // ----------------------------------------------------------------------- 
    @SuppressWarnings("unused") 
    private static final String TAG = CanvasView.class.getSimpleName(); 

    private static final String EXTRA_SUPER_STATE = "EXTRA_SUPER_STATE"; 
    private static final String EXTRA_COLOR = "EXTRA_COLOR"; 
    private static final String EXTRA_ACTION = "EXTRA_ACTION"; 
    private static final String EXTRA_MODIFICATIONS = "EXTRA_MODIFICATIONS"; 
    private static final String EXTRA_CURRENT_MODIFICATION = "EXTRA_CURRENT_MODIFICATION"; 

    // ----------------------------------------------------------------------- 
    // 
    // Enums 
    // 
    // ----------------------------------------------------------------------- 
    public static enum ACTION { 
     PEN, TEXT 
    }; 

    // ----------------------------------------------------------------------- 
    // 
    // Fields 
    // 
    // ----------------------------------------------------------------------- 
    private ArrayList<DrawableItem> mItems; 
    private ACTION mAction; 
    private int mColor; 
    private float mDensityScale; 

    private DrawableItem mCurrentItem; 

    private EditText mEditText; 

    // ----------------------------------------------------------------------- 
    // 
    // Constructor 
    // 
    // ----------------------------------------------------------------------- 
    public CanvasView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     mItems = new ArrayList<CanvasView.DrawableItem>(); 
     mAction = ACTION.PEN; 
     mDensityScale = getResources().getDisplayMetrics().density; 
    } 

    // ----------------------------------------------------------------------- 
    // 
    // Setters 
    // 
    // ----------------------------------------------------------------------- 
    public void setAction(ACTION action) { 
     if (mCurrentItem != null) 
      saveCurrentModification(); 
     mAction = action; 
     invalidate(); 
    } 

    public void setColor(int color) { 
     mColor = color; 
     hideKeyboard(); 
    } 

    public void saveCurrentModification() { 
     if (mCurrentItem instanceof Curve || !((Text) mCurrentItem).isEmpty()) 
      mItems.add(mCurrentItem); 

     mCurrentItem = null; 
    } 

    // ----------------------------------------------------------------------- 
    // 
    // Methods 
    // 
    // ----------------------------------------------------------------------- 
    @Override 
    protected void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 
     for (DrawableItem item : mItems) 
      item.draw(canvas, getMeasuredWidth(), getMeasuredHeight()); 
     if (mCurrentItem != null) 
      mCurrentItem.draw(canvas, getMeasuredWidth(), getMeasuredHeight()); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     switch (mAction) { 
     case PEN: 
      onTouchPen(event); 
      break; 
     case TEXT: 
      onTouchText(event); 
      break; 
     default: 
      break; 
     } 
     return true; 

    } 

    private void onTouchPen(MotionEvent event) { 
     switch (event.getActionMasked()) { 
     case MotionEvent.ACTION_DOWN: 
      mCurrentItem = new Curve(mColor); 
      break; 
     case MotionEvent.ACTION_MOVE: 
      Curve currentItem = (Curve) mCurrentItem; 
      currentItem.addPoint(event.getX(), event.getY()); 
      break; 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      saveCurrentModification(); 
      break; 
     default: 
     } 
     invalidate(); 
    } 

    private void onTouchText(MotionEvent event) { 
     switch (event.getActionMasked()) { 
     case MotionEvent.ACTION_DOWN: { 
      if (mCurrentItem != null) 
       saveCurrentModification(); 
      mCurrentItem = new Text(new PointF(event.getX(), event.getY()), 
        mDensityScale); 
      showKeyboard(); 
     } 
      break; 
     default: 
     } 
    } 

    private void showKeyboard() { 
     mEditText.setVisibility(View.VISIBLE); 
     mEditText.setText(""); 
     mEditText.requestFocus(); 
     InputMethodManager inputMethodManager = (InputMethodManager) getContext() 
       .getSystemService(Context.INPUT_METHOD_SERVICE); 
     inputMethodManager.showSoftInput(mEditText, 
       InputMethodManager.SHOW_IMPLICIT); 
    } 

    private void hideKeyboard() { 
     mEditText.setVisibility(View.INVISIBLE); 
     InputMethodManager imm = (InputMethodManager) getContext() 
       .getSystemService(Context.INPUT_METHOD_SERVICE); 
     imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); 
    } 

    @Override 
    protected Parcelable onSaveInstanceState() { 
     Bundle bundle = new Bundle(); 
     bundle.putParcelable(EXTRA_SUPER_STATE, super.onSaveInstanceState()); 
     bundle.putInt(EXTRA_COLOR, mColor); 
     bundle.putSerializable(EXTRA_ACTION, mAction); 
     bundle.putParcelableArrayList(EXTRA_MODIFICATIONS, mItems); 
     return bundle; 
    } 

    @Override 
    protected void onRestoreInstanceState(Parcelable state) { 
     if (state instanceof Bundle) { 
      Bundle bundle = (Bundle) state; 
      setColor(bundle.getInt(EXTRA_COLOR)); 
      setAction((ACTION) bundle.getSerializable(EXTRA_ACTION)); 
      mItems = bundle.getParcelableArrayList(EXTRA_MODIFICATIONS); 
      super.onRestoreInstanceState(bundle 
        .getParcelable(EXTRA_SUPER_STATE)); 
      return; 
     } else 
      super.onRestoreInstanceState(state); 
    } 

    public OnEditorActionListener getEditorActionListener() { 
     return mEditorActionListener; 
    } 

    public TextWatcher getTextWatcher() { 
     return mTextWatcher; 
    } 

    public void setTextProvider(EditText editText) { 
     mEditText = editText; 
     mEditText.setOnEditorActionListener(getEditorActionListener()); 
     mEditText.addTextChangedListener(mTextWatcher); 
    } 

    public void clearSession() { 
     mItems.clear(); 
     mCurrentItem = null; 
     hideKeyboard(); 
     invalidate(); 
    } 

    // ----------------------------------------------------------------------- 
    // 
    // Listeners 
    // 
    // ----------------------------------------------------------------------- 
    private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() { 
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 
      if (actionId == EditorInfo.IME_ACTION_DONE) { 
       saveCurrentModification(); 
       hideKeyboard(); 
      } 
      return false; 
     } 
    }; 

    private TextWatcher mTextWatcher = new TextWatcher() { 

     @Override 
     public void onTextChanged(CharSequence s, int start, int before, 
       int count) { 
     } 

     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, 
       int after) { 
     } 

     @Override 
     public void afterTextChanged(Editable s) { 
      if (mCurrentItem instanceof Text) { 
       ((Text) mCurrentItem).setText(s.toString()); 
       invalidate(); 
      } 
     } 
    }; 

    // ----------------------------------------------------------------------- 
    // 
    // Inner classes 
    // 
    // ----------------------------------------------------------------------- 
    public static interface OnStartTextInputListener { 
     public void onTextInputStarted(CanvasView view); 
    } 

    private static interface DrawableItem extends Parcelable { 
     public void draw(Canvas canvas, float viewWidth, float viewHeight); 
    } 

    private static class Curve implements DrawableItem { 
     // ----------------------------------------------------------------------- 
     // 
     // Fields 
     // 
     // ----------------------------------------------------------------------- 
     private ArrayList<PointF> mPoints; 
     private int mColor; 
     private Paint mPaint; 

     // ----------------------------------------------------------------------- 
     // 
     // Constructor 
     // 
     // ----------------------------------------------------------------------- 
     public Curve(int color) { 
      mPoints = new ArrayList<PointF>(); 
      mColor = color; 
      mPaint = new Paint(); 
      mPaint.setColor(mColor); 
      mPaint.setStrokeWidth(2); 
     } 

     private Curve(Parcel parcel) { 
      mColor = parcel.readInt(); 
      parcel.readList(mPoints, PointF.class.getClassLoader()); 
     } 

     // ----------------------------------------------------------------------- 
     // 
     // Methods 
     // 
     // ----------------------------------------------------------------------- 
     @Override 
     public void draw(Canvas canvas, float viewWidth, float viewHeight) { 
      for (int i = 1; i < mPoints.size(); ++i) { 
       PointF prev = mPoints.get(i - 1); 
       PointF current = mPoints.get(i); 
       canvas.drawLine(prev.x/viewWidth * canvas.getWidth(), prev.y 
         /viewHeight * canvas.getHeight(), current.x 
         /viewWidth * canvas.getWidth(), current.y/viewHeight 
         * canvas.getHeight(), mPaint); 
      } 
     } 

     public void addPoint(float x, float y) { 
      mPoints.add(new PointF(x, y)); 
     } 

     @Override 
     public int describeContents() { 
      return 0; 
     } 

     @Override 
     public void writeToParcel(Parcel dest, int flags) { 
      dest.writeInt(mColor); 
      dest.writeList(mPoints); 
     } 

     // ----------------------------------------------------------------------- 
     // 
     // Inner classes 
     // 
     // ----------------------------------------------------------------------- 
     public static final Parcelable.Creator<Curve> CREATOR = new Parcelable.Creator<Curve>() { 
      public Curve createFromParcel(Parcel in) { 
       return new Curve(in); 
      } 

      public Curve[] newArray(int size) { 
       return new Curve[size]; 
      } 
     }; 
    } 

    private static class Text implements DrawableItem { 
     // ----------------------------------------------------------------------- 
     // 
     // Constatns 
     // 
     // ----------------------------------------------------------------------- 
     private static final int FONT_SIZE = 14; 
     private static final int PADDING = 4; 
     private static final int BORDER_WIDTH = 2; 
     private static final int BORDER_COLOR = Color.rgb(0, 0, 0); 
     private static final int BOX_COLOR = Color.rgb(255, 255, 255); 
     private static final int FONT_COLOR = Color.rgb(0, 0, 0); 

     // ----------------------------------------------------------------------- 
     // 
     // Fields 
     // 
     // ----------------------------------------------------------------------- 
     private Paint mBoxPaint; 
     private Paint mBorderPaint; 
     private Paint mTextPaint; 

     private float mDensityScale; 

     private PointF mPoint; 
     private String mTextString = ""; 

     // ----------------------------------------------------------------------- 
     // 
     // Constructor 
     // 
     // ----------------------------------------------------------------------- 
     public Text(PointF point, float densityScale) { 
      mPoint = point; 
      mDensityScale = densityScale; 

      mBoxPaint = new Paint(); 
      mBoxPaint.setStyle(Style.FILL); 
      mBoxPaint.setColor(BOX_COLOR); 

      mBorderPaint = new Paint(); 
      mBorderPaint.setStyle(Style.STROKE); 
      mBorderPaint.setColor(BORDER_COLOR); 
      mBorderPaint.setStrokeWidth(getBorderWidth()); 

      mTextPaint = new Paint(); 
      mTextPaint.setTextSize(getFontSize()); 
      mTextPaint.setAntiAlias(true); 
      mTextPaint.setTextAlign(Paint.Align.LEFT); 
      mTextPaint.setColor(FONT_COLOR); 
     } 

     // ----------------------------------------------------------------------- 
     // 
     // Methods 
     // 
     // ----------------------------------------------------------------------- 
     public void setText(String text) { 
      mTextString = text; 
     } 

     public boolean isEmpty() { 
      return Utils.isEmpty(mTextString); 
     } 

     @Override 
     public int describeContents() { 
      return 0; 
     } 

     @Override 
     public void writeToParcel(Parcel dest, int flags) { 
     } 

     private float getFontSize() { 
      return FONT_SIZE * mDensityScale; 
     } 

     private float getPadding() { 
      return PADDING * mDensityScale; 
     } 

     private float getBorderWidth() { 
      return BORDER_WIDTH * mDensityScale; 
     } 

     private float getStringWidth(String string) { 
      Rect bounds = new Rect(); 
      mTextPaint.getTextBounds(string, 0, string.length(), bounds); 
      return bounds.width(); 
     } 

     @Override 
     public void draw(Canvas canvas, float viewWidth, float viewHeight) { 
      ArrayList<String> labelList = new ArrayList<String>(); 

      String[] lines = mTextString.split(" "); 
      String prevLine = ""; 
      float textHeight = (2 * getBorderWidth() + 2 * getPadding()) 
        /viewHeight * canvas.getHeight(); 
      float textWidth = 0; 
      for (int i = 0; i < lines.length; ++i) { 
       String string = lines[i]; 
       String newline = prevLine.concat(" " + string).trim(); 

       float prevLineWidth = (getStringWidth(prevLine) + 2 
         * getBorderWidth() + 2 * getPadding()) 
         /viewWidth * canvas.getWidth(); 
       float newLineWidth = (getStringWidth(newline) + 2 
         * getBorderWidth() + 2 * getPadding()) 
         /viewWidth * canvas.getWidth(); 
       float availableWidth = canvas.getWidth() - mPoint.x/viewWidth 
         * canvas.getWidth() + 1; 

       if (newLineWidth > availableWidth) { 
        if (!Utils.isEmpty(prevLine)) { 
         labelList.add(prevLine); 
         textHeight += (getFontSize() + getPadding()) 
           /viewHeight * canvas.getHeight(); 
         if (prevLineWidth >= textWidth) 
          textWidth = prevLineWidth; 
        } 
        prevLine = string; 
        if (i == lines.length - 1) { 
         prevLineWidth = (getStringWidth(prevLine) + 2 
           * getBorderWidth() + 2 * getPadding()) 
           /viewWidth * canvas.getWidth(); 
         labelList.add(prevLine); 
         if (prevLineWidth > textWidth) 
          textWidth = prevLineWidth; 
         textHeight += getFontSize()/viewHeight 
           * canvas.getHeight(); 
        } 
       } else if (i == lines.length - 1) { 
        labelList.add(newline); 
        if (newLineWidth > textWidth) 
         textWidth = newLineWidth; 
        textHeight += getFontSize()/viewHeight 
          * canvas.getHeight(); 
       } else { 
        prevLine = newline; 
       } 

      } 

      textHeight = Math.min(textHeight, canvas.getHeight() - mPoint.y 
        /viewHeight * canvas.getHeight()); 
      textWidth = Math.min(textWidth, canvas.getWidth() - mPoint.x 
        /viewWidth * canvas.getWidth()); 
      if (isEmpty()) { 
       textWidth = (getPadding() * 2 + getBorderWidth() * 2) 
         /viewWidth * canvas.getWidth(); 
      } 
      canvas.drawRect(mPoint.x/viewWidth * canvas.getWidth(), mPoint.y 
        /viewHeight * canvas.getHeight(), mPoint.x/viewWidth 
        * canvas.getWidth() + textWidth, mPoint.y/viewHeight 
        * canvas.getHeight() + textHeight, mBoxPaint); 
      mBorderPaint.setStrokeWidth(getBorderWidth()/viewWidth 
        * canvas.getWidth()); 
      canvas.drawRect(mPoint.x/viewWidth * canvas.getWidth(), mPoint.y 
        /viewHeight * canvas.getHeight(), mPoint.x/viewWidth 
        * canvas.getWidth() + textWidth, mPoint.y/viewHeight 
        * canvas.getHeight() + textHeight, mBorderPaint); 
      mBorderPaint.setStrokeWidth(getBorderWidth()); 
      float offset = (getBorderWidth() + getPadding() + getFontSize()) 
        /viewWidth * canvas.getWidth(); 
      mTextPaint.setTextSize(getFontSize()/viewHeight 
        * canvas.getHeight()); 
      for (String string : labelList) { 
       canvas.drawText(string, (mPoint.x + getBorderWidth()) 
         /viewWidth * canvas.getWidth(), mPoint.y/viewHeight 
         * canvas.getHeight() + offset, mTextPaint); 
       offset += (getFontSize() + getPadding())/viewHeight 
         * canvas.getHeight(); 
      } 
      mTextPaint.setTextSize(getFontSize()); 
     } 
    } 
Các vấn đề liên quan