【问题标题】:Tips on How to Move Location-connecting Code from Fragment to MainActivity?如何将位置连接代码从 Fragment 移动到 MainActivity 的提示?
【发布时间】:2017-12-03 17:26:55
【问题描述】:

所以我有一个应用开始它的生命有 4 个独立的活动,但最近我决定最好有 1 个主要活动和 4 个片段。

我现在的问题是我之前的 2 个活动,现在是片段,其中包含位置连接代码,我需要将代码移动到我的 MainActivity 以便应用程序正确请求位置访问等,而无需崩溃。

两个片段中的每个片段中的代码几乎相同,因此我将在下面的示例中只展示两个片段中的一个。

我主要关心的问题之一是这个特定的代码:

private void handleNewLocation(Location location) {

        Log.i("TEST handleNewLocation", "Called handleNewLocation");
        // Returns the current adapter in use by the ListView supplied (mListView).
        LocationsAdapter adapter = (LocationsAdapter) mListView.getAdapter();
        adapter.setCurrentLocation(location);
        adapter.notifyDataSetChanged();
    }

如您所见,代码引用了 ListView,片段利用该 ListView 获取该 ListView 的正在使用的适配器:LocationsAdapter adapter = (LocationsAdapter) mListView.getAdapter();

我不知道如何将这段代码移动到 MainActivity 中,并且仍然以某种方式从片段中引用 ListView。为了增加这个问题,我还需要在 MainActivity 中再次使用相同类型的代码,但在 2 的第二个片段中也引用 other ListView。

这是我的片段之一,也是它下面的 MainActivity。

位置片段:

public class Locations extends Fragment implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener, LocationListener {

    public Locations() {
        // Required empty public constructor
    }

    private static final int PERMISSION_REQUEST_ACCESS_FINE_LOCATION = 100;

    private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;

    private static final String LOG_TAG = Locations.class.getName();

    private GoogleApiClient mGoogleApiClient;

    private LocationRequest mLocationRequest;

    Location mLocation = new Location(LocationManager.NETWORK_PROVIDER);

    private ListView mListView;

    public List<Word> locations = new ArrayList<>();

    // If the app already has the permission to access the user's location at high accuracy
    // (fine location), then connect to the GoogleApiClient.
    // If not, no connection-handling methods will be called.
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_ACCESS_FINE_LOCATION: {

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

                    mGoogleApiClient.connect();
                }
            }
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.word_list, container, false);

        mListView = (ListView) rootView.findViewById(R.id.list);

        // The following code checks if the app has permission to access the user's fine location,
        // and requests the permission if necessary:
        if (ContextCompat.checkSelfPermission(getActivity(), ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                    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 MaterialStyledDialog.Builder(getActivity())
                        .setTitle(R.string.title_location_permission)
                        .setDescription(R.string.text_location_permission)
                        .setIcon(R.drawable.ic_place_white_48dp)
                        .withDialogAnimation(true)
                        .setPositiveText(android.R.string.ok)
                        .onPositive(new MaterialDialog.SingleButtonCallback() {
                            @Override
                            public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(getActivity(),
                                        new String[]{ACCESS_FINE_LOCATION},
                                        PERMISSION_REQUEST_ACCESS_FINE_LOCATION);
                            }
                        })
                        .setNegativeText(android.R.string.no)
                        .show();

            } else {

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

                // PERMISSION_REQUEST_ACCESS_FINE_LOCATION is an
                // app-defined int constant. The callback method gets the
                // result of the request.
            }
        }

        // Create the GoogleAPIClient object.
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }

        // Create the LocationRequest object
        mLocationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(10 * 1000)        // 10 seconds, in milliseconds
                .setFastestInterval(1 * 1000); // 1 second, in milliseconds\


        locations.add(new Word("Bristol", R.drawable.bristol,
                41.674009, -71.279006));
        locations.add(new Word("Warren", R.drawable.warren_harbor,
                41.730647, -71.282688));
        locations.add(new Word("Newport", R.drawable.newport_breakers,
                41.486677, -71.315144));
        locations.add(new Word("Jamestown", R.drawable.jamestown,
                41.496313, -71.368435));
        locations.add(new Word("Beavertail Lighthouse", R.drawable.beavertail,
                41.458054, -71.395744));
        locations.add(new Word("Providence", R.drawable.providence,
                41.830279, -71.414955));
        locations.add(new Word("Roger Williams Park", R.drawable.roger_williams_park,
                41.785836, -71.418525));
        locations.add(new Word("Colt State Park", R.drawable.colt_state_park,
                41.682970, -71.297362));
        locations.add(new Word("Blithewold", R.drawable.blithewold,
                41.6540652,-71.2681633));
        locations.add(new Word("Narragansett", R.drawable.narragansett,
                41.433179,-71.457148));
        locations.add(new Word("Barrington", R.drawable.barrington_town_hall,
                41.740674, -71.308610));

        ViewCompat.setNestedScrollingEnabled(mListView, true);

        mListView.setAdapter(new LocationsAdapter(getActivity(), locations, mLocation));

        return rootView;
    }

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

    @Override
    public void onStart() {
        // If the app already has the permission to access the user's location at high accuracy
        // (fine location), then connect to the GoogleApiClient.
        if (ContextCompat.checkSelfPermission(getActivity(),
                ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED || mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
        super.onStart();
    }

    @Override
    public void onResume() {
        // If the app already has the permission to access the user's location at high accuracy
        // (fine location), then connect to the GoogleApiClient.
        if (ContextCompat.checkSelfPermission(getActivity(),
                ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mGoogleApiClient.connect();
        }
        super.onResume();

        // Instantiate the locations ArrayList.
        locations = new ArrayList<>();
    }

    @Override
    public void onPause() {
        super.onPause();
        // If the user has paused the app and the GoogleApiClient is currently connected,
        // remove location updating, and disconnect from the GoogleApiClient.
        if (mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,
                    this);
            mGoogleApiClient.disconnect();

            locations.clear();
        }
    }

    public void onStop() {
        // If the user has stopped the app, disconnect from the GoogleApiClient.
        mGoogleApiClient.disconnect();
        super.onStop();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.i(LOG_TAG, "Location services connected.");

        mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

        if (mLocation == null) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
                    mLocationRequest, this);
        } else {
            handleNewLocation(mLocation);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(LOG_TAG, "Location services suspended. Please reconnect.");
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        if (connectionResult.hasResolution()) {
            try {
                // Start an Activity that tries to resolve the error
                connectionResult.startResolutionForResult(getActivity(),
                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
            } catch (IntentSender.SendIntentException e) {
                e.printStackTrace();
            }
        } else {
            Log.i(LOG_TAG, "Location services connection failed with code " +
                    connectionResult.getErrorCode());
        }
    }

    @Override
    public void onLocationChanged(Location location) {

        handleNewLocation(location);
    }

    private void handleNewLocation(Location location) {

        Log.i("TEST handleNewLocation", "Called handleNewLocation");
        // Returns the current adapter in use by the ListView supplied (mListView).
        LocationsAdapter adapter = (LocationsAdapter) mListView.getAdapter();
        adapter.setCurrentLocation(location);
        adapter.notifyDataSetChanged();
    }
}

MainActivity:

public class MainActivity extends AppCompatActivity {

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

        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);

        CategoryAdapter adapter = new CategoryAdapter(this, getSupportFragmentManager());

        viewPager.setAdapter(adapter);

        // Keeps all 4 Fragments active at once
        viewPager.setOffscreenPageLimit(3);

        final SmartTabLayout tabLayout = (SmartTabLayout ) findViewById(R.id.viewpagertab);

        final LayoutInflater inflater = LayoutInflater.from(tabLayout.getContext());
        final Resources res = tabLayout.getContext().getResources();

        tabLayout.setCustomTabView(new SmartTabLayout.TabProvider() {

            @Override
            public View createTabView(ViewGroup container, int position, PagerAdapter adapter) {

                AppCompatImageView icon = (AppCompatImageView) inflater.inflate(R.layout.tab_bar_icon,container,
                        false);

                switch (position) {
                    case 0:
                        icon.setImageDrawable(res.getDrawable(R.drawable.ic_place_white_24dp));
                        break;
                    case 1:
                        icon.setImageDrawable(res.getDrawable(R.drawable.ic_restaurant_white_24dp));
                        break;
                    case 2:
                        icon.setImageDrawable(res.getDrawable(R.drawable.ic_wallpaper_white_24dp));
                        break;
                    case 3:
                        icon.setImageDrawable(res.getDrawable(R.drawable.ic_account_balance_white_24dp));
                        break;
                    default:
                        throw new IllegalStateException("Invalid position: " + position);
                }
                return icon;
            }
        });

        tabLayout.setViewPager(viewPager);

        // Fragment/Tab Titles
        final String[] mTitleNames = {"Locations", "Food", "Photos", "History"};

        // Default actionbar title
        setActionBarTitle(mTitleNames[0]);

        // Set actionbar title when tab is selected
        tabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                setActionBarTitle(mTitleNames[position]);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    public void setActionBarTitle(String title) {
        getSupportActionBar().setTitle(title);
    }
}

有没有人有任何想法和/或代码可以与我分享,说明我将如何解决我遇到的这个问题?非常感谢!

更新:

我可能应该更详细地解释一下。由于我对所有术语缺乏经验,我希望我的解释不会太难理解。

所以handleNewLocation() 的全部目的是由onLocationChanged 调用,这样当用户的位置发生变化时,它会运行handleNewLocation() 中的代码,它会获取用户的位置并针对其中定义的设置坐标运行它上面片段中的 ArrayList。这将导致两个位置之间的距离被更新为,例如 6.5 英里以外,等等,然后显示在 ListView 中的 TextView 中。

LocationsAdapter 是一个单独的 java 类,我可以在任何地方调用它。 - 所以我认为这不是问题的一部分。

我有数据类型Word,它由ArrayList 使用,并包含计算给定用户和位置坐标并将它们全部转换为DecimalFormat 以显示在给定ListView 中的TextView 中的代码。

我认为我的问题是……我需要在 Locations 类中引用 ArrayList。我需要引用我将 LocationsAdapter 设置为的 ArrayList 和 ListView 的确切实例,如:mListView.setAdapter(new LocationsAdapter(getActivity(), locations, mLocation));

我对 Java 的相对缺乏经验表现为我不知道如何复制代码的所有当前状态,而是在 MainActivity 中拥有所有获取位置的代码,但仍然可以引用 mListView对于每个片段。

这有意义吗?我很难解释它。如果有帮助,我可以提供其他类以供参考?

【问题讨论】:

  • 所有片段都有一个getActivity() 方法。如果你让你的 Activity 有一个用于适配器(或列表数据)的 getter,你的 Fragments 都可以从共享的 Activity 中获取,并填充一个 ListView
  • 您好 cricket_007,感谢您的回复!抱歉,您有什么办法可以在代码示例中向我展示这一点吗?我是一个视觉学习者,如果我没有“看到”我所教的东西,有时我可能会丢失简单的东西,如果这有意义的话! :)

标签: java android listview android-fragments


【解决方案1】:

假设您正在尝试获取位置的 ListView,这是 Activity 中位置适配器的示例

public class MainActivity extends AppCompatActivity 
    implements LocationListener {

    private LocationsAdapter adapter;

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

        // adapter = new LocationsAdapter();
    } 

    public LocationsAdapter getLocationsAdapter() { return adapter; } 

     // onLocationChanged... Add to adapter 

现在,在 Fragments 中,你会得到这样的适配器

LocationsAdapter adapter = ((MainActivity) getActivity()).getLocationsAdapter();
listview.setAdapter(adapter);

【讨论】:

  • 感谢您的回复,我用一些额外的信息更新了我的主要帖子,并尽力解释它 - 我希望它不会太混乱!让我知道我是否需要提供其他任何东西来帮助解决问题。
  • 好的,ListView 并不关心它在哪个类中。你可以只用一个 Arraylist 重建一个适配器。因此,您的 Fragments 可以创建自己的适配器和列表视图,但您需要使用与我的答案非常相似的方法从 Activity 获取 Arraylist。
  • 并且位置监听器可以很容易地移动到Activity中。我个人建议你从那里开始,而不是担心列表视图
  • 好的,明白了。所以我把所有与位置相关的代码都移到了 MainActivity 中,我现在唯一的问题就是 mListView.getAdapter()sn-p。正如预期的那样,当我尝试运行它时,它会抛出 NullPointerException,因为 mListView 引用了我的 word_list.xml 中的 ListView,这是我的 2 个片段用作它们的 ContentView,而 MainActivity 使用 activity_main.xml。由于两个 Fragment 都使用 word_list.xml,并在不同的实例中使用相同的 ListView,我想知道如何拥有两个 mListView.getAdapter() 实例,并避免任何冲突的代码?
  • 啊,天哪,我终于明白了!这一切都说得通。一切都已启动并完美运行。非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
相关资源
最近更新 更多