2011-08-29 11 views
8

मैं एक ऐसे अनुप्रयोग पर काम कर रहा हूं जो मैत्रीपूर्ण टाइल्स से बना ऑफ़लाइन मानचित्र पर ब्याज के विभिन्न बिंदुओं को प्रदर्शित करने के लिए OpenStreetMap (ओएसएम) एपीआई का उपयोग करता है।एंड्रॉइड: संपूर्ण स्क्रीन भरने के लिए एक घुमावदार ओएसएम मानचित्र प्राप्त करना

उन सुविधाओं में से एक जो मैं वर्तमान में कार्यान्वित कर रहा हूं, मानचित्र को फोन (जीपीएस के माध्यम से) द्वारा निर्धारित असर के अनुसार नक्शा घुमाया जा रहा है। मैं बिना किसी प्रयास के वास्तविक रोटेशन को लागू करने में सक्षम था, लेकिन चूंकि मेरा कोड पूरे कैनवास को घुमाता है - शायद एक बेवकूफ दृष्टिकोण - अब मेरे पास स्क्रीन पर खाली कोनों हैं जहां तथ्य की क्षतिपूर्ति के लिए कोई नई टाईल्स लोड नहीं की जा रही है कि घुमावदार टाइल्स अब इन पिक्सेल भरें नहीं।

कुछ गुगलिंग के बाद, मुझे कुछ सुझाव मिले कि मैं इस मुद्दे को हल करने में सक्षम कैसे हो सकता हूं, लेकिन अब तक कोई भाग्य नहीं है।

Mr. Romain Guy's posts में से एक में उन्होंने उल्लेख निम्नलिखित:

मैंने पहले भी यह किया है और यह एक कस्टम ViewGroup कि dispatchDraw में कैनवास घूमता है() विधि बनाने के लिए की आवश्यकता है। आप को MapView के आकार को बढ़ाने की भी आवश्यकता है (ताकि घूर्णन होने पर यह पर्याप्त पिक्सल खींच सके।) आपको प्रेषण टचवेन्ट() में स्पर्श ईवेंट को घुमाने की भी आवश्यकता होगी। या यदि आप Android का उपयोग 3.0 आप बस theMapView.rotate()

कॉल कर सकते हैं मानचित्र रोटेशन पर उनकी सलाह ले रहा है, मैं dispatchDraw() और dispatchTouchEvent() सुझाव के रूप में लागू कर दिया है, लेकिन मैं कुछ समस्या आ रही वह हिस्सा जहां उन्होंने उल्लेख किया कि मुझे MapView के आकार को बढ़ाने की आवश्यकता है?

क्या यह कुछ है जो मैं XML फ़ाइल में करता हूं, जैसा कि this thread में सुझाया गया है?

या, क्या मैं किसी भी तरह से मेरे उप-वर्गीकृत रिलेवेटलाइट क्लास में ऑनमीर() फ़ंक्शन को ओवरराइड कर सकता हूं जो मेरे मानचित्र रोटेशन को संभालता है?

सुझाव और संकेत सबसे अधिक स्वागत है।

अद्यतन:

इस समस्या का एक स्वीकार्य समाधान खोजने के लिए की कोशिश में, मैं कैनवास का आकार बदलने की कोशिश की। सोच यह थी कि एक कैनवास आकार के साथ जो वास्तविक स्क्रीन आकार से बड़ा है, मैं स्क्रीन से रिक्त कोनों को पूरी तरह से स्थानांतरित करने में सक्षम हो सकता हूं। दुर्भाग्यवश, वास्तविक कैनवास.size() विकल्प प्रतीत नहीं होता है; मैंने पाया सबसे अच्छा canvas.scale() था।

canvas.scale() का उपयोग करके, मैं क्षैतिज और ऊर्ध्वाधर आयाम दोनों में 2 के कारक द्वारा कैनवास के पैमाने को बढ़ाने में सक्षम था। इसका मतलब यह है कि, छवि प्रभावी ढंग से ज़ूम हो गई है, जिससे मानचित्र टाइल पर अस्वीकार्य पिक्सेलेशन हो गया है।

क्या किसी को पता है कि कैनवास का आकार कहां घोषित किया जाता है, और यदि कैनवास के आकार को बदलना वास्तव में मेरी समस्या का समाधान कर सकता है?

उत्तर

3

मैं रोमैन गाय की सलाह के बाद समाप्त हुआ (वही सलाह जो मैंने अपने प्रश्न में पोस्ट की थी)। यही है, मैंने 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> 

मैं ने टिप्पणी दी कि इस समाधान नहीं सबसे अच्छा समाधान भी तरह से है करना चाहते हैं, लेकिन यह मेरी सबूत के लिए ठीक काम किया है कि आवेदन स्वीकार करें!

+0

क्या आप 180 के गुणकों के अलावा डिग्री के लिए डिग्री के लिए घुमाए गए हैं, तो क्या आप स्पर्श घटनाओं को अपेक्षाकृत प्रेषित करने में सक्षम थे? पूर्व के लिए: 50, 9 0, 270, 300? – Mani

+0

हां, कोई मनमाना रोटेशन कोण काम करता है। मैं फोन के मैग्नेटोमीटर से रीडिंग प्राप्त करने में सक्षम था, और फिर उन रीडिंग्स का उपयोग कैनवास को घुमाने के लिए करता था ताकि नक्शा के शीर्ष को हमेशा उस ऐतिहासिक स्थल को दिखाया जो सीधे उपयोगकर्ता के सामने था। उम्मीद है कि समझ में आता है! –

संबंधित मुद्दे