=================================
=================================
=================================
<meta http-equiv="content-type" content="text/html; charset=utf-8" /><meta http-equiv="content-type" content="text/html; charset=utf-8" /><meta http-equiv="content-type" content="text/html; charset=utf-8" />
로우레벨 터치 이벤트로 큰 이미지 스크롤
가벼운 터치 스크롤로 로우레벨 터치 이벤트 핸들러에 의해 진행되는
디스플레이 화면 보다 큰 이미지를 터치 이벤트를 이용해 디바이스 에서 스크롤 하는 방법을 배워보자.
안드로이드
플렛폼 2.0과 2.01으로 태스트 되었습니다..
What it looks like:
Description:
디스플레이 화면과 같은 크기의 사각형에 의해 이미지의 부분이 메모리에서 읽혀져와 화면에 그려지는걸 상상해 보자. 이미지 표면을 터치 해서 움직일려면 우리가 무엇을 해야 할가?
- 가장 먼저 큰 이미지를 메모리에 넣어야 한다.
- 디스플레이 화면과 같은 크기의 스크롤할 사각형을 준비 해야 한다.
- 터치 이벤트를 사용하여 스크롤 사각형을 준비한 이미지 위에 움직인다.
- 사각형으로 나누어진 이미지를 디스플레이 화면에 그려준다.
.아... 해석 하기 귀찬어......
0.) In Eclipse, create a new Android Project, targeting Android 2.0 (older versions may work too, but the folders may be slightly different from those shown here). For consistency with this tutorial you may wish to name your main activity LargeImageScroller, and make your package name com.example.largeimagescroller.
1.) Obtain an image resource:
The image resource I used is 1440x1080 - tested with the Droid handset - but you can use a smaller one if you want; it just has to be larger than the display size so you can scroll it. Be careful if you go too much larger, as you may run out of memory. If you are using a different device your memory requirements may vary, and you may need to adjust the size of the image accordingly.
(For testing purposes I tested this with a huge image – 3200x2300 – one I was sure would take up a lot of memory, just to make sure the scrolling was smooth, but this isn't something you'd normally want to do.)
Add the image resource (I've named mine testlargeimg.png) to your /drawable-hdpi folder (may also be named/drawable depending on which Android version you are using).
2.) For convenience, we will run our simple application fullscreen and in landscape mode. To do this modify the project's manifest:
Edit AndroidManifest.xml and add the following to the application tag:
-
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
While we are here you can also add the following if you wish to debug on an actual device (this also goes inside the application tag):
-
android:debuggable="true"
In the activity tag, set the screen orientation to landscape:
-
android:screenOrientation="landscape"
3.) Now we need an activity and a custom view:
We will create a custom view and add it to our activity so that we can handle our own drawing and touch events. The standard way of doing this is:
-
public class LargeImageScroller extends Activity {
-
-
/** Called when the activity is first created. */
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
-
setContentView(new SampleView(this));
-
}
-
-
private static class SampleView extends View {
-
-
public SampleView(Context context) {
-
super(context);
-
}
-
-
@Override
-
public boolean onTouchEvent(MotionEvent event) {
-
}
-
-
@Override
-
protected void onDraw(Canvas canvas) {
-
}
-
}
-
}
There are numerous examples of this type of custom view creation in the Android SDK if you'd like more information.
In onCreate() the only additional code we need is for getting our display width and height. We can use the system service getDefaultDisplay() API to do this:
-
Display display = ((WindowManager)
-
getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
-
-
displayWidth = display.getWidth();
-
displayHeight = display.getHeight();
You might think that getDefaultDisplay() will always return the same values for a given hardware device. Actually, the values will change depending on, for example, screen orientation. On my Droid in landscape mode I see a width of 854 and a height of 480, but in portrait mode these values are reversed.
If you have an application that needs to know when the display settings change, you can hook theonSizeChanged() API (see the Android docs for more). For our application, we are always in landscape mode so these values wont change after we retrieve them.
That's it for the activity. Everything else happens in our custom view.
4.) Set up our SampleView:
Constructor:
In our SampleView constructor we handle the bitmap loading, the scroll rectangle setup and the rectangle that defines how large an area we draw onto (our display rectangle) – in our case, the whole screen.
-
displayRect = new Rect(0, 0, displayWidth, displayHeight);
-
scrollRect = new Rect(0, 0, displayWidth, displayHeight);
-
-
bmLargeImage = BitmapFactory.decodeResource(getResources(),
-
R.drawable.testlargeimg);
We have initialized our scroll rectangle to be exactly the same coordinates as the display rectangle. When we first run our application this means the upper-leftmost portion of our image will be visible.
The bitmap loader code is one of many standard ways to load Android bitmap resources.
Touch event handler:
Our touch event handler is where we process our touch gestures. A gesture is broken into a series of actions, the most common of which are down, move and up, though there are others (see the Android docs for MotionEventfor a complete reference). Information about an event is contained in MotionEvent. For our application we only care about down and move, as you will see.
-
@Override
-
public boolean onTouchEvent(MotionEvent event) {
-
-
switch (event.getAction()) {
-
case MotionEvent.ACTION_DOWN:
-
startX = event.getRawX();
-
startY = event.getRawY();
-
break;
-
-
case MotionEvent.ACTION_MOVE:
-
float x = event.getRawX();
-
float y = event.getRawY();
-
scrollByX = x - startX;
-
scrollByY = y - startY;
-
startX = x;
-
startY = y;
-
invalidate();
-
break;
-
}
-
return true;
-
}
-
}
화 면을 처음에 터치 했을경우 ACTION_DOWN이벤트가 생성 된다. 그후 여러분이 손가락을 움직이면 ACTION_MOVE이벤트가 줄줄이 생성된다. (ACTION_MOVE이벤트는 안드로이드 OS의 통제 하에 일정시간안에 통제된 횟수로 일어난다.)
ACTION_DOWN이벤트와 ACTION_MOVE이벤트가 생성될때 getRawX() getRawY()메소드를 이용하여 터치가 일어난 좌표를 얻어올 수 있다.
결과 우리는 손가락이 어딜 지나 갔는지 알아낼수 있다.
위의 코드에서 ACTION_DOWN이벤트가 발생할시 startX와 startY에 그 좌표를 저장 하게 하였다.
getRawX()와 getRawY()는 항상 절대적인 화면의 좌표를 리턴한다.여러분이 잘 알고 있는 getX()와 getY()메소드는 사용하는 이벤트에 따라 절대적이기 도하고 상대적 이기도 하다.
작은 움직임은을 만들어 ACTION_MOVE이벤트를 호출한후 우리가만든 스크롤 상자에 그 움직임을 적용 하자.
아마 이벤트가 매초 빈번히 발생해서 부드러운 스크롤을 보여줄것이다.
scrollByX 와 scrollByY 에 스크롤 해서 움직인 거리를 저장 하여 뷰를 이동 시켜 변경한다. startX 와 startY 역시 마지막 좌표로 변경 한다.이렇게 해서 움직임을 추적 할 수 있다.
Given that the ACTION_MOVE event may get generated many times, it is best to keep the code that executes from within the event handler to a minimum. This is true for any event handler.
Invalidate() 메소드는 android OS에서 onDraw()메소드를 호출 하게 만든다..
결과적으로 Invalidate()메소드를 호출 하면 화면을 다시 그려준다.
return 값을 true 로 놓아야 ACTION_DOWN이벤트 외에 다른 이벤트들을 감지 할 수 있다.
Draw updater:
Our draw handler, onDraw(), is responsible for calculating the updated scroll rectangle coordinates and drawing the area of the bitmap within this newly updated rectangle.
왼쪽으로 선가락을 밀때 비트맵 이미지를 왼쪽으로 풀링 한다 생각 할 수 있다.사각 상자를 스크롤 하여 이미지의 오른쪽으로 움직이면 정확히 같은 효과를 표현 할 수 있다. So in our ACTION_MOVE event handler, if we calculate a move update that indicates that we are moving to the left, we need to update the scroll rectangle to move to the right. This means we need to add the negative sense of the move update to our current scroll rectangle coordinates:
-
int newScrollRectX = scrollRectX - (int)scrollByX;
-
int newScrollRectY = scrollRectY - (int)scrollByY;
We need to do one more thing before we are ready to draw. We must check our coordinates to make sure no part of our scroll rectangle will be off the bitmap:
-
// Don't scroll off the left or right edges of the bitmap.
-
if (newScrollRectX < 0)
-
newScrollRectX = 0;
-
else if (newScrollRectX > (bmLargeImage.getWidth() - displayWidth))
-
newScrollRectX = (bmLargeImage.getWidth() - displayWidth);
-
-
// Don't scroll off the top or bottom edges of the bitmap.
-
if (newScrollRectY < 0)
-
newScrollRectY = 0;
-
else if (newScrollRectY > (bmLargeImage.getHeight() - displayHeight))
-
newScrollRectY = (bmLargeImage.getHeight() - displayHeight);
The checks against 0 are straightforward: since our left (or top) coordinate is 0 for both the scroll rectangle and the bitmap, this is simply a check to make sure our scroll rectangle x (or y) coordinate is not to the left (or top) of the left (or top) edge of our bitmap.
To understand the check against the right edge, it is helpful to look at the following diagram:
Since we perform our scroll rectangle x coordinate check using the left x coordinate, then in order to perform a check that uses the right edge of the scroll rectangle, we have to take the scroll rectangle's width into account. This will allow us to check the right edge of the scroll rectangle against the right edge of the bitmap. In the example above, we have a bitmap width of 8 and a scroll rectangle width of 3, so we would have a left x coordinate of 5 for our scroll rectangle (= 8-3). y behaves similarly. In our case the scroll rectangle width is also our display width so this is how we end up with (bitmap width – display width) in the code fragment above. The same applies to the height variables.
The hard part is done. Set the newly calculated coordinates into our scroll rectangle, and draw it:
-
scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX + displayWidth,
-
newScrollRectY + displayHeight);
-
Paint paint = new Paint();
-
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
The last detail in our draw handler is to update the original scroll coordinates with the new ones, so we can start over with the same process as we continue to update our drawing in response to user move gestures:
-
scrollRectX = newScrollRectX;
-
scrollRectY = newScrollRectY;
5.) To build and run, you will need to add the variable declarations; see the full source at the end. When you run the example you should be able to smoothly scroll around your image.
One final note:
We never create a new bitmap, once the original is loaded into memory. Continuously creating bitmaps on the fly will kill your performance. In particular, avoid creating bitmaps in response to ACTION_MOVE events. We simply redraw the correct portion of the already loaded bitmap, which is defined by the scroll rectangle's coordinates.
Takeaways:
- The implementation described here is for simple scrolling needs, and for use with images that will fit into memory.
- ACTION_DOWN and ACTION_MOVE events can be used to calculate scroll move updates; you will get several-to-many ACTION_MOVE events for one move gesture.
- To avoid poor performance, try to keep the code that executes from within the event handlers to a minimum.
- To avoid poor performance, don't create bitmaps on the fly (in this tutorial we only create one bitmap on startup).
- If you need to handle different kinds of gestures (fling, long-press, double-tap, etc.) consider an alternative such as GestureDetector.
The Full Source:
You'll need a single large image as described in step 1, recommended size is 1440x1080, though if your device is other than a Droid, your mileage may vary.
You will also need to edit your AndroidManifest.xml as described in step 2.
"/src/your_package_structure/LargeImageScroller.java"
-
package com.example.largeimagescroller;
-
-
import android.app.Activity;
-
import android.os.Bundle;
-
import android.content.Context;
-
import android.graphics.Bitmap;
-
import android.graphics.BitmapFactory;
-
import android.graphics.Canvas;
-
import android.graphics.Paint;
-
import android.graphics.Rect;
-
import android.view.Display;
-
import android.view.MotionEvent;
-
import android.view.View;
-
import android.view.WindowManager;
-
-
public class LargeImageScroller extends Activity {
-
-
// Physical display width and height.
-
private static int displayWidth = 0;
-
private static int displayHeight = 0;
-
-
/** Called when the activity is first created. */
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
- // displayWidth and displayHeight will change depending on screen
-
// orientation. To get these dynamically, we should hook onSizeChanged().
-
// This simple example uses only landscape mode, so it's ok to get them
-
// once on startup and use those values throughout.
-
Display display = ((WindowManager)
-
getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
-
displayWidth = display.getWidth();
-
displayHeight = display.getHeight();
-
-
// SampleView constructor must be constructed last as it needs the
-
// displayWidth and displayHeight we just got.
-
setContentView(new SampleView(this));
-
}
-
-
private static class SampleView extends View {
-
private static Bitmap bmLargeImage; //bitmap large enough to be scrolled
-
private static Rect displayRect = null; //rect we display to
-
private Rect scrollRect = null; //rect we scroll over our bitmap with
-
private int scrollRectX = 0; //current left location of scroll rect
-
private int scrollRectY = 0; //current top location of scroll rect
-
private float scrollByX = 0; //x amount to scroll by
-
private float scrollByY = 0; //y amount to scroll by
-
private float startX = 0; //track x from one ACTION_MOVE to the next
-
private float startY = 0; //track y from one ACTION_MOVE to the next
-
-
public SampleView(Context context) {
-
super(context);
-
-
// Destination rect for our main canvas draw. It never changes.
-
displayRect = new Rect(0, 0, displayWidth, displayHeight);
-
// Scroll rect: this will be used to 'scroll around' over the
-
// bitmap in memory. Initialize as above.
-
scrollRect = new Rect(0, 0, displayWidth, displayHeight);
-
-
// Load a large bitmap into an offscreen area of memory.
-
bmLargeImage = BitmapFactory.decodeResource(getResources(),
-
R.drawable.testlargeimg);
-
}
-
-
@Override
-
public boolean onTouchEvent(MotionEvent event) {
-
-
switch (event.getAction()) {
-
case MotionEvent.ACTION_DOWN:
-
// Remember our initial down event location.
-
startX = event.getRawX();
-
startY = event.getRawY();
-
break;
-
-
case MotionEvent.ACTION_MOVE:
-
float x = event.getRawX();
-
float y = event.getRawY();
-
// Calculate move update. This will happen many times
-
// during the course of a single movement gesture.
-
scrollByX = x - startX; //move update x increment
-
scrollByY = y - startY; //move update y increment
-
startX = x; //reset initial values to latest
-
startY = y;
-
invalidate(); //force a redraw
-
break;
-
}
-
return true; //done with this event so consume it
-
}
-
-
@Override
-
protected void onDraw(Canvas canvas) {
-
-
// Our move updates are calculated in ACTION_MOVE in the opposite direction
-
// from how we want to move the scroll rect. Think of this as dragging to
-
// the left being the same as sliding the scroll rect to the right.
-
int newScrollRectX = scrollRectX - (int)scrollByX;
-
int newScrollRectY = scrollRectY - (int)scrollByY;
-
-
// Don't scroll off the left or right edges of the bitmap.
-
if (newScrollRectX < 0)
-
newScrollRectX = 0;
-
else if (newScrollRectX > (bmLargeImage.getWidth() - displayWidth))
-
newScrollRectX = (bmLargeImage.getWidth() - displayWidth);
-
-
// Don't scroll off the top or bottom edges of the bitmap.
-
if (newScrollRectY < 0)
-
newScrollRectY = 0;
-
else if (newScrollRectY > (bmLargeImage.getHeight() - displayHeight))
-
newScrollRectY = (bmLargeImage.getHeight() - displayHeight);
-
-
// We have our updated scroll rect coordinates, set them and draw.
-
scrollRect.set(newScrollRectX, newScrollRectY,
-
newScrollRectX + displayWidth, newScrollRectY + displayHeight);
-
Paint paint = new Paint();
-
canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
-
-
// Reset current scroll coordinates to reflect the latest updates,
-
// so we can repeat this update process.
-
scrollRectX = newScrollRectX;
-
scrollRectY = newScrollRectY;
-
}
-
}
-
}
Hope this helps!
XCaf
=================================
=================================
=================================
'스마트기기개발관련 > 안드로이드 개발' 카테고리의 다른 글
안드로이드 소켓통신 한글 깨짐현상 관련 (1) | 2011.07.20 |
---|---|
안드로이드 [ Android ] Dialog 위치 이동 (0) | 2011.07.19 |
안드로이드 Making Sense of Multitouch (0) | 2011.07.18 |
안드로이드 Android ScrollView ( H, V, 대각 ) 스크롤 구현 (0) | 2011.07.14 |
안드로이드 matrix 크기 얻기 (0) | 2011.07.14 |