অনক্রিয়াটভিউ এবং অ্যাক্টিভিটিক্রিটযুক্ত টুকরোটিকে দুটিবার কল করা হয়


102

আমি অ্যান্ড্রয়েড 4.0.০ আইসিএস এবং খণ্ড ব্যবহার করে একটি অ্যাপ্লিকেশন বিকাশ করছি।

আইসিএস ৪.০.৩ (এপিআই স্তর 15) এপিআইয়ের ডেমো উদাহরণ অ্যাপ্লিকেশন থেকে এই পরিবর্তিত উদাহরণটি বিবেচনা করুন:

public class FragmentTabs extends Activity {

private static final String TAG = FragmentTabs.class.getSimpleName();

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

    final ActionBar bar = getActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

    bar.addTab(bar.newTab()
            .setText("Simple")
            .setTabListener(new TabListener<SimpleFragment>(
                    this, "mysimple", SimpleFragment.class)));

    if (savedInstanceState != null) {
        bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab"));
        Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number"));
    }

}

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

public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;
    private final Bundle mArgs;
    private Fragment mFragment;

    public TabListener(Activity activity, String tag, Class<T> clz) {
        this(activity, tag, clz, null);
    }

    public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        mArgs = args;

        // Check to see if we already have a fragment for this tab, probably
        // from a previously saved state.  If so, deactivate it, because our
        // initial state is that a tab isn't shown.
        mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
        if (mFragment != null && !mFragment.isDetached()) {
            Log.d(TAG, "constructor: detaching fragment " + mTag);
            FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
            ft.detach(mFragment);
            ft.commit();
        }
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
            Log.d(TAG, "onTabSelected adding fragment " + mTag);
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            Log.d(TAG, "onTabSelected attaching fragment " + mTag);
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            Log.d(TAG, "onTabUnselected detaching fragment " + mTag);
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
    }
}

public static class SimpleFragment extends Fragment {
    TextView textView;
    int mNum;

    /**
     * When creating, retrieve this instance's number from its arguments.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(FragmentTabs.TAG, "onCreate " + (savedInstanceState != null ? ("state " + savedInstanceState.getInt("number")) : "no state"));
        if(savedInstanceState != null) {
            mNum = savedInstanceState.getInt("number");
        } else {
            mNum = 25;
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        Log.d(TAG, "onActivityCreated");
        if(savedInstanceState != null) {
            Log.d(TAG, "saved variable number: " + savedInstanceState.getInt("number"));
        }
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        Log.d(TAG, "onSaveInstanceState saving: " + mNum);
        outState.putInt("number", mNum);
        super.onSaveInstanceState(outState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(FragmentTabs.TAG, "onCreateView " + (savedInstanceState != null ? ("state: " + savedInstanceState.getInt("number")) : "no state"));
        textView = new TextView(getActivity());
        textView.setText("Hello world: " + mNum);
        textView.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
        return textView;
    }
}

}

এই উদাহরণটি চালানো এবং তারপরে ফোনটি ঘোরানো থেকে আউটপুটটি এখানে পাওয়া গেছে:

06-11 11:31:42.559: D/FragmentTabs(10726): onTabSelected adding fragment mysimple
06-11 11:31:42.559: D/FragmentTabs(10726): onCreate no state
06-11 11:31:42.559: D/FragmentTabs(10726): onCreateView no state
06-11 11:31:42.567: D/FragmentTabs(10726): onActivityCreated
06-11 11:31:45.286: D/FragmentTabs(10726): onSaveInstanceState saving: 25
06-11 11:31:45.325: D/FragmentTabs(10726): onCreate state 25
06-11 11:31:45.340: D/FragmentTabs(10726): constructor: detaching fragment mysimple
06-11 11:31:45.340: D/FragmentTabs(10726): onTabSelected attaching fragment mysimple
06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate tab: 0
06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate number: 0
06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView state: 25
06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated
06-11 11:31:45.348: D/FragmentTabs(10726): saved variable number: 25
06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView no state
06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated

আমার প্রশ্ন হ'ল, অনক্রিটভিউ এবং অনঅ্যাক্টিভিটি ক্রিয়েটেড কেন দু'বার বলা হয়? প্রথমবার সংরক্ষিত রাষ্ট্রের সাথে কোনও বান্ডিল এবং দ্বিতীয়বার নাল সেভডআইনস্ট্যান্সস্টেটের সাথে?

এটি ঘূর্ণনের উপর টুকরাটির অবস্থা ধরে রাখতে সমস্যা সৃষ্টি করছে।


4
আমি এই প্রশ্নের সাথে সম্পর্কিত করা যেতে পারে মনে stackoverflow.com/a/8678705/404395
marioosh

উত্তর:


45

আমি এটি সম্পর্কে কিছুক্ষণের জন্য আমার মাথাও আঁচড়ালাম, এবং যেহেতু ডেভের ব্যাখ্যাটি বোঝা সামান্য কঠিন তাই আমি আমার (দৃশ্যত কাজ করা) কোড পোস্ট করব:

private class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        mFragment=mActivity.getFragmentManager().findFragmentByTag(mTag);
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.replace(android.R.id.content, mFragment, mTag);
        } else {
            if (mFragment.isDetached()) {
                ft.attach(mFragment);
            }
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            ft.detach(mFragment);
        }
    }

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

আপনি দেখতে পাচ্ছেন এটি নির্মাণকারীর মধ্যে বিচ্ছিন্ন না হওয়া এবং অ্যাডের পরিবর্তে প্রতিস্থাপন ব্যবহার করা ছাড়াও এটি অ্যান্ড্রয়েড নমুনার মতো বেশ ।

অনেকগুলি স্ক্র্যাচিং এবং ট্রায়াল-অ্যান্ড-ত্রুটির পরে আমি জানতে পেরেছিলাম যে কন্সট্রাক্টরটিতে টুকরোটি খুঁজে পাওয়া দ্বিগুণ অনক্রিয়েটভিউ সমস্যাটি যাদুকরীভাবে দূরে সরে গেছে (আমি ধরে নিই এটি অ্যান্টিবার.সেটসলেক্ট নেভিগেশন আইটেম () পাথের মাধ্যমে কল করার পরে অনট্যাবসিলেক্টের জন্য নাল হয়ে যায় when সংরক্ষণ / পুনরুদ্ধার অবস্থা)।


পুরোপুরি ভাল কাজ করে! তুমি আমার রাতের ঘুম বাঁচিয়েছ! আপনাকে ধন্যবাদ :)
জয়ব্রতিক

আপনি ক্ল্যাগ ভেরিয়েবলটি সরাতে এবং কল থেকে একটি প্যারামিটার সরাতে চাইলে ফ্রেগমেন্ট.সেট ক্লাস ()। getName () ব্যবহার করতে পারেন
বেন সেওয়ার্ডস

"পূর্ববর্তী রেফ। ট্যাবলিস্টনার" অ্যান্ড্রয়েড নমুনা - টিএনএক্স সহ পুরোপুরি কাজ করে। সর্বশেষতম অ্যান্ড্রয়েড "ট্যাবলিস্টনার রেফ।
গ্রেজগোর্জ দেব

ft.commit () পদ্ধতি কলটি কোথায় ??
এমএসউডি

4
@MuhammadBabar দেখুন stackoverflow.com/questions/23248789/... । আপনি যদি স্ক্রিনের addপরিবর্তে replaceএবং ঘোরান ব্যবহার করেন তবে আপনার অনেক টুকরো থাকবে ' onCreateView()
কুলমাইন্ড

27

আমার কেবল একই ক্রিয়াকলাপটি ছিল কেবল একটি ক্রিয়াকলাপের সাথে চালিত (যা কখনও কখনও প্রতিস্থাপন করা যায়) replaced আমি তখন উপলব্ধি করেছিলাম যে আমি অনলাইনে SaveInstanceStateটি কেবলমাত্র খণ্ডে (এবং সেভিডইনস্ট্যান্সস্টেট পরীক্ষা করার জন্য অনক্রিটভিউ) ব্যবহার করি, ক্রিয়াকলাপে নয়।

ডিভাইসটির অন্বেষণে টুকরোযুক্ত ক্রিয়াকলাপটি পুনরায় শুরু হয় এবং অনক্রিট করা হয়। সেখানে আমি প্রয়োজনীয় খণ্ডটি সংযুক্ত করেছি (যা প্রথম শুরুর ক্ষেত্রে সঠিক)।

ডিভাইসটি চালু করার পরে অ্যান্ড্রয়েড প্রথমে সেই টুকরোটি পুনরায় তৈরি করেছিল যা দৃশ্যমান ছিল এবং তারপরে যেখানে আমার খণ্ডটি সংযুক্ত ছিল সেখানে থাকা ক্রিয়াকলাপটিকে অনক্রিট বলা হয়েছিল, এইভাবে আসল দৃশ্যমানটির পরিবর্তে।

এটি এড়াতে আমি কেবল সংরক্ষিত ইনস্ট্যান্সস্টেট পরীক্ষা করার জন্য আমার ক্রিয়াকলাপটি পরিবর্তন করেছি:

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

if (savedInstanceState != null) {
/**making sure you are not attaching the fragments again as they have 
 been 
 *already added
 **/
 return; 
 }
 else{
  // following code to attach fragment initially
 }

 }

এমনকি ক্রিয়াকলাপের SaveInstanceState ওভাররাইটও করি নি।


ধন্যবাদ. ওরিয়েন্টেশন পরিবর্তনের পরে পছন্দসই খণ্ডে সংলাপগুলি প্রদর্শন করার সময় এটি আমাকে অ্যাপকম্প্যাটএটিভিটি + প্রেফারেন্সফ্র্যাগমেন্টকম্প্যাট এবং ক্রাশে সহায়তা করেছিল, কারণ খণ্ড ব্যবস্থাপক দ্বিতীয় খণ্ড তৈরির ক্ষেত্রে বাতিল ছিল।
রোকে

26

ঠিক আছে, আমি যা জানতে পেরেছি তা এখানে।

আমি যা বুঝতে পারি নি তা হ'ল কোনও কনফিগার চেঞ্জ ঘটে (ফোন ঘোরানো) ঘটে এমন একটি ক্রিয়াকলাপের সাথে সংযুক্ত সমস্ত টুকরোগুলি পুনরায় তৈরি করা হয় এবং ক্রিয়াকলাপে ফিরে যুক্ত করা হয়। (যা বোঝায়)

ট্যাবলিস্টনার নির্মাণকারীর মধ্যে যা ঘটছিল তা হ'ল ট্যাবটি যদি এটির সাথে খুঁজে পাওয়া যায় এবং কার্যকলাপের সাথে সংযুক্ত থাকে det নিচে দেখ:

mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
    if (mFragment != null && !mFragment.isDetached()) {
        Log.d(TAG, "constructor: detaching fragment " + mTag);
        FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
        ft.detach(mFragment);
        ft.commit();
    }

পরে ক্রিয়্যাট অন ক্রিয়াকলাপে পূর্ববর্তী নির্বাচিত ট্যাবটি সংরক্ষিত উদাহরণের রাজ্য থেকে নির্বাচন করা হয়েছিল। নিচে দেখ:

if (savedInstanceState != null) {
    bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
    Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab"));
    Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number"));
}

যখন ট্যাবটি নির্বাচন করা হবে তখন এটি অন্যাটিবেসলেটেড কলব্যাকে পুনরায় সংযুক্ত হবে।

public void onTabSelected(Tab tab, FragmentTransaction ft) {
    if (mFragment == null) {
        mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
        Log.d(TAG, "onTabSelected adding fragment " + mTag);
        ft.add(android.R.id.content, mFragment, mTag);
    } else {
        Log.d(TAG, "onTabSelected attaching fragment " + mTag);
        ft.attach(mFragment);
    }
}

সংযুক্ত হওয়া খণ্ডটি হ'ল অনক্রিটভিউ এবং অনঅ্যাক্টিভিটিক্রিটেড পদ্ধতিগুলির দ্বিতীয় কল। (সিস্টেমটি যখন অ্যাক্টিভিটি এবং সমস্ত সংযুক্ত টুকরো পুনরুদ্ধার করছে তখন প্রথমটি হচ্ছে) প্রথমবার অনসেভডাইন্সট্যান্সস্টেট বান্ডেল ডেটা সংরক্ষণ করেছিল তবে দ্বিতীয়বার নয়।

সমাধানটি হ'ল ট্যাবলিস্টনার নির্মাণকারীর মধ্যে টুকরোটি আলাদা না করে কেবল এটি সংযুক্ত করে রেখে দিন। (এটির ফ্রেগমেন্টম্যানেজারে এটি ট্যাগ করে এখনও আপনাকে এটি সন্ধান করতে হবে) এছাড়াও, অনট্যাবসিলিগ্রেটেড পদ্ধতিতে আমি পরীক্ষা করে দেখি যে খণ্ডটি সংযুক্ত করার আগে আলাদা করা হয়েছে কিনা। এটার মতো কিছু:

public void onTabSelected(Tab tab, FragmentTransaction ft) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
                Log.d(TAG, "onTabSelected adding fragment " + mTag);
                ft.add(android.R.id.content, mFragment, mTag);
            } else {

                if(mFragment.isDetached()) {
                    Log.d(TAG, "onTabSelected attaching fragment " + mTag);
                    ft.attach(mFragment);
                } else {
                    Log.d(TAG, "onTabSelected fragment already attached " + mTag);
                }
            }
        }

4
উল্লিখিত "ট্যাবলিস্টনার কনস্ট্রাক্টরে খণ্ড বিচ্ছিন্ন না করা" সমাধানগুলির ফলে ট্যাব টুকরা একে অপরের সাথে ওভারল্যাপ হয়ে যায়। আমি অন্যান্য টুকরোগুলির সামগ্রী দেখতে পাচ্ছি। এটি আমার পক্ষে কাজ করে না।
আকসেল ফাতিহ

@ flock.dux আমি একে অপরকে ওভারল্যাপ করে বোঝাতে চাই না তা নিশ্চিত। অ্যান্ড্রয়েড কীভাবে এগুলি আটকানো হয় তার যত্ন নেয়, আমরা কেবল সংযুক্তি বা বিচ্ছিন্নতা নির্দিষ্ট করি। আরও চলতে হবে। আপনি যদি উদাহরণ কোড সহ একটি নতুন প্রশ্ন জিজ্ঞাসা করেন তবে আমরা আপনার জন্য কী চলছে তা নির্ধারণ করতে পারি।
ডেভ

4
আমার একই সমস্যা ছিল (অ্যান্ড্রয়েড থেকে একাধিক টুকরা নির্মাণকারীর কল)। আপনার অনুসন্ধানটি আমার সমস্যার সমাধান করে: আমি যা বুঝতে পারি নি তা হ'ল কোনও কনফিগারেশনের পরিবর্তন ঘটলে (ফোনটি ঘোরানো হয়) যখন কোনও ক্রিয়াকলাপের সাথে সংযুক্ত সমস্ত টুকরো পুনরায় তৈরি করা হয় এবং আবার ক্রিয়াকলাপে যুক্ত হয়। (যা বোঝায়)
ইউজিন

12

এখানে দুটি আপোভয়েटेड উত্তরগুলি নেভিগেশন মোড সহ কোনও ক্রিয়াকলাপের সমাধান দেখায় NAVIGATION_MODE_TABS, তবে আমার সাথে একটি একই সমস্যা ছিল NAVIGATION_MODE_LIST। স্ক্রিনের ওরিয়েন্টেশন পরিবর্তিত হলে এটি আমার বিরক্তিকরভাবে অনিবার্যভাবে তাদের রাজ্য হারাতে বাধ্য করেছিল, যা সত্যই বিরক্তিকর ছিল। ধন্যবাদ, তাদের সহায়ক কোডের কারণে আমি এটি বের করতে পেরেছি।

মূলত, যখন একটি তালিকা নেভিগেশন, `onNavigationItemSelected () ব্যবহার করে is automatically called when your activity is created/re-created, whether you like it or not. To prevent your Fragment'sonCreateView () from being called twice, this initial automatic call toonNavigationItemSelected ()should check whether the Fragment is already in existence inside your Activity. If it is, return immediately, because there is nothing to do; if it isn't, then simply construct the Fragment and add it to the Activity like you normally would. Performing this check prevents your Fragment from needlessly being created again, which is what causes onCreateView ()` দুইবার বলা হবে!

onNavigationItemSelected()নীচে আমার বাস্তবায়ন দেখুন।

public class MyActivity extends FragmentActivity implements ActionBar.OnNavigationListener
{
    private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item";

    private boolean mIsUserInitiatedNavItemSelection;

    // ... constructor code, etc.

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

        if (savedInstanceState.containsKey(STATE_SELECTED_NAVIGATION_ITEM))
        {
            getActionBar().setSelectedNavigationItem(savedInstanceState.getInt(STATE_SELECTED_NAVIGATION_ITEM));
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState)
    {
        outState.putInt(STATE_SELECTED_NAVIGATION_ITEM, getActionBar().getSelectedNavigationIndex());

        super.onSaveInstanceState(outState);
    }

    @Override
    public boolean onNavigationItemSelected(int position, long id)
    {    
        Fragment fragment;
        switch (position)
        {
            // ... choose and construct fragment here
        }

        // is this the automatic (non-user initiated) call to onNavigationItemSelected()
        // that occurs when the activity is created/re-created?
        if (!mIsUserInitiatedNavItemSelection)
        {
            // all subsequent calls to onNavigationItemSelected() won't be automatic
            mIsUserInitiatedNavItemSelection = true;

            // has the same fragment already replaced the container and assumed its id?
            Fragment existingFragment = getSupportFragmentManager().findFragmentById(R.id.container);
            if (existingFragment != null && existingFragment.getClass().equals(fragment.getClass()))
            {
                return true; //nothing to do, because the fragment is already there 
            }
        }

        getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit();
        return true;
    }
}

আমি এখান থেকে এই সমাধানের জন্য অনুপ্রেরণা নিয়েছি ।


এই সমাধানটি নেভিগেশন ড্রয়ারের সাথে আমার অনুরূপ সমস্যার জন্য কাজ করে। আমি আইডি দ্বারা বিদ্যমান টুকরোটি খুঁজে পেয়েছি এবং এটি পুনরায় তৈরি করার আগে এটি নতুন খণ্ডের মতো একই বর্গ রয়েছে কিনা তা পরীক্ষা করে দেখছি।
উইলিয়াম 18

8

আমার কাছে এটি দেখতে দেখতে এমন কারণ এটি প্রতিবার আপনি নিজের ট্যাবলিস্টনারকে তাত্ক্ষণিকভাবে চালাচ্ছেন ... সুতরাং সিস্টেমটি সেভড ইনস্ট্যান্সস্টেট থেকে আপনার খণ্ডটি পুনরায় তৈরি করছে এবং তারপরে আপনি এটি আবার আপনার অনক্রিটে করছেন।

আপনার এটি এমনভাবে মুড়ে ফেলা উচিত if(savedInstanceState == null)যাতে কোনও সেভড ইনস্ট্যান্সস্টেট না থাকলে এটি কেবল আগুন ধরে যায়।


আমি এটা সঠিক মনে করি না। আমি যখন আমার অ্যাডট্যাব কোডটি ইফ ব্লকে আবদ্ধ করি তখন খণ্ডটি ক্রিয়াকলাপের সাথে সংযুক্ত থাকে তবে কোনও ট্যাব থাকে না। এটি প্রদর্শিত হয় আপনাকে প্রতিবার অনক্রিট পদ্ধতিতে ট্যাবগুলি যুক্ত করতে হবে। আমি আরও ভাল করে বুঝতে পারছি এদিকে নজর রাখছি এবং আরও পোস্ট করব।
ডেভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.