How to use selectors to change icons with the new TabLayout - android

I'm using the new support TabLayout from Android. The thing is that I wanted to use selectors to change the icon when a tab is selected.
I've been looking into the source code and it seems to me that the it never changes the state of the view (and for that reason I can't use the selector).
Does anyone knows some workaround?
Thank you!

Assume your my_selector.xml is,
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/icon_on" android:state_selected="true"/>
<item android:drawable="#drawable/icon_off"/> <!-- default -->
</selector>
then you can call setIcon directly,
tab.setIcon(R.drawable.my_selector);
Verified with 'com.android.support:design:22.2.0'.

I found that when I first set the custom view for each tab in the TabLayout I need to set the first one (index 0) as selected.
TabLayout toolbarTabLayout = (TabLayout) findViewById(R.id.tabs);
toolbarTabLayout.setupWithViewPager(mViewPager);
toolbarTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
toolbarTabLayout.setTabMode(TabLayout.MODE_FIXED);
toolbarTabLayout.setTabTextColors(R.color.colorPrimary, R.color.white);
// Iterate over all tabs and set the custom view
for (int i = 0; i < toolbarTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = toolbarTabLayout.getTabAt(i);
View v=mSectionsPagerAdapter.getTabView(i);
// no tabs are actually selected at start, this will make sure the
// selector for the colors comes in right when initialized
if (i==0)
v.setSelected(true);
tab.setCustomView(v);
}
This seems to force the first tab as selected when the custom view is applied. It really feels like a hack, hopefully someone else will figure out the real issue and propose a better fix.

There is way to set customView as a tab with setCustomView(View view) method. So you can create a textview and set a selector to it and set this view to tab.
Hope it helps you!

If you did everything right (and i believe this) so you arrived at same point than me. Maybe it is a little bug in new android appcompat library.
i found an workaround (it's called Gambiarra in a good Portugues) to solve this problem. you need to call the method select() from Tab class like this:
mTabLayout.getTabAt(x).select();
BUT it's very important: x variable must be different than current selected tab index.

This is what worked for me:
Assuming you have your selectors set in the drawable res folder (like Xingang Huang showed above).
In your MainActivity (where you setup your TabLayout) you include your array of icon selectors and then you loop through it like this:
for (int i = 0; i < yourTabLayout.getTabCount(); i++) {
ImageView imageView = new ImageView(this); //your context, in this case MainActivity.class
imageView.setImageResource(arr_tabIcons[i]); //tabIcons is the array of icons
if (i==0) {
imageView.setSelected(true);
}
yourTabLayout.getTabAt(i).setCustomView(imageView);
}
tab.setIcon(R.drawable.icon)
works as well but in my case the icons looked really small, so I had to use the solution with the ImageView to fill the tab view.
Happy coding ;)

Related

How to show tab indicator on top of tab in android?

I have an app in which I have added TabLayout with three tabs. I want to show tab indicator from bottom to top but the problem is that when I show indicator from bottom to top tab icon also rotate.How do I resolve this issue?
mTabLayout.getTabAt(0).setCustomView(view);
mTabLayout.getTabAt(1).setIcon(tabIcons[1]);
mTabLayout.getTabAt(2).setIcon(tabIcons[2]);
mTabLayout.setRotationX(180);
I have similar requirement in my previous application
Please try below code it works perfectly for me
//First rotate the tab layout
tabOrderType.setRotationX(180);
//Find all childs for tablayout
for (int i = 0; i <tabOrderType.getChildCount() ; i++) {
LinearLayout linearList = ((LinearLayout)tabOrderType.getChildAt(i));
for(int position = 0;position<linearList.getChildCount();position++) {
//One by one again rotate layout for text and icons
LinearLayout item=((LinearLayout) linearList.getChildAt(position));
item.setBackground(getResources().getDrawable(R.drawable.custom_button_border));
item.setRotationX(180);
}
}
This is working nicely in my application and not creating any issue till now.Another way is you need to make custom tab to manage this.
Hope this helps you...if any issue please ask me...
Add this in your TabLayout XML
app:tabIndicatorGravity="top"

Is there a way to display notification badge on Google's official BottomNavigationView menu items introduced in API 25?

I have been trying BottomNavigationView released in API 25. I want to display a notification badge (say a small blue circle with or without a count in it) on one of the menu items in bottom navigation bar.
I have a selector drawable where I have added checked true and checked false states with greyed out drawable which has a BLUE dot on it. When user navigates to other navigation item the whole menu button turns grey and the badge as well. I know this is because itemIconTint is applied to the drawable which is the reason having a different colour badge as part of the icon won't work. Is there any alternate way to achieve this?
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/white"
app:itemIconTint="#color/selector_bottom_nav"
app:itemTextColor="#color/selector_bottom_nav"
android:layout_gravity="bottom"
app:menu="#menu/menu_bottom_nav">
</android.support.design.widget.BottomNavigationView>
That is how I am using it. Removing itemIconTint and changing icon drawable programmatically does not help.
On Android Developers I have found nothing and it being pretty new nothing is available on the web as well.
There are custom libraries for bottom navigation bar but I am looking for its support in the official one.
Any ideas, anyone?
Answering my own question:
I ended up putting 2 ImageViews with badge drawable and visiblity GONE in my layout. I calculate positioning of the badges like this:
private void calculateBadgesPosition() {
int totalNavItems = 3;
for(int i = 0 ; i < bottomNavigation.getChildCount() ; i++) {
View view = bottomNavigation.getChildAt(i);
// Since Y remains same for all badges at least in my case.
// 1.3 is a factor that fits my placement of badge.
double y = getResources().getDisplayMetrics().heightPixels - (view.getMeasuredHeight() * 1.3);
if(image2ndItemBadge != null) {
float x = getResources().getDisplayMetrics().widthPixels/2 + imageClassroomBadge.getMeasuredWidth()/2;
image2ndItemBadge.setX(x);
image2ndItemBadge.setY((float)y);
image2ndItemBadge.requestLayout();
}
// Since BottomNavigationView items are equally distributed you can find
// the right position for 3rd item (in my case last item) by dividing
// BottomNavigationView width by 3 and then by 2 to get the middle of that
// item, which was needed in my case.
if(image3rdItemBadge != null) {
float x = getResources().getDisplayMetrics().widthPixels - ((view.getMeasuredWidth()/totalNavItems)/2);
image3rdItemBadge.setX(x);
image3rdItemBadge.setY((float)y);
image3rdItemBadge.requestLayout();
}
// BottomNavigationView count should always be 1
// but I observed the count was 2 in API 19. So I break after first iteration.
break;
}
}
You can remove the for loop and just check if child count greater than 0 and get that child. I call the above method at the end of onCreate like this:
ViewTreeObserver viewTreeObserver = getWindow().getDecorView().getViewTreeObserver();
viewTreeObservier.addOnGlobalLayoutListener (new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
calculateBadgesPosition();
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
Badge ImageView not being part of the BottomNavigationView Items drawable will not be effected by tint that BottomNavigationView applies on selection/un selection of item. Also if you see your badge not appearing try giving it a higher elevation 8 or 16 or whatever. In my case my badge stayed behind BottomNavigationView probably because it has higher elevation or Z index.
I tested this with 3 different screen sizes and the positioning was same on all.
I hope this helps anyone facing similar issue with official BottomNavigationView.
Also if you have a better approach please share it.
One way of achieving this is using two icons for each item - one with the badge and the other without and replace them programmatically. Alternatively, instead of two icons, you can use a single icon and draw the badge programmatically. So the code can look something like this (assuming you know the index of each item and can get the Drawable for each):
public static void updateItem(BottomNavigationView bottomNavigationView, int index, Drawable icon) {
Menu menu = bottomNavigationView.getMenu();
MenuItem item = menu.getItem(index);
if (item != null) {
item.setIcon(icon);
}
}
Create a layout with a Textview.
Inflate the view by adding the BottomNavigationMenuView as child for BottomNavigationView. Add the count to required menu.
See the below link.
https://stackoverflow.com/a/48269868/4675067

Android:Robotium: Tab click with only icon

I have tab layout.That has only icons not text.I tried with clickOnImageButton and ClickOnButton clickOnImage and also pressOnMenuItem(R.drwable.icon)but not worked.How can i do this with solo?
Note: Image View present at the top of the tab.(tab is at bottom)
Tabhosts are evil. Luckily i also have had to automate them and so know the answer.
what you do is you have to get the tab bar view (android.R.id.tabs) and then cast it to a Tabhost or a ViewGroup then you can get each of the tabs via .getChildAt(x) where x is the index of the tabs.
ViewGroup tabs = (ViewGroup) solo.getView(android.R.id.tabs);
View viewYouWantToDoStuffWith = tabs.getChildAt(x); //change x to the index you want.
In your case you would then want something like:
solo.clickOnView(viewYouWantToDoStuffWith);
you can use method solo.clickOnView(solo.getView(resourceId));
where resourceId could be something like R.id.id_Of_Button.
see this link in that they are using image + text. so remove the text made as image that you have to do it.
http://www.androidhive.info/2011/08/android-tab-layout-tutorial/
this is only for text you can set that with image and made as custom tab.
http://www.androidpeople.com/android-tabhost-tutorial-part-1
Hope it will work for you .

Parallax Effect in Android's ViewPager

I'm trying to emulate a parallax effect when sliding through my fragments, I've read about beginFakeDrag and fakeDragBy but to be honest, I don't know even if it's the best approach to my problem.
Anyone has done something similar with the ViewPager or have a hint about how should I approach this?
Thank you
An easy way to do this without using a custom library is to implement ViewPager.PageTransformer
I created my layouts to use the same id for their background element. For my example all background images will use the id background
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/background"
android:src="#drawable/img12"
android:scaleType="fitXY"/>
Then when I go in to implement ViewPager.PageTransformer I can reference that image like so:
public class Transformer implements ViewPager.PageTransformer{
#Override
public void transformPage(View page, float position) {
if(position >= -1 && position <= 1){
page.findViewById(R.id.background).setTranslationX(-position * page.getWidth() / 2);
} else {
page.setAlpha(1);
}
}
}
Finally, I assign my ViewPager.PageTransformer to my ViewPager like so.
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setPageTransformer(false, new Transformer());
This question is a bit old, but I found it when I was trying to do exactly that...
I implemented my own solution, basically extending ViewPager and overriding onDraw.
You can find all the code with a simple example here
I know that it's a bit old but take a look to that https://github.com/xgc1986/ParallaxPagerLibrary
It not overrides the onDraw method, and the effect not only with images, it works with every kind of view
mPager.setPageTransformer(false, new ParallaxTransformer(R.id.parallaxContent));
R.id.paraallaxContent is the id of the View you want to have this effect
unless the other solutions, don't need any any concrete structure to work, and also is layout independant
demo: youtube
Maybe this library can help you:
https://github.com/garrapeta/ParallaxViewPager
You can take a look at this :
https://github.com/luxsypher/ParallaxViewPager
you can apply a parallax effect on any element of your view and gave them all different speed
This is a ViewPager subclass I wrote, pretty easy to use. You don't need to do anything different with it, just include the dependency and use it as a standard ViewPager. Available on GitHub.
This library is fully customizable in x and y directions and includes alpha effects:
https://github.com/prolificinteractive/ParallaxPager
Installation (as of v0.7, check README for updates):
Add as a Maven Central dependency with Gradle
Use a ParallaxContainer in layout XML instead of ViewPager
Create a layout XML file for each page (the x/y/alpha attributes can be set separately for each object moving in/out of the page)
There are a few copy/paste lines to add to onCreate of your Activity
Maybe this library could be useful:
https://github.com/matrixxun/ProductTour
?
it uses "ViewPager.PageTransformer" for making things move differently inside.

Android Gridview and getChildAt with NullPointerException

I have a UI that consists of a Gridview loaded with a custom adapter. A ButtonAdapter, in this case. So the grid loads fine, the button clicks function like I want them to, but now I have to indicate on the button that it the "active" selection.
I thought I'd do this by just keeping track and changing the background. As it turns out, and based on a couple posts here on SO, the buttons don't actually exist when they are off screen...and even immediately after a scroll. I'll often get a NullPointerException when trying to change a button background after scrolling.
I've tried changing the views in the adapter to RadioButtons and ToggleButtons, but they all offer similar limitations.
The problem seems mostly to do with the getChildAt() that I use on the grid to "unselect" one button, or whatever, when another one is selected.
Is there a workaround for this, or perhaps another suggestion of similar functionality. A vertically scrollable, grid-like format, etc...
Thanks for the help.
EDIT:
Thanks Craigy...I did forget to put a platform on there o.0...i'll add android.
Have you considered using a Selector? In ignorance of how your buttonAdapter works or what you're pulling it from, you can set the background drawable of any View to change according to its state using selectors.
If your selector is defined like so (assuming the presence of appropriate drawables, of course):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_focused="true"
android:drawable="#drawable/list_item_pressed" />
<item android:state_pressed="true"
android:drawable="#drawable/list_item_pressed" />
<item android:state_selected="true"
android:state_activated="true"
android:drawable="#drawable/list_item_selected" />
<item android:state_activated="true"
android:drawable="#drawable/list_item_selected" />
<item android:state_selected="true"
android:drawable="#android:color/black" />
<item android:drawable="#android:color/transparent" />
</selector>
And then set your GridView's choice mode to single Choice:
<GridView
...
android:choiceMode="singleChoice" />
This lets the OS take care of everything for you in terms of remembering which position is selected in the list and then clearing it out for you when you've clicked another one
Set a tag on each of the views that the adapter creates in getView(). Later search for the view with that tag by gridView.findViewByTag() or get the view's tag by view.getTag().
Well, this may not perfectly answer your question but I had done something sort of similar. Basically, I added items to a table and needed to have a remove button associated with the item. When the remove button was clicked, it needed to remove only that item from the table. This could be adapted to your needs so rather than removing the item clicked, it finds the previous item, unclicks it, and then highlights then newly clicked one.
So what I did was give a tag to the buttons themselves (obviously they need to be unique). When a button is clicked, save its tag in something like a sharedPreference for later reference. Then when a new button is clicked, simply find the button with the previously clicked tag and unmark the row that it is in and then mark the row for the newly clicked button. Here is the code I used (sorry the variable names are terrible, I actually had this working in a test app that was never released so I didn't bother giving them better names):
//Previous button clicked
String id = <get this from wherever you choose to store it>
// create a new TableRow
TableRow row = new TableRow(getApplicationContext());
TextView t = new TextView(getApplicationContext());
t.setTextColor(Color.BLACK);
t.setText(unique);
Button b = new Button(getApplicationContext());
b.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v)
{
for(int i = 0; i < table.getChildCount(); i++)
{
TableRow row = (TableRow) table.getChildAt(i);
Button bt = (Button) row.getChildAt(1);
TextView view = (TextView)row.getChildAt(0);
if( id.equals(v.getTag())) //they match, so this is the button that was previously clicked
{
//Put your code here to unclick the previous button and mark the new one as clicked.
}
}
}
});
b.setText(R.string.removeButtonText);
b.setTag(t.getText().toString());
/***BE SURE TO SAVE THE NEW BUTTON TAG (t.getText().toString()) SOMEWHERE LIKE A SHARED PREFERENCE****/
//saving the tag
//add the row to the table
row.addView(t);
row.addView(b);
// add the TableRow to the TableLayout
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Again, I don't suspect this is the exact answer you are looking for, but maybe it will give you an idea to try out. Hopefully this makes some sense, if not please feel free to ask for clarification. Sorry if it is way off base as well.

Categories

Resources