从Activity中调用ViewPager中当前Fragment的方法

我在Android应用程序中监听网络活动。 当事件通过网络传来时,我想更新一些驻留在我的应用程序中不同片段内的TextView。 现在我有一个单一的屏幕应用程序,一次在屏幕上有一个片段。

如何在由FragmentStatePagerAdapter管理的ViewPager内部的现有Fragment上调用方法?

我能够正确地“绘制”UI,它按预期显示在屏幕上。 这一切都有效。 我正在努力做的是在后台线程向我的包含Activity发送消息时调用方法。 该线程长时间运行并将频繁更新每个TextView(想想倒计时器)。

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

我试图辨别“当前”,屏幕上的片段是什么,以便我可以调用它上面的方法来更新其TextViews。 我试图使用我读过的findFragmentByTag方法,但它总是返回一个空的Fragment对象。

具体来说,我这样做:

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; } 

基于阅读吨,特别是这篇文章 – 如何在使用FragmentPagerAdapter时获取现有片段

这是相关代码……

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 sensorDataMap = new SparseArray(); //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 screens = new ArrayList(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

  

好吧,我放弃了正常的方式,并转移到绿色机器人基于EventBus的架构。

以下是相关的java文件,可以帮助您了解我最终在这个问题上的位置。 我基本上转移到我的网络线程中的EventBus并直接在我的片段中接收这些事件。 它极大地简化了我的代码。 在代码上有很多优化(例如将Fragment和Activity生命周期处理程序拉入中心类或接口),但这是将来的。

这是MainActivity.java文件。 现在所有它(基本上)都设置了viewpager并启动了网络线程。

 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 sensorDataMap = new SparseArray(); @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); } } 

这是片段。 它所做的只是配置其视图,并在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); } } 

这是我在网络监听器中调用的,这是Runnable的run)方法...

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

当当!

我希望这将有助于下一个在Android中遇到ViewPager,FragmentStatePagerAdapters,Fragments或线程问题的人。