I have a navigation drawer with many items so the user needs to scroll up and down in order to see all the items.
I would like to reduce the margins between individual menu items, so that all items fit within a standard screen with no need to scroll.
Is there a way to control the margins in between the menu items?
Answer here helped me to reduce space at least between groups. I used the following dimen
<dimen tools:override="true" name="design_navigation_separator_vertical_padding">1dp</dimen>
We can create a drawable and set it as the NavigationView's itemBackground attribute. I will explain below:
If we walk through the NavigationMenuAdapter, we will see there are four types of items:
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_SUBHEADER = 1;
private static final int VIEW_TYPE_SEPARATOR = 2;
private static final int VIEW_TYPE_HEADER = 3;
What we want to work with is VIEW_TYPE_NORMAL. The attributes exposed to developers can be found in the below code:
case VIEW_TYPE_NORMAL:
{
NavigationMenuItemView itemView = (NavigationMenuItemView) holder.itemView;
itemView.setIconTintList(iconTintList);
if (textAppearanceSet) {
itemView.setTextAppearance(textAppearance);
}
if (textColor != null) {
itemView.setTextColor(textColor);
}
ViewCompat.setBackground(
itemView,
itemBackground != null ? itemBackground.getConstantState().newDrawable() : null);
NavigationMenuTextItem item = (NavigationMenuTextItem) items.get(position);
itemView.setNeedsEmptyIcon(item.needsEmptyIcon);
itemView.setHorizontalPadding(itemHorizontalPadding);
itemView.setIconPadding(itemIconPadding);
if (hasCustomItemIconSize) {
itemView.setIconSize(itemIconSize);
}
itemView.setMaxLines(itemMaxLines);
itemView.initialize(item.getMenuItem(), 0);
break;
}
Unfortunately,there is no interface for us to add margins between the NavigationMenuItemView. However, it allows us to set a background. So we can set a customer drawable to the NavigationView with a specific height. We only set a height in that drawable, like:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
<size android:height="60dp"/>
</shape>
Then apply this drawable to the NavigationView in the layout.xml, like:
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:itemBackground="#drawable/bk_menu_item"/>
I understand it is not a perfect solution, but it seems the only solution working in my case.
Paste this in styles.xml
<style name="NavigationTheme" parent="AppTheme">
<item name="android:textSize">16sp</item>
<item name="android:layout_marginBottom">02dp</item>
</style>
In navigation drawer put this in each item:
android:theme="#style/NavigationTheme"
add below code in dimens.xml
<dimen tools:override="true" name="design_navigation_icon_padding">16dp</dimen>
Related
I created a custom style:
<style name="Static">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginEnd">5dp</item>
</style>
Then I extended anko with a static function:
inline fun ViewManager.static(theme: Int = R.style.Static, init: TextView.() -> Unit) = ankoView(::TextView, theme, init)
When I use this in my layout:
static { text = resources.getString(R.string.name) }
The marginEnd value is ignored.
If I add a margin manually in anko:
static { text = resources.getString(R.string.name) }.lparams { marginEnd = dip(5) }
The margin is fine.
Do you guys know what is happening that anko is ignoring my margin value or any other way to define a predefined margin for a extended view anko function?
This is not Anko problem, this is how Android works:
If you are specifying layout_margin in a custom style, this style must be explicitly applied to each individual view that you wish to have the specified margin (as seen in the code sample below). Including this style in a theme and applying it to your application or an activity will not work.
This is because attributes which begin with layout_ are LayoutParams, or as in this example its MarginLayoutParams. Each ViewGroup have it's own LayoutParams implementation. And so layout_margin is not just general attribute that can be applied anywhere. It must be applied within the context of a ViewGroup that specifically defines it as a valid argument.
Look here for more.
As #John pointed in his answer, using a style is not an option to define layout params.
So, I developed a function to use in the applyRecursively that iterates over the views and apply the layouts that I want to apply.
The solution:
I wanted to define matchParent for width and height and a margin of 16dp for a TableView, so I created a new class that extends TableLayout
class TableViewFrame(context: Context) : TableLayout(context)
and then in the function when the view is a TableViewFrame I apply my layouts
fun applyTemplateViewLayouts(view: View) {
when(view) {
is TableViewFrame -> {
when(view.layoutParams) {
is LinearLayout.LayoutParams -> {
view.layoutParams.height = matchParent
view.layoutParams.width = matchParent
(view.layoutParams as LinearLayout.LayoutParams).margin = view.dip(16)
}
}
}
}
}
To use the function, in the view definition, I just pass it in the applyRecursively:
verticalLayout {
tableViewFrame {
tableRow {
...
}
}
}
}.applyRecursively { view -> applyTemplateViewLayouts(view) }
I wrote an article at medium with a more detailed explanation: https://medium.com/#jonathanrafaelzanella/using-android-styles-with-anko-e3d5341dd5b4
I'm trying to bring material text selection handles to my app. I got drawables from the SDK for middle/right/left handle (bitmaps) and text cursor (9-patch), and set:
<item name="android:textSelectHandleLeft">#drawable/text_select_handle_left_mtrl_alpha</item>
<item name="android:textSelectHandleRight">#drawable/text_select_handle_right_mtrl_alpha</item>
<item name="android:textSelectHandle">#drawable/text_select_handle_middle_mtrl_alpha</item>
<item name="android:textCursorDrawable">#drawable/text_cursor_mtrl_alpha</item>
It works as expected. However, in Lollipop these drawables are tinted with a particular color in XML using the android:tint attribute, which I can't use on API<21. So I'm trying to set a color filter at runtime.
Text cursor does not get tinted. I think this might be due to it being a 9 patch. How can a 9-patch drawable be filtered at runtime? I tried probably all of PorterDuff.Modes.
Right/left handles are black, while middle handle is white.
I.e., non of them is green as I would like. Why?
As you can see above, I set up four ImageView below my edit text, and they instead get tinted.
private void setUpTextCursors() {
Drawable left = getResources().getDrawable(R.drawable.text_select_handle_left_mtrl_alpha);
Drawable right = getResources().getDrawable(R.drawable.text_select_handle_right_mtrl_alpha);
Drawable middle = getResources().getDrawable(R.drawable.text_select_handle_middle_mtrl_alpha);
Drawable cursor = getResources().getDrawable(R.drawable.text_cursor_mtrl_alpha);
ColorFilter cf = new PorterDuffColorFilter(mGreenColor, PorterDuff.Mode.SRC_IN);
/**
* tint my ImageViews, but no effect on edit text handles
*/
left.setColorFilter(cf);
right.setColorFilter(cf);
middle.setColorFilter(cf);
/**
* no effect whatsoever
*/
cursor.setColorFilter(cf);
}
Looks like here we have both a 9-patch tinting issue - since filter fails even on test ImageViews - and an issue related to the fact that none of the applied filters get considered by the text selection manager.
Relevant source code about that is from the TextView class and from this Editor hidden helper class which I found somehow. Spent some time on it but still can't tell why my filters are ignored.
To #pskink: let cursor be the filtered drawable, I can have:
<ImageView
android:id="#id/1"
android:src="#drawable/cursor_drawable" />
<ImageView
android:id="#id/2" />
The first won't be tinted, but if I call imageView2.setBackground(cursor), then it's tinted.
Also if I have
<item name="android:textSelectHandle">#drawable/cursor_drawable</item>
this affects the edit selection (because I override the default cursor) but it's not tinted, again.
you need to override the default Resources used by your Activity:
// your activity source file
Resources res;
#Override
public Resources getResources() {
if (res == null) {
res = new TintResources(super.getResources());
}
return res;
}
the custom Resources class will override getDrawable() method so you can intercept creating your Drawables and set up the color filter, for example:
class TintResources extends Resources {
public TintResources(Resources resources) {
super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
}
#Override
public Drawable getDrawable(int id) throws NotFoundException {
Drawable d = super.getDrawable(id);
if (id == R.drawable.text_cursor_material) {
// setup #drawable/text_cursor_material
d.setColorFilter(0xff00aa00, PorterDuff.Mode.SRC_IN);
}
return d;
}
}
the same way you can setup other Drawables (#drawable/text_select_handle_*_material), note you need that not direct way since EditText doesn't have getter methods for accessing those Drawables
This is just a partial answer, and we can also consider it quite bad, since it's a workaround. I was able to load just the handles (i.e., the BitmapDrawables) inside the edittext (or any other selection stuff) by pointing at XML files rather than at raw png files. I.e. I set:
<item name="android:textSelectHandleLeft">#drawable/text_select_handle_left_material</item>
<item name="android:textSelectHandleRight">#drawable/text_select_handle_right_material</item>
<item name="android:textSelectHandle">#drawable/text_select_handle_middle_material</item>
<item name="android:textCursorDrawable">#drawable/text_cursor_material</item>
where these are xml drawables like:
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/text_select_handle_left_mtrl_alpha" />
or
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/text_cursor_mtrl_alpha" />
If I filter these drawables, I found them tinted both in views and in selections. So I altered my method like such:
private void setUpTextCursors() {
ColorFilter cf = new PorterDuffColorFilter(mColorControlActivated, PorterDuff.Mode.SRC_IN);
BitmapDrawable left = (BitmapDrawable) getResources().getDrawable(R.drawable.text_select_handle_left_material);
BitmapDrawable middle = (BitmapDrawable) getResources().getDrawable(R.drawable.text_select_handle_middle_material);
BitmapDrawable right = (BitmapDrawable) getResources().getDrawable(R.drawable.text_select_handle_right_material);
// NinePatchDrawable cursor = (NinePatchDrawable) getResources().getDrawable(R.drawable.text_cursor_material);
left.setColorFilter(cf);
right.setColorFilter(cf);
middle.setColorFilter(cf);
// cursor.setColorFilter(cf); this does not work: cursor still white!
}
However, while this works for left, right, and middle, something is still wrong with the 9-patch cursor, because I can't get it tinted.
I've been creating apps without much XML, creating views programmatically. I'd like to switch to XML. So I wrote an XML file for a RelativeLayout, and I need to inflate it into an existing class (a subclass of RelativeLayout, of course) that has all the implementation logic.
How do I inflate into "this" in the constructor?
By the way, what's really the advantage of XML? When I create views in the code, I scale fonts and images and also move views around depending on the screen's size, orientation, aspect ratio, etc. With XML approach, I'd have to create a separate XML for all possible configurations...
Constructor code:
public OrderEditControl()
{
super(LmcActivity.W.getApplicationContext());
Resources res = LmcActivity.W.getResources();
setBackgroundColor(Color.TRANSPARENT);
headers = res.getStringArray(R.array.item_list_columns);
widths = new int[headers.length];
createLabels();
createButtons();
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_TOP);
lp.addRule(RIGHT_OF, labels[LabelType.CUSTOMER.ordinal()].getId());
lp.addRule(LEFT_OF, buttons[ButtonType.FIND_CUSTOMER.ordinal()].getId());
customerView = new TextView(LmcActivity.W.getApplicationContext());
customerView.setTextColor(Color.BLACK);
customerView.setId(400);
customerView.setTypeface(Typeface.DEFAULT_BOLD);
customerView.setGravity(Gravity.CENTER_VERTICAL);
addView(customerView, lp);
lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_TOP, labels[LabelType.SHIP_TYPE.ordinal()].getId());
lp.addRule(ALIGN_BOTTOM, labels[LabelType.SHIP_TYPE.ordinal()].getId());
lp.addRule(RIGHT_OF, labels[LabelType.SHIP_TYPE.ordinal()].getId());
shipSpinner = new Spinner(LmcActivity.W);
shipSpinner.setId(401);
shipSpinner.setAdapter(shipAdapter);
shipSpinner.setOnItemSelectedListener(this);
addView(shipSpinner, lp);
deliveryView = new EditText(LmcActivity.W.getApplicationContext());
deliveryView.setGravity(Gravity.CENTER_VERTICAL);
deliveryView.setSingleLine();
deliveryView.setId(402);
addView(deliveryView);
lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RIGHT_OF, labels[LabelType.COMMENTS.ordinal()].getId());
lp.addRule(LEFT_OF, buttons[ButtonType.ITEMS.ordinal()].getId());
lp.addRule(ALIGN_TOP, labels[LabelType.COMMENTS.ordinal()].getId());
commentView = new EditText(LmcActivity.W.getApplicationContext());
commentView.setGravity(Gravity.TOP);
commentView.setId(403);
addView(commentView, lp);
lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
lp.addRule(BELOW, commentView.getId());
itemList = new ListView(LmcActivity.W.getApplicationContext());
itemList.addHeaderView(createRow(null, null), null, false);
itemList.setOnItemClickListener(this);
itemList.setAdapter(itemAdapter);
itemList.setCacheColorHint(0);
itemList.setBackgroundColor(Color.TRANSPARENT);
itemList.setId(404);
addView(itemList, lp);
lays[0] = new LayParm(false);
lays[1] = new LayParm(true);
}
/** create the view's buttons */
private void createButtons()
{
for (int i = 0; i < N_BUT; ++i)
{
Button but = i == ButtonType.ITEMS.ordinal() ?
new TextGlassButton(2.4f, LmcActivity.W.getResources().getString(R.string.items), Color.WHITE) :
new EffGlassButton(1.2f, butEffects[i]);
but.setId(BUT_ID + i);
but.setOnClickListener(this);
buttons[i] = but;
if (i == ButtonType.DATE.ordinal())
addView(but);
else
{
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (i < 2)
lp.addRule(ALIGN_PARENT_TOP);
else
lp.addRule(BELOW, BUT_ID + i - 2);
if (i % 2 == 0)
lp.addRule(ALIGN_PARENT_RIGHT);
else
lp.addRule(LEFT_OF, BUT_ID + i - 1);
addView(but, lp);
}
}
}
/** create text labels */
private void createLabels()
{
Paint paint = AFDraw.W.textPaint;
paint.setTextSize(Universe.TEXT_SIZE);
paint.setTypeface(LmcActivity.W.defaultTypeface);
String[] titles = LmcActivity.W.getResources().getStringArray(R.array.order_labels);
for (int i = 0; i < titles.length; ++i)
{
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_LEFT);
if (i == 0)
lp.addRule(ALIGN_PARENT_TOP);
else
lp.addRule(BELOW, LABEL_ID + i - 1);
TextView tv = new TextView(LmcActivity.W.getApplicationContext());
tv.setText(titles[i]);
tv.setTextColor(Color.BLACK);
tv.setId(LABEL_ID + i);
tv.setTypeface(LmcActivity.W.defaultTypeface);
tv.setGravity(Gravity.CENTER_VERTICAL);
labels[i] = tv;
addView(tv, lp);
labelWidth = Math.max(labelWidth, paint.measureText(titles[i]));
}
labelWidth += Universe.TEXT_SIZE * 0.5f;
dateWidth = paint.measureText("00/00/00") + Universe.TEXT_SIZE * 1.5f;
}
#scriptocalypse is generally right, but subclassing some layouts and inflating custom layout to this class helps to separate different abstractions. There are so many bad tutorials, in which everything is done in the Activity. I see that the world's new comming programmers will code only crap looking applications.
With custom layout you can do in Activity only such a thing:
medicineView.putMedicine(medicineList);
instead of all crappy adapter creations and looking for views...
Firstly you should create some view for your custom View:
<RelativeLayout ...>
<!-- You put all your views here -->
</RelativeLayout>
Secondly if you are sattisfied with your view, you should change the root to merge tag:
<merge ...>
<!-- You put all your views here -->
</merge>
This is very important. We begin design with RelativeLayout tags in order to IDE know how to draw layouts, and how to do completions. But if we leave it as it is, we will end up in two nested RelativeLayouts it will be something like that in the end:
<RelativeLayout ...> <!-- That is your class -->
<RelativeLayout ...> <!-- This is inflated from layout -->
<!-- You put all your views here -->
</RelativeLayout>
</RelativeLayout>
If you change your layout to "merge" then it will look like this:
<RelativeLayout ...> <!-- That is your class -->
<merge...> <!-- This is inflated from layout -->
<!-- You put all your views here -->
</merge>
</RelativeLayout>
and will be merged to its root:
<RelativeLayout ...> <!-- That is your class, merged with layout -->
<!-- You put all your views here -->
</RelativeLayout>
At the end you must subclass demanded View or ViewGroup:
public class CustomView extends RelativeLayout {
public CustomView(Context context) {
super(context);
initialize();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.id.your_layout, this, true);
// find your views, set handlers etc.
}
}
Usage
Just like #scriptocalypse already said. In another layout you use this like that:
<SomeLayout>
<com.foo.CustomView>
</SomeLayout>
First, to answer your main question:
you would not want to inflate an XML RelativeLayout into your RelativeLayout class. You'd extend RelativeLayout and then declare an instance of your RelativeLayout in an XML file, like so:
// com.foo.MyRelativeLayout.java
public class MyRelativeLayout extends RelativeLayout{
/**
* Implement MyRelativeLayout
*/
}
and...
// layout_example.xml
<?xml version="1.0" encoding="utf-8"?>
<com.foo.MyRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Put More Views in here... -->
<TextView
android:id="#+id/customer_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/customer_name_placeholder" />
<!-- and on... -->
</com.foo.MyRelativeLayout>
But more to the point, if you're using the XML to lay out your file, you don't need any of those instantiations or .addRule() method invocations inside your MyRelativeLayout file because you've done it declaratively in XML instead.
To answer your second question of "Why do you want to use XML anyway?"
There are many reasons. Maybe these apply to you, and maybe they don't, but they're ones that I can think of fairly easily that have been relevant in my work.
You don't actually have to create a new layout file for every separate screen size or use case. For the most part, a single layout file will suffice for most screens. You might find that you will have size/resolution/orientation specific dimens.xml or style.xml files, but unless you want a dramatically different arrangement for your different possibilities then the layouts themselves don't repeat themselves too often.
You can use a visual editor. This is important if you're working in teams, and your teammates don't like to or want to use only Java to lay out their screens. While I and others gladly create View and Layout subclasses to fit our needs, I know of literally nobody who prefers to use Java as their primary layout language. Finding people who will work with you (or a job where everyone else uses the XML tools) could be challenging.
If you're creating tools for other people to use (like the above-mentioned folks who prefer XML) you can actually give them custom attributes to work with, that make positioning and layout more powerful. These attributes could be hard-coded in the XML, or they could be references to any of the other Android resources (drawable/string/color/integer/boolean/etc...). As a contrived example, but one based on your code, you could give your users the ability to specify a number of buttons to create rather than rely on the N_BUT variable. You could give it a default value, but offer users a way to change it in XML.
Here is an example:
// somelayout.xml
<?xml version="1.0" encoding="utf-8"?>
<com.foo.MyRelativeLayout
xmlns:param="http://schemas.android.com/apk/res-auto"
style="#style/MyRelativeLayoutStyle"
param:numberOfButtons="3">
</com.foo.MyRelativeLayout>
and in a different file...
//attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyRelativeLayout">
<attr name="numberOfButtons" format="reference|integer" />
</declare-styleable>
</resources>
and in your MyRelativeLayout, you access those attributes from the AttributeSet in its constructor (the one called by Android when it uses XML to create a layout).
Using the style="#style/foo" syntax can allow you to create "classes" of styles that can apply to all kinds of views without actually making a View subclass. Let's say you know that you always want to have a set of parameters that hold true for all your Button elements but don't want to subclass Button.
For example:
// styles.xml
<style name="BaseButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
<item name="android:background">#drawable/bg_common_button</item>
<item name="android:textColor">#color/white</item>
<item name="android:textSize">#dimens/base_button_text_size</item>
<!-- ^^ that dimen value could vary from screen size to screen size, but the style likely won't -->
</style>
// button_layout.xml
<Button
android:id="#+id/styled_button"
style="#style/BaseButton" /> <!-- and you're done -->
// some_other_layout.xml
<LinearLayout
style="#style/BaseLinearLayout">
<Button style="#style/BaseButton" android:text="Button1" />
<Button style="#style/BaseButton" android:text="Button2" />
<Button style="#style/BaseButton" android:text="Button3" />
</LinearLayout>
If you would like to instantiate that button using code, then you can use the LayoutInflater to inflate that specific button's layout and use that wherever you want. In fact, you can create all manner of components in XML and then inflate them at runtime.
LayoutInflater inflater = LayoutInflater.from(YourActivity.this);
Button theInflatedButton = inflater.inflate(R.layout.button_layout.xml, null);
Of course, the canonical example is ListViews and the items that you wish to populate them. You'd create a listview item layout xml and then inflate that whenever your Adapter is in need of a new convertView instance.
I have created the action bar by
ActionBar actionbar = getActionBar()
The background of the action bar is changed by
actionbar.setBackgroundDrawable(actionBarBackgroundImage);
Now I need to change the action bar tabs underline color programmatically. Is there any method to change the action bar tabs underline color?
Alternatively you could use Android Action Bar Style Generator to easily theme your action bar and tabs.
Here is a much easier way. I know you were looking for a programmatic change, but this one is REALLY easy.
I've been struggling with this for days, but finally found the solution. I'm using AppCompat. You can set colorAccent in your theme and that will change the highlight color on your ActionBar. Like so:
<item name="colorAccent">#color/highlightcolor</item>
Here it is in context:
<style name="LightTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/darkgrey</item>
<item name="colorPrimaryDark">#color/black</item>
<item name="colorAccent">#color/highlightcolor</item>
</style>
Where I originally posted this answer: Android Tab underline color not changing
I'll suggest you use ActionBarSherlock. There is one sample available in the library named "Style ActionBar". (this is only way you can change ActionBar tabs underline color)
if you have customized ActionBar then You have to add this style in ActionBar Style
or here is way how to Do this
create style like below (here i have used ActionBarShareLock if you don't want to use then use android-support-v4.jar for support all Android OS Version)
<style name="Theme.AndroidDevelopers" parent="Theme.Sherlock.Light">
<item name="android:actionBarTabStyle">#style/MyActionBarTabStyle</item>
<item name="actionBarTabStyle">#style/MyActionBarTabStyle</item>
</style>
<!-- style for the tabs -->
<style name="MyActionBarTabStyle" parent="Widget.Sherlock.Light.ActionBar.TabBar">
<item name="android:background">#drawable/actionbar_tab_bg</item>
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
actionbar_tab_bg.xml
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="#drawable/ad_tab_unselected_holo" />
<item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="#drawable/ad_tab_selected_holo" />
<item android:state_selected="false" android:state_pressed="true" android:drawable="#drawable/ad_tab_selected_pressed_holo" />
<item android:state_selected="true" android:state_pressed="true" android:drawable="#drawable/ad_tab_selected_pressed_holo" />
applay this style in your activity in android manifest file
<activity
android:name="com.example.tabstyle.MainActivity"
android:label="#string/app_name"
android:theme="#style/Theme.AndroidDevelopers" >
for more detail check this answer and this article.
EDITED : 29-09-2015
ActionBarSherlock is deprecated so alternatively you can use android design support library and android app appcompat library for TOOLBAR(Action-Bar is deprecated so..) and TABS.
use TabLayout like below
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="center"
app:tabMode="scrollable"
app:tabSelectedTextColor="#color/white"
app:tabIndicatorColor="#color/colorPrimary"
app:tabIndicatorHeight="2dip"
app:tabTextAppearance="?android:attr/textAppearanceMedium"
app:tabTextColor="#color/colorAccent" />
here is sample of android design support library with tab
Refer this, for customize action bar,
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- the theme applied to the application or activity -->
<style name="CustomActivityTheme" parent="#android:style/Theme.Holo">
<item name="android:actionBarStyle">#style/MyActionBar</item>
<!-- other activity and action bar styles here -->
</style>
<!-- style for the action bar backgrounds -->
<style name="MyActionBar" parent="#android:style/Widget.Holo.ActionBar">
<item name="android:background">#drawable/ab_background</item>
<item name="android:backgroundStacked">#drawable/ab_background</item>
<item name="android:backgroundSplit">#drawable/ab_split_background</item>
</style>
</resources>
I tried many of the suggestions posted here and other places with no luck. But I think I managed to piece together a (albeit not perfect) solution.
The TabWidget is using a selector. Essentially it is showing a different 9 patch image depending on the state of the tab (selected, pressed, etc.). I finally figured out that you could generate a selector programmatically. I started with generated 9 patches from http://android-holo-colors.com/ (color: #727272, TabWidget: Yes).
The biggest issue was setting the color. Setting the color filter did nothing. So, I ended up changing the colors of each of the pixels of the 9 patch image inside a loop.
...
/**
* <code>NinePatchDrawableUtility</code> utility class for manipulating nine patch resources.
*
* #author amossman
*
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class NinePatchDrawableUtility {
// Matches the colors in the supported drawables
private static final int TAB_UNDERLINE_HIGHLIGHT_COLOR = 1417247097;
private static final int TAB_UNDERLINE_COLOR = -8882056;
private static final int TAB_PRESSED_COLOR = -2122745479;
private Resources resources;
public NinePatchDrawableUtility(Resources resources) {
this.resources = resources;
}
/**
* Create a <code>StateListDrawable</code> that can be used as a background for the {#link android.widget.TabWidget}</br></br>
*
* <code>
* FragmentTabHost tabHost = ...</br>
* NinePatchUtility ninePatchUtility = new NinePatchUtility(getResources());</br>
* TabWidget tabWidget = tabHost.getTabWidget();</br>
* for (int i = 0; i < tabWidget.getChildCount(); i++) {</br>
* tabWidget.getChildAt(i).setBackground(ninePatchUtility.getTabStateListDrawable(titleColor));</br>
* }
* </code>
*
* #param tintColor The color to tint the <code>StateListDrawable</code>
* #return A new <code>StateListDrawable</code> that has been tinted to the given color
*/
public StateListDrawable getTabStateListDrawable(int tintColor) {
StateListDrawable states = new StateListDrawable();
states.addState(new int[] {android.R.attr.state_pressed},
changeTabNinePatchColor(resources, R.drawable.cc_tab_selected_pressed_holo, tintColor));
states.addState(new int[] {android.R.attr.state_focused},
changeTabNinePatchColor(resources, R.drawable.cc_tab_selected_focused_holo, tintColor));
states.addState(new int[] {android.R.attr.state_selected},
changeTabNinePatchColor(resources, R.drawable.cc_tab_selected_holo, tintColor));
states.addState(new int[] { },
changeTabNinePatchColor(resources, R.drawable.cc_tab_unselected_holo, tintColor));
return states;
}
/**
* Change the color of the tab indicator.</br></br>
*
* Supports only the following drawables:</br></br>
*
* R.drawable.cc_tab_selected_pressed_holo</br>
* R.drawable.cc_tab_selected_focused_holo</br>
* R.drawable.cc_tab_selected_holo</br>
* R.drawable.cc_tab_unselected_holo</br></br>
*
* Note: This method is not efficient for large <code>Drawable</code> sizes.
*
* #param resources Contains display metrics and image data
* #param drawable The nine patch <code>Drawable</code> for the tab
* #param tintColor The color to tint the <code>Drawable</code>
* #return A new <code>NinePatchDrawable</code> tinted to the given color
*/
public NinePatchDrawable changeTabNinePatchColor(Resources resources, int drawable, int tintColor) {
int a = Color.alpha(tintColor);
int r = Color.red(tintColor);
int g = Color.green(tintColor);
int b = Color.blue(tintColor);
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;
Bitmap bitmap = BitmapFactory.decodeResource(resources, drawable, opt);
for (int x = 0; x < bitmap.getWidth(); x++) {
for (int y = 0; y < bitmap.getHeight(); y++) {
int color = bitmap.getPixel(x, y);
if (color == TAB_PRESSED_COLOR) {
bitmap.setPixel(x, y, Color.argb((int)(a * 0.5), r, g, b));
} else if (color == TAB_UNDERLINE_HIGHLIGHT_COLOR) {
bitmap.setPixel(x, y, Color.argb((int)(a * 0.9), r, g, b));
} else if (color == TAB_UNDERLINE_COLOR) {
bitmap.setPixel(x, y, tintColor);
}
}
}
return new NinePatchDrawable(resources, bitmap, bitmap.getNinePatchChunk(), new Rect(), null);
}
}
Example of usage:
/**
* Theme the tab widget with the defined background color and title color set
* in the TabManager
* #param tabWidget
*/
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
public void theme(TabWidget tabWidget) {
ColorDrawable backgroundDrawable = new ColorDrawable(backgroundColor);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
tabWidget.setBackground(backgroundDrawable);
tabWidget.setAlpha(0.95f);
} else {
backgroundDrawable.setAlpha(242);
tabWidget.setBackgroundDrawable(backgroundDrawable);
}
NinePatchDrawableUtility ninePatchUtility = new NinePatchDrawableUtility(resources);
for (int i = 0; i < tabWidget.getChildCount(); i++) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
tabWidget.getChildAt(i).setBackground(ninePatchUtility.getTabStateListDrawable(titleColor));
} else {
tabWidget.getChildAt(i).setBackgroundDrawable(ninePatchUtility.getTabStateListDrawable(titleColor));
}
View tabView = tabWidget.getChildTabViewAt(i);
tabView.setPadding(0, 0, 0, 0);
TextView tv = (TextView) tabView.findViewById(android.R.id.title);
tv.setSingleLine(); // set the texts on the tabs to be single line
tv.setTextColor(titleColor);
}
}
Got the solution for changing the Tab Highlighter Color after 1 long day of search.Just 2 lines of code makes this work perfect!
Go to values/styles.xml and add the code below in ActionBar Theme
<item name="colorAccent">#color/Tab_Highlighter</item>
Now give the color for Tab_Highlighter in colors.xml
<color name="Tab_Highlighter">#ffffff</color>
you can use this code:
actionBar.setStackedBackgroundDrawable(new ColorDrawable(yourColor));
I'm new in Android development and I'm writing a small app to understand how it works. I've got all working, but at the moment I can't get a point about custom drawable states... let me explain with some sample code.
Here is my attrs.xml, in which I declare a attribute with name "oddMonth", which is boolean:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DayView">
<attr name="oddMonth" format="boolean"/>
</declare-styleable>
</resources>
Then I have a custom View:
<?xml version="1.0" encoding="utf-8"?>
<com.example.calendar.DayView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="#drawable/dayview_state" >
<TextView android:id="#+id/day_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:paddingRight="3dp" />
</com.example.calendar.DayView>
So I put the line "android:background="#drawable/dayview_state"", which refers to file dayview_state.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:easycalendar="http://schemas.android.com/apk/res/com.example.calendar">
<item easycalendar:oddMonth ="true" android:drawable="#drawable/customborder_odd" />
<item easycalendar:oddMonth ="false" android:drawable="#drawable/customborder_even"/>
</selector>
So far... for what I can understand.... I have a attribute defined in attrs.xml. This attribute represents the state for my custom view. According to the boolean value of this attribute my app will load one of two different xml (that are not important here), each of one defines a different drawable. So the final step is to build my custom class! Follows a extract from the class:
public class DayView extends RelativeLayout {
private static final int[] STATE_ODD_MONTH = { R.attr.oddMonth };
private boolean mOddmonth = true;
public DayView(Context mContext, AttributeSet attrs) {
super(mContext, attrs);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mOddmonth) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, STATE_ODD_MONTH);
return drawableState;
} else {
return super.onCreateDrawableState(extraSpace);
}
}
public boolean isOddMonth() {
return mOddmonth;
}
public void setOddMonth(boolean oddMonth) {
if (mOddmonth != oddMonth) {
mOddmonth = oddMonth;
refreshDrawableState();
}
}
}
Ok... so I have here a private variable mOddMonth, whith getter and setter. The constructor which is used to inflate this view elsewhere. Another private variable:
private static final int[] STATE_ODD_MONTH = { R.attr.oddMonth };
which is a array made up of only one int value, that is a reference to the attribute oddMonth defined in attrs.xml. And the inherited method:
#Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mOddmonth) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, STATE_ODD_MONTH);
return drawableState;
} else {
return super.onCreateDrawableState(extraSpace);
}
}
which I can't really "deeply" understand... well, it seems to me that I add a state if the local variable mOddMonth is true, otherwise not. So... my code works only if I replace my dayview_state.xml with the following:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:easycalendar="http://schemas.android.com/apk/res/com.example.calendar">
<item easycalendar:oddMonth ="true" android:drawable="#drawable/customborder_odd" />
<item android:drawable="#drawable/customborder_even"/>
</selector>
In this way the first layout is loaded if THERE IS the state, otherwise will be loaded the second one. But WHAT ABOUT THE VALUE of the state? Nowhere in my code I set the value for this variable/attribute.... where I'm wrong?
I would recommend you reword your question b/c it wasn't clear what you were asking until I read your comment to #kcoppock's answer, which is -
"what i want to do (or I think I should do) is to set this value
somewhere in code according to the actual status of my custom view,
and then force it to render again.... Or I shouldn't?"
At any point, you can query the view to get it drawable state using View.getDrawableState.
If based on this, you want to re-render your drawable, then you have several options.
First of all you can call Drawable.invalidateSelf. But you rarely need to do that because usually your drawable is set as a view's background drawable which is automatically drawn for you in the draw method (not onDraw, which is what you draw). So all you need to do in that case is to invalidate the view (view.invalidate), it will automatically redraw your background drawable (hence picking up your drawable state change).
If you are using your drawable not as a background but for your main drawing then you draw your drawables in onDraw. A simple myDrawable.draw(canvas) should be enough. But remember to vall view.invalidate to trigger the onDraw method.
You're correct; you'll need to assign that value in your constructor with the AttributeSet variable:
TypedArray values = context.obtainStyledAttributes(attrs, STATE_ODD_MONTH);
boolean isOddMonth = values.getBoolean(R.attr.oddMonth, false);
mOddmonth = isOddMonth;
values.recycle();
I believe this should do the trick. I usually use a declare-styleable tag in attrs.xml instead of hardcoding an int[], but I believe it should work identically.