java - method - fragment android example



Appel de méthodes sur le fragment actuel dans un ViewPager à partir de l'activité (1)

J'écoute l'activité réseau dans une application Android. Lorsque des événements surviennent sur le réseau, je voudrais mettre à jour certaines TextView qui résident à l'intérieur de différents Fragments dans mon application. À l'heure actuelle, j'ai une application à écran unique où un fragment est sur l'écran à la fois.

Comment puis-je appeler une méthode sur un fragment existant à l'intérieur d'un ViewPager, géré par un FragmentStatePagerAdapter?

Je suis capable de "peindre" l'interface utilisateur correctement, il obtient à l'écran comme prévu. Tout ça fonctionne Ce que j'ai du mal à faire, c'est d'appeler une méthode quand un thread d'arrière-plan envoie un message à mon Activity contenant. Le thread est long et mettra à jour chaque TextView fréquemment (pensez à un compte à rebours).

{thread} -> Actvity.handleMessage () -> {current} Fragment.update ();

J'essaie de discerner ce qu'est le Fragment à l'écran "courant" afin que je puisse appeler une méthode dessus pour mettre à jour ses TextViews. J'essaie d'utiliser l'approche findFragmentByTag que j'ai lue, mais elle renvoie toujours un objet Fragment nul.

Spécifiquement je fais ceci:

private String getFragmentTag(int viewPagerId, int fragmentPosition){
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}

//Take the data from the thread (network) and react to it.
@Override
public boolean handleMessage(Message msg) {
    //tell the fragment to update itself        
    //Log.v(TAG, "Handler: " + msg.what);

    int currentFragmentIndex = pager.getCurrentItem();
    Log.v(TAG, "Current Fragment Index: " + Integer.toString(currentFragmentIndex));

    String fTag = getFragmentTag(pager.getId(), currentFragmentIndex );
    Log.v(TAG, "Current Fragment Tag: " + fTag);

    ////THIS IS WHERE I AM HAVING TROUBLE!////
    Log.v(TAG, "fragment: " + getSupportFragmentManager().findFragmentByTag(fTag));
    ////THIS ALWAYS OUTPUTS:
    ////fragment: null



    //now do the update
    //currentFragment.update(sensorDataMap);

    return true;
}

Basé sur la lecture d'une tonne et spécifiquement ce post - Comment obtenir des fragments existants lors de l'utilisation de FragmentPagerAdapter

Voici le code pertinent ...

MainActivity.java

public class MainActivity extends FragmentActivity implements Callback {
    //receive messages from the other thread
    private  Handler handler = new Handler(this);

    //the runnable listening to the network
    private UDPListener udpl;

    //we will parse the handler data into this map and store the values by int keys
    private SparseArray<String> sensorDataMap = new SparseArray<String>();

    //for logging and convenience
    private static final String TAG = "BRRT";   

    //set up the fragment manager
    private ViewPager pager;
    private ScreenFragmenStatePagerAdapter fragmentManager;
    private ScreenFragment currentFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //remove the titlebar
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        //keep the screen on while we are running
        this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        //set up the pager to move through the fragments
        pager = (ViewPager) findViewById(R.id.viewPager);
        fragmentManager = new ScreenFragmenStatePagerAdapter(getSupportFragmentManager());
        pager.setAdapter(fragmentManager);

        //start up the thread
        Log.v(TAG, "start thread");
        startUDPListener(handler);
        Log.v(TAG, "past start thread");    
    }


    //start up the thread to listen to the network, pass in the handle to this thread for messages
    private void startUDPListener(Handler handler){
        //spawn the listener thread, pass in the context and the handler object
        try{
            udpl = new UDPListener(this.getApplicationContext(), handler, sensorDataMap);

            //TODO remove this debug flag
            //DEBUG
            udpl.setDebug(true);
            //DEBUG 

            //set up the threaded reading, event throwing
            Thread reader = new Thread(udpl);
            reader.start();

        } catch(Exception e){
            //TODO real logging
            Log.v(TAG, "caught after trying to start thread");
            Log.v(TAG, e.getMessage());
        }

    }

    //fragile, as this depends on the current naming convention for these fragment IDs in support/v4.
    private String getFragmentTag(int viewPagerId, int fragmentPosition){
         return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
    }

    //Take the data from the thread (network) and react to it. This will get back unparsed strings
    //and have to parse them in this thread, with the UI
    @Override
    public boolean handleMessage(Message msg) {
        //tell the fragment to update itself        
        //Log.v(TAG, "Handler: " + msg.what);

        int currentFragmentIndex = pager.getCurrentItem();
        Log.v(TAG, "Current Fragment Index: " + Integer.toString(currentFragmentIndex));

        String fTag = getFragmentTag(pager.getId(), currentFragmentIndex );
        Log.v(TAG, "Current Fragment Tag: " + fTag);


        Log.v(TAG, "fragment: " + getSupportFragmentManager().findFragmentByTag(fTag));
        //Log.v(TAG, "Current Fragment Null? " + (currentFragment == null));


        //now do the update
        //currentFragment.update(sensorDataMap);

        return true;
    }

    @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();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

}

ScreenFragmenStatePagerAdapter .java

public class ScreenFragmenStatePagerAdapter extends FragmentStatePagerAdapter {
    //the set of screens we are interested in.
    private ArrayList<ScreenConfig> screens = new ArrayList<ScreenConfig>(6);

    //for logging and convenience
    private static final String TAG = "CC";

    //convenience variable to let us know what screen we are pointing at, logically
    private ScreenConfig currentScreenConfig;

    public ScreenFragmenStatePagerAdapter(FragmentManager fm) {
        super(fm);

        //set up the data.
        setupScreenData();
    }


    @Override
    public Fragment getItem(int pos) {
        //Log.v(TAG, "fragment pos: " + pos);   

        //hopefully this will always work
        currentScreenConfig = screens.get(pos);
        ScreenFragment sf;

        if (currentScreenConfig.eventCount() == 1){
            sf = OneUpScreen.newInstance();
        } else {
            sf = FourUpScreen.newInstance();
        }

        sf.setConfig(currentScreenConfig);
        return sf;
    }


    @Override
    public int getCount() {
        return screens.size();
    }


    //class that maps the various events to their descriptions in a screen by screen way
    //do all the grunt work of mapping screens to events
    private void setupScreenData(){
        //TODO convert this stuff to JSON so we can pass them around in Bundles

        //a 1up screen
        ScreenConfig configGun = new ScreenConfig("Gun");
        configGun.addEvent(204, "TimeToGun","Ttg", "TTG");
        screens.add(configGun);

        //the rest are 4up screens
        ScreenConfig configBowman = new ScreenConfig("Bowman");
        configBowman.addEvent(5, "ExTws","Tws", "TWS");
        configBowman.addEvent(34, "ExLayTimeOnStrb", "LayTimeOnStrb", "Time Stb");
        configBowman.addEvent(37, "ExLayTimeOnPort", "LayTimeOnPort", "Time Prt");
        configBowman.addEvent(113, "ExNextMarkTwa", "NextMarkTwa", "TWA");
        screens.add(configBowman);

        ScreenConfig configPit = new ScreenConfig("Pit");
        configPit.addEvent(88, "ExMarkTime","MarkTime", "Time Mark");
        configPit.addEvent(113, "ExNextMarkTwa", "NextMarkTwa", "Next TWA");
        configPit.addEvent(111, "ExNextMarkRng", "NextMarkRng", "Next Range");
        configPit.addEvent(112, "ExNextMarkBrg", "NextMarkBrg", "Next Brg");        
        screens.add(configPit);

        ScreenConfig configUpwindTrimmer = new ScreenConfig("Upwind Trimmer");
        configUpwindTrimmer.addEvent(5, "ExTws","Tws", "TWS");
        configUpwindTrimmer.addEvent(1, "ExBsp", "Bsp", "BSP");
        configUpwindTrimmer.addEvent(54, "ExTargBspN", "TargBspN", "Tgt BSP");
        configUpwindTrimmer.addEvent(4, "ExTwa", "Twa", "TWA");     
        screens.add(configUpwindTrimmer);   

        ScreenConfig configDownwindTrimmer = new ScreenConfig("Downwind Trimmer");
        configDownwindTrimmer.addEvent(5, "ExTws","Tws", "TWS");
        configDownwindTrimmer.addEvent(4, "ExTwa", "Twa", "TWA");
        configDownwindTrimmer.addEvent(53, "ExTargTwaN", "TargTwaN", "Tgt BSP");
        configDownwindTrimmer.addEvent(58, "ExPolarBspPercent", "PolarBspPercent", "P Bsp %");      
        screens.add(configDownwindTrimmer);

        ScreenConfig configHelmsman = new ScreenConfig("Helmsman");
        configHelmsman.addEvent(5, "ExTws","Tws", "TWS");
        configHelmsman.addEvent(1, "ExBsp", "Bsp", "BSP");
        configHelmsman.addEvent(6, "ExTwd", "Twd", "TWD");
        configHelmsman.addEvent(4, "ExTwa", "Twa", "TWA");      
        screens.add(configHelmsman);

    }
}

activty_main.xml

<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/viewPager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

Answer #1

Ok, j'ai renoncé à la manière normale et je suis passé à une architecture basée sur EventBus par un robot vert.

Voici les fichiers java pertinents pour vous aider à voir où je me suis retrouvé sur ce problème. Je suis passé à l'affichage d'un EventBus dans mon thread réseau et à la réception directe de ces événements dans mes Fragments. Cela a grandement simplifié mon code. Il y a beaucoup d'optimisation à faire sur le code (des choses comme tirer les gestionnaires de cycle de vie Fragment et Activity dans une classe ou une interface centrale) mais c'est dans le futur.

Voici le fichier MainActivity.java. Maintenant tout ce qu'il fait (essentiellement) est mis en place le viewpager et lancer le thread réseau.

public class MainActivity extends FragmentActivity {
    //the runnable listening to the network
    private UDPListener udpl;

    //for logging and convenience
    private static final String TAG = "BRRT";   

    //set up the fragment manager
    private ViewPager pager;
    private ScreenFragmenStatePagerAdapter fragmentManager;

    //we will parse the handler data into this map and store the values by int keys
    private SparseArray<String> sensorDataMap = new SparseArray<String>();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //remove the titlebar
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        //keep the screen on while we are running
        this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        //set up the pager to move through the fragments
        pager = (ViewPager) findViewById(R.id.viewPager);
        fragmentManager = new ScreenFragmenStatePagerAdapter(getSupportFragmentManager());
        pager.setAdapter(fragmentManager);
    }

    @Override
    public void onResume(){

        //start up the thread
        Log.v(TAG, "start thread");
        startUDPListener();
        Log.v(TAG, "past start thread");    

        super.onResume();
    }


    //start up the thread to listen to the network, pass in the handle to this thread for messages
    private void startUDPListener(){
        //spawn the listener thread, pass in the context and the handler object
        try{
            udpl = new UDPListener(this.getApplicationContext(), sensorDataMap);

            //TODO remove this debug flag
            //DEBUG
            udpl.setDebug(true);
            //DEBUG 

            //set up the threaded reading, event throwing
            Thread reader = new Thread(udpl);
            reader.start();

        } catch(Exception e){
            //TODO real logging
            Log.v(TAG, "caught after trying to start thread");
            Log.v(TAG, e.getMessage());
        }

    }

    @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();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

}

Voici un fragment. Tout ce qu'il fait est de configurer sa vue, et écouter les événements sur le EventBus.

public class OneUpScreen extends ScreenFragment {   
    private Button name;
    private TextView c00Title, c00Data;

    //DEBUG
    public String title;

    //DEBUG
    private int counter = 0;

    //for logging and convenience
    private static final String TAG = "BRRT";   


    @Override
    public void onResume(){
        EventBus.getDefault().register(this);
        super.onResume();
    }

    @Override
    public void onDestroy(){
        EventBus.getDefault().unregister(this);
        super.onDestroy();
    }

    //process the bus messaging
    public void onEventMainThread(ExpeditionEvent event){      <-- READ IT FROM THE BUS
        Log.v(TAG, "OneUp event received: " + event.getEventId() + " : " + event.getEventScreenValue());
        c00Data.setText(""+counter);
        counter++;
    }


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

        name = (Button) v.findViewById(R.id.lblTitle);
        c00Title = (TextView) v.findViewById(R.id.c00Title);
        c00Data = (TextView) v.findViewById(R.id.c00Data);

        Bundle bundle = this.getArguments();

        //initial set up of the text on the screen
        //null check
        name.setText((bundle.getString("name") == null) ? "error" : bundle.getString("name"));
        c00Title.setText((bundle.getString("c00Title") == null) ? "error" : bundle.getString("c00Title"));
        title = bundle.getString("c00Title");

        return v;
    }

    public OneUpScreen(){

    }

    public static OneUpScreen newInstance() {
        OneUpScreen frag = new OneUpScreen();
        return frag;
    }

    //attach the config for this instance.
    @Override
    public void setConfig(ScreenConfig sc){
        //set up the data to paint the screen for the first time
        Bundle b = new Bundle();

        //now we have to parse some stuff into a bundle and send the bundle to the fragment
        b.putString("name", sc.getName());
        b.putString("c00Title", sc.getEvents().get(0).getCleanName());
        b.putInt("c00Data", sc.getEvents().get(0).getEventID());

        //pass it along
        this.setArguments(b);
    }

}

Voici ce que j'appelle dans l'écouteur de réseau, c'est dans la course) la méthode du Runnable ...

//throw out an event for each item
EventBus.getDefault().post(new ExpEvent(key, value));   <-- PUT IT ON THE BUS

Ta-DA !!!

J'espère que cela aidera la prochaine personne qui a des problèmes avec ViewPager, FragmentStatePagerAdapters, Fragments ou le filetage dans Android.





android-viewpager