简体   繁体   中英

Using Bundle to pass data between class and fragment

I'm trying to use Bundle to pass data from the Query class (i'm "setting the arguments" in the Query class) to one of the Fragment classes (i need to "getArguments()" in this fragment class), however i have done some basic debugging using logs and have seen that the fragment is being created before the Query class is being implemented. Therefore when i "getArguments" from within the fragment class it is NULL because the arguments have not been set yet in the Query class.

I want to use the data from the Query class in the fragment class. Can anybody advise me as to what to do? As you can probably tell i'm relatively new to Android and don't really understand Fragments either.

Context: I'm developing a Weather application. I have three fragments for today, tomorrow and seven day forecasts (inspired by the tabbed layout Google Now's weather card). I also have a Query class querying an online API for JSON data as well as of course the main activity class.

MainActivity class:

package com.example.desno.testnavtablayout;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;

import org.json.JSONObject;

import java.util.concurrent.ExecutionException;

import tabFragments.SevenDayFragment;
import tabFragments.TodayFragment;
import tabFragments.TomorrowFragment;

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {

    public Double latitude;
    public Double longitude;

    private static final String API_KEY = "&APPID=";                    // Placeholder for the openweathermaps API key
    private static final String QUERY_URL = "http://api.openweathermap.org/data/2.5/weather?q=";        // Placeholder for the openweathermaps URL
    private static final String UNITS = "&mode=json&units=metric&cnt=7";                                // Placeholder specifying the data type and unit type of this data being retrieved

    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide
     * fragments for each of the sections. We use a
     * {@link FragmentPagerAdapter} derivative, which will keep every
     * loaded fragment in memory. If this becomes too memory intensive, it
     * may be best to switch to a
     * {@link android.support.v4.app.FragmentStatePagerAdapter}.
     */
    private SectionsPagerAdapter mSectionsPagerAdapter;

    /**
     * The {@link ViewPager} that will host the section contents.
     */
    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);


        // Initialise the Android location manager to get the users current location automatically:
        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        String provider = locationManager.getBestProvider(new Criteria(), false);
        // Handle what happens if the user doesn't grant permission for location (this has been generated automatically):
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
        {
            //  TODO: Consider calling
            //  ActivityCompat#requestPermissions
            //  here to request the missing permissions, and then overriding
            //  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
            //  to handle the case where the user grants the permission. See the documentation
            //  for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location location = locationManager.getLastKnownLocation(provider);     // Get last known location of the user

        latitude = location.getLatitude();                               // Storage variable for the users latitude
        longitude = location.getLongitude();                             // Storage variable for the users logitude

        Query query = new Query();                                              // Create a new instance of the Query class, pass it the URL with the values of the users GPS coordinates embedded and execute it
        query.execute("http://api.openweathermap.org/data/2.5/weather?lat=" + String.valueOf(latitude) + "&lon=" + String.valueOf(longitude) + UNITS + API_KEY);
    }

    // Receives the value of searchLocationQuery and manipulates it
    private JSONObject queryForRequestedLocation(String location)
    {
        String parsedData = "";                                                                 // Placeholder for URL with embedded search value
        try
        {
            parsedData = new Query().execute(QUERY_URL + location + UNITS + API_KEY).get();     // Pass the URL with embedded search, units and API key to the Query class for parsing
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        public PlaceholderFragment() {
        }

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        /*public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }*/

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.content_main, container, false);
            TextView textView = (TextView) rootView.findViewById(R.id.section_label);
            textView.setText(getString(R.string.section_format, getArguments().getInt(ARG_SECTION_NUMBER)));

            return rootView;
        }
    }

    /**
     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/pages.
     */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {

            // getItem is called to instantiate the fragment for the given page.
            // Return a PlaceholderFragment (defined as a static inner class below).
            switch(position){
                case 0 : return TodayFragment.newInstance();
                case 1 : return TomorrowFragment.newInstance();
                case 2 : return SevenDayFragment.newInstance();
                // default: return MyFragment.newInstance();
/* It is better to use default so that it always returns a fragment and no problems would ever occur */
            }
            return null; //if you use default, you would not need to return null


            /*// getItem is called to instantiate the fragment for the given page.
            // Return a PlaceholderFragment (defined as a static inner class below).
            return PlaceholderFragment.newInstance(position + 1);*/
        }

        @Override
        public int getCount() {
            // Show 3 total pages.
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "SECTION 1";
                case 1:
                    return "SECTION 2";
                case 2:
                    return "SECTION 3";
            }
            return null;
        }
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_camera) {
            // Handle the camera action
        } else if (id == R.id.nav_gallery) {

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

}

Query class:

package com.example.desno.testnavtablayout;

/**
 * Created by desno on 30/05/2016.
 */

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

import org.json.JSONObject;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import tabFragments.TodayFragment;

public class Query extends AsyncTask<String, Void, String>
{
    private static String locationName;

    // Query for the data
    @Override
    protected String doInBackground(String... urls)
    {
        String queryResult = "";                                                        // Storage variable for the json data
        URL url;                                                                        // URL is used as address to the data needed
        HttpURLConnection urlConnection = null;                                         // Make a remote request using the HttpURLConnection class

        // Surround with try/catch so the application doesn't crash in the case of an exception such as no internet connection
        try
        {
            url = new URL(urls[0]);                                                     // Create a new URL from what is supplied
            urlConnection = (HttpURLConnection) url.openConnection();                   // Establish a URL connection and pass it to a HttpURLConnection
            InputStream inputStream = urlConnection.getInputStream();                   // Get an InputStream from the HttpURLConnection
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);   // Create an InputStreamReader to read the InputStream
            int inputStreamData = inputStreamReader.read();                             // Store the data from the InputStreamReader

            // Need a while loop because when InputStreamReader is finished the result is equal to -1
            while (inputStreamData != -1)
            {
                char current = (char) inputStreamData;
                queryResult += current;
                inputStreamData = inputStreamReader.read();
            }

            return queryResult;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    // Parse the json data
    @Override
    protected void onPostExecute(String queryResult)
    {
        super.onPostExecute(queryResult);

        try
        {
            JSONObject jsonObject = new JSONObject(queryResult);                        // Create a new json object and pass it the data from the queryResult string
            JSONObject weatherData = new JSONObject(jsonObject.getString("main"));      // Create another json object to contain more specific weather data such as all the data within the "main" braces

            Double temperature = Double.parseDouble(weatherData.getString("temp"));     // Grab the temperature value, which is currently stored as a string within the weatherData object, and store it as a double labeled "temperature"

            locationName = jsonObject.getString("name");                         // Create storage variable for the location name

            Bundle args = new Bundle();
            args.putString("LocationName", locationName);
            TodayFragment todayFragment = new TodayFragment();
            todayFragment.setArguments(args);
            Log.i("Loc working in query=", args.toString());

           // MainActivity.temperatureTextView.setText(temperature.toString()+ " °C");    // Set the value of the temperature TextView equal to the value of the temperature variable and add a degrees celsius symbol
           // MainActivity.locationTextView.setText(locationName);                        // Set the value of the location TextView equal to the value of the locationName variable

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

TodayFragment class:

package tabFragments;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import com.example.desno.testnavtablayout.Query;
import com.example.desno.testnavtablayout.R;

import java.security.PublicKey;

/**
 * Created by desno on 09/09/2016.
 */
public class TodayFragment extends Fragment
{
    Button ClickMe;
    TextView tv;
    public String loc;

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

        Bundle bundle = this.getArguments();
        if (bundle == null)
        {
            //loc = bundle.getString("LocationName");
            Log.i("Bundle is STILL null", "TodayFrag OnCreate()");
        }
    }

    public TodayFragment()
    {

    }

    public static TodayFragment newInstance()
    {
        TodayFragment fragment = new TodayFragment();
        return fragment;
    }

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

        ClickMe = (Button) rootView.findViewById(R.id.button);
        tv = (TextView) rootView.findViewById(R.id.textView2);

        ClickMe.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                if(tv.getText().toString().contains("Hello"))
                {
                    tv.setText("Hi");
                }
                else tv.setText("Hello");
            }
        });

        /*String strtext = getArguments().getString("LocationName");
        if (strtext != null)
        {
            Log.i("Location=", strtext.toString());
        }*/
        Bundle bundle = this.getArguments();
        if (bundle == null)
        {
            //loc = bundle.getString("LocationName");
            Log.i("Bundle is null", "TodayFrag OnCreateView()");
        }
        return rootView;
    }

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

        Bundle bundle = this.getArguments();
        if (bundle == null) {
            //String i = bundle.getString("LocationName");
            Log.i("Bundle is null", "in TodayFrag onActivityCreated()");
        }
    }

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

        Bundle bundle = this.getArguments();
        if (bundle == null)
        {
            //loc = bundle.getString("LocationName");
            Log.i("Bundle is STILL null", "TodayFrag OnViewStateRestored()");
        }
    }
}

try running the Query after you know the fragments have been created. here's two ideas...

one possibility might be to pass the query parameters to the TodayFragment / TomorrowFragment / SevenDayFragment and let them invoke their own queries. something like this:

public class TodayFragment extends Fragment {
    private static final String BUNDLE_KEY_SOME_PARAM = "BUNDLE_KEY_SOME_PARAM";

    public static TodayFragment newInstance(String someParam) {
        final Bundle creationArgs = new Bundle();
        creationArgs.putString(BUNDLE_KEY_SOME_PARAM, someParam);

        final TodayFragment instance = new TodayFragment();
        instance.setArguments(creationArgs);

        return instance;
    }

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

        // run the Query locally
        final String someParam = getArguments().getString(BUNDLE_KEY_SOME_PARAM);

        new Query().execute(someParam);
    }
}

if you don't like the idea putting that type of logic your fragments, you could have them call back to MainActivity to run the query as needed. maybe something like this:

interface QueryExecutor {
    void query(String someParam);
}

interface QueryResultListener {
    void results(String someResult);
}

public class TodayFragment extends Fragment implements QueryResultListener {
    private static final String BUNDLE_KEY_SOME_PARAM = "BUNDLE_KEY_SOME_PARAM";

    public static TodayFragment newInstance(String someParam) {
        final Bundle creationArgs = new Bundle();
        creationArgs.putString(BUNDLE_KEY_SOME_PARAM, someParam);

        final TodayFragment instance = new TodayFragment();
        instance.setArguments(creationArgs);

        return instance;
    }

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

        final QueryExecutor queryExecutor = (QueryExecutor)getActivity();

        final String someParam = getArguments().getString(BUNDLE_KEY_SOME_PARAM);

        queryExecutor.query(someParam);
    }

    @Override
    public void results(String someResult) {
        // handle the query results...
    }
    …
}

i hope that helps.

Why not simply doing the query from within the Fragment? That would be the simplest solution, I guess.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM