मैं रोमैन गाय की सलाह के बाद समाप्त हुआ (वही सलाह जो मैंने अपने प्रश्न में पोस्ट की थी)। यही है, मैंने RelativeLayout
को विस्तारित करके अपना खुद का कस्टम ViewGroup
बनाया, और फिर मैंने पूरे स्क्रीन के कवरेज प्रदान करने के लिए अपने mapView
के आकार में वृद्धि की।RelativeLayout ViewGroup
को विस्तारित करना आवश्यक था ताकि मैं dispatchDraw(...)
, onMeasure(...)
, साथ ही dispatchTouchEvent(...)
फ़ंक्शंस को मेरे एप्लिकेशन के लिए इच्छित मानचित्र रोटेशन सुविधाओं को सक्षम करने के लिए ओवरराइड कर सकूं।
dispatchDraw(...)
फ़ंक्शन onDraw(...)
फ़ंक्शन पर कॉल को अनिवार्य रूप से कॉल करता है, उस फ़ंक्शन में इनपुट पर कुछ विशिष्ट कुशलताएं करता है और फिर इसे संसाधित करने के लिए रिलीज़ करता है। हमारे मामले में, हम वास्तविक onDraw(...)
फ़ंक्शन पर जाने से पहले mapView
कैनवास को घुमाएंगे। यही कारण है कि हमें इस फ़ंक्शन को ओवरराइड करने की आवश्यकता होगी।
विशेष रूप से, dispatchDraw(...)
फ़ंक्शन एक कैनवास ऑब्जेक्ट इनपुट के रूप में लेता है, जो (इस मामले में) ओएसएम mapView
ऑब्जेक्ट (जैसा कि नीचे XML फ़ाइल में परिभाषित किया गया है) का प्रतिनिधित्व करता है। यदि कैनवास पर एक रोटेशन लागू किया जाना है, तो हम नक्शे के केंद्र का पता लगाना चाहते हैं, मानचित्र का अनुवाद (यानी स्थानांतरित) करना चाहते हैं ताकि नक्शा का केंद्र समन्वय प्रणाली की उत्पत्ति पर बैठे, मानचित्र को घुमाया समन्वय प्रणाली की उत्पत्ति, और फिर, आखिरकार, हम इस संशोधित कैनवास को प्रतिपादन पाइपलाइन में अगले चरण में प्रेषित करना चाहेंगे।
इसके लिए मेरा कोड नीचे है; ध्यान दें कि Manager
मेरा स्वयं का सिंगलटन सृजन है जो आपके कार्यान्वयन में तब तक अस्तित्व में नहीं रहेगा जबतक कि आप स्वयं को लिख नहीं लेते!
/**
* @param pCanvas
* @return void
*
* This function intercepts all dispatches to the onDraw function of the
* super, and it rotates the canvas in accordance with the user's wishes
* using the phone bearing as determined either through the magnetometer
* or GPS fixes.
*/
@Override
protected void dispatchDraw(final Canvas pCanvas) {
final long startMs = System.currentTimeMillis();
// If automatic map rotation has been enabled, get bearing from phone:
if (Manager.getInstance().getMapRotationMode() != Constants.DISABLED) {
mBearing = Manager.getInstance().getPhoneBearing();
// Save the state of the transformation matrix:
pCanvas.save(Canvas.MATRIX_SAVE_FLAG);
// getWidth() and getHeight() return the size of the canvas as
// defined in the XML file, and not the size of the screen!
int canvasOffsetX = -(getWidth()/2) + (screenWidth/2);
int canvasOffsetY = -(getHeight()/2) + (screenHeight/2);
// Set origin of canvas to center of map:
pCanvas.translate(canvasOffsetX, canvasOffsetY);
// Rotate the canvas to the correct bearing:
pCanvas.rotate(-mBearing, getWidth()/2, getHeight()/2);
// Pass on the rotated canvas, and restore after that:
super.dispatchDraw(pCanvas);
// Balance out the call to save, and restore the matrix to
// saved state:
pCanvas.restore();
} // end if
else { // If map rotation has not been enabled:
super.dispatchDraw(pCanvas);
} // end else
final long endMs = System.currentTimeMillis();
if (LOG_ENABLED) {
Log.i(TAG, "mapView Dispatch Time: " + (endMs - startMs) + "ms");
} // end if
} // end dispatchDraw()
अगला क्योंकि OSM mapView
कैनवास के किसी भी रोटेशन नक्शा न केवल चित्रमय प्रतिनिधित्व घूर्णन समाप्त होता है हम, dispatchTouchEvent(...)
ओवरराइड करने के लिए की आवश्यकता होगी, लेकिन यह भी सब कुछ बाकी है कि गतिविधि से संबंधित (यह एक पक्ष के रूप में होता मेरे विशिष्ट कार्यान्वयन का प्रभाव); यानी, टच इवेंट निर्देशांक mapView
कैनवास के सापेक्ष बने रहें और वास्तविक फोन के सापेक्ष नहीं। उदाहरण के लिए, अगर हम कल्पना करते हैं कि कैनवास 180 डिग्री से घूर्णन किया जा रहा है, तो यदि उपयोगकर्ता बाईं ओर मानचित्र को पैन करने का प्रयास करता है, तो यह बदले में नक्शे पर ले जायेगा, क्योंकि सब कुछ उल्टा है!
कोड में, आप इस प्रकार इस समस्या के लिए सही कर सकता है:
/**
* @param event
* @return boolean
*
* This function intercepts all interactions with the touch display (that is,
* all touchEvents), and for each finger on the screen, or pointer, the
* function applies the necessary rotation to counter the rotation of the
* map. The coordinate of each pointer is also modified so that it returns
* the correct location on the enlarged canvas. This was necessary to return
* the correct coordinate for actions such as double-tap, and proper icon
* identification upon clicking an icon.
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// Get the number of pointers (i.e. fingers on screen) from the passed
// in MotionEvent:
float degrees = Manager.getInstance().getPhoneBearing();
int numPointers = event.getPointerCount();
int[] pointerIDs = new int[numPointers];
PointerCoords[] pointerCoords = new PointerCoords[numPointers];
// Extract all pointers from the touch event:
for (int i = 0; i < numPointers; i++) {
pointerIDs[i] = event.getPointerId(i);
pointerCoords[i] = new PointerCoords();
event.getPointerCoords(i, pointerCoords[i]);
} // end for
// Correct each pointer coordinate set for map rotation:
for (int i = 0; i < numPointers; i++) {
// x and y end up representing points on the canvas, although they
// are derived from points on the screen:
float x = pointerCoords[i].x;
float y = pointerCoords[i].y;
// Get the center of the MapView:
int centerX = getWidth()/2;
int centerY = getHeight()/2;
// Convert to radians
float rad = (float) ((degrees * Math.PI)/180f);
float s = (float) Math.sin(rad);
float c = (float) Math.cos(rad);
// Translate point to origin:
x -= centerX;
y -= centerY;
// Apply rotation
float tmpX = x * c - y * s;
float tmpY = x * s + y * c;
x = tmpX;
y = tmpY;
// Offset the coordinates to compensate for the fact that the
// canvas is 1200 by 1200, the phone screen is smaller, and
// they don't overlap nicely:
x += (600 - (screenWidth/2)) * c - (600 - (screenHeight/2)) * s;
y += (600 - (screenWidth/2)) * s + (600 - (screenHeight/2)) * c;
// Translate point back:
x += centerX;
y += centerY;
pointerCoords[i].x = x;
pointerCoords[i].y = y;
// Catlog:
if (LOG_ENABLED) Log.i(TAG, "(" + x + ", " + y + ")");
} // end for
// Create new event to pass along the modified pointers.
// Need API level 9 or higher to make this work!
MotionEvent newEvent = MotionEvent.obtain(event.getDownTime(), event
.getEventTime(), event.getAction(), event.getPointerCount(),
pointerIDs, pointerCoords, event.getMetaState(), event
.getXPrecision(), event.getYPrecision(), event
.getDeviceId(), event.getEdgeFlags(),
event.getSource(), event.getFlags());
// Dispatch the newly modified touch event:
return super.dispatchTouchEvent(newEvent);
} // end dispatchTouchEvent()
अंत में, नक्शा गतिविधि ठीक से काम करने के लिए इसी एक्सएमएल हो रही करने के लिए चाल अन्य सभी के लिए एक FrameLayout
अभिभावक के रूप में उपयोग करने के लिए है मेरे लेआउट में जीयूआई तत्व। इसने मुझे अपने नेक्सस वन (480 से 800) पर प्रदर्शन के आयामों की तुलना में mapView
आयामों को काफी बड़ा बनाने की अनुमति दी। match_parent
और इसी तरह के पैरामीटर का उपयोग करते समय इस समाधान ने मुझे अपने FrameLayout
के अंदर RelativeLayout
घोंसला करने की अनुमति दी, जबकि डिवाइस के वास्तविक प्रदर्शन आयामों का सम्मान करते हुए भी।
मेरी एक्सएमएल लेआउट के संबंधित भाग इस तरह दिखता है:
<?xml version="1.0" encoding="utf-8"?>
<!--Note that the layout width and height is defined in px and not dip!-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/MapViewLayout">
<a.relevant.path.RotatingRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="1200px"
android:layout_height="1200px">
<org.osmdroid.views.MapView
android:id="@+id/mapview"
android:layout_width="1200px"
android:layout_height="1200px"
android:enabled="true"
android:clickable="true"/>
</a.relevant.path.RotatingRelativeLayout>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/leftSlideHandleButton"
android:layout_width="match_parent"
android:layout_height="60dip"
android:layout_centerHorizontal="true"
android:background="#D0000000">
<Button
android:id="@+id/mapZoomOutButton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/zoom_out_button"
android:layout_alignParentLeft="true"
android:onClick="zoomOutButton"/>
<Button
android:id="@+id/mapZoomInButton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/zoom_in_button"
android:layout_alignParentRight="true"
android:onClick="zoomInButton"/>
<TextView
android:id="@+id/headerSpeedText"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="#33B5E5"
android:text="Speed: "
android:textSize="12sp"
android:paddingLeft="15dip"
android:layout_toRightOf="@id/mapZoomOutButton"/>
<TextView
android:id="@+id/headerSpeedReading"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="#33B5E5"
android:text="N/A"
android:textSize="12sp"
android:paddingLeft="27dip"
android:layout_toRightOf="@id/headerSpeedText"/>
<TextView
android:id="@+id/headerBearingText"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="#33B5E5"
android:text="Bearing: "
android:paddingLeft="15dip"
android:textSize="12sp"
android:layout_toRightOf="@id/mapZoomOutButton"
android:layout_below="@id/headerSpeedText"/>
<!-- Et Cetera... -->
</RelativeLayout>
</FrameLayout>
मैं ने टिप्पणी दी कि इस समाधान नहीं सबसे अच्छा समाधान भी तरह से है करना चाहते हैं, लेकिन यह मेरी सबूत के लिए ठीक काम किया है कि आवेदन स्वीकार करें!
क्या आप 180 के गुणकों के अलावा डिग्री के लिए डिग्री के लिए घुमाए गए हैं, तो क्या आप स्पर्श घटनाओं को अपेक्षाकृत प्रेषित करने में सक्षम थे? पूर्व के लिए: 50, 9 0, 270, 300? – Mani
हां, कोई मनमाना रोटेशन कोण काम करता है। मैं फोन के मैग्नेटोमीटर से रीडिंग प्राप्त करने में सक्षम था, और फिर उन रीडिंग्स का उपयोग कैनवास को घुमाने के लिए करता था ताकि नक्शा के शीर्ष को हमेशा उस ऐतिहासिक स्थल को दिखाया जो सीधे उपयोगकर्ता के सामने था। उम्मीद है कि समझ में आता है! –