=================================
=================================
=================================
작성: 202psj.tistory.com
스크롤 뷰 컨트롤 작성시 터치부분 스크롤 주의 사항
이 두 api 차이가 있으므로 뷰어에 따른 상대좌표 절대좌표 표시가 다르므로 꼭 구분해서 사용하자.
터치 스크롤 뷰에는 event.getRawX(); 를 많이 사용하는 편이다. 또한 특정뷰어에 대한 좌표를 구할때
event.getX(); 또 사용해서 구하는게 또 유리하게 작용할때두 있다.
event.getRawX();
event.getX();
밑내용 발췌
getRawX() and getRawY(): 화면의 절대 좌표를 제공한다. 화면 좌표를 알 수 있는 getX(), getY() 는 사용하는 방법에 따라 절대 좌표나 상대좌표를 제공한다. 주의해야 한다.
//----------------------------------------------------------------------
출처: http://gtko.tistory.com/205
Large Image Scrolling Using Low Level Touch Events
from:http://www.anddev.org/large_image_scrolling_using_low_level_touch_events-t11182.html
Description:
메모리에 적재된 이미지의 일정 부분이 윈도우에 보이는 것을 상상해 보자. 이 위도우는 디스플레이와 동일한 크기이다. 이 윈도우에 있는 이미지의 표면에 터치 이벤트가 발생하는 것을 어떻게 사용할 수 있을까? 이를 수행하기 위해서는:
- 커다란 이미지를 메모리에 적재한다.
- 우리 디스플레이와 동일한 크기의 스크롤 가능한 rectangle을 설정한다.
- touch event로 이 스크롤 rectangle을 움직이자.
- 이 scroll rectangle 사이의 이미지 부분을 그린다.
메모리 보다 커다란 이미지를 보이게 하려면 어떻게 하나?
여기서 이야기하는 이미지는 메모리에 적재 가능한 크기이다. 만약 메모리 보다 커다란 이미지를 적재하고자 하면 streaming/cache (웹 같은 곳에서) 솔루션이나 compression/decompressiont (local file)을 적용해야 한다.
GestureDetector 와 GestureBuilder 가 무엇인지?
GestureDetector는 fling, long-press or double-tap 같은 동작을 처리하기 좋다. GestureDetector를 사용하기 어려운 동작이나 제공하지 않는 동작은 GestureBuilder로 구성할 수 있다.
구현
0) 새로운 안드로이드 프로젝트를
android 2.0 이상
main activity LargeImageScroller
package name com.example.largeimagescroller
1) 이미지 확보
1440x1080 크기의 이미지.
* 너무 큰 이미지를 사용하면 ran out of memory 를 만날 수 있다.
이 이미지를 testlargeimg.png 이란 이름으로 /drawable-hdpi 에 위치시킨다. (안드로이드 버전에 따라 drawable 도 무방)
2)
AndroidManifest.xml
- <application ...
- android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
- android:debuggable="true"
- <Activity ...
- android:screenOrientation="landscape"
1. 전체 화면 모드와 가로 모드로 설정
2. 실제 디바이스에서 디버깅
5. 가로 모드
3) LargeImageScroller Activity
SampleView 를 Custom view로 설정한다.
- 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) {
- }
- }
- }
디스플레이 크기를 알기 위해
- Display display = ((WindowManager)
- getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
- displayWidth = display.getWidth();
- displayHeight = display.getHeight();
getDefaultDisplay() 가 항상 같은 화면 크기를 돌려주는 것은 아니다. 즉, 화면의 orientation에 따라 값이 달라 진다.
만약 orientaion이 변경되는 상황을 알고 싶으면 onSizeChanged() API 로 hook 하면 된다.
4) SampleView 작성
ㄱ) 생성자
- displayRect = new Rect(0, 0, displayWidth, displayHeight);
- scrollRect = new Rect(0, 0, displayWidth, displayHeight);
- bmLargeImage = BitmapFactory.decodeResource(getResources(),
- R.drawable.testlargeimg);
scroll rectangle과 display rectangle을 일치시키므로서 앱을 실행하고 이미지의 좌-상 부분이 화면에 맞게 표시될 것이다.
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.
ㄴ) Touch event handler:
터치 이벤트를 down, up, move 의 동작으로 처리한다.
- @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 이벤트가 이어진다. 이벤트가 발생한 좌표는 getRawX() and getRawY()로 확인 가능하다.
getRawX() and getRawY(): 화면의 절대 좌표를 제공한다. 화면 좌표를 알 수 있는 getX(), getY() 는 사용하는 방법에 따라 절대 좌표나 상대좌표를 제공한다. 주의해야 한다.
연속적인 ACTION_MOVE 를 scroll rectangle 에 적용하고 있다. 이 과정은 몇번씩 반복되어 이미지를 부드럽게 스크롤이 되게 합니다.
scrollByX and scrollByY 는 이미지가 그려지기에 필요한 스크롤의 총량을 보관하고 있다.
startX and startY 는 MOVE 의 움직임에 대한 값을 가진다.
ACTION_MOVE 이벤트는 많이 발생하는데 event handler 에서 실행되는 것을 최소화 시키는게 좋다. 어떤 이벤트 핸들러라도 마찬가지다.
Given that the ACTION_MOVE event may get generated many times, it is best to keep the code that executes from within the event handlerto a minimum. This is true for any event handler.
Invalidate(): OS 에게 우리 뷰를 redraw 해준다. redraw는onDraw() (discussed below), where we update the coordinates of the scroll rectangle and repaint the enclosed bitmap portion
return true: OS에게 이벤트 처리를 여기서 완료하고 이 이벤트를 더 이상 다른 뷰등에 전달하지 않게 한다.
Draw updater:
onDraw()는 갱신된 scroll rectangle 좌표 계산과 bitmap 영역을 갱신된 좌표 rectangle 에 새롭게 그려야 한다.
화면에 터치한 손가락을 왼쪽으로 이동하면 scroll rectangle 아래에서 bitmap 이미지가 왼쪽으로 당겨질 것으로 생각한다 - 이것은 scroll rectangle이 오른쪽으로 이동하는 것을 의미한다.
즉 ACTION_MOVE event handler에서 이미지가 왼쪽으로 움직이는 것은 scroll rectangle이 오른쪽으로 움직이는 것이다. .
- 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);
좌표 0 에 대한 검사: our left (or top) coordinate is 0 for both the scroll rectangle and the bitmap,
오른쪽에 대한 검사는 아래 그림을 보면 이해가 쉽다.
이제 계산된 그림을 그리는 부분이 남았다...
- scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX +displayWidth,
- newScrollRectY +displayHeight);
- Paint paint = new Paint();
- canvas.drawBitmap(bmLargeImage, scrollRect, displayRect, paint);
마지막으로 scroll 좌표를 새로운 좌표로 변경해 준다.,
- scrollRectX = newScrollRectX;
- scrollRectY = newScrollRectY;
5) 빌드
One final note:
여기서는 새로운 bitmap 을 생성하는 코드는 없다. 이벤트 처리시 새로운 bitmap을 생성하는 것은 성능을 잡아 먹는다. ACTION_MOVE events handler에서 bitmap을 새로 생성하는 대신에 메모리에 적재된 bitmap 을 새로 그리는 방법을 사용한 것이다.
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 theevent 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.
"/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;
- }
- }
- }
=================================
=================================
=================================
출처: http://utime.blog.me/150091436416
하나의 포인터로 터치 했을 경우 상하좌우 구별 하는 셈플이다.
main.xml
<?xml version="1.0" encoding="utf-8"?> android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/textview_state" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/textview_action" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> |
터치의 정보와 상태 값을 보여주기 위한 화면이다.
MotionEventTest.java
package com.androidstudy.MotionEventTest; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.widget.TextView; public class MotionEventTest extends Activity { private TextView mTxView = null; private TextView mTxAction = null; private MotionEventAction mea = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mea = new MotionEventAction(); mTxView = (TextView)findViewById(R.id.textview_state); mTxAction = (TextView)findViewById(R.id.textview_action); } private void setLogMsg( StringBuilder sMsg, final String sTitle, final String sState) { sMsg.append( sTitle ).append(" = ").append( sState ).append("\r\n"); } private void setLogMsg( StringBuilder sMsg, final String sTitle, final int iState) { this.setLogMsg(sMsg, sTitle, Integer.toString(iState) ); } private void setLogMsg( StringBuilder sMsg, final String sTitle, final float fState) { this.setLogMsg(sMsg, sTitle, Float.toString(fState) ); } private void setLogMsg( StringBuilder sMsg, final String sTitle, final long lState) { this.setLogMsg(sMsg, sTitle, Long.toString(lState) ); } private class MotionEventAction { private float mDownX = -1; private float mDownY = -1; private float mUpX = -1; private float mUpY = -1; public float IgnoreValue = 10; public MotionEventAction() { // TODO Auto-generated constructor stub } protected void ActionLeftMoveEvent(float fGapValue) { mTxAction.setText( "ActionLeftMoveEvent = " + Float.toString(fGapValue) ); } protected void ActionRightMoveEvent(float fGapValue) { mTxAction.setText( "ActionRightMoveEvent = " + Float.toString(fGapValue) ); } protected void ActionUpMoveEvent(float fGapValue) { mTxAction.setText( "ActionUpMoveEvent = " + Float.toString(fGapValue) ); } protected void ActionDownMoveEvent(float fGapValue) { mTxAction.setText( "ActionDownMoveEvent = " + Float.toString(fGapValue) ); } private void ActionCheck() { float GapX = mDownX - mUpX; float GapY = mDownY - mUpY; float AbsGapX = Math.abs(GapX); float AbsGapY = Math.abs(GapY); if( (AbsGapX< IgnoreValue) && (AbsGapY<IgnoreValue)) { // 이 간격은 무시. mTxAction.setText( "Ignore Action" ); return; } if( AbsGapX < AbsGapY ) { if( mDownY < mUpY ) ActionDownMoveEvent(AbsGapY); else ActionUpMoveEvent(AbsGapY); }else { if( mDownX < mUpX ) ActionRightMoveEvent(AbsGapX); else ActionLeftMoveEvent(AbsGapX); } } public void ActionMotionEvent( MotionEvent event ) { switch( event.getActionMasked() ) { case MotionEvent.ACTION_DOWN : { mDownX = event.getX(); mDownY = event.getY(); break; } case MotionEvent.ACTION_UP : { mUpX = event.getX(); mUpY = event.getY(); ActionCheck(); break; } } } } @Override public boolean onTouchEvent(MotionEvent event) { StringBuilder sMsg = new StringBuilder(); this.setLogMsg( sMsg, "ActionMasked", event.getActionMasked() ); this.setLogMsg( sMsg, "DeviceId ", event.getDeviceId() ); this.setLogMsg( sMsg, "PointerCount", event.getPointerCount() ); this.setLogMsg( sMsg, "RawX ", event.getRawX() ); this.setLogMsg( sMsg, "RawY ", event.getRawY() ); this.setLogMsg( sMsg, "X ", event.getX() ); this.setLogMsg( sMsg, "Y ", event.getY() ); this.setLogMsg( sMsg, "DownTime ", event.getDownTime() ); this.setLogMsg( sMsg, "EventTime ", event.getEventTime() ); this.setLogMsg( sMsg, "Size ", event.getSize() ); sMsg.append("\r\n"); mTxView.setText( sMsg.toString() ); sMsg = null; mea.ActionMotionEvent(event); return super.onTouchEvent(event); } } |
터치 이벤트를 받아 얼마만큼 움직였는지 차이를 구해 상하좌우를 구별한다.
각각 방향 이벤트 마다 얼마만큼 움직였는지 값을 전달하도록 돼 있다.
MotionEventAction 클래스를 상속받아 각각 방향 이벤트 함수를 Override 하여 사용하면 바람직 할 듯 하다.
[출처] android : onTouchEvent 로 상하좌우 구분하기|작성자 시간한줌
=================================
=================================
=================================
'스마트기기개발관련 > 안드로이드 개발' 카테고리의 다른 글
android || In-app billing - error in generated IInAppBillingService.java - 빌드시 'gen' 에서 IInAppBillingService 관련 오류가 날때 (0) | 2020.09.22 |
---|---|
ADT 22 업그레이드 후 라이브러리 오류 관련 - Private Libraries 관련 (0) | 2020.09.22 |
안드로이드 에러 - Error generating final archive: Debug certificate expired on (0) | 2020.09.22 |
안드로이드 배포 관련 (0) | 2020.09.22 |
안드로이드 테블릿, 모바일 고정 하단바 스테이터스바 상태바 빼고 화면 크기 구하기 관련 (0) | 2020.09.22 |