2014-12-23 2 views
0

В моем приложении у меня есть ImageView, который я превратил в Bitmap для редактирования. Мне нужно определить, какие пиксели в ImageView были затронуты пользователем. Кроме того, если пользователь рисует линию пальцем, мне нужно знать все пиксели, которые были затронуты, чтобы их изменить. Как определить, какие пиксели были затронуты?Как найти все пиксели, затронутые представлением изображения?

+0

Не могли бы вы подробнее рассказать о том, что вы подразумеваете под «необходимостью определять, какие пиксели на ImageView были затронуты пользователем»? Похоже, вы хотите создать какой-то инструмент для редактирования изображений, в котором пользователю разрешено «рисовать» путь пальцем по растровому изображению. Если это так, вы пытались просто зарегистрировать прикосновение и перетащить слушателей для своего вида и разобраться с координатами событий? – ulix

+0

Я начинающий программист, поэтому я действительно не знал, с чего начать. В моем приложении я показываю один рисунок, и когда пользователь прикасается к рис., Эти пиксели превращаются в соответствующие пиксели на другой рис. Это создает эффект смешивания. Я знаю, как смешивать фотографии, но не знаю, как заставить слушателей прикоснуться. –

ответ

2

Ok Jonah, вот несколько направлений для вас.
Я предполагаю, что вы хотите, чтобы эффект смешивания быстро реагировал на ввод пользователя, поэтому прежде всего вам лучше перейти на пользовательский SurfaceView вместо ImageView, потому что он более подходит для рисования анимаций с высокой частотой кадров, необходимых для 2D-игр и анимаций. Я настоятельно рекомендую вам прочитать this guide; уделяя особое внимание части об использовании SurfaceView, прежде чем идти дальше. Вам в основном нужно создать класс, который расширяет SurfaceView и реализует SurfaceHolder.Callback. Затем это представление будет отвечать за прослушивание событий пользовательского касания и визуализации кадров для анимации эффекта смешивания.
Взгляните на следующий код в качестве ссылки:

public class MainView extends SurfaceView implements SurfaceHolder.Callback { 
     public MainView(Context context, AttributeSet attrs) { 
      super(context, attrs); 
      SurfaceHolder holder = getHolder(); 
      holder.addCallback(this);  // Register this view as a surface change listener 
      setFocusable(true);    // make sure we get key events 
     } 

     @Override 
     public boolean onTouchEvent(MotionEvent event) { 
      super.onTouchEvent(event); 

      // Check if the touch pointer is the one you want 
      if (event.getPointerId(event.getActionIndex()) == 0) { 
       switch (event.getAction()) { 
       case MotionEvent.ACTION_DOWN: 
        // User touched screen... 
       case MotionEvent.ACTION_CANCEL: 
        // User dragged his finger out of the view bounds... 
       case MotionEvent.ACTION_UP: 
        // User raised his finger... 
       case MotionEvent.ACTION_MOVE: 
        // User dragged his finger... 

       // Update the blending effect bitmap here and trigger a frame redraw, 
       // if you don't already have an animation thread to do it for you. 

       return true; 
      } 

      return false; 
     } 

     /* 
     * Callback invoked when the Surface has been created and is ready to be 
     * used. 
     */ 
     public void surfaceCreated(SurfaceHolder holder) { 
      // You need to wait for this call back before attempting to draw 
     } 

     /* 
     * Callback invoked when the Surface has been destroyed and must no longer 
     * be touched. WARNING: after this method returns, the Surface/Canvas must 
     * never be touched again! 
     */ 
     public void surfaceDestroyed(SurfaceHolder holder) { 
      // You shouldn't draw to this surface after this method has been called 
     } 
    } 

Затем используйте его на макет вашего «рисунок» деятельности, как это:

<com.xxx.yyy.MainView 
     android:id="@+id/main_view" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

Чтобы привлечь к этой поверхности вам нужен следующий код:

 Canvas c = null; 

     try { 
      c = mSurfaceHolder.lockCanvas(null); 

      synchronized (mSurfaceHolder) { 
       if (c != null) 
        c.drawBitmap(blendingImage, 0, 0, null); // Render blending effect 
      } 
     } catch (Exception e) { 
      Log.e("SurfaceView", "Error drawing frame", e); 
     } finally { 
      // do this in a finally so that if an exception is thrown 
      // during the above, we don't leave the Surface in an 
      // inconsistent state 
      if (c != null) { 
       mSurfaceHolder.unlockCanvasAndPost(c); 
      } 
     } 

Полностью функциональный пример нецелесообразно вставлять в ответ, поэтому я рекомендую вам загрузить Lunar Lander sample game from Google для полного рабочего примера. Обратите внимание, однако, что вам не понадобится нить игровой анимации (хотя это не повредит ей), как и тот, который был закодирован в примере Lunar Lander, если вам нужен только эффект смешивания. Цель этого потока - создать игровой цикл, в котором игровые кадры постоянно генерируются для анимации объектов, которые могут или не могут зависеть от ввода пользователя. В вашем случае все, что вам нужно, это вызвать перерисовку кадров после обработки каждого события касания.

EDIT: Следующий код является исправлением, чтобы получить код, который вы предоставили в комментариях, работая.
Вот изменения в MainActivity:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    // put pics from drawables to Bitmaps 
    Resources res = getResources(); 
    BitmapDrawable bd1 = (BitmapDrawable) res.getDrawable(R.drawable.pic1); 

    // FIX: This block makes `operation` a mutable bitmap version of the loaded resource 
    // This is required because immutable bitmaps can't be changed 
    Bitmap tmp = bd1.getBitmap(); 
    operation = Bitmap.createBitmap(tmp.getWidth(), tmp.getHeight(), Bitmap.Config.ARGB_8888); 
    Canvas c = new Canvas(operation); 
    Paint paint = new Paint(); 
    c.drawBitmap(tmp, 0f, 0f, paint); 

    BitmapDrawable bd2 = (BitmapDrawable) res.getDrawable(R.drawable.pic2); 
    bmp = bd2.getBitmap(); 

    myView = new MainView(this, operation, bmp); 
    FrameLayout preview = (FrameLayout) findViewById(R.id.preview); 
    preview.addView(myView); 

} 
... 

Вот изменения в классе MainView:

public class MainView extends SurfaceView implements Callback { 

    private SurfaceHolder holder; 
    private Bitmap operation; 
    private Bitmap bmp2; 
    private boolean surfaceReady; 

    // took out AttributeSet attrs 
    public MainView(Context context, Bitmap operation, Bitmap bmp2) { 
     super(context); 

     this.operation = operation; 
     this.bmp2 = bmp2; 

     holder = getHolder();  // Fix: proper reference the instance variable 
     holder.addCallback(this); // Register this view as a surface change 
            // listener 
     setFocusable(true); // make sure we get key events 
    } 

    // Added so the blending operation is made in one place so it can be more easily upgraded 
    private void blend(int x, int y) { 
     if (x >= 0 && y >= 0 && x < bmp2.getWidth() && x < operation.getWidth() && y < bmp2.getHeight() && y < operation.getHeight()) 
      operation.setPixel(x, y, bmp2.getPixel(x, y)); 
    } 

    // Added so the drawing is now made in one place 
    private void drawOverlays() { 
     Canvas c = null; 
     try { 
      c = holder.lockCanvas(null); 
      synchronized (holder) { 
       if (c != null) 
        c.drawBitmap(operation, 0, 0, null); // Render blending 
                  // effect 
      } 
     } catch (Exception e) { 
      Log.e("SurfaceView", "Error drawing frame", e); 
     } finally { 
      // do this in a finally so that if an exception is thrown 
      // during the above, we don't leave the Surface in an 
      // inconsistent state 
      if (c != null) { 
       holder.unlockCanvasAndPost(c); 
      } 
     } 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     super.onTouchEvent(event); 

     if (!surfaceReady)  // No attempt to blend or draw while surface isn't ready 
      return false; 

     // Check if the touch pointer is the one you want 
     if (event.getPointerId(event.getActionIndex()) == 0) { 
      switch (event.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
       // User touched screen. Falls through ACTION_MOVE once there is no break 

      case MotionEvent.ACTION_MOVE: 
       // User dragged his finger... 
       blend((int) event.getX(), (int) event.getY()); 

      } 
      // Update the blending effect bitmap here and trigger a frame 
      // redraw, 
      // if you don't already have an animation thread to do it for you. 
      drawOverlays(); 
      return true; 
     } 

     return false; 
    } 

    /* 
    * Callback invoked when the Surface has been created and is ready to be 
    * used. 
    */ 
    public void surfaceCreated(SurfaceHolder holder) { 
     surfaceReady = true; 
     drawOverlays(); 
    } 

    /* 
    * Callback invoked when the Surface has been destroyed and must no longer 
    * be touched. WARNING: after this method returns, the Surface/Canvas must 
    * never be touched again! 
    */ 
    public void surfaceDestroyed(SurfaceHolder holder) { 
     // You shouldn't draw to this surface after this method has been called 
     surfaceReady = false; 
    } 

    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
      int height) { 
     // TODO Auto-generated method stub 
    } 

} 

Этот код работает для меня. Я просто надеюсь, что ничего не забыл =)
Сообщите мне, если у вас все еще есть проблемы, хорошо?

+0

Спасибо большое! Но как установить обновленное растровое изображение на Surface View? И что я вкладываю в OnActionUp? –

+0

Вы не «устанавливаете» растровое изображение, как если бы это был ImageView. Вы захватываете объект Canvas и рисуете растровое изображение на нем (как показано в последнем блоке кода). Теперь о событиях касания «MotionEvent.ACTION_UP» обычно важно сообщить вам, что «жест», начатый 'MotionEvent.ACTION_DOWN', закончился. Это может быть или не быть важно для вас. Скажем, например, у вас есть игра, в которой пользователь должен перетаскивать фигуры на доске. 'MotionEvent.ACTION_UP' - это событие, в котором говорится, что пользователь только что закончил перетаскивание, поэтому пришло время разместить кусок в ближайшей действующей доске. – ulix

+0

Я когда через все шаги, но приложение все еще имеет ошибки. Вот ссылки на мою главную учетную запись: https://docs.google.com/document/d/16KfcnpjVPUbHeM3VlfBX55FFbPFHJAPDeRqJl3r8Tyo/pub и MainView https://docs.google.com/document/d/1x15Mn-dl2BNx__EORl94e4XEwwLfRsciG7GUQP331YU/pub Что я делаю неправильно ? –

0

Так что ответ на этот вопрос заключается в том, что вам нужно быть немного умным, но это действительно не должно быть так плохо. Вместо того, чтобы размещать весь код, чтобы делать то, что вы хотите сделать, я дам вам ссылку here и объяснение.

Таким образом, управляя событиями касания приложения, вы можете определить средние координаты события касания. Используя эту информацию, вы можете определить центр всех затронутых пикселей и продолжить отслеживать это, когда линия рисуется пальцем. Для отслеживания прямой линии используйте

case MotionEvent.ACTION_DOWN: 
case MotionEvent.ACTION_UP: 

Оговорки, необходимые для определения начала и конца строки. Если вы хотите, чтобы отслеживать линию, которая не является прямым и нарисовано, вы будете нуждаться немного больше отслеживания с помощью

case MotionEvent.ACTION_MOVE: 

и что вы должны получить достаточно начать. Вам может понадобиться немного логики, чтобы справиться с тем, что вы будете рисовать очень тонкую линию, и я подозреваю, что это совсем не то, что вы собираетесь делать. Может быть, это правда. В любом случае это должно быть хорошим местом для начала работы.

EDIT

Что касается вашего первого комментария, here это ссылка, которую вы можете использовать для примера. Однако я должен сделать небольшой отказ от ответственности. Чтобы все части могли работать вместе правильно, сначала это может показаться простым. Уверяю вас, что это один из самых простых примеров и разбивает учебник на разделы.

За то, что вы хотели бы сделать, я думаю, что вы хотите, чтобы обратить особое внимание на раздел 2 (не следует путать с шагом 2):

не
2. Facilitate Drawing 

Я предлагаю это потому, что он показывает различные способы для использования информации из TouchEvent. Предметы, включенные в раздел 1, немного объяснят среду для настройки отображения захваченных данных TouchEvent, в то время как раздел 3 в основном касается эстетики. Это не может напрямую ответить на ваш вопрос, но я подозреваю, что он доставит вас туда, где вам нужно.

Счастливое кодирование! Оставьте комментарий, если у вас есть какие-либо вопросы.

+0

Можете ли вы отправить ссылку простого рабочего кода, чтобы я мог начать с места, так как я довольно потерян. Большое спасибо! –

Смежные вопросы