【问题标题】:Calling methods on the current Fragment in a ViewPager from Activity从 Activity 调用 ViewPager 中当前 Fragment 的方法
【发布时间】:2014-10-13 19:12:23
【问题描述】:

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

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

我能够正确地“绘制”用户界面,它按预期显示在屏幕上。这一切都有效。当后台线程向我的包含活动发送消息时,我遇到的困难是调用一个方法。该线程运行时间很长,并且会频繁更新每个 TextView(想想倒计时)。

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

我试图辨别“当前”的屏幕片段是什么,以便我可以调用它的方法来更新它的 TextViews。我正在尝试使用我读过的 findFragmentByTag 方法,但它总是返回一个 null 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;
}

基于阅读大量内容,特别是这篇文章 - How to get existing fragments when using 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<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" />

【问题讨论】:

    标签: java android android-activity android-fragments android-viewpager


    【解决方案1】:

    好吧,我放弃了正常的方式,转而使用绿色机器人基于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<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);
        }
    
    }
    

    这是一个片段。它所做的只是配置它的视图,并监听 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 或线程问题的人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-15
      • 2012-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多