【问题标题】:Have tabs preload their Fragments when app starts and have tabs save state让标签在应用程序启动时预加载其片段并让标签保存状态
【发布时间】:2017-04-08 21:25:14
【问题描述】:

我有 3 个标签:

  • 首页
  • 谷歌地图
  • 设置

我有一个主要活动,它有一个 TabLayout 来显示 3 个选项卡:

public class MainActivity extends AppCompatActivity implements MyMapFragment.OnFragmentInteractionListener, SettingsFragment.OnFragmentInteractionListener {

private static final int INDEX_HOME_FRAGMENT = 0;
private static final int INDEX_MAP_FRAGMENT = 1;
private static final int INDEX_SETTINGS_FRAGMENT = 2;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);

    tabLayout.addTab(tabLayout.newTab().setText("Home"));
    tabLayout.addTab(tabLayout.newTab().setText("Map"));
    tabLayout.addTab(tabLayout.newTab().setText("Settings"));

    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();


            switch(tab.getPosition()) {

                case INDEX_HOME_FRAGMENT:
                    HomeFragment homeFragment = new HomeFragment();
                    transaction.replace(R.id.frame_layout, homeFragment);
                    // Commit the transaction
                    transaction.commit();

                    break;

                case INDEX_MAP_FRAGMENT:
                    MyMapFragment myMapFragment = new MyMapFragment();

                    transaction.replace(R.id.frame_layout, myMapFragment);
                    // Commit the transaction
                    transaction.commit();

                    break;

                case INDEX_SETTINGS_FRAGMENT:
                    SettingsFragment settingsFragment = new SettingsFragment();
                    transaction.replace(R.id.frame_layout, settingsFragment);
                    // Commit the transaction
                    transaction.commit();
                    break;

                default:
                    //error occured here
                    Log.e("Error occured = ", " TAB_ERROR");
                    break;
            }
        }
    });

    // Check that the activity is using the layout version with
    // the fragment_container FrameLayout
    if (findViewById(R.id.frame_layout) != null) {
        if (savedInstanceState != null) {
            return;
        }
        HomeFragment homeFragment = new HomeFragment();
        // Add the fragment to the 'fragment_container' FrameLayout
        getSupportFragmentManager().beginTransaction()
                .add(R.id.frame_layout, homeFragment ).commit();
    }
}

@Override
public void onFragmentInteraction(Uri uri) {
    //do nothing
}
}

当一个标签被点击时,这个 Activity 也会处理。单击选项卡时,它应该在 FrameLayout 中显示片段。
这一切都很好。唯一的问题是当我点击地图标签时,谷歌地图总是刷新。
我希望地图在应用程序启动时预加载,因此当用户单击地图选项卡时,地图应该已经加载。同样,当用户离开地图选项卡并返回时,它应该像以前一样保存地图。目前,当用户返回地图选项卡时,地图会重新加载,并且需要一些时间才能再次加载。

这是我的地图片段:

public class MyMapFragment extends SupportMapFragment
        implements OnMapReadyCallback,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener {

    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
    GoogleMap mGoogleMap;
    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;
    Marker lastOpenned = null;


    @Override
    public void onResume() {
        super.onResume();

        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mGoogleMap == null) {
            getMapAsync(this);
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        //stop location updates when Activity is no longer active
        if (mGoogleApiClient != null) {
            //LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,this);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        mGoogleMap = googleMap;


        LatLngBounds.Builder builder = new LatLngBounds.Builder();

        mGoogleMap.addMarker(new MarkerOptions()
                .position(new LatLng(40.76793169992044, -73.98180484771729))
                .title("San Francisco"));
        mGoogleMap.addMarker(new MarkerOptions()
                .position(new LatLng(41.76793169992044, -72.98180484771729))
                .title("Las Vegas"));

        builder.include(new LatLng(40.76793169992044, -73.98180484771729));
        builder.include(new LatLng(41.76793169992044, -72.98180484771729));
        LatLngBounds bounds = builder.build();

        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 200));

        mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
            public boolean onMarkerClick(Marker marker) {

                return true;
            }
        });

        //Initialize Google Play Services
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(getActivity(),
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                //Location Permission already granted
                buildGoogleApiClient();
                mGoogleMap.setMyLocationEnabled(true);
            } else {
                //Request Location Permission
                checkLocationPermission();
            }
        } else {
            buildGoogleApiClient();
            mGoogleMap.setMyLocationEnabled(true);
        }
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        if (ContextCompat.checkSelfPermission(getActivity(),
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    }


    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onLocationChanged(Location location) {

    }

    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                    Manifest.permission.ACCESS_FINE_LOCATION)) {

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(getActivity())
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(getActivity(),
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .create()
                        .show();


            } else {
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(getActivity(),
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(getActivity(),
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) {

                        if (mGoogleApiClient == null) {
                            buildGoogleApiClient();
                        }
                        mGoogleMap.setMyLocationEnabled(true);
                    }

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(getActivity(), "permission denied", Toast.LENGTH_LONG).show();
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request
        }
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }


    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}

【问题讨论】:

    标签: android google-maps android-fragments


    【解决方案1】:

    如果您不想重新加载地图,我建议您不要在每次单击选项卡时都创建new MyMapFragment()

    一个简单的方法是一个简单的包装片段持有者类。

    public class MainActivity extends AppCompatActivity 
        implements MyMapFragment.OnFragmentInteractionListener, SettingsFragment.OnFragmentInteractionListener {
    
        static class FragmentTab {
            String name;
            Fragment frag;
    
            public FragmentTab(String name, Fragment f) {
                this.name = name;
                this.frag = f;
            }
        }
    
        // These are only created once, not per-click
        private List<FragmentTab> tabs = Arrays.asList(
            new FragmentTab("Home", new HomeFragment()),
            new FragmentTab("Map", new MyMapFragment()),
            new FragmentTab("Settings", new SettingsFragment()),
        );
    

    然后,您可以迭代并索引该列表以清理其余代码

    for (FragmentTab tab : tabs) {
        tabLayout.addTab(tabLayout.newTab().setText(tab.name));
    }
    
    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
    
            getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.frame_layout, tabs.get(tab.getPosition()).frag)
                .commit();
        }
    };
    

    LinkedHashMap&lt;String, Fragment&gt; 也可以,但将 tab.getPosition() 替换为 tab.getTitle()(我认为)

    【讨论】:

    • 感谢您的回复。我已经实现了代码,但不幸的是地图片段仍然没有预加载。当我打开应用程序并等待一段时间然后单击地图选项卡时,地图就会开始加载。当我再次单击主页选项卡,然后再次单击地图选项卡时,地图仍会刷新,并且不会按原样保存地图(缩放级别、缺少标记等)。
    • 您可能需要将那些在 Fragment 的onSaveInstanceState 中保存并在onCreateView 中恢复必要的值,然后
    • 对不起,我可能遗漏了一些东西。如果我必须设置缩放级别,标记返回它就需要动画和放大以及所有这些。我希望它立即拥有与以前完全相同的地图,而无需缩小地图并读取标记。这也会在应用程序启动时预加载地图片段吗?基本上以前我使用的是viewpager。使用使用 setOffscreenPageLimit() 的 viewpager,我能够预加载页面(片段),当我滑动/单击选项卡时,页面就像以前一样。我正在尝试模仿相同的行为。
    • 如果 ViewPager 以前可以工作,为什么要删除它?请参阅“ViewPager 集成”。 developer.android.com/reference/android/support/design/widget/…
    • 我想使用 viewpager,但遇到了一个我无法解决的问题:stackoverflow.com/questions/43290476/… 所以我现在正在尝试不同的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多