attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="InteractiveImageView">
<attr name="play_anim" format="reference|integer" />
</declare-styleable>
</resources>
usage_example(activity_main).xml
<com.doitandroid.mylottie.InteractiveImageView
app:play_anim="#raw/icon_home">
</com.doitandroid.mylottie.InteractiveImageView>
When I want to add this view programmatically, How to do this?
MainActivity.java:
LinearLayout linearLayout = findViewById(R.id.main_ll);
InteractiveImageView interactiveImageView = new InteractiveImageView(this);
linearLayout.addView(interactiveImageView);
I don't know how to add app:play_anim="#raw/icon_home" this part.
You should have a constructor in the form of:
public InteractiveImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
loadAttributes(attrs); //Here use the attributes.
}
Use AttributeSet to pass your values.
Example:
private void loadAttributes(AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.AudioPlayerView);
mBackgroundDrawable = typedArray.getDrawable(R.styleable.AudioPlayerView_player_background);
}
}
You should have a function on your custom view like
public void setPlayAnim(#RawRes int playAnim) {
// do something
}
then you can call from code like
interactiveImageView.setPlayAnim(R.raw.somthing)
Related
I have created a class CustomPreference, which inherits from the androidx Preference class, for the purpose of setting a custom layout made of my own components. And I defined a styleable attr on that custom Preference class. But now I'm trying to understand how to pass along the value from that custom styleable attr on to the attribute on my underlying SpecialCustomComponent.
CustomPreference.java
public class CustomPreference extends Preference {
private Context context;
private AttributeSet attrs;
public CustomPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context = context;
this.attrs = attrs;
setLayoutResource(R.layout.preference_widget_custom);
}
// other required constructor overloads omitted
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
// calling this method here results in an error because
// a.getResourceId(R.styleable.PreferenceWidgetCustom_preferenceTitle, 0)
// returns no resource Id at this point
setPreferenceTitle(holder);
}
#Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
super.onAttachedToHierarchy(preferenceManager);
this.preferenceManager = preferenceManager;
// alternatively, calling this method here would result in a
// different error, since I don't have access to the viewHolder
// here. To make this work, I tried preserving "viewHolder" in a
// field inside onBindViewHolder (which I don't think I'm
// supposed to do anyways), but the viewHolder reference was
// null, so it didn't work.
setPreferenceTitle();
}
// This is my incorrect attempt at setting "myText" to the value of "preferenceTitle"
private void setPreferenceTitle(PreferenceViewHolder holder) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PreferenceWidgetSwitchCustom, 0, 0);
try {
int preferenceTitleId = a
.getResourceId(R.styleable.PreferenceWidgetCustom_preferenceTitle, 0);
((SpecialCustomComponent) holder.itemView).setText(a.getResources().getString(preferenceTitleId));
}
finally {
a.recycle();
}
}
}
I declared a custom attribute for my CustomPreference in attrs.xml that I am hoping to use to set the underlying "myText" attribute of my custom component in the layout.
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PreferenceWidgetCustom">
<attr name="preferenceTitle" format="string" />
</declare-styleable>
</resources>
The layout for each CustomPreference is defined here in preference_widget_custom.xml. Notice the attribute "myText", which is what I would like to set to the value of "preferenceTitle" defined above.
preference_widget_custom.xml
<?xml version="1.0" encoding="utf-8"?>
<SpecialCustomComponent
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:myText="this is the field I would like to be set by 'preferenceTitle'" />
I am using these preferences in a PreferenceScreen layout like this:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="Settings">
<CustomPreference
android:key="pref_one"
app:preferenceTitle="Preference #1"/>
<CustomPreference
android:key="pref_two"
app:preferenceTitle="Preference #2"/>
</PreferenceScreen>
It's generally wrong to provide a string resource as a styleable and that's why you're vastly overcomplicating a simple localization job. Any resource can be accessed with this pattern: [<package_name>.]R.<resource_type>.<resource_name>.
So this would be res/values/strings.xml:
<resources>
<string name="title_preference_01">Title 01</string>
<string name="summary_preference_01">Summary 01</string>
<string name="title_preference_02">Title 02</string>
<string name="summary_preference_02">Summary 02</string>
<resources>
Which then can be accessed throughput the application:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="Settings">
<CustomPreference
android:title="#string/title_preference_01"
android:summary="#string/summary_preference_01"
android:key="pref_one" />
<CustomPreference
android:title="#string/title_preference_02"
android:summary="#string/summary_preference_02"
android:key="pref_two" />
</PreferenceScreen>
Localize your app explains this all in high detail.
Besides, even if it were actual stylable values and not strings commonly used for localization, you'd still need to apply the PreferenceWidgetCustom style to SpecialCustomComponent in order to access it's values.
I solved my problem. As Martin pointed out a styleable is not really intended for strings anyways. As it turns out, using the built-in android:title attribute instead of a custom styleable made things really easy. All I have to do is call this.getTitle() to access its value from my CustomPreference class.
public class CustomPreference extends Preference {
public CustomPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(R.layout.preference_widget_custom);
}
// other required constructor overloads omitted
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
setPreferenceTitle();
}
private void setPreferenceTitle() {
((SpecialCustomComponent) holder.itemView).setText(this.getTitle());
}
}
I have a class derived from a standard library widget, how can I read one of the base class' xml attributes in the constructor? For example, how would I get the value of "android:layout_height" in the following?:
class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
int layoutHeightParamFromXmlAttributes = ?;
}
}
I'm interested in reading other "android:x" attributes in this way, this is just an example.
Thanks
You can try something like this:
In your widget constructor.
If you get custom attribute then try this.
if (attrs != null) {
TypedArray attributeArray = context.obtainStyledAttributes(attrs,R.styleable.CustomTextView);
//for font
String fontName = attributeArray.getString(R.styleable.CustomTextView_font_name);
}
And if you get default property like height and width then try this:
int width=attributeArray.getLayoutDimension(0,ViewGroup.LayoutParams.WRAP_CONTENT);
int height = attributeArray.getLayoutDimension(1,ViewGroup.LayoutParams.WRAP_CONTENT);//index is based on your xml file
Define style in attrs.xml file
<declare-styleable name="CustomTextView">
<attr name="font_name" format="string" />
</declare-styleable>
In your layout file add this:
<com.package.CustomTextView
android:id="#+id/customFTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:font_name="OpenSans-Regular.ttf"
android:gravity="center"/>
Hope this explanation helps you. :)
I have a custom progress bar used around the app which has a attribute for setting the indeterminate color. Since I am using the support library I am trying to have the progress bar colored on older android versions too.
In attrs I have something like:
<declare-styleable name="TestView">
<attr name="testColor" format="color"/>
</declare-styleable>
When declaring the view:
<com.TestView
....
app:testColor="#color"
/>
Then my custom view is like this:
public class TestView extends ProgressBar {
public TestView(Context context) {
super(context);
applyTint(Color.WHITE);
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TestView, 0, 0);
try {
applyTint(attributes.getColor(R.styleable.TestView_testColor, Color.WHITE));
} finally {
attributes.recycle();
}
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, android.support.design.R.style.Base_Widget_AppCompat_ProgressBar);
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TestView, 0, 0);
try {
applyTint(attributes.getColor(R.styleable.TestView_testColor, Color.WHITE));
} finally {
attributes.recycle();
}
}
private void applyTint(int color) {
if (Build.VERSION.SDK_INT >= 21) {
setIndeterminateTintList(ColorStateList.valueOf(color));
} else {
getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
}
}
The problem I have is that it seems that the attribute of color is somehow shared between instances of TestView.
How can I have each view to keep its own attribute value?
Later edit: seems to work fine on android 6 but fails on 4.4.2
Got the answer at https://stackoverflow.com/a/37434219/379865
Basically I had to update my tint apply code with:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
DrawableCompat.setTint(DrawableCompat.wrap(getIndeterminateDrawable()), color);
else {
DrawableCompat.wrap(getIndeterminateDrawable()).mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
I have a custom view A that has a TextView. I Made a method that returns the resourceID for the TextView. If no text is defined the method will return -1 by default.
I also have a custom view B that inherits from view A. My custom view has the text 'hello'. When I call the method to get the attribute of the super class I get -1 back instead.
In the code there is also an example of how i'm able to retrieve the value but it feels kind of hacky.
attrs.xml
<declare-styleable name="A">
<attr name="mainText" format="reference" />
</declare-styleable>
<declare-styleable name="B" parent="A">
<attr name="subText" format="reference" />
</declare-styleable>
Class A
protected static final int UNDEFINED = -1;
protected void init(Context context, AttributeSet attrs, int defStyle)
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.A, defStyle, 0);
int mainTextId = getMainTextId(a);
a.recycle();
if (mainTextId != UNDEFINED)
{
setMainText(mainTextId);
}
}
protected int getMainTextId(TypedArray a)
{
return a.getResourceId(R.styleable.A_mainText, UNDEFINED);
}
Class B
protected void init(Context context, AttributeSet attrs, int defStyle)
{
super.init(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.B, defStyle, 0);
int mainTextId = getMainTextId(a); // this returns -1 (UNDEFINED)
//this will return the value but feels kind of hacky
//TypedArray b = context.obtainStyledAttributes(attrs, R.styleable.A, defStyle, 0);
//int mainTextId = getMainTextId(b);
int subTextId = getSubTextId(a);
a.recycle();
if (subTextId != UNDEFINED)
{
setSubText(subTextId);
}
}
Another solution I have found so far is to do the following. I also think this is kind of hacky.
<attr name="mainText" format="reference" />
<declare-styleable name="A">
<attr name="mainText" />
</declare-styleable>
<declare-styleable name="B" parent="A">
<attr name="mainText" />
<attr name="subText" format="reference" />
</declare-styleable>
How to get an attribute from a super class of a custom view?
I can't seem to find any good examples on how inheritance works with custom views.
Apparently this is the right way to do it:
protected void init(Context context, AttributeSet attrs, int defStyle) {
super.init(context, attrs, defStyle);
TypedArray b = context.obtainStyledAttributes(attrs, R.styleable.B, defStyle, 0);
int subTextId = getSubTextId(b);
b.recycle();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.A, defStyle, 0);
int mainTextId = getMainTextId(a);
a.recycle();
if (subTextId != UNDEFINED) {
setSubText(subTextId);
}
}
There is an example at the source of TextView.java. at line 1098
In a custom view group, I have a TextView as a child. I want to set this TextView's textColor based on android:textColor value. So in res/values/styles.xml I have:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomViewGroupTextView">
<attr name="android:textColor" />
</declare-styleable>
</resources>
And in CustomViewGroup's constructor, I have this:
private TextView mTextView;
public CustomViewGroup(Context context) {
super(context);
initTextView(context, attrs);
}
public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initTextView(context, attrs);
}
public CustomViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initTextView(context, attrs);
}
private void initTextView(Context context, AttributeSet attrs) {
mTextView = new TextView(
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomViewGroupTextView);
// Set text color
ColorStateList textColor = ta.getColorStateList(R.styleable.MinutiaeTextView_android_textColor);
if (textColor != null) {
mTextView.setTextColor(textColor);
}
}
My question is: how do I properly do mTextView.setTextColor? Anyone can put a whole color state list or a single color value in android:textColor. Or will I get a ColorStateList with all the same color if someone put a single color in android:textColor?
According to the documentation:
The value may be either a single solid color or a reference to a color or complex ColorStateList description
So, if the user set the color to a single value, you will get a single value, else you will get a reference to a ColorStateList.
There are some ways to change the color of a TextView:
mTextView.setTextColor(Color.RED); (RED, WHITE, BLACK....)
mTextView.setTextColor(Color.rgb(200,0,0));
mTextView.setTextColor(getResources().getColor(R.color.yourcolor));
mTextView.setTextColor(0xAARRGGBB);
EDIT:
In your layout xml file use this property in the specific TextView:
android:textColor="Here"
Where you read Here you can write:
android:color/white (black, red...)
color/yourcolorname
#738184
Etc...