I was trying to make a tricky layout for which i need the Android's default tab indicator color.
I have searched a lot but every where I find how to change and customize tab indicator but could not find how to get color code in hex of default tab indicator.
I did some research for your question, I hope this will help you.
The tab indicator color is set in the Inner Class SlidingTabStrip of the class TabLayout (Code). Sadly you can't access this variable.
private class SlidingTabStrip extends LinearLayout {
private final Paint mSelectedIndicatorPaint;
// ...
void setSelectedIndicatorColor(int color) {
if (mSelectedIndicatorPaint.getColor() != color) {
mSelectedIndicatorPaint.setColor(color);
ViewCompat.postInvalidateOnAnimation(this);
}
}
}
But in a constructor of the TabLayout the default tab indicator color is set.
public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Add the TabStrip
mTabStrip = new SlidingTabStrip(context);
addView(mTabStrip, LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout, defStyleAttr, R.style.Widget_Design_TabLayout);
// <-- HERE
mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));
}
I think you need to access R.styleable.TabLayout_tabIndicatorColor to get what you want. I don't have the possibility right now to test if and how it works but I hope this helps you a bit.
Update
I tried this at home and it seems to work. I used this code in the onCreate() method of my Activity
TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.TabLayout, 0, R.style.Widget_Design_TabLayout);
// returns -16738680 in my case which is the accentColor
int color = a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0);
But I saw, that R.styleable.TabLayout_tabIndicatorColor just links to the accentColor. Maybe this is the better way to get what you want.
<style name="Base.Widget.Design.TabLayout" parent="android:Widget">
<item name="tabIndicatorColor">?attr/colorAccent</item>
<!-- other items -->
</style>
Related
Since Android 23, the android:foreground XML attribute (and corresponding setForeground() method) should be available to all Views, and not just FrameLayout instances as was previously the case.
Yet, for some reason, whenever I create a Button instance which inherits from TextView -> View, I can't seem to get a foreground to show at all.
Here is an example button definition that doesn't work appear to show the foreground:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:text="Primary Full Width"
style="#style/Button.Primary" />
And here is the defintion of the Button.Primary style:
<style name="Button.Primary" parent="Widget.AppCompat.Button">
<item name="android:background">#drawable/button_primary_background_selector</item>
<item name="android:foreground">#drawable/button_primary_foreground_selector</item>
<item name="android:textAppearance">#style/TextAppearance.FF.Button</item>
<item name="android:minHeight">80dp</item>
<item name="android:height">80dp</item>
<item name="android:minWidth">200dp</item>
<item name="android:defaultWidth">288dp</item>
<item name="android:maxWidth">400dp</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
<item name="android:gravity">center_vertical|center_horizontal</item>
<item name="android:paddingStart">70dp</item>
<item name="android:paddingEnd">70dp</item>
<item name="android:stateListAnimator">#null</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
<item name="android:drawablePadding">10dp</item>
</style>
I've confirmed that setting the foreground attribute in the style or in the <Button> definition does not change anything, the foreground fails to show regardless. The background shows correctly, as does the text, it's just the foreground that's missing.
I've also tried setting the foreground to a solid color instead of a state selector drawable, with no success.
After removing the style elements one by one and retesting, I narrowed down the issue to the inclusion of the android:singleLine attribute in the style.
Removing this attribute made it so any TextViews or Buttons using my style would properly show the foreground drawable as desired. Looking through the implementation of TextView.java where singleLine is defined, I'm still struggling to determine how setting this attribute causes foregrounds to be ignored, but I will update this answer if I find out.
I know that use of singleLine is deprecated but unfortunately I still need the functionality provided by the combination of the singleLine / ellipsize attributes which are not available when replacing with maxLines usage.
Edit: After writing out my answer above, I decided to do some more investigation and uncovered a few more details.
First, I created a custom view that extended from AppCompatButton so I could attempt to re-implement the functionality singleLine / ellipsize provided (namely, showing text on a single line by replacing newline characters with spaces and then also adding ellipsis if the text ran off the view).
Reading through the TextView source code, I found a section of code that is called when the singleLine attribute is set to true:
private void applySingleLine(boolean singleLine, boolean applyTransformation,
boolean changeMaxLines) {
mSingleLine = singleLine;
if (singleLine) {
setLines(1);
setHorizontallyScrolling(true);
if (applyTransformation) {
setTransformationMethod(SingleLineTransformationMethod.getInstance());
}
}
...
}
So I tried extracting those lines and adding to my own implementation:
private void updateMaxlinesLocally(int maxLines) {
if (maxLines == 1) {
Log.d("Button", "max lines was 1, setting to single line equivalent");
setLines(1);
setHorizontallyScrolling(true);
setTransformationMethod(SingleLineTransformationMethod.getInstance());
// reset any text that may have already been set
setText(getText());
} else {
Log.d("Button", "max lines was : " + maxLines);
}
}
But when this code ran, I saw the same issue from before where foreground drawables would no longer be visible.
After further testing, I was able to narrow down the issue to the setHorizontallyScrolling(true) method call. If I commented just this line out of my custom implementation, I'm able to preserve the singleLine / ellipsize functionality I had before and foreground drawables show as expected! Narrowing it down to that method still didn't help me figure out the root cause in the TextView base implementation, but if anyone has any details on that, feel free to comment with more information.
Here is my final custom Button class which simply looks at the maxLines attribute and simulates the old singleLine functionality when maxLines is set to 1, avoiding the method call that would prevent foregrounds from showing. In case it's useful...
public class Button extends AppCompatButton {
public Button(Context context) {
super(context);
init(context, null, 0);
}
public Button(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public Button(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
#Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
updateMaxlinesLocally(maxLines);
}
private void init(Context context, AttributeSet attributeSet, int defStyleAttr) {
int[] set = {
android.R.attr.maxLines
};
TypedArray a = context.obtainStyledAttributes(
attributeSet, R.styleable.Button);
int maxLines = a.getInt(R.styleable.Button_android_maxLines, -1);
a.recycle();
updateMaxlinesLocally(maxLines);
}
private void updateMaxlinesLocally(int maxLines) {
if (maxLines == 1) {
Log.d("Button", "max lines was 1, setting to single line equivalent");
setLines(1);
// The act of setting horizontally scrolling to true is what disables foregrounds from
// showing...
// setHorizontallyScrolling(true);
setTransformationMethod(SingleLineTransformationMethod.getInstance());
// reset any text that may have already been set
setText(getText());
} else {
Log.d("Button", "max lines was : " + maxLines);
}
}
}
and from attrs.xml:
<declare-styleable name="Button">
<attr name="android:maxLines" format="integer" />
</declare-styleable>
As stated by Keith setHorizontallyScrolling(true), scrolling makes the foreground disappear. One can easily reproduce the problem.
But why does horizontal scrolling affect the foreground?
Setting horizontal (or vertical scroll) tells the TextView it contains a larger area than it's layout bounds.
If you print the scrollX / scrollY values, normally it won't be zeroes but a very big number instead, even if the content is not scrollable by users.
On the other hand, the foreground is designed to scroll with the content, so it's basically drawn starting from (0, 0) of the canvas, not the top left corner of the visible content. So it's drawn far far away from the actual view bounds. You can open the View.java for the source code of onDrawForeground for that.
What can I do
Disable scroll by calling setHorizontallyScrolling(false) after setting the singleline attribute or anything that implies scrolling. Beware not to get scroll enabled trying to initialize / update the view.
translate the foreground drawable, e.g. update the bounds from setBounds by adding scrollX & scrollY
I wanted to make a view containing a progress view and a button. Through the view's xml I wanted to add fields that define button and porgress bar style.
What I've done so far but it does not work:
<io.**.**.view.ButtonLoading android:id="#+id/b_recover_password"
android:layout_width="wrap_content"
android:gravity="center"
app:styleButton="#style/ButtonGrey"
app:styleProgress="#style/ProgressBar"
app:textButton="#string/recover_password"
android:layout_height="wrap_content"/>
Code:
a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ButtonLoading,
0, 0);
int buttonId = 0;// R.style.Widget_AppCompat_Button;
try {
buttonId = a.getResourceId(R.styleable.ButtonLoading_styleButton, 0);
} catch (Exception e) {
}
button = new Button(getContext(), attrs, buttonId);
LayoutParams lpButton = createLayoutParams();
button.setLayoutParams(lpButton);
color = button.getCurrentTextColor();
int progressId = 0;// R.style.Widget_AppCompat_ProgressBar;
try {
progressId = a.getResourceId(R.styleable.ButtonLoading_styleProgress, 0);
} catch (Exception e) {
}
progressBar = new ProgressBar(getContext(), attrs, progressId);
LayoutParams lpProgressBar = createLayoutParams();
lpProgressBar.addRule(RelativeLayout.CENTER_IN_PARENT);
progressBar.setLayoutParams(lpProgressBar);
LayoutParams lpRelativeLayout = createLayoutParams();
setLayoutParams(lpRelativeLayout);
addView(button);
addView(progressBar);
try {
String value = a.getString(R.styleable.ButtonLoading_textButton);
button.setText(value);
} catch (Exception e) {
}
a.recycle();
Styleable:
<declare-styleable name="ButtonLoading">
<attr name="styleButton" format="reference" />
<attr name="styleProgress" format="reference" />
<attr name="textButton" format="string" />
</declare-styleable>
Someone help me? thanks
The problem is in your constructors for Button and ProgressBar. Let's consider two of the constructors. (Emphasis is mine.) (Documentation)
View(Context context, AttributeSet attrs, int defStyleAttr) [Appropriate for below API 21]
Perform inflation from XML and apply a class-specific base style from a theme attribute. This constructor of View allows subclasses to use their own base style when they are inflating. For example, a Button class's constructor would call this version of the super class constructor and supply R.attr.buttonStyle for defStyleAttr; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.
View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) [Appropriate for API 21+]
Perform inflation from XML and apply a class-specific base style from a theme attribute or style resource. This constructor of View allows subclasses to use their own base style when they are inflating.
Focusing on the last two arguments.
defStyleAttr int: An attribute in the current theme that contains a reference to a style resource that supplies default values for the view. Can be 0 to not look for defaults.
defStyleRes int: A resource identifier of a style resource that supplies default values for the view, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.
It is confusing because there are resource ids pointing to resource ids, but bear with me.
What you have defined for buttonId and progressId is a resource id that points to a style (<style...). This style resource id is appropriate to use for the defStyleRes attribute of the Button and ProgressBar constructors as noted .above. You are trying to use each of these resource values as an id that points to an attribute in the current theme. In other words, you are saying that R.attr.styleButton (styleButton) is an attribute in the current theme which it is not as currently defined. To fix this, change the theme style to the following:
styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="styleButton">#style/ButtonGrey</item>
...
</style>
To make what you have work with API 21+ you can leave the leave the style and layout files as they are and change the custom view code to something like the following. (I am only showing code for the button, but the progress bar would be similar.) You may want to create a separate style file for API 21+ (styles-v21.xml).
public CustomViewGroup(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ButtonLoading,
0, 0);
int defStyleRes = 0;
try {
defStyleRes = a.getResourceId(R.styleable.ButtonLoading_styleButton, 0);
} catch (Exception e) {
// do something
}
Button button;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// defStyleRes is only used if defStyleAttr == 0
// or can't be found in the current theme.
button = new Button(getContext(), attrs, defStyleAttr, defStyleRes);
} else {
button = new Button(getContext(), attrs,
(defStyleAttr != 0) ? defStyleAttr : R.attr.styleButton);
}
try {
String value = a.getString(R.styleable.ButtonLoading_textButton);
button.setText(value);
} catch (Exception e) {
// do something
}
addView(button);
a.recycle();
}
Setting the text of the button proceeds as you have coded. Styles are trickier. To makeapp:styleButton="#style/ButtonGrey" work as you intended for all APIs, I think that you would have to do something like this.
I hope this helps.
The my styles is:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="android:buttonStyle">#style/ButtonAppTheme</item>
<item name="android:progressBarStyle">#style/ProgressBarBase</item>
</style>
<style name="ProgressBarBase" parent="android:Widget.Holo.Light.ProgressBar">
<item name="android:textColorTertiary">#color/color_grey</item>
</style>
<style name="ProgressBar" parent="android:Widget.Holo.Light.ProgressBar">
<item name="android:textColorTertiary">#color/color_black</item>
</style>
The problem is that you always have the "ProgressBarBase" style. That is, it is not overriding.
I have a lot of views using one and the same color as a background. I want to change the color of all views when I receive a call from the server programmatically. I don't want to call for every view
view.setBackgroundColor(new color);
Is there a way to change a color code that is in colors.xml.
Short answer: No, you can't. The resources are defined at compile time.
See this question for a similar case: How can I programmatically change the value of a color in colors.xml?
You can't replace the value of the color in the xml file. But you
can create different themes which are used in your application and
change the theme dynamically
See this tutorial:
http://www.developer.com/ws/android/changing-your-android-apps-theme-dynamically.html
What I end up doing is create a custom class that sets the color form preference. And use this class everywhere I want to change the color. And next time the view is drawn it gets the new color. Something like this:
public class ColoredToolbar extends android.support.v7.widget.Toolbar {
public ColoredToolbar(Context context) {
super(context);
setBackgroundColor(context);
}
public ColoredToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(context);
}
public ColoredToolbar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setBackgroundColor(context);
}
private void setBackgroundColor(Context context) {
int color = PreferenceHelper.getToolBarColor(context, Preferences.PREF_TITLE_BAR_COLOR_KEY);
this.setBackgroundColor(color);
}
}
I am using a custom seekbar to show a graph. I had done till this. I am showing this graph by applying a background drawable to the seekbar. Now my problem is, I need to set the blue one as progress drawable and need to set the background of seekbar as the red graph. So that when progress happens thumb moves over red the area where thumb passed should be changed to blue color like a masking effect. Can any one tell the best possible way to do this. My pictures are shown below
After reading all the questions and answers I hope this should be your scenario to get your thing done...
1.Create two graphs
As per your logic.
2.Generate two drwables from the particular bitmaps....
Drawable G_bg = new BitmapDrawable(Red graph bitmap);
Drawable G_pg = new BitmapDrawable(Blue graph bitmap);
3.And then customize your seek bar using layer list created through the java code.
ClipDrawable c=new ClipDrawable(G_pg, Gravity.LEFT,ClipDrawable.HORIZONTAL);
LayerDrawable ld =new LayerDrawable (new Drawable[]{G_bg,c});
4.Apply this layer list to your seekbar.
Graphbar.setProgressDrawable(ld);
This should work like you wanted....Thanksss
Is this not what you wanted?
my_seek_bar_progress.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#android:id/background"
android:drawable="#drawable/red_graph"/>
<item android:id="#android:id/progress">
<clip android:drawable="#drawable/blue_graph" />
</item>
</layer-list>
in Fragment or Activity layout:
<com.example.seekbaroverlay.MySeekBar
android:id="#+id/mySeekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100" />
MySeekBar.java:
public class MySeekBar extends SeekBar {
public MySeekBar(Context context) {
this(context, null);
}
public MySeekBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MySeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setProgressDrawable(context.getResources().getDrawable(R.drawable.my_seek_bar_progress));
setThumb(context.getResources().getDrawable(R.drawable.my_thumb));
}
}
You should use a custom progressDrawable for your SeekBar. See this blog post for a great tutorial.
You can create a custom view.Override it's onTouch() method to change position of thumb.Also override it's onDraw() method and first draw red graph as background of your view and then the blue one from position that corresponds to the position of thumb.
So I have looked around and found out that android.R.styleable is no longer part of the SDK even though it is still documented here.
That wouldn't really be an issue if it was clearly documented what the alternative is. For example the AOSP Calendar App is still using the android.R.styleable
// Get the dim amount from the theme
TypedArray a = obtainStyledAttributes(com.android.internal.R.styleable.Theme);
lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
a.recycle();
So how would one get the backgroundDimAmount without getting the int[] from android.R.styleable.Theme?
What do I have to stick into obtainStyledAttributes(int []) in order to make it work with the SDK?
The CustomView API demo shows how to retrieve styled attributes. The code for the view is here:
https://github.com/android/platform_development/blob/master/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
The styleable array used to retrieve the text, color, and size is defined in the <declare-styleable> section here:
https://github.com/android/platform_development/blob/master/samples/ApiDemos/res/values/attrs.xml#L24
You can use <declare-styleable> to define any list of attributes that you want to retrieve as a group, containing both your own and ones defined by the platform.
As far as these things being in the documentation, there is a lot of java doc around the styleable arrays that makes them useful to have in the documentation, so they have been left there. However as the arrays change, such as new attributes being added, the values of the constants can change, so the platform ones can not be in the SDK (and please do not use any tricks to try to access them). There should be no need to use the platform ones anyway, because they are each there just for the implementation of parts of the framework, and it is trivial to create your own as shown here.
In the example, they left out the reference to the Context 'c':
public ImageAdapter(Context c) {
TypedArray a = c.obtainStyledAttributes(R.styleable.GalleryPrototype);
mGalleryItemBackground = a.getResourceId(
R.styleable.GalleryPrototype_android_galleryItemBackground, 0);
a.recycle();
return mGalleryItemBackground;
}
Changing obtainStyledAttributes to c.obtainStyledAttributes should work
Example of pulling out standard attribute (background) in a custom view which has its own default style. In this example the custom view PasswordGrid extends GridLayout. I specified a style for PasswordGrid which sets a background image using the standard android attribute android:background.
public class PasswordGrid extends GridLayout {
public PasswordGrid(Context context) {
super(context);
init(context, null, 0);
}
public PasswordGrid(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.passwordGridStyle);
init(context, attrs, 0);
}
public PasswordGrid(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
if (!isInEditMode()) {
TypedArray stdAttrs = context.obtainStyledAttributes(attrs,
new int[] { android.R.attr.background }, // attribute[s] to access
defStyle,
R.style.PasswordGridStyle); // Style to access
// or use any style available in the android.R.style file, such as
// android.R.style.Theme_Holo_Light
if (stdAttrs != null) {
Drawable bgDrawable = stdAttrs.getDrawable(0);
if (bgDrawable != null)
this.setBackground(bgDrawable);
stdAttrs.recycle();
}
}
}
Here is part of my styles.xml file:
<declare-styleable name="passwordGrid">
<attr name="drawOn" format="color|reference" />
<attr name="drawOff" format="color|reference" />
<attr name="pathWidth" format="integer" />
<attr name="pathAlpha" format="integer" />
<attr name="pathColor" format="color" />
</declare-styleable>
<style name="PasswordGridStyle" parent="#android:style/Widget.GridView" >
<!-- Style custom attributes. -->
<item name="drawOff">#drawable/ic_more</item>
<item name="drawOn">#drawable/ic_menu_cut</item>
<item name="pathWidth">31</item>
<item name="pathAlpha">129</item>
<item name="pathColor">#color/green</item>
<!-- Style standard attributes -->
<item name="android:background">#drawable/pattern_bg</item>
</style>
This appears to be a bug in the SDK. I have filed an issue on it, which you may wish to star so as to receive updates on it.
As a worksaround, you can use reflection to access the field:
Class clazz=Class.forName("android.R$styleable");
int i=clazz.getField("Theme_backgroundDimAmount").getInt(clazz);