টুকরা ব্যবহার করে অ্যান্ড্রয়েডে প্রতিটি ট্যাবের জন্য পৃথক ব্যাক স্ট্যাক


158

আমি একটি অ্যান্ড্রয়েড অ্যাপে নেভিগেশনের জন্য ট্যাবগুলি প্রয়োগ করার চেষ্টা করছি। যেহেতু ট্যাবএকটিভিটি এবং অ্যাক্টিভিটি গ্রুপটি অবচিত হ'ল আমি তার পরিবর্তে টুকরোটি ব্যবহার করে এটি প্রয়োগ করতে চাই।

আমি জানি প্রতিটি ট্যাবের জন্য কীভাবে একটি টুকরো সেট আপ করতে হবে এবং যখন কোনও ট্যাব ক্লিক করা হয় তখন টুকরাগুলি স্যুইচ করা যায়। তবে আমি কীভাবে প্রতিটি ট্যাবের জন্য পৃথক ব্যাক স্ট্যাক রাখতে পারি?

উদাহরণস্বরূপ খণ্ড A এবং B ট্যাব 1 এর অধীনে থাকবে এবং খণ্ড সি এবং ডি ট্যাব 2 এর অধীনে থাকবে the তারপরে টুকরো 2 এ ফ্রেমমেন্ট বি'র সাথে প্রতিস্থাপন করা যেতে পারে যখন খণ্ড সি প্রদর্শিত হবে। যদি ট্যাব 1 টি নির্বাচিত হয় তবে খণ্ড খ আরও একবার প্রদর্শিত হবে। এই মুহুর্তে ফ্র্যাগমেন্ট এ দেখানোর জন্য পিছনের বোতামটি ব্যবহার করা উচিত should

এছাড়াও, ডিভাইসটি ঘোরানোর সময় প্রতিটি ট্যাবের রাজ্য বজায় রাখা গুরুত্বপূর্ণ।

বি আর মার্টিন

উত্তর:


23

ফ্রেমওয়ার্কটি স্বয়ংক্রিয়ভাবে আপনার জন্য এটি করবে না। প্রতিটি ট্যাবের জন্য আপনার নিজের পিছনের স্ট্যাকগুলি তৈরি এবং পরিচালনা করতে হবে।

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

আপনি যদি কোনও ওয়েব ব্রাউজার ইউআই এর মতো কিছু তৈরির চেষ্টা করছেন, কোনও ইউএক্স পাওয়ার জন্য যা ব্যবহারকারীর কাছে প্রাকৃতিক যে প্রসঙ্গের উপর নির্ভর করে আচরণের অনেক সূক্ষ্ম টুইটগুলি জড়িত করতে চলেছে, তাই আপনাকে অবশ্যই নিজের ব্যাক স্ট্যাকটি করতে হবে ফ্রেমওয়ার্কটিতে কিছু ডিফল্ট বাস্তবায়নের উপর নির্ভর করার পরিবর্তে পরিচালন। উদাহরণস্বরূপ, ব্যাক কী কীভাবে স্ট্যান্ডার্ড ব্রাউজারের সাথে বিভিন্ন উপায়ে এটি ব্যবহার করতে এবং এর বাইরে যেতে পারে সে সম্পর্কে কীভাবে মনোযোগ দেওয়ার চেষ্টা করুন। (ব্রাউজারের প্রতিটি "উইন্ডো" মূলত একটি ট্যাব)


7
এটা করবেন না। এবং কাঠামোটি খুব কমই অকেজো। এটি আপনাকে এই ধরণের জিনিসের জন্য স্বয়ংক্রিয় সমর্থন দেয় না, যা আমি বলেছিলাম যে খুব বিশেষায়িত পরিস্থিতিতে যেখানে আপনার যেভাবে পশ্চাদপসরণটি সাবধানতার সাথে নিয়ন্ত্রণ করা দরকার সেখানে ব্যতীত আমি কোনও শালীন ব্যবহারকারীর অভিজ্ঞতা অর্জনের কল্পনা করতে পারি না can't
হ্যাকবড

9
এই ধরণের নেভিগেশন, তারপরে আপনার প্রতিটি ট্যাবে পৃষ্ঠাগুলির ট্যাব এবং হায়ারার্কি রয়েছে আইফোনের অ্যাপ্লিকেশনগুলির জন্য উদাহরণস্বরূপ (আপনি অ্যাপ স্টোর এবং আইপড অ্যাপ্লিকেশন চেক করতে পারেন)। আমি তাদের ব্যবহারকারীর অভিজ্ঞতা বেশ শালীন মনে করি।
দিমিত্রি রয়াদনেঙ্কো

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

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

22
@ হ্যাকবড আমি আপনার পয়েন্টগুলি অনুসরণ করার চেষ্টা করছি তবে কাস্টম ব্যাক-স্ট্যাক আচরণ কার্যকর করতে সমস্যা হচ্ছে having আমি বুঝতে পারি যে এটির ডিজাইনের সাথে জড়িত থাকার পরে, আপনার পক্ষে এটি কতটা সহজ হতে পারে তার একটি দৃ .় অন্তর্দৃষ্টি থাকবে। এটি কি সম্ভব যে ওপির ব্যবহারের ক্ষেত্রে একটি ডেমো অ্যাপ্লিকেশন রয়েছে, কারণ এটি সত্যিই খুব সাধারণ পরিস্থিতি, বিশেষত আমাদের মধ্যে যারা এই অনুরোধগুলি করেন তাদের ক্লায়েন্টদের জন্য আইওএস অ্যাপ্লিকেশন লিখতে এবং পোর্ট করতে হয় .... আলাদা ব্যবস্থাপনার প্রতিটি ফ্রেগমেন্টঅ্যাক্টিভিটির মধ্যে টুকরো টুকরো টুকরো টুকরো।
রিচার্ড লে ম্যাসুরিয়ার

138

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

আমার এই জাতীয় স্ক্রিন প্রবাহের প্রয়োজন (প্রতিটি ট্যাবটিতে 2 টি ট্যাব এবং 2 টি ভিউ সহ একটি সর্বনিম্ন নকশা),

tabA
    ->  ScreenA1, ScreenA2
tabB
    ->  ScreenB1, ScreenB2

আমার অতীতেও একই প্রয়োজনীয়তা ছিল এবং আমি TabActivityGroupএগুলি করেছিলাম (যা সেই সময়েও হ্রাস করা হয়েছিল) এবং ক্রিয়াকলাপগুলি। এবার আমি টুকরো টুকরোটি ব্যবহার করতে চাইছিলাম।

সুতরাং আমি এটি এটি কিভাবে।

1. একটি বেস টুকরা ক্লাস তৈরি করুন

public class BaseFragment extends Fragment {
    AppMainTabActivity mActivity;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivity = (AppMainTabActivity) this.getActivity();
    }

    public void onBackPressed(){
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data){
    }
}

আপনার অ্যাপ্লিকেশনের সমস্ত খণ্ড এই বেস শ্রেণিকে প্রসারিত করতে পারে। আপনি যদি বিশেষ টুকরোগুলি ব্যবহার করতে চান তবে আপনার এটির ListFragmentজন্যও একটি বেস ক্লাস তৈরি করা উচিত। আপনি এর ব্যবহার সম্পর্কে পরিষ্কার হবে onBackPressed()এবং onActivityResult()যদি আপনি পূর্ণ পোস্ট পড়তে ..

২. কিছু ট্যাব শনাক্তকারী তৈরি করুন, প্রকল্পের যে কোনও জায়গায় অ্যাক্সেসযোগ্য

public class AppConstants{
    public static final String TAB_A  = "tab_a_identifier";
    public static final String TAB_B  = "tab_b_identifier";

    //Your other constants, if you have them..
}

এখানে ব্যাখ্যা করার মতো কিছু নেই ..

৩. ঠিক আছে, মূল ট্যাব ক্রিয়াকলাপ - দয়া করে কোডে মন্তব্যগুলি পড়ুন ..

public class AppMainFragmentActivity extends FragmentActivity{
    /* Your Tab host */
    private TabHost mTabHost;

    /* A HashMap of stacks, where we use tab identifier as keys..*/
    private HashMap<String, Stack<Fragment>> mStacks;

    /*Save current tabs identifier in this..*/
    private String mCurrentTab;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_main_tab_fragment_layout);

        /*  
         *  Navigation stacks for each tab gets created.. 
         *  tab identifier is used as key to get respective stack for each tab
         */
        mStacks             =   new HashMap<String, Stack<Fragment>>();
        mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
        mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());

        mTabHost                =   (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setOnTabChangedListener(listener);
        mTabHost.setup();

        initializeTabs();
    }


    private View createTabView(final int id) {
        View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
        ImageView imageView =   (ImageView) view.findViewById(R.id.tab_icon);
        imageView.setImageDrawable(getResources().getDrawable(id));
        return view;
    }

    public void initializeTabs(){
        /* Setup your tab icons and content views.. Nothing special in this..*/
        TabHost.TabSpec spec    =   mTabHost.newTabSpec(AppConstants.TAB_A);
        mTabHost.setCurrentTab(-3);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_home_state_btn));
        mTabHost.addTab(spec);


        spec                    =   mTabHost.newTabSpec(AppConstants.TAB_B);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_status_state_btn));
        mTabHost.addTab(spec);
    }


    /*Comes here when user switch tab, or we do programmatically*/
    TabHost.OnTabChangeListener listener    =   new TabHost.OnTabChangeListener() {
      public void onTabChanged(String tabId) {
        /*Set current tab..*/
        mCurrentTab                     =   tabId;

        if(mStacks.get(tabId).size() == 0){
          /*
           *    First time this tab is selected. So add first fragment of that tab.
           *    Dont need animation, so that argument is false.
           *    We are adding a new fragment which is not present in stack. So add to stack is true.
           */
          if(tabId.equals(AppConstants.TAB_A)){
            pushFragments(tabId, new AppTabAFirstFragment(), false,true);
          }else if(tabId.equals(AppConstants.TAB_B)){
            pushFragments(tabId, new AppTabBFirstFragment(), false,true);
          }
        }else {
          /*
           *    We are switching tabs, and target tab is already has atleast one fragment. 
           *    No need of animation, no need of stack pushing. Just show the target fragment
           */
          pushFragments(tabId, mStacks.get(tabId).lastElement(), false,false);
        }
      }
    };


    /* Might be useful if we want to switch tab programmatically, from inside any of the fragment.*/
    public void setCurrentTab(int val){
          mTabHost.setCurrentTab(val);
    }


    /* 
     *      To add fragment to a tab. 
     *  tag             ->  Tab identifier
     *  fragment        ->  Fragment to show, in tab identified by tag
     *  shouldAnimate   ->  should animate transaction. false when we switch tabs, or adding first fragment to a tab
     *                      true when when we are pushing more fragment into navigation stack. 
     *  shouldAdd       ->  Should add to fragment navigation stack (mStacks.get(tag)). false when we are switching tabs (except for the first time)
     *                      true in all other cases.
     */
    public void pushFragments(String tag, Fragment fragment,boolean shouldAnimate, boolean shouldAdd){
      if(shouldAdd)
          mStacks.get(tag).push(fragment);
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      if(shouldAnimate)
          ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }


    public void popFragments(){
      /*    
       *    Select the second last fragment in current tab's stack.. 
       *    which will be shown after the fragment transaction given below 
       */
      Fragment fragment             =   mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);

      /*pop current fragment from stack.. */
      mStacks.get(mCurrentTab).pop();

      /* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }   


    @Override
    public void onBackPressed() {
        if(mStacks.get(mCurrentTab).size() == 1){
          // We are already showing first fragment of current tab, so when back pressed, we will finish this activity..
          finish();
          return;
        }

        /*  Each fragment represent a screen in application (at least in my requirement, just like an activity used to represent a screen). So if I want to do any particular action
         *  when back button is pressed, I can do that inside the fragment itself. For this I used AppBaseFragment, so that each fragment can override onBackPressed() or onActivityResult()
         *  kind of events, and activity can pass it to them. Make sure just do your non navigation (popping) logic in fragment, since popping of fragment is done here itself.
         */
        ((AppBaseFragment)mStacks.get(mCurrentTab).lastElement()).onBackPressed();

        /* Goto previous fragment in navigation stack of this tab */
            popFragments();
    }


    /*
     *   Imagine if you wanted to get an image selected using ImagePicker intent to the fragment. Ofcourse I could have created a public function
     *  in that fragment, and called it from the activity. But couldn't resist myself.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(mStacks.get(mCurrentTab).size() == 0){
            return;
        }

        /*Now current fragment on screen gets onActivityResult callback..*/
        mStacks.get(mCurrentTab).lastElement().onActivityResult(requestCode, resultCode, data);
    }
}

৪. app_main_tab_fraament_layout.xml (যদি কেউ আগ্রহী হন।)

<?xml version="1.0" encoding="utf-8"?>
<TabHost
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>

        <FrameLayout
            android:id="@+android:id/realtabcontent"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>

        <TabWidget
            android:id="@android:id/tabs"
            android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="0"/>

    </LinearLayout>
</TabHost>

৫. অ্যাপট্যাবস্ট্রিস্টফ্রেগমেন্ট.জভা (ট্যাব এ-র প্রথম খণ্ড, সমস্ত ট্যাবগুলির জন্য সমান)

public class AppTabAFragment extends BaseFragment {
    private Button mGotoButton;

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

        mGoToButton =   (Button) view.findViewById(R.id.goto_button);
        mGoToButton.setOnClickListener(listener);

        return view;
    }

    private OnClickListener listener        =   new View.OnClickListener(){
        @Override
        public void onClick(View v){
            /* Go to next fragment in navigation stack*/
            mActivity.pushFragments(AppConstants.TAB_A, new AppTabAFragment2(),true,true);
        }
    }
}

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

সম্পাদনা:

যদি কেউ একটি পূর্ণ প্রকল্প চায়, আমি একটি নমুনা প্রকল্প গিথুবকে ঠেলে দিয়েছি ।


2
প্রতিটি খণ্ডের জন্য ডেটা সংরক্ষণ করা, তাদের প্রত্যেককে পুনরায় তৈরি করা, স্ট্যাকগুলি পুনর্নির্মাণ করা হচ্ছে ... সাধারণ ওরিয়েন্টেশন পরিবর্তনের জন্য এত বেশি কাজ।
মাইকেল ইলার্স স্মিথ

3
@ ওমেগটাই আপনার সাথে সম্পূর্ণরূপে একমত .. সমস্ত সমস্যা দেখা দেয় যেহেতু অ্যান্ড্রয়েড আমাদের জন্য স্ট্যাক পরিচালনা করে না ( যা আইওএস করে এবং একাধিক টুকরো টানা দিক বা ট্যাবটি বাতাসের মতো হয় ) এবং যা আমাদের এই প্রশ্ন / এ মূল আলোচনায় ফিরিয়ে নিয়ে যায় একটি থ্রেড এখন আর ফিরে যাওয়া ভাল নয় ..
কৃষ্ণভদ্র

1
@ রঞ্জিত এটি কারণ আপনি যখন ট্যাবটি স্যুইচ করেন তখন খণ্ডগুলি প্রতিবারই পুনরায় তৈরি করা হয় once আমি যখন একটি ট্যাব থেকে বি তে স্যুইচ করি তখন একটি ট্যাব মেমরি থেকে মুক্ত হয়। তাই আপনার ডেটা ক্রিয়াকলাপে সংরক্ষণ করুন এবং সার্ভারের কাছ থেকে নেওয়ার চেষ্টা করার আগে ক্রিয়াকলাপে ডেটা রয়েছে কিনা তা প্রতিবার পরীক্ষা করুন।
কৃষ্ণভদ্র

2
@ কৃষ্ণভদ্র ঠিক আছে, এটি আরও ভাল শোনাচ্ছে। আমি ভুল হলে আমাকে সংশোধন করতে দিন। আপনার উদাহরণ অনুসারে, কেবলমাত্র একটি ক্রিয়াকলাপ এবং তাই একটি বান্ডিল। বেসফ্রেগমেন্টে অ্যাডাপ্টারের উদাহরণ তৈরি করুন (আপনার প্রকল্পটি রিফ্রেশ করে) এবং সেখানে ডেটা সংরক্ষণ করুন। দেখুন যখনই নির্মাণ করা হবে সেগুলি ব্যবহার করুন।
রেনজিথ

1
কাজ করতে পেলাম। অনেক ধন্যবাদ. পুরো প্রকল্পটি আপলোড করা ভাল ধারণা ছিল! :-)
বিনয় ডাব্লু

96

আপনি সম্প্রতি একটি অ্যাপ্লিকেশনটির জন্য বর্ণনা করেছেন ঠিক একই আচরণটি আমাদের কার্যকর করতে হয়েছিল। অ্যাপ্লিকেশনটির পর্দা এবং সামগ্রিক প্রবাহ ইতিমধ্যে সংজ্ঞায়িত করা হয়েছে সুতরাং আমাদের এটির সাথে আটকে থাকতে হবে (এটি একটি আইওএস অ্যাপ্লিকেশন ক্লোন ...)। ভাগ্যক্রমে, আমরা অন-স্ক্রিনের ব্যাক বোতামগুলি থেকে মুক্তি পেতে পেরেছি :)

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

সুতরাং প্রয়োজনীয়তাগুলির প্রতিটি ট্যাবে কিছু ট্যাব এবং বাসা বাঁধার স্ক্রিন থাকা দরকার:

tab 1
  screen 1 -> screen 2 -> screen 3
tab 2
  screen 4
tab 3
  screen 5 -> 6

ইত্যাদি ...

সুতরাং বলুন: ব্যবহারকারী ট্যাব 1 এ শুরু হয়, পর্দা 1 থেকে স্ক্রিন 2 এবং পরে স্ক্রিন 3 এ নেভিগেট করে, তারপরে তিনি ট্যাব 3 এ স্যুইচ করেন এবং স্ক্রিন 4 থেকে 6 এ নেভিগেট করেন; যদি ট্যাব 1 এ ফিরে যায়, তার আবার স্ক্রিন 3 দেখতে হবে এবং যদি সে পিছনে চাপ দেয় তবে তার পর্দা 2 এ ফিরে আসা উচিত; আবার ফিরে এসে তিনি স্ক্রিন 1 এ আছেন; ট্যাব 3 এ স্যুইচ করুন এবং তিনি আবার 6 স্ক্রিনে আছেন।

অ্যাপ্লিকেশনটির প্রধান ক্রিয়াকলাপটি মেইনট্যাবঅ্যাক্টিভিটি যা ট্যাবঅ্যাক্টিভিটি প্রসারিত করে। প্রতিটি ট্যাব একটি ক্রিয়াকলাপের সাথে যুক্ত, এক্টিভিটিআইএনটিব 1, 2 এবং 3 বলতে দিন এবং তারপরে প্রতিটি পর্দা একটি খণ্ড হবে:

MainTabActivity
  ActivityInTab1
    Fragment1 -> Fragment2 -> Fragment3
  ActivityInTab2
    Fragment4
  ActivityInTab3
    Fragment5 -> Fragment6

প্রতিটি ক্রিয়াকলাপটিএনটি একসাথে কেবলমাত্র একটি টুকরো ধারণ করে এবং অন্য একটির জন্য কীভাবে একটি টুকরো প্রতিস্থাপন করতে হয় তা (একভিটিগ্রুপ হিসাবে প্রায় একই)) দুর্দান্ত জিনিসটি হ'ল প্রতিটি ট্যাবটির জন্য এইভাবে পৃথক ব্যাক স্ট্যাকগুলি মন্টেন করা বেশ সহজ।

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

abstract class ActivityInTab extends FragmentActivity { // FragmentActivity is just Activity for the support library.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_in_tab);
    }

    /**
     * Navigates to a new fragment, which is added in the fragment container
     * view.
     * 
     * @param newFragment
     */
    protected void navigateTo(Fragment newFragment) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();

        ft.replace(R.id.content, newFragment);

        // Add this transaction to the back stack, so when the user presses back,
        // it rollbacks.
        ft.addToBackStack(null);
        ft.commit();
    }

}

ক্রিয়াকলাপ_আইন_ট্যাব.এক্সএমএল কেবল এটি:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:isScrollContainer="true">
</RelativeLayout>

আপনি দেখতে পাচ্ছেন, প্রতিটি ট্যাবের জন্য ভিউ লেআউটটি একই ছিল। কারণ এটি প্রতিটি ফ্রেমকে ধারণ করে এমন একটি ফ্রেমলআউট content খণ্ডগুলি হ'ল প্রতিটি স্ক্রিনের দর্শন।

কেবল বোনাস পয়েন্টের জন্য, আমরা যখন ব্যবহারকারী পিছনে টিপায় এবং আর ফিরে যাওয়ার মতো আরও কিছু টুকরো না থাকে তখন আমরা একটি নিশ্চিত ডায়ালগ দেখানোর জন্য কিছুটা ছোট কোড যুক্ত করেছি:

// In ActivityInTab.java...
@Override
public void onBackPressed() {
    FragmentManager manager = getSupportFragmentManager();
    if (manager.getBackStackEntryCount() > 0) {
        // If there are back-stack entries, leave the FragmentActivity
        // implementation take care of them.
        super.onBackPressed();
    } else {
        // Otherwise, ask user if he wants to leave :)
        showExitDialog();
    }
}

এটি বেশ সেটআপ। আপনি দেখতে পাচ্ছেন যে প্রতিটি ফ্রেগমেন্টএটিভিটি (বা কেবলমাত্র অ্যান্ড্রয়েড> 3 এ কার্যকলাপ) তার নিজস্ব ফ্রেগমেন্টম্যানেজারের সাথে সমস্ত ব্যাক-স্ট্যাকিংয়ের যত্ন নিচ্ছে।

ActivityInTab1 এর মতো ক্রিয়াকলাপটি সত্যিই সহজ হবে, এটি কেবল এটি প্রথম টুকরা (অর্থাত্ স্ক্রিন) দেখিয়ে দেবে:

public class ActivityInTab1 extends ActivityInTab {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        navigateTo(new Fragment1());
    }
}

তারপরে, যদি কোনও খণ্ডকে অন্য খণ্ডে নেভিগেট করতে হয়, তবে এটির জন্য কিছুটা বাজে কাস্টিং করতে হবে ... তবে এটি খুব খারাপ নয়:

// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());

সুতরাং এটি বেশ। আমি নিশ্চিত যে এটি খুব ক্যানোনিকাল নয় (এবং বেশিরভাগই নিশ্চিত খুব ভাল নয়) সমাধান নয়, তাই আমি পাকা অ্যান্ড্রয়েড বিকাশকারীদের জিজ্ঞাসা করতে চাই যে এই কার্যকারিতাটি অর্জনের জন্য আরও ভাল পদ্ধতির কী হতে পারে, এবং যদি এটি না হয় তবে "এটি কীভাবে এটি হয় না অ্যান্ড্রয়েডে "সম্পন্ন", আপনি যদি আমাকে এমন কোনও লিঙ্ক বা উপাদানের দিকে নির্দেশ করতে পারেন তবে আমি এটি প্রশংসা করতে পারি যে এটির কাছে যাওয়ার অ্যান্ড্রয়েড উপায় কোনটি (ট্যাবস, ট্যাবগুলিতে নেস্টেড স্ক্রীন ইত্যাদি) বোঝায় । মন্তব্যগুলিতে এই উত্তরটি ছিন্ন করতে নির্দ্বিধায় :)

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


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


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

মন্তব্য বোল্ডার জন্য ধন্যবাদ। হ্যাঁ, আমি টুকরো টুকরো এপিআই সম্পর্কে আরও একমত হতে পারি না। আমি ইতিমধ্যে নেস্টেড টুকরাগুলির সমস্যায় পড়েছি (এ কারণেই আমরা "এক টুকরোটিকে অন্য একের সাথে প্রতিস্থাপন করুন" পদ্ধতির জন্য গিয়েছিলাম)।
মহামারী

1
আমি সমস্ত কার্যক্রমের মাধ্যমে এটি বাস্তবায়ন করেছি। আমি যা পেয়েছিলাম তা পছন্দ করি না এবং আমি খণ্ডগুলি চেষ্টা করে যাচ্ছি। এটি আপনার অভিজ্ঞতার বিপরীত! প্রতিটি ট্যাবে শিশু দৃষ্টিভঙ্গির জীবনচক্র পরিচালনা করতে এবং আপনার নিজের পিছনের বোতামটি বাস্তবায়নের জন্য ক্রিয়াকলাপগুলির সাথে প্রচুর বাস্তবায়ন রয়েছে। এছাড়াও, আপনি কেবলমাত্র সমস্ত দর্শনগুলির একটি রেফারেন্স রাখতে পারবেন না বা আপনি স্মৃতিচারণ করবেন। আমি আশা করি খণ্ডগুলি এটি করবে: 1) মেমরির পৃথক পৃথককরণের সাথে টুকরোটির জীবনকাল সমর্থন করবে এবং 2) পিছনের বোতামটির কার্যকারিতা বাস্তবায়নে সহায়তা করবে প্লাস, আপনি যদি এই প্রক্রিয়াটির জন্য খণ্ড ব্যবহার করেন তবে ট্যাবলেটগুলিতে চালানো কি সহজ হবে না?
গ্রেগ

ব্যবহারকারী ট্যাবগুলি স্যুইচ করলে কী ঘটে? খণ্ডটির ব্যাকস্ট্যাকটি মুছে ফেলা হবে? ব্যাকস্ট্যাকটি কীভাবে থাকবে তা নিশ্চিত করবেন?
গ্রেগম

1
@gregm আপনি যদি 1 টি ট্যাব যান <-> আমার মতো 1 টি ক্রিয়াকলাপ, ট্যাবগুলি স্যুইচ করা থাকে তখন প্রতিটি ট্যাবটির ব্যাকস্ট্যাক থাকবে কারণ ক্রিয়াকলাপগুলি আসলে জীবিত রাখা হয়েছে; এগুলি কেবল বিরতি দিয়ে পুনরায় শুরু করা হয়েছে। ট্যাবগুলি যখন একটি ট্যাবঅ্যাক্টিভিটিতে স্যুইচ করা থাকে তখন কার্যকলাপগুলি ধ্বংস হয়ে যায় এবং পুনরায় তৈরি করার কোনও উপায় আছে কিনা তা আমি জানি না। যাইহোক, আপনি যদি আমার পরামর্শ মতো ক্রিয়াকলাপের অভ্যন্তরে টুকরো টুকরো টুকরো করে প্রতিস্থাপন করেন তবে সেগুলি ধ্বংস হয়ে যায় (এবং ব্যাকস্ট্যাকটি পপ করার পরে পুনরায় তৈরি করা হয়)। সুতরাং যে কোনও সময় আপনার ট্যাব প্রতি সর্বাধিক এক টুকরো টিকে থাকবে।
মহামারী


6

খণ্ডগুলিতে শক্তিশালী উল্লেখ সংরক্ষণ করা সঠিক উপায় নয়।

ফ্র্যাগমেন্টম্যানেজার putFragment(Bundle, String, Fragment)এবং সরবরাহ করে saveFragmentInstanceState(Fragment)

হয় একটি ব্যাকস্ট্যাক বাস্তবায়নের জন্য যথেষ্ট।


ব্যবহার putFragment, পরিবর্তে একটি টুকরা প্রতিস্থাপন, আপনি পুরানো এক বিচ্ছিন্ন নতুন এক যোগ করুন। ব্যাকস্ট্যাকের সাথে যুক্ত হওয়া প্রতিস্থাপনের লেনদেনে ফ্রেমওয়ার্কটি এটিই করে। putFragmentসক্রিয় খণ্ডগুলির বর্তমান তালিকায় একটি সূচক সঞ্চয় করে এবং সেগুলির অংশগুলি ওরিয়েন্টেশন পরিবর্তনের সময় ফ্রেমওয়ার্ক দ্বারা সংরক্ষণ করা হয়।

দ্বিতীয় উপায়ে, ব্যবহার saveFragmentInstanceStateকরে পুরো টুকরো টুকরো টুকরো টুকরো টুকরো করার পরিবর্তে কোনও বান্ডলে সংরক্ষণ করে। এই পদ্ধতির ব্যবহারের ফলে পিছনের স্ট্যাকটি ম্যানিপুলেট করা সহজ হয়ে যায়, আপনি যখনই চান কোনও খণ্ড পপ করতে পারেন।


আমি এই ব্যবহারের জন্য দ্বিতীয় পদ্ধতিটি ব্যবহার করেছি:

SignInFragment ----> SignUpFragment ---> ChooseBTDeviceFragment
               \                          /
                \------------------------/

আমি চাই না যে ব্যবহারকারীটি তৃতীয়টি থেকে ফিরে বোতামটি টিপে সাইন আপ স্ক্রিনটিতে ফিরে আসুক। আমি তাদের মধ্যে ফ্লিপ অ্যানিমেশনগুলিও ব্যবহার করি (ব্যবহার করে onCreateAnimation), তাই হ্যাকি সমাধানগুলি কাজ করবে না, ব্যবহারকারীকে স্পষ্টভাবে কিছু লক্ষ্য করা ঠিক নয়, ব্যতীত কমপক্ষে।

এটি একটি কাস্টম ব্যাকস্ট্যাকের জন্য বৈধ ব্যবহারের কেস, যা ব্যবহারকারী প্রত্যাশা করে তা করে ...

private static final String STATE_BACKSTACK = "SetupActivity.STATE_BACKSTACK";

private MyBackStack mBackStack;

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

    if (state == null) {
        mBackStack = new MyBackStack();

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction tr = fm.beginTransaction();
        tr.add(R.id.act_base_frg_container, new SignInFragment());
        tr.commit();
    } else {
        mBackStack = state.getParcelable(STATE_BACKSTACK);
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable(STATE_BACKSTACK, mBackStack);
}

private void showFragment(Fragment frg, boolean addOldToBackStack) {
    final FragmentManager fm = getSupportFragmentManager();
    final Fragment oldFrg = fm.findFragmentById(R.id.act_base_frg_container);

    FragmentTransaction tr = fm.beginTransaction();
    tr.replace(R.id.act_base_frg_container, frg);
    // This is async, the fragment will only be removed after this returns
    tr.commit();

    if (addOldToBackStack) {
        mBackStack.push(fm, oldFrg);
    }
}

@Override
public void onBackPressed() {
    MyBackStackEntry entry;
    if ((entry = mBackStack.pop()) != null) {
        Fragment frg = entry.recreate(this);

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction tr = fm.beginTransaction();
        tr.replace(R.id.act_base_frg_container, frg);
        tr.commit();

        // Pop it now, like the framework implementation.
        fm.executePendingTransactions();
    } else {
        super.onBackPressed();
    }
}

public class MyBackStack implements Parcelable {

    private final List<MyBackStackEntry> mList;

    public MyBackStack() {
        mList = new ArrayList<MyBackStackEntry>(4);
    }

    public void push(FragmentManager fm, Fragment frg) {
        push(MyBackStackEntry.newEntry(fm, frg);
    }

    public void push(MyBackStackEntry entry) {
        if (entry == null) {
            throw new NullPointerException();
        }
        mList.add(entry);
    }

    public MyBackStackEntry pop() {
        int idx = mList.size() - 1;
        return (idx != -1) ? mList.remove(idx) : null;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        final int len = mList.size();
        dest.writeInt(len);
        for (int i = 0; i < len; i++) {
            // MyBackStackEntry's class is final, theres no
            // need to use writeParcelable
            mList.get(i).writeToParcel(dest, flags);
        }
    }

    protected MyBackStack(Parcel in) {
        int len = in.readInt();
        List<MyBackStackEntry> list = new ArrayList<MyBackStackEntry>(len);
        for (int i = 0; i < len; i++) {
            list.add(MyBackStackEntry.CREATOR.createFromParcel(in));
        }
        mList = list;
    }

    public static final Parcelable.Creator<MyBackStack> CREATOR =
        new Parcelable.Creator<MyBackStack>() {

            @Override
            public MyBackStack createFromParcel(Parcel in) {
                return new MyBackStack(in);
            }

            @Override
            public MyBackStack[] newArray(int size) {
                return new MyBackStack[size];
            }
    };
}

public final class MyBackStackEntry implements Parcelable {

    public final String fname;
    public final Fragment.SavedState state;
    public final Bundle arguments;

    public MyBackStackEntry(String clazz, 
            Fragment.SavedState state,
            Bundle args) {
        this.fname = clazz;
        this.state = state;
        this.arguments = args;
    }

    public static MyBackStackEntry newEntry(FragmentManager fm, Fragment frg) {
        final Fragment.SavedState state = fm.saveFragmentInstanceState(frg);
        final String name = frg.getClass().getName();
        final Bundle args = frg.getArguments();
        return new MyBackStackEntry(name, state, args);
    }

    public Fragment recreate(Context ctx) {
        Fragment frg = Fragment.instantiate(ctx, fname);
        frg.setInitialSavedState(state);
        frg.setArguments(arguments);
        return frg;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(fname);
        dest.writeBundle(arguments);

        if (state == null) {
            dest.writeInt(-1);
        } else if (state.getClass() == Fragment.SavedState.class) {
            dest.writeInt(0);
            state.writeToParcel(dest, flags);
        } else {
            dest.writeInt(1);
            dest.writeParcelable(state, flags);
        }
    }

    protected MyBackStackEntry(Parcel in) {
        final ClassLoader loader = getClass().getClassLoader();
        fname = in.readString();
        arguments = in.readBundle(loader);

        switch (in.readInt()) {
            case -1:
                state = null;
                break;
            case 0:
                state = Fragment.SavedState.CREATOR.createFromParcel(in);
                break;
            case 1:
                state = in.readParcelable(loader);
                break;
            default:
                throw new IllegalStateException();
        }
    }

    public static final Parcelable.Creator<MyBackStackEntry> CREATOR =
        new Parcelable.Creator<MyBackStackEntry>() {

            @Override
            public MyBackStackEntry createFromParcel(Parcel in) {
                return new MyBackStackEntry(in);
            }

            @Override
            public MyBackStackEntry[] newArray(int size) {
                return new MyBackStackEntry[size];
            }
    };
}

2

দাবি পরিত্যাগী:


আমি মনে করি যে এটি সম্পর্কিত সমাধান পোস্ট করার জন্য এটিই সেরা জায়গা এটি একই ধরণের সমস্যার জন্য যা আমি বেশ স্ট্যান্ডার্ড অ্যান্ড্রয়েড স্টাফ বলে মনে হয় for এটি সবার সমস্যার সমাধান করবে না, তবে এটি কিছুকে সাহায্য করতে পারে।


যদি আপনার টুকরোগুলির মধ্যে প্রাথমিক পার্থক্যটি কেবলমাত্র তাদের ডেটা ব্যাক আপ করা ডেটা হয় (যেমন, অনেক বড় বিন্যাসের পার্থক্য নয়), তবে আপনাকে সম্ভবত খণ্ডটি প্রতিস্থাপনের প্রয়োজন হতে পারে না, তবে কেবল অন্তর্নিহিত ডেটা সরিয়ে নেবেন এবং দৃশ্যটি রিফ্রেশ করুন।

এই পদ্ধতির জন্য একটি সম্ভাব্য উদাহরণের বর্ণনা এখানে দেওয়া হয়েছে:

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

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

1) উপযুক্ত 'থেকে' এবং 'থেকে' ক্ষেত্রগুলির সাথে একটি নতুন অ্যাডাপ্টার তৈরি করুন যা তালিকায় যুক্ত হওয়া এবং নতুন কার্সার দ্বারা ফিরে আসা কলামগুলিতে নতুন আইটেম দর্শনের সাথে মিলবে।

2) এই অ্যাডাপ্টারটি তালিকাভিউয়ের জন্য নতুন অ্যাডাপ্টার হিসাবে সেট করুন।

3) ক্লিক করা আইটেমের উপর ভিত্তি করে একটি নতুন ইউআরআই তৈরি করুন এবং নতুন ইউআরআই (এবং অভিক্ষেপ) দিয়ে কার্সার লোডার পুনরায় চালু করুন। এই উদাহরণস্বরূপ, ইউআরআইটি নির্দিষ্ট প্রশ্নের সাথে ম্যাপ করা হয়েছে নির্বাচনের আরগগুলি ইউআই থেকে নিচে দিয়ে গেছে।

৪) যখন ইউআরআই থেকে নতুন ডেটা লোড করা হবে, অ্যাডাপ্টারের সাথে যুক্ত কার্সরটিকে নতুন কার্সারে স্যুপ করুন, এবং তালিকাটি পুনরায় রিফ্রেশ করবে।

এর সাথে কোনও ব্যাকস্ট্যাক যুক্ত নেই যেহেতু আমরা লেনদেনগুলি ব্যবহার করছি না, সুতরাং শ্রেণিবদ্ধতা থেকে বেরিয়ে আসার সময় আপনাকে নিজের নিজের তৈরি করতে হবে বা উল্টাতে কোয়েরিগুলি খেলতে হবে। যখন আমি এটি চেষ্টা করেছি, ক্যোয়ারীগুলি যথেষ্ট দ্রুত ছিল যে আমি কেবল ওএনব্যাকপ্রেসড (আবার) এ আবার সঞ্চালন করব যতক্ষণ না আমি স্তরক্রমের শীর্ষে না পৌঁছাচ্ছি, যেখানে ফ্রেমওয়ার্কটি আবার পিছনের বোতামটি নিয়ে যায়।

যদি আপনি নিজেকে একইরকম পরিস্থিতিতে খুঁজে পান তবে ডক্সটি অবশ্যই নিশ্চিত করুন: http://developer.android.com/guide/topics/ui/layout/listview.html

http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html

আমি আশা করি এটা কারো সাহায্যে লাগবে!


যদি কেউ এটি করে থাকে এবং সেকশনআইন্ডেক্সার (যেমন আলফাবেট ইন্ডেক্সার) ব্যবহার করে তবে আপনি খেয়াল করতে পারেন অ্যাডাপ্টার প্রতিস্থাপনের পরে আপনার দ্রুত স্ক্রোলিং কাজ করে না। দুর্ভাগ্যজনক বাগের মতো, তবে অ্যাডাপ্টারের পরিবর্তে এমনকি একেবারে নতুন সূচক দিয়েও, ফাস্টস্ক্রোল দ্বারা ব্যবহৃত বিভাগগুলির তালিকা আপডেট করে না। একটি ওয়ার্কঅ্যারাউন্ড নেই, দয়া করে দেখুন: সমস্যা বর্ণনা এবং কার্যসংক্রান্ত
courtf

2

আমার ঠিক একই সমস্যা ছিল এবং একটি ওপেন সোর্স গিথুব প্রকল্প বাস্তবায়িত হয়েছিল যা স্ট্যাকড ট্যাব, ব্যাক এবং আপ নেভিগেশনকে কভার করে এবং ভালভাবে পরীক্ষিত এবং নথিভুক্ত:

https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs

এটি নেভিগেশন ট্যাব এবং টুকরা স্যুইচিং এবং আপ এবং পিছনে নেভিগেশন হ্যান্ডলিংয়ের জন্য একটি সহজ এবং ছোট কাঠামো। প্রতিটি ট্যাবের নিজস্ব টুকরো টুকরো রয়েছে। এটি অ্যাকশনবারারলক ব্যবহার করে এবং এপিআই স্তর 8 এর সাথে সামঞ্জস্যপূর্ণ।


2

এটি একটি জটিল সমস্যা কারণ অ্যান্ড্রয়েড কেবলমাত্র 1 ব্যাক স্ট্যাক পরিচালনা করে তবে এটি সম্ভাব্য। ট্যাব স্ট্যাকার নামে একটি লাইব্রেরি তৈরি করতে আমার বেশ কয়েকদিন লেগেছিল যা আপনি যা খুঁজছেন ঠিক তা করে: প্রতিটি ট্যাবের জন্য খণ্ডের ইতিহাস। এটি ওপেন সোর্স এবং সম্পূর্ণ নথিভুক্ত এবং গ্রেডের সাথে সহজেই অন্তর্ভুক্ত করা যায়। আপনি গিথুব এ লাইব্রেরিটি পেতে পারেন: https://github.com/smart-fun/TabStacker

আচরণটি আপনার প্রয়োজনের সাথে মিলে যায় তা দেখতে আপনি নমুনা অ্যাপও ডাউনলোড করতে পারেন:

https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp

আপনার যদি কোনও প্রশ্ন থাকে তবে কোনও মেল ছাড়তে দ্বিধা করবেন না।


2

কেউ যদি সন্ধান করে এবং তার প্রয়োজনের জন্য সেরাটি বেছে নিতে চায় তবে আমার নিজের সমাধানটি প্রস্তাব করতে চাই।

https://github.com/drusak/tabactivity

লাইব্রেরি তৈরির উদ্দেশ্যটি বেশ ব্যানাল - এটি আইফোনের মতো প্রয়োগ করুন।

প্রধান সুবিধা:

  • ট্যাবলয়আউট সহ android.support.design লাইব্রেরি ব্যবহার করুন;
  • প্রতিটি ট্যাবে ফ্রেগমেন্টম্যানেজার ব্যবহার করে (টুকরো'র রেফারেন্স সংরক্ষণ না করে) এর নিজস্ব স্ট্যাক থাকে;
  • গভীর সংযোগের জন্য সমর্থন (যখন আপনাকে নির্দিষ্ট ট্যাব এবং এতে নির্দিষ্ট খণ্ডের স্তর খুলতে হবে);
  • ট্যাবগুলির সঞ্চয় / পুনরুদ্ধার;
  • ট্যাবগুলিতে টুকরো টুকরো করার জন্য অভিযোজিত জীবনচক্র পদ্ধতি;
  • আপনার প্রয়োজনের জন্য কার্যকর করা বেশ সহজ।

ধন্যবাদ, এটি বেশ সহায়ক হয়েছে। আমাকে ListFragmentএস এর পাশাপাশি ব্যবহার করতে হবে Fragmentতাই আমি বেসট্যাবফ্রেগমেন্ট.জভাটি বেসট্যাবলিস্টফ্র্যাগমেন্ট.জভাতে অনুলিপি করেছিলাম এবং এর তালিকাফ্র্যাগমেন্টটি প্রসারিত করেছি। তারপরে আমাকে কোডের বিভিন্ন অংশগুলি পরিবর্তন করতে হয়েছিল যেখানে এটি সর্বদা বেসট্যাবফ্র্যাগমেন্ট আশা করে। একটি ভাল উপায় আছে কি?
প্রাইমহলো

দুর্ভাগ্যক্রমে, তালিকাফ্র্যাগমেন্ট সম্পর্কে ভাবেনি। প্রযুক্তিগতভাবে এটি সঠিক সমাধান, তবে এটির জন্য ট্যাবফ্র্যাগমেন্ট এবং এর বেসঅ্যাটব্লিস্টফ্র্যাগমেন্টের উদাহরণগুলির জন্য অতিরিক্ত চেকগুলির প্রয়োজন হবে। ভিতরে তালিকাগুলির সাথে টুকরো টুকরো ব্যবহার করার জন্য আরেকটি পন্থা (তালিকাগুলি প্রয়োগের সাথে হুবহু একই)। আমি এটি সম্পর্কে চিন্তা করব। আমাকে এটি নির্দেশ করার জন্য ধন্যবাদ!
কাসুরদ

1

একটি সহজ সমাধান:

প্রতিবার আপনি ট্যাব / রুট ভিউ কল পরিবর্তন করবেন:

fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

এটি ব্যাকস্ট্যাকটি সাফ করবে। আপনি রুট খণ্ডটি পরিবর্তন করার আগে এটিকে কল করতে ভুলবেন না।

এবং এটি দিয়ে টুকরা যোগ করুন:

FragmentTransaction transaction = getFragmentManager().beginTransaction();
NewsDetailsFragment newsDetailsFragment = NewsDetailsFragment.newInstance(newsId);
transaction.add(R.id.content_frame, newsDetailsFragment).addToBackStack(null).commit();

নোট করুন .addToBackStack(null)এবং transaction.addউদাহরণস্বরূপ এর সাথে পরিবর্তন করা যেতে পারে transaction.replace


-1

এই থ্রেডটি খুব আকর্ষণীয় এবং দরকারী ছিল।
আপনার ব্যাখ্যা এবং কোডের জন্য কৃষ্ণভদ্রকে ধন্যবাদ, আমি আপনার কোডটি ব্যবহার করেছি এবং কিছুটা উন্নত করেছি, পরিবর্তনের কনফিগারেশন থেকে (মূলত ঘোরানো) স্ট্যাকস, কারেন্টট্যাব ইত্যাদি বজায় রাখতে দিয়েছি।
একটি বাস্তব 4.0.০.৪ এবং ২.৩..6 ডিভাইসে পরীক্ষিত, এমুলেটরটিতে পরীক্ষা করা হয়নি

আমি কোডের এই অংশটি "AppMainTabActivity.java" এ পরিবর্তন করি, বাকিরা একই থাকে। হয়তো কৃষ্ণভদ্র তাঁর কোডে এটি যুক্ত করবেন।

ক্রিয়েটে ডেটা পুনরুদ্ধার করুন:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.app_main_tab_fragment_layout);

    /*  
     *  Navigation stacks for each tab gets created..
     *  tab identifier is used as key to get respective stack for each tab
     */

  //if we are recreating this activity...
    if (savedInstanceState!=null) {
         mStacks = (HashMap<String, Stack<Fragment>>) savedInstanceState.get("stack");
         mCurrentTab = savedInstanceState.getString("currentTab");
    }
    else {
    mStacks = new HashMap<String, Stack<Fragment>>();
    mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
    mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());

    }

    mTabHost = (TabHost)findViewById(android.R.id.tabhost);
    mTabHost.setup();

    initializeTabs();

  //set the listener the last, to avoid overwrite mCurrentTab everytime we add a new Tab
    mTabHost.setOnTabChangedListener(listener);
}

ভেরিয়েবলগুলি সংরক্ষণ করুন এবং বান্ডিল এ দিন:

 //Save variables while recreating
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putSerializable("stack", mStacks);
    outState.putString("currentTab", mCurrentTab);
    //outState.putInt("tabHost",mTabHost);
}

পূর্ববর্তী কারেন্টটিব উপস্থিত থাকলে এটি সেট করুন, অন্যথায় একটি নতুন ট্যাব_এ তৈরি করুন:

public void initializeTabs(){
    /* Setup your tab icons and content views.. Nothing special in this..*/
    TabHost.TabSpec spec    =   mTabHost.newTabSpec(AppConstants.TAB_A);

    spec.setContent(new TabHost.TabContentFactory() {
        public View createTabContent(String tag) {
            return findViewById(R.id.realtabcontent);
        }
    });
    spec.setIndicator(createTabView(R.drawable.tab_a_state_btn));
    mTabHost.addTab(spec);


    spec                    =   mTabHost.newTabSpec(AppConstants.TAB_B);
    spec.setContent(new TabHost.TabContentFactory() {
        public View createTabContent(String tag) {
            return findViewById(R.id.realtabcontent);
        }
    });
    spec.setIndicator(createTabView(R.drawable.tab_b_state_btn));
    mTabHost.addTab(spec);

//if we have non default Tab as current, change it
    if (mCurrentTab!=null) {
        mTabHost.setCurrentTabByTag(mCurrentTab);
    } else {
        mCurrentTab=AppConstants.TAB_A;
        pushFragments(AppConstants.TAB_A, new AppTabAFirstFragment(), false,true);
    }
}

আমি আশা করি এটি অন্যান্য লোককে সহায়তা করে।


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

-1

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

আমি ফ্রেমের ব্যাকস্ট্যাকের সাথে কাজের জন্য উপরের কোডটি অনুকূলিতকরণ করছি

এটি নীচে ট্যাবভিউ

প্রধান কার্যকলাপ ক্লাস

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.TabHost;
import android.widget.TextView;

import com.strikersoft.nida.R;
import com.strikersoft.nida.abstractActivity.BaseActivity;
import com.strikersoft.nida.screens.tags.mapTab.MapContainerFragment;
import com.strikersoft.nida.screens.tags.searchTab.SearchFragment;
import com.strikersoft.nida.screens.tags.settingsTab.SettingsFragment;

public class TagsActivity extends BaseActivity {
    public static final String M_CURRENT_TAB = "M_CURRENT_TAB";
    private TabHost mTabHost;
    private String mCurrentTab;

    public static final String TAB_TAGS = "TAB_TAGS";
    public static final String TAB_MAP = "TAB_MAP";
    public static final String TAB_SETTINGS = "TAB_SETTINGS";

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
        getActionBar().hide();
        setContentView(R.layout.tags_activity);

        mTabHost = (TabHost) findViewById(android.R.id.tabhost);

        mTabHost.setup();

        if (savedInstanceState != null) {
            mCurrentTab = savedInstanceState.getString(M_CURRENT_TAB);
            initializeTabs();
            mTabHost.setCurrentTabByTag(mCurrentTab);
            /*
            when resume state it's important to set listener after initializeTabs
            */
            mTabHost.setOnTabChangedListener(listener);
        } else {
            mTabHost.setOnTabChangedListener(listener);
            initializeTabs();
        }
    }

    private View createTabView(final int id, final String text) {
        View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
        ImageView imageView = (ImageView) view.findViewById(R.id.tab_icon);
        imageView.setImageDrawable(getResources().getDrawable(id));
        TextView textView = (TextView) view.findViewById(R.id.tab_text);
        textView.setText(text);
        return view;
    }

    /*
    create 3 tabs with name and image
    and add it to TabHost
     */
    public void initializeTabs() {

        TabHost.TabSpec spec;

        spec = mTabHost.newTabSpec(TAB_TAGS);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_tag_drawable, getString(R.string.tab_tags)));
        mTabHost.addTab(spec);

        spec = mTabHost.newTabSpec(TAB_MAP);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_map_drawable, getString(R.string.tab_map)));
        mTabHost.addTab(spec);


        spec = mTabHost.newTabSpec(TAB_SETTINGS);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_settings_drawable, getString(R.string.tab_settings)));
        mTabHost.addTab(spec);

    }

    /*
    first time listener will be trigered immediatelly after first: mTabHost.addTab(spec);
    for set correct Tab in setmTabHost.setCurrentTabByTag ignore first call of listener
    */
    TabHost.OnTabChangeListener listener = new TabHost.OnTabChangeListener() {
        public void onTabChanged(String tabId) {

            mCurrentTab = tabId;

            if (tabId.equals(TAB_TAGS)) {
                pushFragments(SearchFragment.getInstance(), false,
                        false, null);
            } else if (tabId.equals(TAB_MAP)) {
                pushFragments(MapContainerFragment.getInstance(), false,
                        false, null);
            } else if (tabId.equals(TAB_SETTINGS)) {
                pushFragments(SettingsFragment.getInstance(), false,
                        false, null);
            }

        }
    };

/*
Example of starting nested fragment from another fragment:

Fragment newFragment = ManagerTagFragment.newInstance(tag.getMac());
                TagsActivity tAct = (TagsActivity)getActivity();
                tAct.pushFragments(newFragment, true, true, null);
 */
    public void pushFragments(Fragment fragment,
                              boolean shouldAnimate, boolean shouldAdd, String tag) {
        FragmentManager manager = getFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        if (shouldAnimate) {
            ft.setCustomAnimations(R.animator.fragment_slide_left_enter,
                    R.animator.fragment_slide_left_exit,
                    R.animator.fragment_slide_right_enter,
                    R.animator.fragment_slide_right_exit);
        }
        ft.replace(R.id.realtabcontent, fragment, tag);

        if (shouldAdd) {
            /*
            here you can create named backstack for realize another logic.
            ft.addToBackStack("name of your backstack");
             */
            ft.addToBackStack(null);
        } else {
            /*
            and remove named backstack:
            manager.popBackStack("name of your backstack", FragmentManager.POP_BACK_STACK_INCLUSIVE);
            or remove whole:
            manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
             */
            manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
        ft.commit();
    }

    /*
    If you want to start this activity from another
     */
    public static void startUrself(Activity context) {
        Intent newActivity = new Intent(context, TagsActivity.class);
        newActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(newActivity);
        context.finish();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString(M_CURRENT_TAB, mCurrentTab);
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onBackPressed(){
        super.onBackPressed();
    }
}

tags_activity.xml

<

?xml version="1.0" encoding="utf-8"?>
<TabHost
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>
        <FrameLayout
            android:id="@+android:id/realtabcontent"
            android:background="@drawable/bg_main_app_gradient"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
        <TabWidget
            android:id="@android:id/tabs"
            android:background="#EAE7E1"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="0"/>
    </LinearLayout>
</TabHost>

tags_icon.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/tabsLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bg_tab_gradient"
    android:gravity="center"
    android:orientation="vertical"
    tools:ignore="contentDescription" >

    <ImageView
        android:id="@+id/tab_icon"
        android:layout_marginTop="4dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView 
        android:id="@+id/tab_text"
        android:layout_marginBottom="3dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/tab_text_color"/>

</LinearLayout>

এখানে চিত্র বর্ণনা লিখুন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.