I have TabActivityGroup:
MainActivity class contain some tab, that name loading from db. Sales, Admin, Inquiry like wise I have tab name
For Sales I created SalesActivityGroup.That class is :
public class SalesActivityGroup extends ActivityGroup {
public static SalesActivityGroup group;
private ArrayList<View> history;
private LocalActivityManager mActivityManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.history = new ArrayList<View>();
group = this;
mActivityManager = getLocalActivityManager();
Intent i = new Intent(getBaseContext(), SalesRouteActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("positions", -1);
i.putExtras(bundle);
View view = mActivityManager.startActivity("Sales",i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP )).getDecorView();
replaceView(view);
}
public void replaceView(View v) {
history.add(v);
setContentView(v);
}
public void back(){
if ( history.size() > 1 ){
history.remove(history.size() - 1);
View v = history.get(history.size() - 1);
setContentView(v);
}
else {
this.finish();
}
}
#Override
public void onBackPressed() {
SalesActivityGroup.group.back();
}
}
SalesRouteActivity is first Activity .In there i want to set up the title name.I did using this ways.But not working
public class SalesRouteActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.sales_routes);
//getWindow().setTitle("Route");
View viewToLoad = LayoutInflater.from(SalesActivityGroup.group).inflate(R.layout.sales_routes, null);
this.setContentView(viewToLoad);
//this.setTitle("Route");
//getWindow().setTitle("Route");
SalesActivityGroup.group.setTitle("Route");
}
}
Please advice me How can i set the Title name.
Thanks in advance
You can access the parent tab activity like
getParent().getParent().setTitle("New Tilte");
EXPLANATION:
Based on my understanding,
When you call the getParent the first time, you get the activity group that started the child activity.
When you call getParent the second time, you will get the tab activity that started the activity group.
setTitle should work for the activity window which is held by the tabactivity. The sub activities are rendered in the framelayout of the tab activity. So in the child activities access the parent tab activity to set the title.
The best way to do this is to implement the method
protected void onChildTitleChanged(Activity childActivity,CharSequence title) {
super.onChildTitleChanged(childActivity, title);
setTitle(title);
}
Implement this method in the parent activity. For example, In my case I have three activities.
Home Activity
Artists Activity
Album Activity
My Home activity contains a TabHost with Artists activity and Album activity.
I implemented the above method in the Home Activity. The title for Artists activity and Album activity is set in the OnResume methods of those respective activities.
It is not advisable to use ActivityGroup, it has been deprecated.
Refer to this link
Please use Fragment and FragmentManager using Compatibility Library
Related
I have an Activity that contains a TabLayout linked to a ViewPager. The ViewPager contains three fragments, and each fragment has a RecyclerView containing a list of articles. When you click on an a list item, it starts a new Activity with the full article text. After I start the new Activity, then press the back button to return to the first Activity, it starts with the first tab selected.
I would like to be able to start a new Activity from any tab, then when the back button is pressed, it goes back to the previous activity with the same tab selected. And preferably, maintain the state within the RecyclerView (i.e. how far it is scrolled down). How can I achieve this?
I have tried using onSaveInstanceState to save the viewPager.getCurrentItem(). It worked for when the device was rotated, but the saved instance state does not seem to be called after a new Activity is started.
The tabbed Activity:
public class ArticleActivity extends BaseActivity {
#Bind(R.id.toolbar) Toolbar mToolbar;
#Bind(R.id.content) ViewPager mViewPager;
#Bind(R.id.tabs) TabLayout mTabLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_article);
ButterKnife.bind(this);
setupActionBar();
FragmentPagerAdapter adapter = new BaseFragmentPagerAdapter(this, getSupportFragmentManager(),
NewsFragment.newInstance();
EventsFragment.newInstance();
BulletinFragment.newInstance();
);
mViewPager.setAdapter(adapter);
mTabLayout.setupWithViewPager(mViewPager);
}
private void setupActionBar() {
setSupportActionBar(mToolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null) {
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
}
And one of the fragments contained in the ViewPager:
public class NewsFragment extends BaseFragment implements
Observer<DataResponse<NewsArticle>> {
#BindDimen(R.dimen.divider_padding_start) float mDividerPadding;
#Bind(R.id.recycler) RecyclerView mRecyclerView;
#Bind(R.id.progress) RecyclerView mProgressBar;
#Bind(R.id.refresh_layout) SwipeRefreshLayout mRefreshLayout;
#Inject RestService mRestService;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recycler_refresh, container, false);
ButterKnife.bind(this, view);
mRefreshLayout.setColorSchemeColors(
ThemeUtil.getColorAttribute(getContext(), R.attr.colorAccent));
mRefreshLayout.setProgressBackgroundColorSchemeColor(
ThemeUtil.getColorAttribute(getContext(), R.attr.colorPrimary));
mRefreshLayout.setOnRefreshListener(this::getArticles);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.addItemDecoration(new DividerDecoration(getContext(), mDividerPadding));
getArticles();
return view;
}
#Override
public CharSequence getTitle(Context context) {
return context.getString(R.string.title_fragment_news);
}
#Override
publiv void onCompleted() {
mRefreshLayout.setRefreshing(false);
}
#Override
public void onError(Throwable exception) {
Timber.d(exception, "Failed to retrieve articles");
}
#Override
public void onNext(DataResponse<NewsArticle> response) {
mProgressBar.setVisibility(View.GONE);
ArticleAdapter adapter = new ArticleAdapter(getContext(), response.data());
adapter.setOnItemClickListener(this::onItemClick);
mRecyclerView.swapAdapter(adapter, false);
}
private void getArticles() {
mRestService.getNews().subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()).subscribe(this);
}
public void onItemClick(Article article) {
Intent intent = new Intent(getActivity(), ArticleDetailActivity.class);
intent.putExtra("article", article);
startActivity(intent);
}
}
I was curious, so I just tested it. The Activity doesn't actually get destroyed when I start the new Activity. It only gets destroyed (then recreated) after I press the back button. I don't understand why it would do that, if the Activity does not need to be destroyed when a new one starts, why would it need to be destroyed when it simply comes to the foreground?
I have another activity (my SettingsActivity) that does not have a parent Activity, and just calls finish() when the back button is pressed. If I start this Activity from my ArticleActivity, the ArticleActivity never gets destroyed, and saves its state perfectly.
I found my answer here: ActionBar 'up' button destroys parent activity, 'back' does not
And here: How can I return to a parent activity correctly?
The parent (ArticleActivity) was getting destroyed after the Back button was pressed in the child Activity because that is the behavior of the "standard" launch mode. I set android:launchMode="singleTop" for the ArticleActivity in the manifest, which gives me the desired launch behavior. Now when Back is pressed in the child Activity, the parent is not recreated.
I have a fragment which has a TextView, an EditText and a Button. I also have 2 activities which include this fragment and at onClick of the button in one of the activities, the other is started. Via the intent, the text in the edittext is passed which becomes the text of the textview of the other activity.
I had two design decisions to choose from
Create two such fragments classes with appropriate methods that construct the appropriate intents. Access the UI elements from inside the respective fragment object and start the activities.
Create only one fragment class. onClick the, event is passed down to a particular method in the activities (both the activities have this method) and the activities have the logic to build the intent and start the other activity
Consider what would happen if there are 100 such activities. The first method would have us write 100 different fragment classes with custom methods, but in the second method, it is a single class and the activities have the custom logic in a particularly named method.
Therefore I chose to go with the second choice and I realized that the UI elements could not be instantiated in the onCreate method of activity as the fragment's layout is not inflated yet. I am doing the instantiation in onStart as a workaround.
Is that bad practice or is there a better design pattern to follow?
The recommended pattern is to create a holder interface which any activity that wants to instantiate your fragment must implement. Also to set data for views in your new fragment then create a newInstance() factory method on your fragment.
I tend to approach it like this;
class FooFragment implements Fragment {
private static final String TEXT_FOR_TEXTVIEW = "textForTextView";
private FooFragmentHolder mHolder;
/*
* Rather than creating your fragment in your layout directly
* you should instead instantiate it using this class in your
* activity.
*/
public static FooFragment newInstance(String text) {
Bundle data = new Bundle();
data.putString(TEXT_FOR_TEXTVIEW, text);
FooFragment fooFragment = new FooFragment();
fooFragment.setArguments(data);
return fooFragment;
}
public interface FooFragmentHolder {
public void buttonPressed(String editTextContent);
}
/*
* When we create the fragment with the activity we use onAttach to get
* our holder implementation (the activity)
*/
#Override
public void onAttach(Activity activity) {
if (activity instanceof FooFragmentHolder) {
mHolder = (FooFragmentHolder) activity;
} else {
throw new IllegalStateException("Containing activity must implement FooFragmentHolder");
}
}
#Override
public void onCreateView(Inflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_foo, container, false);
final EditText editText = (EditText) view.findViewById(R.id.edit_text);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(Button button) {
mHolder.buttonPressed(editText.getText());
}
})};
TextView textView = (TextView) view.findViewById(R.id.text_view);
Bundle args = getArguments();
if (args != null) {
textView.setText(args.getString(TEXT_FOR_TEXTVIEW));
}
return view;
}
}
Now in your activity you just need to implement the FooFragmentHolder interface and use the newInstance method we created;
class FooActivity extends Activity implements FooFragment.FooFragmentHolder {
private static final String TEXT_FOR_TEXTVIEW = "textForTextView";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentLayout(R.layout.activity_foo);
// Instead of creating your fragment in your layout, create a holder
// layout and attach a new instance of your fragment using a fragment
// transaction.
FooFragment fooFragment = FooFragment.newInstance(getIntent().getStringExtra(TEXT_FOR_TEXTVIEW));
getFragmentManager().beginTransaction()
.replace(R.id.content, fooFragment)
.commit();
}
#Override
public void buttonPressed(String editTextContent) {
// In this case just starting the next FooActivity, but logic could be
// applied for any other activity.
Intent intent = new Intent(this, FooActivity.class)
.putExtra(TEXT_FOR_TEXTVIEW, editTextContent);
startActivity(intent);
}
}
I decided to settle with the following patter --
Any activity which includes this fragment should implement an interface like
public interface ViewsCreatedListener {
public void onViewsCreated();
}
The activity would then look like
public class ExampleActivity extends Activity implements ViewsCreatedListener {
.
.
.
.
#Override
public void onViewsCreated() {
//Initiate the views here and do what gotta be done
}
}
The fragment should check that any activity that includes this fragment should implement that interface using the onAttach method and onActivityCreated, the activity is notified
public class ExampleFragment extends Fragment {
ViewsCreatedListener listener = null;
.
.
.
.
#Override
public onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (ViewsCreatedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ViewsCreatedListener");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listener.onViewsCreated();
}
}
Doing this way, the fragment just provides the UI and the including activities decide as to what should be done with the UI elements included via the fragment. This maximizes reusability.. DRY... :-D
I have 3 tabs in my sample application with activity group. First tab contains search activity i.e.Home/Root activity and am displaying the results of search in another activity but under same tab i.e Tab1. When I press back button in result activity, it is going to search activity. Everything works fine till here. Now I want to go search activity by pressing tab1 instead of pressing back button. How can achieve this? I tried something like this
public class TabSample extends TabActivity {
public TabHost tabHost;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec("tab1").setIndicator("OPT")
.setContent(new Intent(this, TabGroup1Activity.class)));
tabHost.addTab(tabHost.newTabSpec("tab2").setIndicator("EDIT")
.setContent(new Intent(this, TabGroup2Activity.class)));
tabHost.setCurrentTab(1);
tabHost.setOnTabChangedListener(new OnTabChangeListener() {
public void onTabChanged(String arg0) {
if (tabHost.getCurrentTabTag().equals("tab1")) {
//What should I do to display search activity here
} else {
tabHost.setCurrentTab(1);
}
}
});
tabHost.setFocusable(true);
tabHost.requestFocus();
}
}
Can anyone please help let me know how to invoke search activity when tab is pressed? What will go into if part? Because if I use tabHost.setCurrentTab(index), it will display result activity but not search activity.
NOTE: I followed the tutorial given in this link.
I think what you want to do is this: when the 'tab1' tag is selected, go back to TabGroup1Activity if (and only if) the current activity is not that activity (basically you want to simulate a 'back' press).
If so, what you want is this:
if (getCurrentActivity().getClass() != TabGroup1Activity.class)
getCurrentActivity().finish()
I'm not 100% sure I understand you fully, but let's see :)
In your onTabChanged listener you can switch on which tab have been tabed, and then open the activity as normal inside an activitygroup:
public void onTabChanged(String tabId) {
if (tabId.contentEquals("tab1")) {
Intent intent = new Intent(tabHost.getContext(), TabGroup1Activity.class);
View view = StartGroup.group.getLocalActivityManager().startActivity("tab1", intent).getDecorView();
StartGroup.group.setContentView(view);
}
}
I just reviewed my code and think there's a bit more to explain here. The problem is that you don't stack activities as normal. Instead the workaround is to make a content stack and change these instead. So what I have done is to create a class StartGroup which extends
ButtonHandlerActivityGroup:
public class StartGroup extends ButtonHandlerActivityGroup {
// Keep this in a static variable to make it accessible for all the nested activities, lets them manipulate the view
public static StartGroup group;
// Need to keep track of the history if you want the back-button to work properly,
// don't use this if your activities requires a lot of memory.
private ArrayList<View> history;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.history = new ArrayList<View>();
group = this;
// Start the root activity within the group and get its view
View view = getLocalActivityManager().startActivity("UserList", new Intent(this, UserList.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
replaceView(view);
}
public void back() {
if (history.size() > 0) {
// pop the last view
history.remove(history.size()-1);
setContentView(history.get(history.size()-1));
} else {
finish();
}
}
}
Then from the TabMaster class or what you call it you can use the StartGroup class to change the content view of an activity group.
This is something I wrote to work on devices from 2.2, so there might be an easier and more androidish way to accomplished it, but this works on almost all devices :)
Here is another thread where the use a similar approach:
Launching activities within a tab in Android
Let me know if I can help more.
There is an ArrayList in your ActivityGroup so override onPause() method in ActivityGroup and remove all the ids from ArrayList except the first one which must be your SearchActivity.
So when you go to other tab then comes back to SearchActivity( or on Tab1 ) Home will be displayed.
How could I few layout and require to create in one Tabactivity.
I have try the code below but no luck got error.
tabHost.addTab(tabHost.newTabSpec("Sales Order").setIndicator("Sales Order").setContent(R.layout.frm_txn_so_item_list));
OK let me explain clearly.
I have a code below, as you can see I have 4 tab layout page. Each of it has it own activity class. I have a button which is belong to cls_so_item_list.class, whenever I am try to call it in my cls_so it always return me null value.
So I have come out an idea, to remove all the tab page(item,product,summary,Report) activity classes then create it one standalone class which is cls_so.
My question is how do I put the layout page inside the tabHost.addTab? Thanks
public class cls_so extends TabActivity implements OnClickListener {
protected TabHost tabHost;
int intSalesOrderId;
src_txn_so.cls_so_obj objSalesOrder;
static final String LIST_ID = "list_id";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tabHost = getTabHost();
newTabIntent("Item", null, cls_so_item_list.class);
newTabIntent("Product",
getResources().getDrawable(R.drawable.so_product),
cls_so_prd_list.class);
newTabIntent("Summary",
getResources().getDrawable(R.drawable.so_summary),
cls_so_summary.class);
newTabIntent("Report",
getResources().getDrawable(R.drawable.so_report),
cls_so_summary.class);
Button btnSOLineDiscount = (Button) findViewById(R.id.txn_so_btn_line_discount);
btnSOLineDiscount.setOnClickListener(this);
tabHost.setCurrentTab(0);
}
protected void newTabIntent(String label, Drawable icon, Class<?> pageClass) {
TabSpec tabSpec = tabHost.newTabSpec(label);
tabSpec.setIndicator(label, icon);
Intent SOIntent = new Intent().setClass(this,pageClass);
SOIntent.putExtra(LIST_ID, -1);
tabSpec.setContent(new Intent(this, pageClass));
tabHost.addTab(tabSpec);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
It doesn't work like that. In your main layout, add in the FrameLayout with the id of tabcontent an element <include> that points on that layout.
In your code you'll have to change it to.
tabHost.addTab(tabHost.newTabSpec("Sales Order").setIndicator("Sales Order").setContent(R.id.my_included_layout));
if you're <include> has the id "#+id/my_included_layout"
Greetings,
I am trying to get the Click - event when clicking on the currently selected tab of my TabActivity. The onTabChangedHandler is only called whenever the tab is changed, not if the currently active Tab is clicked. The debugger tells me i have the onClickListener Registered for the TabWidget within my TabHost.
Am i registering for the wrong View?
Also, I am unable to create a Context Menu for the Tabs, only for its content, is this problem related?
public class TestDroidViewTab extends TabActivity
implements TabContentFactory
, OnTabChangeListener, OnClickListener {
private static final String LOG_KEY = "TEST";
ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TabHost tabHost = getTabHost();
TabHost.TabSpec ts = tabHost.newTabSpec("ID_1");
ts.setIndicator("1");
ts.setContent(this);
tabHost.addTab(ts);
ts = tabHost.newTabSpec("ID_2");
ts.setIndicator("2");
ts.setContent(this);
tabHost.addTab(ts);
ts = tabHost.newTabSpec("ID_3");
ts.setIndicator("3");
ts.setContent(this);
tabHost.addTab(ts);
tabHost.setOnClickListener(this);
tabHost.setOnTabChangedListener(this);
}
public void onClick(View v) {
Log.d(LOG_KEY, "OnClick");
}
public void onTabChanged(String tabId) {
Log.d(LOG_KEY, "OnTabChanged");
}
If you want to see that a particular tab is clicked, you need to add your listener to the tab itself, not the TabHost.
The hierarchy of views in a tab implementation is:
TabHost
TabWidget
(tab)
(tab)
FrameLayout
The tabs are added at runtime by calling: tabHost.addTab(tabHost.newTabSpec(""));
You can then get a handle to the individual tabs by calling: getTabWidget().getChildAt(4);
In essence, you are adding your OnClickListener to a child of the TabWidget. You can now pick up the clicks on your individual tab. However, this will override the default behavior which changes the content when a tab is clicked. So, to get your content to change, your OnClickListener will need to do that for you.
Here is a full example, which lets you intercept the click event, and change the content below the tab:
final String myTabTag = "My Tab";
final int myTabIndex = 3;
getTabHost().addTab( getTabHost().newTabSpec(myTabTag) );
getTabWidget().getChildAt(myTabIndex).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (getTabHost().getCurrentTabTag().equals(myTabTag)) {
getTabHost().setCurrentTab(myTabIndex );
}
}
});
use setOnTabChangedListener instead of OnClickListener ;)
static TabHost tabHost;
tabHost = getTabHost();
tabHost.setOnTabChangedListener(new OnTabChangeListener() {
#Override
public void onTabChanged(String arg0) {
Log.i("******Clickin Tab number ... ", "" + tabHost.getCurrentTab());
}
});
Your clause is wrong, use:
...
if (getTabHost().getCurrentTabTag().equals(myTabTag) == false) {
getTabHost().setCurrentTab(myTabIndex );
}
...
into my code, it shows some errors and ask me to create new methods in
those names like getTabWidget(), getTabHost(), etc. Waiting for your
response.
Try this
tabHost.getTabHost().setCurrentTab(myTabIndex);