2013-06-09 9 views
5

मुझे यह कोड उन प्रश्नों में से एक में मिला है जो एंड्रॉइड में आकर्षित करने के लिए कह रहे थे, लेकिन फिर इसका उपयोग करते समय और मेरे ऐप में इसका परीक्षण करते समय, मुझे पता चला कि यह कुशल नहीं है बड़ी चीजें या कई पथ ड्राइंग। समस्या onDraw के अंदर कोड से आती है क्योंकि प्रत्येक बार invalidate() को onDraw कहा जाता है, जिसमें एक लूप होता है जो paths को canvas पर फिर से खींचता है, और इसमें अधिक पथ जोड़कर, यह बहुत धीमा हो जाता है।एंड्रॉइड ड्रॉइंग व्यू बहुत धीमा है

यहाँ क्लास है:

public class DrawingView extends View implements OnTouchListener { 
private Canvas m_Canvas; 

private Path m_Path; 

private Paint m_Paint; 

ArrayList<Pair<Path, Paint>> paths = new ArrayList<Pair<Path, Paint>>(); 

ArrayList<Pair<Path, Paint>> undonePaths = new ArrayList<Pair<Path, Paint>>(); 

private float mX, mY; 

private static final float TOUCH_TOLERANCE = 4; 

public static boolean isEraserActive = false; 

private int color = Color.BLACK; 
private int stroke = 6; 

public DrawingView(Context context, AttributeSet attr) { 
    super(context); 
    setFocusable(true); 
    setFocusableInTouchMode(true); 

    setBackgroundColor(Color.WHITE); 

    this.setOnTouchListener(this); 

    onCanvasInitialization(); 
} 

public void onCanvasInitialization() { 
    m_Paint = new Paint(); 
    m_Paint.setAntiAlias(true); 
    m_Paint.setDither(true); 
    m_Paint.setColor(Color.parseColor("#000000")); 
    m_Paint.setStyle(Paint.Style.STROKE); 
    m_Paint.setStrokeJoin(Paint.Join.ROUND); 
    m_Paint.setStrokeCap(Paint.Cap.ROUND); 
    m_Paint.setStrokeWidth(2); 

    m_Canvas = new Canvas(); 

    m_Path = new Path(); 
    Paint newPaint = new Paint(m_Paint); 
    paths.add(new Pair<Path, Paint>(m_Path, newPaint)); 
} 

@Override 
public void setBackground(Drawable background) { 
    mBackground = background; 
    super.setBackground(background); 
} 

@Override 
protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
    super.onSizeChanged(w, h, oldw, oldh); 
} 

public boolean onTouch(View arg0, MotionEvent event) { 
    float x = event.getX(); 
    float y = event.getY(); 

    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
     touch_start(x, y); 
     invalidate(); 
     break; 
    case MotionEvent.ACTION_MOVE: 
     touch_move(x, y); 
     invalidate(); 
     break; 
    case MotionEvent.ACTION_UP: 
     touch_up(); 
     invalidate(); 
     break; 
    } 
    return true; 
} 

@Override 
protected void onDraw(Canvas canvas) { 
    for (Pair<Path, Paint> p : paths) { 
     canvas.drawPath(p.first, p.second); 
    } 
} 

private void touch_start(float x, float y) { 

    if (isEraserActive) { 
     m_Paint.setColor(Color.WHITE); 
     m_Paint.setStrokeWidth(50); 
     Paint newPaint = new Paint(m_Paint); // Clones the mPaint object 
     paths.add(new Pair<Path, Paint>(m_Path, newPaint)); 
    } else { 
     m_Paint.setColor(color); 
     m_Paint.setStrokeWidth(stroke); 
     Paint newPaint = new Paint(m_Paint); // Clones the mPaint object 
     paths.add(new Pair<Path, Paint>(m_Path, newPaint)); 
    } 

    m_Path.reset(); 
    m_Path.moveTo(x, y); 
    mX = x; 
    mY = y; 
} 

private void touch_move(float x, float y) { 
    float dx = Math.abs(x - mX); 
    float dy = Math.abs(y - mY); 
    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { 
     m_Path.quadTo(mX, mY, (x + mX)/2, (y + mY)/2); 
     mX = x; 
     mY = y; 
    } 
} 

private void touch_up() { 
    m_Path.lineTo(mX, mY); 

    // commit the path to our offscreen 
    m_Canvas.drawPath(m_Path, m_Paint); 

    // kill this so we don't double draw 
    m_Path = new Path(); 
    Paint newPaint = new Paint(m_Paint); // Clones the mPaint object 
    paths.add(new Pair<Path, Paint>(m_Path, newPaint)); 
} 

public void onClickUndo() { 
    if (!paths.isEmpty()) {//paths.size() > 0) { 
     undonePaths.add(paths.remove(paths.size() - 1)); 
     undo = true; 
     invalidate(); 
    } 
} 

public void onClickRedo() { 
    if (!undonePaths.isEmpty()){//undonePaths.size() > 0) { 
     paths.add(undonePaths.remove(undonePaths.size() - 1)); 
     undo = true; 
     invalidate(); 
    } 
}} 

लेकिन मैं इंटरनेट पर खोज की फिर से बैठक के लिए एक बेहतर तरीका खोजने के लिए, तो मैं निम्नलिखित पाया:

1 निर्माता के लिए निम्न जोड़ें:

protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
    super.onSizeChanged(w, h, oldw, oldh); 
    mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); 
    m_Canvas = new Canvas(mBitmap); 
} 
01:
mBitmapPaint = new Paint(Paint.DITHER_FLAG); 

2 अवहेलना निम्न कोड के साथ onSizeChanged

3 OnDraw में डाल इस:

protected void onDraw(Canvas canvas) { 
    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 
    if (!paths.isEmpty()) 
     canvas.drawPath(paths.get(paths.size() - 1).first, paths.get(paths.size() - 1).second); 
} 

यह दृष्टिकोण काम करता है और यह दृश्य को धीमा नहीं करता है, लेकिन इस दृष्टिकोण के साथ समस्या यह है कि मैं पूर्ववत नहीं हो सकता है और कार्यक्षमताओं फिर से करना है।

मैंने दूसरे दृष्टिकोण के साथ पूर्ववत करने और फिर से करने के लिए कई चीजों की कोशिश की, लेकिन मैं इसे नहीं कर सका। तो मैं यहां जो कुछ पूछ रहा हूं वह तीन चीजों में से एक है: 1. दूसरे दृष्टिकोण के साथ पूर्ववत करने और फिर से करने का एक तरीका 2. एक अन्य दृष्टिकोण जो पूर्ववत करना और को फिर से करना संभव बनाता है 3. एक पूरी नई कक्षा जिसमें सब कुछ पहले से ही किया गया है, एक ओपन सोर्स लाइब्रेरी या कुछ की तरह।

यदि आप कर सकते हैं तो कृपया मदद करें। धन्यवाद

संपादित करें 1

ठीक है, तो मैं इसे इस तक ही सीमित नीचे और फिर मैं कुछ भी अधिक, मैं अब 8 घंटे से अधिक के लिए कोशिश कर रहे हैं नहीं कर सका। यह पूर्ववत होने तक काम करता है (आप जितना चाहें उतने पथ को पूर्ववत कर सकते हैं), फिर फिर से ड्राइंग करते समय सभी शेष पथ गायब हो जाते हैं, मुझे नहीं पता कि यह क्या करता है।

@Override 
protected void onDraw(Canvas canvas) { 
    if (mBitmap != null) 
     canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 
    if (!paths.isEmpty() && !undo) 
     canvas.drawPath(paths.get(paths.size() - 1).first, paths.get(paths.size() - 1).second); 

    if (undo) { 
     setBackground(mBackground); 
     for (Pair<Path, Paint> p : paths) 
      canvas.drawPath(p.first, p.second); 

     mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); 
     m_Canvas = new Canvas(mBitmap); 

     undo = false; 
    } 
} 

तो बुनियादी तौर पर मैं क्या किया पहली बार में पहली दृष्टिकोण का उपयोग (पहले पूर्ववत कहा जाता है), तो अगर पूर्ववत क्लिक किया जाता है, undotrue पर सेट है और if (undo) तहत कोड निष्पादित किया जाता है, जो वास्तव में पहले दृष्टिकोण है (फिर से सभी पथों की गणना), फिर मैं सभी पथों को फिर से mBitmap में गणना करने का परिणाम खींचता हूं, इसलिए जब भी onDraw को फिर से बुलाया जाता है, तो यह उस पर सबसे ऊपर खींचता है, लेकिन उस भाग को अभी भी काम करने की ज़रूरत है, मुझे उम्मीद है कि कोई उस भाग में मदद कर सकता है।

+0

मुझे यह कहने में डर है कि यदि आप इतने सारे पथ खींचते हैं तो आपका विचार धीमा हो जाएगा। आपको एक ऐसी विधि खोजने का प्रयास करना चाहिए जिसमें एक बहुत लंबा रास्ता खींचना शामिल हो। – jcw

+0

यदि आप हमेशा पुराने लोगों के शीर्ष पर नए पथ खींचते हैं तो आप बिटकमैप में अंतिम एन पथ को छोड़कर सभी विधियों को जोड़ सकते हैं और पिछले विधि के साथ अंतिम एन खींच सकते हैं। फिर आप इन अंतिम एन को कम से कम –

उत्तर

0

मुझे यकीन नहीं है कि यह पूर्ववत और फिर से करने का सबसे अच्छा तरीका है या नहीं। हालांकि नीचे मेरे डिवाइस (सैमसंग गैलेक्सी एस 3) पर काम किया। ड्रा तेजी से प्रतीत होता है और पूर्ववत ठीक काम करता है। मुझे लगता है कि प्रदर्शन को और बढ़ाने के लिए नीचे संशोधित किया जा सकता है।

public class MainActivity extends Activity { 
MyView mv; 
LinearLayout ll; 
private ArrayList<Path> undonePaths = new ArrayList<Path>(); 
private ArrayList<Path> paths = new ArrayList<Path>(); 
Button b; 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     mv= new MyView(this); 
      mv.setDrawingCacheEnabled(true); 
      ll= (LinearLayout) findViewById(R.id.ll); 
      ll.addView(mv); 
      b= (Button) findViewById(R.id.button1); 
      b.setOnClickListener(new OnClickListener() 
      { 
       @Override 
       public void onClick(View v) { 
        // TODO Auto-generated method stub 
        if (paths.size() > 0) { 
         undonePaths.add(paths 
           .remove(paths.size()-2)); 
         mv.invalidate(); 
        } 
       } 

      }); 

    } 
    public class MyView extends View implements OnTouchListener { 

      private Canvas mCanvas; 
      private Path mPath; 
      private Paint mPaint; 

      // private ArrayList<Path> undonePaths = new ArrayList<Path>(); 
      private float xleft, xright, xtop, xbottom; 

      public MyView(Context context) { 
       super(context); 
       setFocusable(true); 
       setFocusableInTouchMode(true); 
       this.setOnTouchListener(this); 
       mPaint = new Paint(); 
       mPaint.setAntiAlias(true); 
       mPaint.setColor(Color.RED); 
       mPaint.setStyle(Paint.Style.STROKE); 
       mPaint.setStrokeJoin(Paint.Join.ROUND); 
       mPaint.setStrokeCap(Paint.Cap.ROUND); 
       mPaint.setStrokeWidth(6); 
       mCanvas = new Canvas(); 
       mPath = new Path(); 
       paths.add(mPath); 
      } 

      @Override 
      protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
       super.onSizeChanged(w, h, oldw, oldh); 
      } 

      @Override 
      protected void onDraw(Canvas canvas) { 
       for (Path p : paths) { 
        canvas.drawPath(p, mPaint); 
       } 
      } 

      private float mX, mY; 
      private static final float TOUCH_TOLERANCE = 0; 

      private void touch_start(float x, float y) { 
       mPath.reset(); 
       mPath.moveTo(x, y); 
       mX = x; 
       mY = y; 
      } 

      private void touch_move(float x, float y) { 
       float dx = Math.abs(x - mX); 
       float dy = Math.abs(y - mY); 
       if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { 
        mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2); 
        mX = x; 
        mY = y; 
       } 
      } 

      private void touch_up() { 
       mPath.lineTo(mX, mY); 
       // commit the path to our offscreen 
       mCanvas.drawPath(mPath, mPaint); 
       // kill this so we don't double draw 
       mPath = new Path(); 
       paths.add(mPath); 
      } 

      @Override 
      public boolean onTouch(View arg0, MotionEvent event) { 
       float x = event.getX(); 
       float y = event.getY(); 

       switch (event.getAction()) { 
       case MotionEvent.ACTION_DOWN: 

        touch_start(x, y); 
        invalidate(); 
        break; 
       case MotionEvent.ACTION_MOVE: 
        touch_move(x, y); 
        invalidate(); 
        break; 
       case MotionEvent.ACTION_UP: 
        touch_up(); 
        invalidate(); 
        break; 
       } 
       return true; 
      } 
     } 
    } 

गतिविधि_माइन।xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 

    <LinearLayout 
     android:id="@+id/ll" 
     android:layout_width="match_parent" 
     android:layout_height="fill_parent" 
     android:layout_weight="1" 
     android:orientation="vertical" > 

</LinearLayout> 

<Button 
    android:id="@+id/button1" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_gravity="center" 
    android:text="Undo" /> 

</LinearLayout> 
+0

पर हटा सकते हैं यह अभी भी मेरे प्रश्न में वर्णित एक ही प्रदर्शन समस्या है। लेकिन वैसे भी धन्यवाद। –

+0

@AmjadAbuSaa आप किस डिवाइस पर परीक्षण करते हैं? – Raghunandan

+0

एस 3, नोट 10.1, और एम्यूलेटर एस 3 सबसे तेज़ है जैसा आपने कहा था, लेकिन फिर भी हमें ऐप्स बनाने के दौरान धीमे उपकरणों पर विचार करना चाहिए, है ना? –

5

ऐसे मामले को संभालने का तरीका एक बिटमैप है जिसमें दृश्य का आकार है। टच इवेंट्स पर, बिटमैप के कैनवास में खींचे। ऑन ड्रा में, बस 0m पर कैनवास में बिटमैप खींचें। पूर्ववत/फिर से करने के लिए। आप बिटमैप मिटा सकते हैं और सभी पथों को फिर से खींच सकते हैं। यह थोड़ा अधिक समय लेता है, लेकिन यह केवल एक बार पूर्ववत/फिर से होता है। यदि उपयोगकर्ता आमतौर पर एक पूर्ववत/फिर से करते हैं। आप केवल एक कदम पीछे के लिए एक और बिटमैप करके अनुकूलित कर सकते हैं।

+0

क्या आप संपादित कर सकते हैं, कृपया? आपके उत्तर –

+0

संख्या के लिए धन्यवाद, अभी भी कोई समस्या है, कृपया इसे देखें –

+0

कुछ चीजें: पूर्ववत करें, आप बिटमैप साफ़ करें, इसलिए अगली ऑन ड्रा के पास एक खाली बिटमैप होगा। मुझे यकीन नहीं है कि अगर आप बिटमैप में पूर्ववत में सभी पथ खींचते हैं। इसे उस स्थान पर करें जहां आप पूर्ववत क्लिक को संभालते हैं, इस तरह आपको ड्रॉ में पूर्ववत करने के बारे में चिंतित होने की आवश्यकता नहीं है। ऑप्टिमाइज़ करने के लिए एक और चीज, जैसा कि अन्य ने सुझाव दिया है, कई छोटे लोगों के बजाय एक लंबा रास्ता उपयोग करना है। – yoah

1

ठीक है, यहाँ है कि मैं क्या अंत में साथ आया था, समस्या यह है कि मैं पूर्ववत पर बिटमैप, जो रास्तों की हानि हो बनाने से पहले कैनवास के लिए पथ आकर्षित OnDraw के बाद पूर्ववत था:

@Override 
    protected void onDraw(Canvas canvas) { 
     if (mBitmap != null) 
      canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 
     if (!paths.isEmpty()) { 
      canvas.drawPath(paths.get(paths.size() - 1).first, paths.get(paths.size() - 1).second); 
     } 
    } 

    public void onClickUndo() { 
     if (paths.size() >= 2) { 
      undonePaths.add(paths.remove(paths.size() - 2)); 
      mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 
      m_Canvas = new Canvas(mBitmap); 

      for (Pair<Path, Paint> p : paths) 
       m_Canvas.drawPath(p.first, p.second); 
      invalidate(); 
     } 
    } 

    public void onClickRedo() { 
     if (undonePaths.size() >= 2){ 
      paths.add(undonePaths.remove(undonePaths.size() - 2)); 
      mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 
      m_Canvas = new Canvas(mBitmap); 

      for (Pair<Path, Paint> p : paths) 
       m_Canvas.drawPath(p.first, p.second); 
      invalidate(); 
     } 
    } 

बार-बार सभी पथों को चित्रित करना अभी भी वहां है लेकिन onDraw() में नहीं है, जो बहुत अधिक ड्राइंग के प्रदर्शन में सुधार करता है। लेकिन उपयोगकर्ता को onClickUndo() और onClickRedo() में थोड़ी देर देरी हो सकती है यदि उसने बहुत सारे पथ खींचे हैं, जहां वहां स्क्रैच से पथ फिर से खींचे जा रहे हैं, लेकिन प्रति क्लिक केवल एक बार।

संबंधित मुद्दे