I have an application where I have 5 tabs set up, and I it works perfectly except for one issue I noticed recently. On large screen sizes, for some reason, the tabs are transparent, I only noticed this when I tried out the application on one of my colleagues large android device. So I then set up the emulator to a large screen size of 10 inches, and its the same thing.
So in order to fix this, I used this code
public static void setTabColor(TabHost tabhost) {
for (int i = 0; i < tabhost.getTabWidget().getChildCount(); i++) {
tabhost.getTabWidget().getChildAt(i)
.setBackgroundColor(Color.parseColor("#292929")); // unselected
// }
tabhost.getTabWidget().getChildAt(tabhost.getCurrentTab())
.setBackgroundColor(Color.parseColor("#8b8b8b")); // selected
}
}
Then I use OnTabChangeListener and place
setTabColor(tabHost);
under each tab change. This fixed the issue so that the tab colours are the same for all screen sizes. However the problem now is that the divider is gone. I tried using
tabHost.getTabWidget().setDividerDrawable(R.drawable.separator);
but the problem is this piece of code must be called before you set the content of the tabs and seeing I have set up
setTabColor(tabHost);
on every tab change, it makes it rather useless as it works when application loads up, but when you switch tab, it is gone again, and I can't call it again otherwise application will crash.
Does anyone have any suggestions as to how I can fix this issue? And also could someone explain to me why the tabs are transparent on larger screens? Why does Android do this?
Thanks in advance for any assistance
This is my solution for divider lines in tab bar,
this is the tab bar sample code.
<TabWidget
android:id="#android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#drawable/tab_bar_bg_full"
android:divider="#null"
>
</TabWidget>
this is the first tab
<?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:gravity="center"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/shuffle_start_btn" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/tab_bar_divider_new" />
</LinearLayout>
Here i included the divider line in tab xml and it worked for me
I fixed this issue, turns out I could just color the tabs in and did not need to use the tab divider after all. Used this method here
public static void setTabColor(TabHost tabhost) {
for (int i = 0; i < tabhost.getTabWidget().getChildCount(); i++) {
tabhost.getTabWidget().getChildAt(i)
.setBackgroundColor(Color.parseColor("#292929")); // unselected
// }
tabhost.getTabWidget().getChildAt(tabhost.getCurrentTab())
.setBackgroundColor(Color.parseColor("#8b8b8b")); // selected
}
}
To color in tabs
Related
I came across a frustrating glitch in my app.
I have a FragmentTabHost in my Activity.
This tab host is hosting 5 fragments, each of these getting rendered correctly.
Now, the trick is that when I rotate the screen, sometimes the fragment is not being rendered.
The tab widget is still being rendered and is clickable, but the view corresponding to that fragment is not displayed.
Here is an example below.
Note that I put a red background on my realtabcontent FrameLayout:
Everything is working fine
Rotating the screen: randomly the screen becomes partially empty
Switching to other tabs executes the OnCreate/OnCreateView of the relevant fragment, but nothing is displayed
Switching back to the first screen does not even show the button of the Fragment anymore
Note that:
- the first time I rotate, there is still a button showing up (it belongs to the fragment that should be rendered).
- once the bug is triggered, I can switch to other tabs. The other tabs are loaded correctly (notice the action bar changing), but nothing is rendered in the FrameLayout
- If I go back to the first tab, the button that was still being displayed is not even rendered anymore
Notice also a detail: the reddot of the tab disappears, even though the code that set the reddot is correctly executed. It's as if the tab host is not responding to the drawing requests.
Here is the code in charge of displaying the reddot:
public void showReddotForTab(int tabIndx, Boolean showFlag){
try {
if (tabIndx < mTabHost.getTabWidget().getTabCount()) {
RelativeLayout tabRootView = (RelativeLayout) mTabHost.getTabWidget().getChildTabViewAt(tabIndx);
WhovaNotificationBadge notifBadge = tabRootView.findViewById(R.id.notif_badge);
if (showFlag) {
notifBadge.setVisibility(View.VISIBLE);
notifBadge.setLabel(null);
}
else {
notifBadge.setVisibility(View.GONE);
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
Here is the layout of the activity containing the FragmentTabHost
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentTabHost
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_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" />
<FrameLayout
android:id="#+id/realtabcontent"
android:background="#color/red"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="2px"
android:background="#color/separate_gray"/>
<TabWidget
android:id="#android:id/tabs"
android:orientation="horizontal"
android:background="#color/new_whova_tab_bg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="6dp"
android:layout_weight="0"/>
</LinearLayout>
</androidx.fragment.app.FragmentTabHost>
<TextView
android:id="#+id/text_network"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="40dp"
android:layout_alignParentTop="true"
android:background="#color/orange"
android:text="#string/network_off_indicator_msg"
android:visibility="gone"
android:textColor="#color/white"
android:gravity="center"/>
</RelativeLayout>
Here is the code setting up the tabhost
mTabHost = findViewById(android.R.id.tabhost);
mTabHost.setOnTabChangedListener(tabId -> {
...
})
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.getTabWidget().setDividerDrawable(null);
//! simplified below, but the code is simlar to it
//! spec is basically TabHost.TabSpec spec = mTabHost.newTabSpec("someID").setImageResource(...).setIndicator(...).setContent(tag -> findViewById(R.id.realtabcontent));
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
Found how to solve it, but I have no idea what is the reason.
My actionbar is a toolbar containing an AppCompatSpinner.
The spinner is only used for the tab showed in the pictures and has setOnItemSelectedListener set.
When rotating the screen, the listener is called (not sure why, because the initial selection is done before setting the listener and not selection occurred afterwhile.
Either way, sometimes when this listener is called, the data loaded from DB is still processing in the background. The weird thing being that once it is done, we refresh the UI but it stays blank and the other tabs are affected.
I suspect that there is something wrong in the way the listener is called and it blocks the processing of other UI events, but not sure.
When I set the listener within a post, the problem disappear
spinner.post(() -> {
spinner.setOnItemSelectedListener(...)
})
I am facing a problem. I have a custom layout for toolbar and in my activity xml, I am including this toolbar. The structure of my activity is:
Toolbar layout on top using include tag.
The ViewPager
TabLayout at bottom to get bottom tabs.
The problem I am facing is that on some devices, it is coming out perfect but in a few devices (tested on moto G series devices) the bottom tab is leaving space.
Please Refer the Images below:
In Normal Phones when it shows expected UI
In MOTO G series
The xml code is following
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#efeded"
android:weightSum="11"
android:orientation="vertical">
<include layout="#layout/custom_tool_bar" />
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="10" />
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
style="#style/BottomTabLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#fff"
android:layout_weight="1"
app:tabIndicatorColor="#color/colorPrimaryDark"
app:tabSelectedTextColor="#color/colorPrimaryDark"
/>
</LinearLayout>
For bottom tabs you can use bottomnavigationview it provides better performance and works in MOTO devices with out any problem. Check this
https://developer.android.com/reference/android/support/design/widget/BottomNavigationView.html
remove android:weightSum=11 attribute in your parent LinearLayout;
change ViewPager's android:layout_weight=10 attribute to android:layout_weight=1;
remove TabLayout's android:layout_weight=1 attribute
and try again.
I found the problem why is it happening. I have used a library - ResideMenu in my Activity and it has a function to detect whether the device has hardware Back and Menu buttons or the digital ones.
#Override
protected boolean fitSystemWindows(Rect insets) {
int bottomPadding = viewActivity.getPaddingBottom() + insets.bottom;
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME);
if (!hasBackKey || !hasHomeKey) {//there's a navigation bar
bottomPadding += getNavigationBarHeight();
}
this.setPadding(viewActivity.getPaddingLeft() + insets.left,
viewActivity.getPaddingTop() + insets.top,
viewActivity.getPaddingRight() + insets.right,
bottomPadding);
insets.left = insets.top = insets.right = insets.bottom = 0;
return true;
}
This is not working properly. For some devices it works fine but for some devices it's not able to determine the buttons. So in some devices it is going up leaving a space for digital buttons.
Anyways, I could not find a solution for this, tried a few StackOverflow but it didn't work out, please post if anyone finds something.
I saw a lot of questions that say how to set the color differently for selected(active) and unselected(inactive) tabs. I also know that google provides void setTabTextColors (int normalColor, int selectedColor) to achieve this.
My requirement is a little different, I am working on a quiz app with a TabLayout and CardView. TabLayout allows the user to navigate between questions and CardView is used to display the questions.
I need to set the color of the tabs for which the user has already selected an answer differently than that for which the user has not answered yet. By default the TextColor is black but if the user selects an answer then the tabcolor should change to blue (just for eg.) and it should remain that way till the user exits. I have a int array called Select that will hold the value of the option that the user has selected (The values range between 1 - 4). While allocating the Select array I also initialize it with -1. I thought of setting up a loop and then if the array is -1 leave the tab as it is or set the tabcolor to blue.
How can I achieve this functionality?
You can work with TabLayout internals by querying for this children and changing TextViews manually. This can break your code when you upgrade to another support library version, but as long as you keep track and test when updating, it should work:
private void updateTabTextColors() {
LinearLayout tabsContainer = (LinearLayout) mTabLayout.getChildAt(0);
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
LinearLayout item = (LinearLayout) tabsContainer.getChildAt(i);
TextView tv = (TextView) item.getChildAt(1);
tv.setTextColor(Select[i] == -1 ? Color.BLACK : Color.BLUE);
}
}
Just enhancing Marcelo Liberato's answer to support custom background for each tab item.
LinearLayout tabsContainer = (LinearLayout) mTabLayout.getChildAt(0);
LinearLayout childLayout1 = (LinearLayout)tabsContainer.getChildAt(2);
LinearLayout childLayout2 = (LinearLayout)tabsContainer.getChildAt(3);
LinearLayout tabView = (LinearLayout) childLayout1.getChildAt(0).getParent();
tabView.setBackgroundResource(R.drawable.ic_blue_selector);
tabView = (LinearLayout) childLayout2.getChildAt(0).getParent();
tabView.setBackgroundResource(R.drawable.ic_red_selector);
Custom xml file:
<?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:id="#+id/ll_tab_holder"
android:orientation="vertical">
<LinearLayout
android:id="#+id/ll_tab_icon_title_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/tab_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter" />
<TextView
android:id="#+id/tab_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textAppearance="#style/lasuCustomTabText" />
</LinearLayout>
<TextView
android:id="#+id/tab_only_title"
android:layout_width="match_parent"
android:layout_height="56dp"
android:textAllCaps="true"
android:textSize="12sp"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#drawable/ic_tab_text_color_selector" />
</LinearLayout>
Output:
If you are interested in using a library for this functionality, this library works well.
https://github.com/astuetz/PagerSlidingTabStrip
As in the doc getTabTextColors() -> Gets the text colors for the different states (normal, selected) used for the tabs. the tabs can only have 2 states. The only way to achieve what you want if to inherit Tab class and add a new state, something like: tabAlreadyVisited. Then #Override the draw method to change background color based on the tabAlreadyVisited attribute value. Or change the text color with setTabTextColors
It's possible to set custom view for your tab
TabLayout.Tab yourTab = tabLayout.newTab();
yourTab.setCustomView(R.layout.red_text_view);
And red_text_view.xml is
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="sans-serif-medium"
android:gravity="center"
android:textColor="#f44336"/>
If you use the #android:id/text1 default Tab's settext should work. You could do whatever you want with your custom view.
I have been searching for an answer to this for days, and while some things kinda work (and most don't), I'm hoping I can find the best practice for what I'm trying to do.
I'm trying to get a notification bar to display in my app. Ideally, it would slide down from the top, while shifting other elements in the layout to accommodate. Here's an illustration to help: illustration
Here is how the layout is structured (I took out a bit for brevity):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false">
<!-- notification -->
<RelativeLayout
android:id="#+id/notification_bar"
android:layout_width="match_parent"
android:layout_height="40dip"
android:background="#drawable/notification_background"
android:visibility="visible">
<!-- end notifcation -->
</RelativeLayout>
<!-- header -->
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="47dip"
android:layout_alignParentTop="true" >
<ImageView
android:id="#+id/header_bg"
android:layout_width="match_parent"
android:layout_height="47dip"
android:src="#drawable/home_header" />
<!-- end header -->
</RelativeLayout>
<!-- buttons -->
<LinearLayout
android:id="#+id/buttons"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/footer"
android:layout_below="#id/notification_bar"
android:layout_gravity="center_horizontal">
<ImageButton
android:id="#+id/button1"
android:src="#drawable/button1"
android:layout_width="86dip"
android:layout_height="65dip"
android:layout_weight=".4" />
<ImageButton
android:id="#+id/button2"
android:src="#drawable/button2"
android:layout_width="98dip"
android:layout_height="73dip"
android:layout_weight=".2" />
<ImageButton
android:id="#+id/button3"
android:src="#drawable/button3"
android:layout_width="86dip"
android:layout_height="71dip"
android:layout_weight=".4" />
<!-- end buttons -->
</LinearLayout>
<!-- footer -->
<LinearLayout
android:id="#id/footer"
android:layout_width="match_parent"
android:layout_height="76dip"
android:layout_alignParentBottom="true"
android:background="#drawable/home_footer" >
<!-- end footer -->
</LinearLayout>
</RelativeLayout>
MAIN PROBLEM:
To start, I animated the notification bar. It moves, and at the end of the animation, it snaps back into place. Yes, I have fillAfter set to true. It doesn't make a difference. Regardless, the 3 items that should shift are clickable, and from what I've read, the elements haven't actually moved, they just look like they have.
SECONDARY PROBLEM:
The entire view is a RelativeLayout, however the three elements are in a LinearLayout set via the XML to be layout_below the notification bar. I had hoped that shifting the notification bar would squeeze this LinearLayout, shifting the buttons to accommodate, but no such luck. If I have to shift the three elements as separate animations, that's fine. I've tried that, but they suffer from the same "snap-back" issue the notification bar does. I was hoping there would be a simpler, more logical approach, however.
I've found a number of posts about this snap-back problem, but none of the solutions quite work (or make sense to me, granted a bit of a noob). It sounds like something needs to happen in the onAnimationEnd handler. I think it's something with adjusting the LayoutParams, but I'm not sure how or what to do there.
I'm targeting for API 8 (2.2), so the Honeycomb animation APIs won't help. I've looked into NineOldAndroids, which looks promising, but figure there has got to be a way to do this with the native API.
** Bonus points if we can get the notification bar to be dismissed, and everything moves back to its original position.
** UPDATE: The following code KIND OF works **
Here is the animation method to slide the notification bar out:
private void showNotification() {
mNotificationBar.setVisibility(View.VISIBLE);
Animation slideOut = AnimationUtils.loadAnimation(this, R.anim.slide_notification_out);
slideOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// do nothing
}
#Override
public void onAnimationRepeat(Animation animation) {
// do nothing
}
#Override
public void onAnimationEnd(Animation animation) {
// do SOMETHING
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mNotificationBar.getLayoutParams();
params.addRule(RelativeLayout.BELOW, mHeader.getId());
mNotificationBar.setLayoutParams(params);
mNotificationBar.clearAnimation();
}
});
mNotificationBar.startAnimation(slideOut);
}
Altering the LayoutParams on AnimationEnd keeps the Notification bar in place. AND, when the animation is done, the Buttons layout squeezes to accommodate! BUT, the button layout doesn't smoothly animate like the Notification Bar, it just snaps into place at the end of the animation. Also, the Notification Bar also jumps a bit at the very end of the animation, I'm guessing because the layout is being redrawn. SO CLOSE, but so far.
Snap back problem
You need to define the notification in the final place that you want it to appear in the layout. For you it's probably as the first item in the LinearLayout you refer above. Then you set visibilityto gone.
Finally you use a piece of code similar to the one bellow (I´m using it to animate buttons into the screen):
private void buttonFadeOut() {
linear_layout_buttons.startAnimation(AnimationUtils.loadAnimation(MyMapActivity.this, android.R.anim.slide_out_right));
linear_layout_buttons.setVisibility(View.GONE);
}
private void buttonFadeIn() {
if(linear_layout_buttons.getVisibility() == View.GONE){
linear_layout_buttons.startAnimation(AnimationUtils.loadAnimation(MyMapActivity.this, R.anim.slide_in_right));
linear_layout_buttons.setVisibility(View.VISIBLE);
}
}
Squeeze problem
This one I've never tried with an animation, but you can set the android:layout_weight="1.0" in each one of the items in your relative layout, to split the available space equaly between them (or play with the value to assign diferent space for each).
Regards.
I have a TabHost holding 5 tabs.
As far as I can see, there has to be one tab selected at all times.
I need a way to unselect all my tabs so none will be selected.
If the tabhost is meant by general to have one tab selected at all times,
how can I make it appear (UI speaking) as if the tab isn't selected?
try this:
final TabWidget tabWidget = tabHost.getTabWidget();
final int n = tabWidget.getChildCount();
for (int i = 0; i < n; ++i) {
tabWidget.getChildAt(i).setSelected(false);
}
or you can add hidden tab and select it when you want unselect a tab
tabHost.addTab(
tabHost.newTabSpec("hiddenTab").setIndicator(""),
MyFragment.class,
null
);
tabHost.getTabWidget().getChildTabViewAt(hiddenTabIndex).setVisibility(View.GONE);
and select this tab when you want
tabHost.setCurrentTab(hiddenTabIndex);
This is not possible AFAIK. but yes,you can set the selected tab's color to look like it is unselected and set a blank layout over it by managing a global variable when you make it 'unselected' and setting up normal layout when you want it to be shown normally to user. But this is kind of a trick.
Hope,you get my point!
EDIT :
Suppose you have set String what="disappear" somewhere in your code to show it 'unselected',then you can use this function to change color of tab:
Main.class:
//Change The Backgournd Color of Tabs
public void setTabColor(TabHost tabhost) {
for(int i=0;i<tabhost.getTabWidget().getChildCount();i++)
{
tabhost.getTabWidget().getChildAt(i).setBackgroundColor(Color.parseColor("#FFFFFF"))); //unselected white colored
}
if(!what.equals("disappear"))
tabhost.getTabWidget().getChildAt(tabhost.getCurrentTab()).setBackgroundColor(Color.parseColor("FF0000"))); // selected red colored
else
tabhost.getTabWidget().getChildAt(tabhost.getCurrentTab()).setBackgroundColor(Color.parseColor("FFFFFF"))); // selected but show as unselected with white color
}
And in your activity class(which is opened by that selected tab):
FirstActivity.class:
if(what.equals("disappear"))
setContentView(R.layout.blank);
else
setContentView(R.layout.first_layout);
blank.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"
android:id="#+id/layout"
android:background="#ffffff"
android:gravity="center">
<!-- You can make background transperent by setting it to "00ffffff" -->
<!-- You can also add this textview to guide user -->
<!--
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Click Any Tab To Start
/>
-->
</LinearLayout>
For this purpose, maybe using tabHost is not the proper way ?