Обновите TextView в ViewPager из другого фрагмента

Я пытаюсь обновить TextView во фрагменте, нажав кнопку на другом фрагменте.

На самом деле я реализовал функцию обратного вызова для Activity, и она работает, поскольку Logcat сообщает, что текст в TextView был изменен. Проблема в том, что текстовое представление, показанное в первом фрагменте, не обновляется до нового значения! Как будто фрагмент нужно обновить или что-то в этом роде...

Вот код активности ActionBarTabsPager:

import java.util.ArrayList;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentManager.OnBackStackChangedListener;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActionBar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ActionBar.Tab;
import android.support.v4.app.SupportActivity;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.TextView;

/**
 * Demonstrates combining the action bar with a ViewPager to implement a tab UI
 * that switches between tabs and also allows the user to perform horizontal
 * flicks to move between the tabs.
 */
public class ActionBarTabsPager extends FragmentActivity implements SecondFragment.OnButtonClickedXListener{
    ViewPager  mViewPager;
    TabsAdapter mTabsAdapter;
    FragmentManager fm;



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

        setContentView(R.layout.actionbar_tabs_pager);



        if (savedInstanceState == null) {
             Fragment newFragment = new FirstFragment();
             FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
             ft.add(R.id.abs__custom, newFragment, "FirstFragment").commit();

             Fragment newFragment2 = new FirstFragment();
             FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction();
             ft2.add(R.id.abs__custom, newFragment2, "SecondFragment").commit();
        }
        getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        ActionBar.Tab tab1 = getSupportActionBar().newTab().setText("Fragment 1");
        ActionBar.Tab tab2 = getSupportActionBar().newTab().setText("Fragment 2");
        //ActionBar.Tab tab3 = getSupportActionBar().newTab().setText("Fragment 1");
        //ActionBar.Tab tab4 = getSupportActionBar().newTab().setText("Fragment 2");

        mViewPager = (ViewPager)findViewById(R.id.pager);
        mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);

            mTabsAdapter.addTab(tab1, FirstFragment.class);
            mTabsAdapter.addTab(tab2, SecondFragment.class);//LoaderCursorSupport.CursorLoaderListFragment.class);
            //mTabsAdapter.addTab(tab3, FirstFragment.class);//LoaderCustomSupport.AppListFragment.class);
            //mTabsAdapter.addTab(tab4, SecondFragment.class);//LoaderThrottleSupport.ThrottledLoaderListFragment.class);


        if (savedInstanceState != null) {
            getSupportActionBar().setSelectedNavigationItem(savedInstanceState.getInt("index"));
        }



    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("index", getSupportActionBar().getSelectedNavigationIndex());
    }

    /**
     * This is a helper class that implements the management of tabs and all
     * details of connecting a ViewPager with associated TabHost.  It relies on a
     * trick.  Normally a tab host has a simple API for supplying a View or
     * Intent that each tab will show.  This is not sufficient for switching
     * between pages.  So instead we make the content part of the tab host
     * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
     * view to show as the tab content.  It listens to changes in tabs, and takes
     * care of switch to the correct paged in the ViewPager whenever the selected
     * tab changes.
     */
    public class TabsAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
        private final Context mContext;
        private final ActionBar mActionBar;
        private final ViewPager mViewPager;
        private final ArrayList<String> mTabs = new ArrayList<String>();

        public TabsAdapter(FragmentActivity activity, ActionBar actionBar, ViewPager pager) {
            super(activity.getSupportFragmentManager());
            mContext = activity;
            mActionBar = actionBar;
            mViewPager = pager;
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);
        }

        public void addTab(ActionBar.Tab tab, Class<?> clss) {
            mTabs.add(clss.getName());
            mActionBar.addTab(tab.setTabListener(this));
            notifyDataSetChanged();
        }

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

        @Override
        public Fragment getItem(int position) {
            return Fragment.instantiate(mContext, mTabs.get(position), null);
        }


        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            mActionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            mViewPager.setCurrentItem(tab.getPosition());

        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        }
    }

    @Override
    public void OnButtonClickedX(View v) {

        if (v==findViewById(R.id.button1)){
            Log.i("TRIGGERED","TRIGGERED");

            FirstFragment ff = (FirstFragment) getSupportFragmentManager().findFragmentByTag("FirstFragment");

            View root = ff.getView();
            TextView tv = (TextView) root.findViewById(R.id.textView1);
            Log.i("Text before Edit",""+tv.getText());
            tv.setText("MODIFIED");
            Log.i("Text after Edit",""+tv.getText());




        }
        // TODO Auto-generated method stub

    }





}

Первый фрагмент:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FirstFragment extends Fragment {
    int mNum;


    /**
     * Create a new instance of FirstFragment, providing "num"
     * as an argument.
     */
    static FirstFragment newInstance(int num) {

        FirstFragment f = new FirstFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    /**
     * When creating, retrieve this instance's number from its arguments.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNum = getArguments() != null ? getArguments().getInt("num") : 1;
    }

    /**
     * The Fragment's UI is just a simple text view showing its
     * instance number.
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.myfrag1, container, false);
        return v;
    }




}

Второй фрагмент:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.SupportActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;

public  class SecondFragment extends Fragment {
    int mNum;
    OnButtonClickedXListener mListener;

    /**
     * Create a new instance of CountingFragment, providing "num"
     * as an argument.
     */
    static SecondFragment newInstance(int num) {
        SecondFragment f = new SecondFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    /**
     * When creating, retrieve this instance's number from its arguments.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNum = getArguments() != null ? getArguments().getInt("num") : 1;
    }

    /**
     * The Fragment's UI is just a simple text view showing its
     * instance number.
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.myfrag2, container, false);
        View button1 = v.findViewById(R.id.button1);
        button1.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mListener.OnButtonClickedX(v);
                // TODO Auto-generated method stub

            }
        });



        return v;
    }

    public interface OnButtonClickedXListener{
        public void OnButtonClickedX(View v);
    }

    @Override
    public void onAttach(SupportActivity activity) {
        // TODO Auto-generated method stub
        super.onAttach(activity);
        try {
            mListener = (OnButtonClickedXListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnButtonClickedXListener");
        }
    }

}

myfrag1.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >



    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center"
        android:text="No String"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

myfrag2.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="Button" />

</LinearLayout>

ИЗМЕНИТЬ:

Даже изменение ContainerViewId на 0 в ft.add() никак не влияет на окончательный рендеринг. ТАК Я предполагаю, что рендеринг управляется

mTabsAdapter.addTab(tab1, FirstFragment.class);
mTabsAdapter.addTab(tab2, SecondFragment.class);

Тема все равно та же.

Вот actionbar_tabs_pager.xml

<?xml version="1.0" encoding="utf-8"?>

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

person Mariux    schedule 05.02.2012    source источник


Ответы (4)


РЕШЕНО!

Переопределение instantiateItem() в TabsAdapter и добавление ViewPager в качестве ContainerViewID в FragmentTransaction сделали это!

Вот и работает вся FragmentActivity!

import java.util.ArrayList;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentManager.OnBackStackChangedListener;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActionBar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ActionBar.Tab;
import android.support.v4.app.SupportActivity;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.TextView;

/**
 * Demonstrates combining the action bar with a ViewPager to implement a tab UI
 * that switches between tabs and also allows the user to perform horizontal
 * flicks to move between the tabs.
 */
public class ActionBarTabsPager extends FragmentActivity implements SecondFragment.OnButtonClickedXListener{
    ViewPager  mViewPager;
    TabsAdapter mTabsAdapter;
    FragmentManager fm;



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

        Log.i("ONCREATE START","ONCREATE START");


        setContentView(R.layout.actionbar_tabs_pager);


        if (savedInstanceState == null) {


            Fragment newFragment = new FirstFragment();
            Fragment newFragment2 = new SecondFragment();

            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.add(R.id.pager, newFragment, "FirstFragment");
            ft.add(R.id.pager, newFragment2, "SecondFragment");
            ft.commit();


       }

        getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        ActionBar.Tab tab1 = getSupportActionBar().newTab().setText("Fragment 1");
        ActionBar.Tab tab2 = getSupportActionBar().newTab().setText("Fragment 2");
        //ActionBar.Tab tab3 = getSupportActionBar().newTab().setText("Fragment 1");
        //ActionBar.Tab tab4 = getSupportActionBar().newTab().setText("Fragment 2");

        mViewPager = (ViewPager)findViewById(R.id.pager);
        mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);

            mTabsAdapter.addTab(tab1, FirstFragment.class);
            mTabsAdapter.addTab(tab2, SecondFragment.class);//LoaderCursorSupport.CursorLoaderListFragment.class);
            //mTabsAdapter.addTab(tab3, FirstFragment.class);//LoaderCustomSupport.AppListFragment.class);
            //mTabsAdapter.addTab(tab4, SecondFragment.class);//LoaderThrottleSupport.ThrottledLoaderListFragment.class);



        if (savedInstanceState != null) {
            getSupportActionBar().setSelectedNavigationItem(savedInstanceState.getInt("index"));
        }

        Log.i("ONCREATE END","ONCREATE END");

    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        Log.i("onSaveInstanceState START","onSaveInstanceState START");
        super.onSaveInstanceState(outState);
        outState.putInt("index", getSupportActionBar().getSelectedNavigationIndex());
        Log.i("onSaveInstanceState END","onSaveInstanceState END");

    }

    /**
     * This is a helper class that implements the management of tabs and all
     * details of connecting a ViewPager with associated TabHost.  It relies on a
     * trick.  Normally a tab host has a simple API for supplying a View or
     * Intent that each tab will show.  This is not sufficient for switching
     * between pages.  So instead we make the content part of the tab host
     * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
     * view to show as the tab content.  It listens to changes in tabs, and takes
     * care of switch to the correct paged in the ViewPager whenever the selected
     * tab changes.
     */
    public class TabsAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
        private final Context mContext;
        private final ActionBar mActionBar;
        private final ViewPager mViewPager;
        private final ArrayList<String> mTabs = new ArrayList<String>();
        private FragmentTransaction mCurTransaction = null;


        public TabsAdapter(FragmentActivity activity, ActionBar actionBar, ViewPager pager) {
            super(activity.getSupportFragmentManager());
            mContext = activity;
            mActionBar = actionBar;
            mViewPager = pager;
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);


        }

        public void addTab(ActionBar.Tab tab, Class<?> clss) {
            Log.i("addTab","addTab");

            mTabs.add(clss.getName());
            mActionBar.addTab(tab.setTabListener(this));
            notifyDataSetChanged();
        }

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

        @Override
        public Object instantiateItem(View container, int position) {
            if (mCurTransaction == null) {
                mCurTransaction = getSupportFragmentManager().beginTransaction();
            }
            // TODO Auto-generated method stub
            Fragment fragment = getItem(position);

            if (fragment!=null){
                Log.i("Fragment Found!","Fragment Found! "+fragment.getTag());
                mCurTransaction.attach(fragment);
                }


            return fragment;//super.instantiateItem(container, position);
        }

        @Override
        public Fragment getItem(int position) {
            Log.i("getItem","getItem");

            if (position==0)
                {Log.i("position=0","position=0");
                return getSupportFragmentManager().findFragmentByTag("FirstFragment");}

            else if (position==1)
            {Log.i("position=1","position=1");
                return getSupportFragmentManager().findFragmentByTag("SecondFragment");}

            else return null;//Fragment.instantiate(mContext, mTabs.get(position), null);

        }


        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            mActionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            mViewPager.setCurrentItem(tab.getPosition());

        }

        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        }


    }

    @Override
    public void OnButtonClickedX(View v) {

        if (v==findViewById(R.id.button1)){
            Log.i("TRIGGERED","TRIGGERED");

            FirstFragment ff = (FirstFragment) getSupportFragmentManager().findFragmentByTag("FirstFragment");

            View root = ff.getView();
            TextView tv = (TextView) root.findViewById(R.id.textView1);
            Log.i("Text before Edit",""+tv.getText());
                  tv.setText("MODIFIED");
                  Log.i("Text after Edit",""+tv.getText());




        }
        // TODO Auto-generated method stub

    }





}

Спасибо vbsteven!

person Mariux    schedule 09.02.2012

Я предполагаю, что фрагменты, которые вы видите на экране, — это не те фрагменты, которые вы вручную добавляете с помощью тегов «FirstFragment» и «SecondFragment», а вместо этого фрагменты, которые добавляются методом getItem вашего TabsAdapter.

Если вы посмотрите на его реализацию, он создает новые фрагменты.

    @Override
    public Fragment getItem(int position) {
        return Fragment.instantiate(mContext, mTabs.get(position), null);
    }

Эти вновь созданные фрагменты добавляются в FragmentManager с пользовательскими тегами, сгенерированными в FragmentPagerAdapter (который расширяет TabsAdapter).

person vbsteven    schedule 09.02.2012
comment
Я закомментировал FragmentTransaction, и на самом деле пользовательский интерфейс создан правильно. SO, вероятно, вызывается getItem(), когда mTabsAdapter.addTab(tab1, FirstFragment.class); mTabsAdapter.addTab(tab2, SecondFragment.class); называется. Итак, как я могу вернуть экземпляры FirstFragment и SecondFragment без повторного создания ими экземпляра? - person Mariux; 09.02.2012
comment
Вам придется переопределить методы instanceItem/getItem, чтобы они использовали ваши фрагменты вместо создания новых. Вероятно, лучше создать новый подкласс PagerAdapter (для справки см. исходный код FragmentPagerAdapter), который учитывает существующие фрагменты. - person vbsteven; 09.02.2012

Я предполагаю, что это случай "перекрывающихся" фрагментов - не редкость для тех, кто впервые пробует фрагменты. Каждый раз, когда вы повторно отображаете фрагмент (отображаете фрагмент, показываете другой, затем возвращаетесь к первому отображаемому фрагменту), вы создаете новый фрагмент позади видимого фрагмента. «Фрагмент зеркала» остается сверху, закрывая вам вид на фрагмент позади него. Только прокручивая этот фрагмент, вы увидите, что фрагмент позади действительно обновляется.

В большинстве случаев это просто случай, когда вы повторяете то, что вы делали в XML, в своей FragmentActivity. Опубликуйте свой макет xml, чтобы специалисты по фрагментам могли лучше диагностировать вашу проблему.

person josephus    schedule 05.02.2012
comment
Я не думаю, что это проблема макета, так как фрагменты не объявлены в xml, все они добавлены из исходного кода.. возможно, что-то есть в TabsAdapter, но я не могу понять это.. - person Mariux; 06.02.2012

Также опубликуйте файл actionbar_tabs_pager.xml.

Не знаю, решит ли это ваши проблемы, но в этой части вашего кода вы добавляете FirstFragment() для обоих фрагментов...

Fragment newFragment = new FirstFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.abs__custom, newFragment, "FirstFragment").commit();

Fragment newFragment2 = new FirstFragment();
FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction();
ft2.add(R.id.abs__custom, newFragment2, "SecondFragment").commit();

... и вы могли бы написать это проще:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.abs__custom, newFragment, "FirstFragment");
ft.add(R.id.abs__custom, newFragment2, "SecondFragment");
ft.commit();
person ramdroid    schedule 06.02.2012
comment
Это не решает проблему, однако спасибо за указание на это. оказалось, что даже присвоение 0 функции ft.add() для макета, как в 'ft.add(0, newFragment, FirstFragment);' не влияет на визуализацию. Я предполагаю, что «mTabsAdapter.addTab (tab1, FirstFragment.class);» это то, что отображает макет, а не транзакцию. - person Mariux; 06.02.2012
comment
И где в xml вы определили R.id.abs__custom? - person ramdroid; 07.02.2012
comment
R.id.abs__custom.xml не имеет значения, взгляните на редактирование! - person Mariux; 09.02.2012