MapView trong một mảnh (Honeycomb)


81

bây giờ SDK cuối cùng đã ra mắt với google apis - cách tốt nhất để tạo Phân đoạn với MapView là gì? MapView cần một MapActivity để hoạt động bình thường.

Việc có Activity quản lý các Fragment kế thừa từ MapActivity (giải pháp không tốt vì nó đi ngược lại với ý tưởng rằng các Fragment được tự chứa) và sử dụng một bố cục dựa trên xml thông thường không hoạt động. Tôi nhận được một NullPointerException trong MapActivity.setupMapView ():

E / AndroidRuntime (597): Do: java.lang.NullPointerException gây ra
E / AndroidRuntime (597): tại com.google.android.maps.MapActivity.setupMapView (MapActivity.java:400)
E / AndroidRuntime (597): tại com.google.android.maps.MapView. (MapView.java:289)
E / AndroidRuntime (597): tại com.google.android.maps.MapView. (MapView.java:264)
E / AndroidRuntime (597): tại com.google.android.maps.MapView. (MapView.java:247)

Ý tưởng thứ hai của tôi là tạo MapView theo chương trình và chuyển hoạt động được liên kết (thông qua getActivity ()) dưới dạng Ngữ cảnh cho phương thức khởi tạo MapView. Không hoạt động:

E / AndroidRuntime (834): Do: java.lang gây ra.
E / AndroidRuntime (834): tại com.google.android.maps.MapView. (MapView.java:291)
E / AndroidRuntime (834): tại com.google.android.maps.MapView. (MapView.java:235)
E / AndroidRuntime (834): tại de.foo.FinderMapFragment.onCreateView (FinderMapFragment.java:225)
E / AndroidRuntime (834): tại android.app.FragmentManagerImpl.moveToState (FragmentManager.java:708)
E / AndroidRuntime (834): tại android.app.FragmentManagerImpl.moveToState (FragmentManager.java:900)
E / AndroidRuntime (834): tại android.app.FragmentManagerImpl.addFragment (FragmentManager.java:978)
E / AndroidRuntime (834): tại android.app.Activity.onCreateView (Activity.java:4090)
E / AndroidRuntime (834): tại android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:664)

Tôi đoán thực sự nên có một cái gì đó giống như MapFragment xử lý các luồng nền mà MapView cần ... Vậy phương pháp tốt nhất hiện tại để làm điều này là gì?

Trân trọng cảm ơn từ Đức, Valentin


2
Tôi đã báo cáo một yêu cầu tính năng cho điều này. Hãy gắn dấu sao nó. code.google.com/p/android/issues/detail?id=15347
Jeremy Edwards

Câu trả lời:


8

Kể từ ngày 12 tháng 3 năm 2012, Google đã phát hành Google Maps Android API v2 . Bây giờ bạn có thể quên về những vấn đề này. https://developers.google.com/maps/documentation/android/

Ví dụ sử dụng API mới - https://developers.google.com/maps/documentation/android/start#add_a_map

API này sẽ hoạt động cho ít nhất Android API 8, vì vậy hãy sử dụng nó;).

Vì vậy, bây giờ bạn có thể chỉ cần sử dụng lớp phân đoạn "com.google.android.gms.maps.MapFragment". Nó sẽ hiển thị bản đồ trong Hoạt động của bạn. Ví dụ về bố cục từ liên kết ở trên:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.MapFragment"/>

87

Tôi đã quản lý để giải quyết điều này bằng cách sử dụng TabHost trong phân đoạn.

Đây là ý tưởng (ngắn gọn):

  1. MainFragmentActivitymở rộng FragmentActivity(từ thư viện hỗ trợ) và có MapFragment.

  2. MyMapActivitymở rộng MapActivityvà chứa MapView.

  3. LocalActivityManagerFragment máy chủ LocalActivityManager

  4. MapFragmentmở rộng LocalActivityManagerFragment.

  5. LocalActivityManagerchứa MyMapActivityhoạt động trong đó.

Triển khai ví dụ: https://github.com/inazaruk/map-fragment .


nhập mô tả hình ảnh ở đây


4
Nếu sau đó bạn muốn MapActivity để có thể comm với Hoạt động cha mẹ (đó là dễ dàng với một Fragment) sau đó bạn có thể sử dụng Activity.getParent ()
Dori

3
Bạn không thực sự cần phải sử dụng một TabHost, bạn cũng có thể chỉ cần sử dụng một LocalActivityManager và đính kèm các cửa sổ trở lại nội dung của đoạn bạn
ChristophK

4
Trong trường hợp ai đó đang thắc mắc, đây là mã:Intent i = new Intent(getActivity().getParent(), MyMapActivity.class); Window w = localActivityManager.startActivity("tag", i); currentView=w.getDecorView(); currentView.setVisibility(View.VISIBLE); currentView.setFocusableInTouchMode(true); ((ViewGroup) currentView).setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); this.contentView.addView(currentView);
ChristophK,

8
hãy cẩn thận, LocalActivityManager hiện không được dùng nữa
louiscoquio

4
Nó hoạt động tốt đối với tôi, sử dụng các tab và phân đoạn. Đối với những người hỏi về lời giải thích, tôi muốn xem dự án và mã của nó, nó dễ hiểu. Và có, LocalActivityManagerkhông được dùng nữa, nhưng đây chỉ là một bản hack, vấn đề này sẽ được giải quyết đúng cách khi họ quyết định đưa vào hỗ trợ thích hợp cho Maps trong Fragment.
mdelolmo

23

Như đã thảo luận tại Google Groups , Peter Doyle cũng đã xây dựng một thư viện tương thích tùy chỉnh hỗ trợ Google Maps. android-support-v4-googlemaps

Tuy nhiên, cũng có một nhược điểm:

Hiện tại, một nhược điểm là TẤT CẢ các lớp mở rộng FragmentActivity đều là MapActivity. Có thể tạo một lớp riêng biệt (tức là FragmentMapActivity), nhưng nó yêu cầu một số cấu trúc lại mã FragmentActivity.


6
Điều này cũng có nghĩa là ứng dụng của bạn sẽ không được cập nhật với thư viện hỗ trợ mới nhất. Mặc dù android-support-v4-googlemaps đã được cập nhật một số lần, nhưng nó hiện còn 2 phiên bản. Tôi đồng ý với những người khác rằng thư viện đã sửa đổi này là một cuộc tấn công lớn hơn so với việc sử dụng LocalActivityManager không dùng nữa.
Stan Kurdziel

1
github.com/piledoyle/android-support-v4-googlemaps hiện đã được cập nhật (r10 kể từ thông báo này). Tôi cũng đã cấu trúc lại mọi thứ nên sẽ rất dễ dàng để cập nhật trong tương lai.
Pete Doyle

Tôi có một câu hỏi. Nếu ứng dụng đã được triển khai và cài đặt, nhược điểm của việc "không được cập nhật với thư viện hỗ trợ mới nhất" là gì?
hendrix

Vấn đề chính của phương pháp này, IMO, là bạn sẽ không thể triển khai ứng dụng của mình với hỗ trợ Maps tùy chọn. Vì tất cả các FragmentActive đều mở rộng MapActivity, nên nó sẽ đơn giản gặp sự cố trên các thiết bị không có Google API (ví dụ: Kindle Fire).
Paul Lammertsma.

13

Chỉ để làm rõ câu trả lời. Tôi đã thử cách tiếp cận do inazaruk và ChristophK đề xuất. Trên thực tế, bạn có thể chạy bất kỳ hoạt động nào trong một phân đoạn - không chỉ google maps. Đây là mã triển khai hoạt động bản đồ google dưới dạng một đoạn nhờ inazaruk và ChristophK.

import com.actionbarsherlock.app.SherlockFragment;
import android.view.Window;

import android.app.LocalActivityManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MapFragment extends SherlockFragment {
    private static final String KEY_STATE_BUNDLE = "localActivityManagerState";

    private LocalActivityManager mLocalActivityManager;

    protected LocalActivityManager getLocalActivityManager() {
        return mLocalActivityManager;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle state = null;
        if (savedInstanceState != null) {
            state = savedInstanceState.getBundle(KEY_STATE_BUNDLE);
        }

        mLocalActivityManager = new LocalActivityManager(getActivity(), true);
        mLocalActivityManager.dispatchCreate(state);
    }

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
            //This is where you specify you activity class
        Intent i = new Intent(getActivity(), GMapActivity.class); 
        Window w = mLocalActivityManager.startActivity("tag", i); 
        View currentView=w.getDecorView(); 
        currentView.setVisibility(View.VISIBLE); 
        currentView.setFocusableInTouchMode(true); 
        ((ViewGroup) currentView).setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        return currentView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBundle(KEY_STATE_BUNDLE,
                mLocalActivityManager.saveInstanceState());
    }

    @Override
    public void onResume() {
        super.onResume();
        mLocalActivityManager.dispatchResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mLocalActivityManager.dispatchPause(getActivity().isFinishing());
    }

    @Override
    public void onStop() {
        super.onStop();
        mLocalActivityManager.dispatchStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mLocalActivityManager.dispatchDestroy(getActivity().isFinishing());
    }
}

2
Nhưng giải pháp duy nhất tránh được LocalActivityManager(như đã thảo luận ở đây: code.google.com/p/android/issues/detail?id=15347 ) liên quan đến việc mở rộng tất cả FragmentActivities , điều này dường như thậm chí còn kém tối ưu hơn.
Estel

LocalActivityManager bị phản đối nhiều hơn vì ActivityGroup không được dùng nữa, vẫn hữu ích cho tình huống này và có thể Google sẽ suy nghĩ kỹ về việc loại bỏ nó trong các bản phát hành trong tương lai nếu mọi người đang sử dụng nó để giải quyết các thiếu sót của họ.
straya

4

Tin tốt từ Google về điều này. Hôm nay họ sẽ phát hành một API Google Maps mới , với các bản đồ trong nhà và MapFragment.

With this new API, adding a map to your Activity is as simple as:

<fragment
  android:id="@+id/map"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  class="com.google.android.gms.maps.MapFragment" />

Không chính xác. Hãy thử sử dụng nó trong FragmentPagerAdapter.
Thomas Dignan

Nếu bạn đang nói về quan điểm đen, có một hack nhỏ để sửa chữa nó stackoverflow.com/questions/13837697/...
Marcelo

cảm ơn. Tôi đã kết thúc thấy điều đó sau đó nhưng bây giờ tôi đang sử dụng phiên bản api bản đồ đầu tiên với polaris.
Thomas Dignan

2

API Google Maps không phải là một phần của AOSP. Miễn là không có nhân viên Google nào phản hồi thì hầu như không thể biết liệu sẽ có MapFragment trong tương lai hay không.

Một giải pháp thay thế có giới hạn là sử dụng WebViewFragmentvà lạm dụng nó để tải lên một maps.google.comURL tùy chỉnh .


1
Đây là hy vọng API bản đồ sẽ sớm được cập nhật với các mảnh và bản vẽ vector!
Nathan Schwermann

2

Rất tiếc là Google vẫn chưa phản hồi. FWIW nếu bạn thực sự cần làm điều này, tôi không tìm thấy cách nào khác ngoài:

Có hoạt động quản lý tab kế thừa từ MapActivity, tạo MapView trong đó theo chương trình, có mapfragment.xml chứa ViewGroup và thêm MapView vào ViewGroup bằng cách sử dụng

((ViewGroup) getFragmentManager (). FindFragmentById (R.id.finder_map_fragment) .getView ()). AddView (mapView) ;;

Rõ ràng điều này đi ngược lại hoàn toàn với ý kiến ​​cho rằng các mảnh vỡ được coi là khép kín nhưng ...


Cảm ơn, việc mở rộng Hoạt động quản lý phân mảnh MapActivity đã hoạt động hoàn hảo cho tôi.
Pete Doyle

2

Đây là phiên bản MonoDroid ( Mono cho Android ) của MapFragment rất đơn giản :

public class MapFragment : Fragment
{
    // FOLLOW http://stackoverflow.com/questions/5109336/mapview-in-a-fragment-honeycomb
    private static  String KEY_STATE_BUNDLE = "localActivityManagerState";

    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        Bundle state = null;
        if (savedInstanceState != null) {
            state = savedInstanceState.GetBundle(KEY_STATE_BUNDLE);
        }
        mLocalActivityManager = new LocalActivityManager(Activity, true);
        mLocalActivityManager.DispatchCreate(state);
    }

    public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        //This is where you specify you activity class
        Intent i = new Intent(Activity, typeof(SteamLocationMapActivity)); 
        Window w = mLocalActivityManager.StartActivity("tag", i); 
        View currentView=w.DecorView; 
        currentView.Visibility = ViewStates.Visible; 
        currentView.FocusableInTouchMode = true; 
        ((ViewGroup) currentView).DescendantFocusability = DescendantFocusability.AfterDescendants;
        return currentView;
    }

    private LocalActivityManager mLocalActivityManager;
    protected LocalActivityManager GetLocalActivityManager() {
        return mLocalActivityManager;
    }   


    public override void OnSaveInstanceState(Bundle outState)
    {
        base.OnSaveInstanceState(outState);
        outState.PutBundle(KEY_STATE_BUNDLE,mLocalActivityManager.SaveInstanceState());
    }

    public override void OnResume()
    {
        base.OnResume();
        mLocalActivityManager.DispatchResume();

    }

    public override void OnPause()
    {
        base.OnPause();
        mLocalActivityManager.DispatchPause(Activity.IsFinishing);
    }

    public override void OnStop()
    {
        base.OnStop();
        mLocalActivityManager.DispatchStop();
    }
}



1

Tôi có thể nhận được giải pháp:

  1. tạo lớp TempFragmentActivity mở rộng MapActivity
  2. có một đối tượng MapView bên trong TempFragmentActivity (giống như định nghĩa thông thường trong xml)
  3. loại bỏ biểu mẫu đối tượng MapView này (LinearLayout) (ngoại lệ void sau)
  4. giữ đối tượng MapView này ở một nơi nào đó (ví dụ: thành viên tĩnh của TempFragmentActivity)
  5. trong Fragment của bạn, hãy thêm đối tượng MapView này bằng mã (không xác định bằng xml) vào một số LinearLayout

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.