Плавная прокрутка холста в Android

Я новичок в Android.

Я рисую растровые изображения, линии и фигуры на холсте внутри метода OnDraw (холст холста) своего представления. Мне нужна помощь в реализации плавной прокрутки в ответ на перетаскивание пользователем. Я искал, но не нашел никаких руководств, которые бы помогли мне в этом.

Ссылка на Canvas, кажется, говорит о том, что если Canvas создается из Bitmap (называемого, скажем, bmpBuffer), то все, что нарисовано на Canvas, также отображается в bmpBuffer. Можно ли было бы использовать bmpBuffer для реализации прокрутки ... возможно, скопировать его обратно на холст, сдвинувшись на несколько пикселей за раз? Но если я использую Canvas.drawBitmap для отрисовки bmpBuffer обратно на Canvas, сдвинутый на несколько пикселей, не будет ли поврежден bmpBuffer? Возможно, поэтому мне следует скопировать bmpBuffer в bmpBuffer2, а затем отрисовать bmpBuffer2 обратно на холст.

Более простой подход состоял бы в том, чтобы нарисовать линии, фигуры и т. Д. Прямо в буфере Bitmap, а затем нарисовать этот буфер (со сдвигом) на холсте, но насколько я могу видеть различные методы: drawLine (), drawShape () и т. д. недоступны для рисования в Bitmap ... только в Canvas.

Могу ли я иметь 2 холста? Один из которых будет построен из растрового изображения буфера и использоваться просто для построения линий, форм и т. Д., А затем растровое изображение буфера будет нарисовано на другом холсте для отображения в представлении?

Я приветствую любой совет!

Ответы на подобные вопросы здесь (и на других сайтах) относятся к «блиттингу». Я понимаю концепцию, но не могу найти ничего о "blit" или "bitblt" в документации Android. Являются ли Canvas.drawBitmap и Bitmap.Copy эквивалентами Android?


person prepbgg    schedule 16.01.2010    source источник
comment
Сегодня утром немного погуглил. Согласно этой веб-странице, markmail.org/message/oedvjxi3dhokzq23 у меня может быть второй холст, так что я Я исследую эту идею.   -  person prepbgg    schedule 17.01.2010
comment
См. Подробнее об этом в новом ответе ниже.   -  person prepbgg    schedule 27.03.2011


Ответы (5)


У меня тоже была эта пробема,

Рисовал я вот так:

Canvas BigCanvas = new Canvas();
Bitmap BigBitmap = new Bitmap(width,height);

int ScrollPosX , ScrollPosY  // (calculate these with the onScrollEvent handler)

void onCreate()
{
   BigCanvas.SetBitmap(BigBitmap);
}

onDraw(Canvas TargetCanvas)
{
   // do drawing stuff
   // ie.  BigCanvas.Draw.... line/bitmap/anything

   //draw to the screen with the scrolloffset

   //drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)
   TargetCanvas.DrawBitmap(BigBitmap(new Rect(ScrollPosX,ScrollPosY,ScrollPosX + BigBitmap.getWidth(),ScrollPosY + BigBitmap.getHeight(),new Rect(0,0,ScreenWidth,ScreenHeight),null);
}

для плавной прокрутки вам нужно будет создать какой-то метод, который занимает несколько точек после прокрутки (то есть первая точка прокрутки и 10-я), вычесть их и прокручивать по этому числу в каждом цикле, что делает его постепенно медленнее (ScrollAmount - повороты - трение).

Надеюсь, это даст вам больше информации.

person Mervin    schedule 18.06.2010
comment
Спасибо, Мервин, за ваш ответ. Я не уверен, что понимаю, как работает ваша прокрутка. Вы ждете, пока не будет получено 10 событий прокрутки, прежде чем начинать перемещать растровое изображение? Я предполагал, что для того, чтобы экран выглядел полностью отзывчивым, мне нужно будет перерисовать растровое изображение на холсте, как только будет получено первое событие прокрутки. И поскольку мой материал для рисования отнимает довольно много времени, я решил убрать это из метода onDraw, чтобы попытаться сделать программу максимально отзывчивой на прокрутку. - person prepbgg; 31.07.2010

Кажется, я нашел ответ. Я поместил основную часть кода рисования (который ранее был в onDraw ()) в новый метод doDrawing (). Этот метод начинается с создания нового растрового изображения большего размера, чем экран (достаточно большого, чтобы вместить весь рисунок). Затем он создает второй холст, на котором нужно выполнить подробный рисунок:

    BufferBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
    Canvas BufferCanvas = new Canvas(BufferBitmap);

Остальная часть метода doDrawing () занята подробным рисованием в BufferCanvas.

Теперь весь метод onDraw () выглядит следующим образом:

    @Override protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(BufferBitmap, (float) -posX, (float) -posY, null);
}

Переменные позиции posX и posY инициализируются значением 0 в методе приложения onCreate (). Приложение реализует OnGestureListener и использует аргументы distanceX и distanceY, возвращаемые в уведомлении OnScroll, для увеличения posX и posY.

Кажется, это все, что нужно для плавной прокрутки. Или я что-то не замечаю !?

person prepbgg    schedule 17.01.2010
comment
Одна вещь, которую я обнаружил с момента написания этого ответа, заключается в том, что требуется дополнительный код для повторного использования растрового изображения буфера (и любых других объектов растрового изображения) при повороте экрана. Это связано с тем, что при повороте экрана основное действие приложения завершается и перезапускается, но память, используемая объектами Bitmap, не восстанавливается системой автоматически. Таким образом, кажется важным вызывать метод recycle () для каждого растрового изображения, когда он больше не нужен. В случае растрового изображения буфера ответ, кажется, состоит в том, чтобы переопределить метод onDestroy Activity и вызвать в нем recycle () растрового изображения. - person prepbgg; 27.01.2010
comment
Этого кода можно избежать, просто поместив android:configChanges="orientation" в Manifest. См. Ответ Рибо на этот вопрос от 04.01.2011. - person prepbgg; 05.01.2011
comment
пожалуйста, помогите в этом вопросе stackoverflow.com/questions/11720702 / холст-частичное увеличение / - person kamal_tech_view; 30.07.2012
comment
Извините, kamal и WildBill, что медленно реагируем. Я недавно не просматривал эту ветку. Я рад, что ты решил свою проблему, Камал. WildBill, я сначала вызываю doDrawing () из onCreate () основного действия после того, как представление было создано; и я снова вызываю doDrawing () всякий раз, когда нужно изменить изображение в растровом изображении буфера. - person prepbgg; 17.05.2013

Продолжение ответа Виктору ...

На самом деле ситуация более сложная. Поскольку процесс doDrawing довольно медленный (занимает 2-3 секунды на моем старом медленном телефоне HTC Hero), я счел желательным вывести всплывающее сообщение, чтобы сообщить пользователю, что это происходит, и указать причину. Очевидным способом сделать это было создать новый метод, содержащий всего 2 строки:

public void redrawBuffer(String strReason) {
    Toaster.Toast(strReason, "Short");`
    doDrawing();
}

и вызывать этот метод из других мест в моей программе вместо doDrawing ().

Однако я обнаружил, что тостер либо никогда не появлялся, либо вспыхивал так ненадолго, что его нельзя было прочитать. Мое обходное решение заключалось в использовании обработчика проверки времени, чтобы заставить программу спать на 200 миллисекунд между отображением Toast и вызовом doDrawing (). Хотя это немного задерживает начало перерисовки, я считаю, что это цена, которую стоит заплатить с точки зрения удобства использования программы, потому что пользователь знает, что происходит. reDrawBuffer () теперь читает:

public void redrawBuffer(String strReason) {
    Toaster.Toast(strReason, "Short");
    mTimeCheckHandler.sleep(200);    
}`

а код обработчика (который вложен в мой класс View):

private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();

class timeCheckHandler extends Handler {
@Override
    public void handleMessage(Message msg) {
        doDrawing();
    }
    public void sleep(long delayMillis) {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
}`
person prepbgg    schedule 26.03.2011

Нет необходимости перезапускать деятельность! (За ответ от Prepbgg от 27 января 10 на его «ответ» от 17 января 10). Вместо того, чтобы повторно использовать растровое изображение и нести накладные расходы, связанные с перезагрузкой активности, вы можете избежать загрузки приложения, поместив атрибут android: configChanges, показанный ниже, в элементе activity файла AndroidManifest.xml для приложения. Это сообщает системе, что приложение будет обрабатывать изменения ориентации и что не нужно перезапускать приложение.

<activity android:name=".ANote"
    android:label="@string/app_name"
    android:configChanges="orientation|screenLayout">
    <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Этот метод можно использовать для получения уведомления при изменении ориентации:

public void onConfigurationChanged(Configuration  newConfig) {
    super.onConfigurationChanged(newConfig);
    prt("onConfigurationChanged: "+newConfig);

    if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
      prt("  PORTRAIT");
    } else {
      prt("  LANDSCAPE");
    }
} // end of onConfigurationChanged
person Ribo    schedule 04.01.2011
comment
Это очень интересно. (Фактически, я на довольно раннем этапе остановил приложение от изменения ориентации, поместив android: screenOrientation = portrait в манифест. Я сделал это, чтобы решить проблемы с памятью.) Однако я хотел бы обрабатывать вращения, если это возможно. Когда вы говорите об изменении ориентации обработки приложения, что мне нужно сделать для этого? Будет ли метод View onDraw по-прежнему вызываться автоматически? Или я просто, возможно, отвечаю на вращение, аннулируя представление? - person prepbgg; 05.01.2011
comment
Я только что заменил android:screenOrientation="portrait" в манифесте на android:configChanges="orientation", и приложение просто работает! (Нет необходимости иметь какой-либо код для проверки изменения ориентации ... экран перерисовывается без какого-либо вмешательства с моей стороны.) LogCat предполагает, что телефон тратит около 250 мс на сборку мусора при каждом изменении ориентации. Пока не вижу проблем. Большое спасибо за то, что пришли с этим предложением. - person prepbgg; 05.01.2011

Prepbgg: я не думаю, что код будет работать, потому что canvas.drawBitmap не рисует в растровом изображении, а рисует растровое изображение на холсте.

Поправьте меня, если я ошибаюсь!

person Gaurav Vaish    schedule 04.05.2010
comment
Спасибо, МастерГаурав. Я согласен с тем, что canvas.drawBitmap (BufferBitmap, ...) ничего не отображает в BufferBitmap. Однако у меня есть отдельный метод doDrawing (), в котором объекты (линии, растровые изображения и т. Д.) Рисуются в BufferCanvas и, следовательно, в BufferBitmap. Код действительно работает (хотя он иногда заикается и / или дает сбой ... Я не знаю, связано ли это с тем, что мой код рисования неисправен, или есть другие ошибки.) - person prepbgg; 31.07.2010