Android how to use app attribute for custom class options [duplicate] - android

I know it is possible to create custom UI element (by way of View or specific UI element extension). But is it possible to define new properties or attributes to newly created UI elements (I mean not inherited, but brand new to define some specific behavior I am not able to handle with default propertis or attributes)
e.g. element my custom element:
<com.tryout.myCustomElement
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Element..."
android:myCustomValue=<someValue>
/>
So is it possible to define MyCustomValue?
Thx

Yes. Short guide:
Create an attribute XML
Create a new XML file inside /res/values/attrs.xml, with the attribute and its type
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="MyCustomElement">
<attr name="distanceExample" format="dimension"/>
</declare-styleable>
</resources>
Basically you have to set up one <declare-styleable /> for your view that contains all your custom attributes (here just one). I never found a full list of possible types, so you need to look at the source for one I guess. Types that I know are reference (to another resource), color, boolean, dimension, float, integer and string. They are pretty self-explanatory
Use the attributes in your layout
That works the same way you did above, with one exception. Your custom attribute needs its own XML namespace.
<com.example.yourpackage.MyCustomElement
xmlns:customNS="http://schemas.android.com/apk/res/com.example.yourpackage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Element..."
customNS:distanceExample="12dp"
/>
Pretty straight forward.
Make use of the values you get passed
Modify the constructor of your custom view to parse the values.
public MyCustomElement(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomElement, 0, 0);
try {
distanceExample = ta.getDimension(R.styleable.MyCustomElement_distanceExample, 100.0f);
} finally {
ta.recycle();
}
// ...
}
distanceExample is a private member variable in this example. TypedArray has lots of other things to parse other types of values.
And that's it. Use the parsed value in your View to modify it, e.g. use it in onDraw() to change the look accordingly.

In your res/values folder create attr.xml. There you can define your attribues:
<declare-styleable name="">
<attr name="myCustomValue" format="integer/boolean/whatever" />
</declare-styleable>
When you then want to use it in your layout file you have to add
xmlns:customname="http://schemas.android.com/apk/res/your.package.name"
and then you can use the value with customname:myCustomValue=""

Yes , you can.Just use <resource> tag.
like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CodeFont" parent="#android:style/TextAppearance.Medium">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#00FF00</item>
<item name="android:typeface">monospace</item>
</style>
</resources>
link from official website

Related

Android layout resources for Accessibility vision impairment

Android applications currently support different layout resources based on orientation, screen size, day and night etc.
However, I would like to provide layouts targeted at users with vision impairments, for instance use layouts with YELLOW background and BLACK text.
Have I missed something that Android already supports?
Can I implement custom res/layout-WAI or res/layout-DDA folders?
You can't create custom configuration qualifiers.
The current supported qualifiers are listed here.
I will suggest the following workaround (Example):
Create a special layout for WAI for each existing layout, with the same name, but with the suffix "_wai"
example_layout.xml
example_layout_wai.xml
Create a method to resolve the appropriate layout based on system needs. Say we have a method isWAI(), resolve method will look something like:
public int resolveLayoutResourceID(int layoutResID) {
int newLayoutResID = layoutResID;
if (isWAI()) {
String layoutResName = getResources().getResourceEntryName(layoutResID);
String newLayoutResName = layoutResName + "_wai";
newLayoutResID = getResources().getIdentifier(newLayoutResName, "layout", getPackageName());
}
return newLayoutResID;
}
Create a BaseActivity class for all your classes (or use a utility static function), that will override the setContentView method. There you will add a logic to select the layout.
#Override
public void setContentView(int layoutResID) {
int newLayoutResID = resolveLayoutResourceID(layoutResID)
super.setContentView(newLayoutResID);
}
Color (YELLOW background and BLACK text), fonts and font size is a topic for Styles and Themes.
Unless visually imapaired people need own layouts (arrangement, ordering of gui elements) you can implement a setting with a style chooser that can be applied to every layout
Instead of providing two different layouts, you can parametrize views in a single layout. Thus, views of your layout would take parameters (e.g. background color, text color) from the context theme they are inflated in.
So, this is what we want to achieve:
<android.support.constraint.ConstraintLayout
android:background="?attr/bgColor"
... >
<TextView
android:textColor="?attr/textColor"
... />
</android.support.constraint.ConstraintLayout>
?attr/someAttribute would be taken from the theme of the current context.
Create attributes at attrs.xml in values/:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyAttrs">
<attr name="bgColor" format="reference|color"/>
<attr name="textColor" format="color"/>
</declare-styleable>
</resources>
In styles.xml declaring two themes extending from a common theme:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
</style>
<style name="AppTheme.Default">
<item name="bgColor">#color/red</item>
<item name="textColor">#color/blue</item>
</style>
<style name="AppTheme.Accessibility">
<item name="bgColor">#color/orange</item>
<item name="textColor">#color/yellow</item>
</style>
</resources>
Then, in your activity perform the assertion and set a correct theme:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
setTheme(isAccessibility ? R.style.AppTheme_Accessibility : R.style.AppTheme_Default);
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
...
}
Or, if you have to do it on runtime, you may use ContextThemeWrapper to inflate specific view with appropriate theme.
Context wrapper = new ContextThemeWrapper(MyFragment.this.getContext(), R.style.AppTheme_Accessibility);
// inflating with a `wrapper`, not with the activity's theme
View themedView = View.inflate(wrapper, R.layout.some_layout, parent);
This is much better approach then providing two separate layouts, because it refrains you from maintaining two layouts when a change happens in UI.

Error when getting a dimension in theme for margins

Background
I'm trying to set a custom margin value on the listView items based on the selected theme.
The app has multiple themes, and the user can choose which theme to use , which I set by calling "setTheme()" .
The problem
whatever I try, I get this error:
java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x2
note that this occurs only for the margin attribute, and so far no other attribute has caused this.
What I've tried
first, here's the xml snippets I've used
attrs.xml
<attr name="listview_item__horizontal_spacing" format="dimension" />
styles.xml
<style name="AppTheme_HoloDark" parent="#style/Theme.Sherlock">
<item name="listview_item__horizontal_spacing">4dp</item>
....
the layout of the listView item:
<RelativeLayout ...
android:layout_marginLeft="?attr/listview_item__horizontal_spacing" >
I've also tried using "reference" for the attribute type, and reference to a "dimen" resource, but it also cause the same exception.
Another thing I've tried is getting it dynamically:
public static int getResIdFromAttribute(final Activity activity,final int attr)
{
final TypedValue typedvalueattr=new TypedValue();
activity.getTheme().resolveAttribute(attr,typedvalueattr,true);
return typedvalueattr.resourceId;
}
...
final int spacingResId=getResIdFromAttribute(activity,R.attr.listview_item__horizontal_spacing);
but for some reason I get 0 as the result of this call. Only when using this method it worked.
The question
What is going on? How can I avoid this?
Is there really no way to overcome this but using code (when inflating the xml) ?
You don't need to use attr XML, and this is your issue.
Place your the value you want in a file named dimens.xml in your res folder (same location as strings.xml).
This file will look something like this:
<resources>
<dimen name="value1">15dp</dimen>
<dimen name="value2">20dp</dimen>
</resources>
Then in your layout XML, you can reference the dimen directly (just as you would reference a string), something like this:
<RelativeLayout ...
android:layout_marginLeft="#dimen/value1" >
or when defining a style, in your XML it would look like:
<style name="MyStyle1">
<item name="android:layout_marginLeft">#dimen/value1</item>
</style>
and then for your other style:
<style name="MyStyle2">
<item name="android:layout_marginLeft">#dimen/value2</item>
</style>

Styling Custom Views

I have a few custom views in my Android project and I've added the relevant details to the attrs.xml file. And now I can implement my objects through XML. This works fine.
How do I style these elements? When I try to use my custom attributes in the styles.xml is get an error "No resource found that matches the given name:"
For using the custom views in normal xml developement I use xmlns:app="http://schemas.android.com/apk/res/bla.bla.bla". What is the correct for use in styles?
This is what my style looks like currently
<style name="Journey_DaySelect_Sunday">
<item name="app:onImage">#drawable/day_sunday_selected</item>
<item name="app:offImage">#drawable/day_sunday</item>
</style>
After more intensive searching on Google I gave up finding it answered elsewhere, and by chance tried using the absolute namespace of my generated R file it worked. May this solve all your problems.
USE THE NAMESPACE CONTAINING YOUR R FILE
<style name="Journey_DaySelect_Sunday" parent="Journey_DaySelect">
<item name="AppZappy.NIRailAndBus:onImage">#drawable/day_sunday_selected</item>
<item name="AppZappy.NIRailAndBus:offImage">#drawable/day_sunday</item>
</style>
For clarification, the item's name attribute should be the same as what is in the attrs.xml's declare-styleable name attribute + ":" + the attribute name.
For example:
attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.chuck.norris">
<attr name="actionBarTextColor" format="color"/>
</declare-styleable>
</resources>
style.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="myNewStyle">
<item name="android:textColor">#FFFF0000</item>
<item name="com.chuck.norris:actionBarTextColor">#ffff0000</item>
</style>
</resources>
You can then apply this style to all activities by using a theme in your manifest.xml file. Anywhere that a custom view exists that wants to use the "actionBarTextColor" attribute, you can then use the Java code:
TypedArray typedArray = context.obtainStyledAttributes(attrSet, R.styleable.com_chuck_norris);
COLOR_ACTION_BAR_TEXT = typedArray.getColor(R.styleable.com_chuck_norris_actionBarTextColor, 0xff0000ff);
typedArray.recycle();
I'm not sure why you cannot just define your schema in your style.xml file as was asked above, but it seems to be a limitation of style.xml.
try this solution
<style name="Journey_DaySelect_Sunday">
<item name="onImage">#drawable/day_sunday_selected</item>
<item name="offImage">#drawable/day_sunday</item>
</style>
reference(Chinese)
if you guys think it useful,I will translate it.

multiple style values inside a view

I have 2 styles defined inside styles.xml. I want to apply it to a textview. How to implement that using style = "#style/"
You can't. You will have to create a style which combines the two styles. (Or create just one style that inherits from one of your styles, and add the extra data of the second style).
You can make a style that inherit the other style
For example:
<style name="Side_Menu_Button" parent="android:attr/buttonStyleSmall">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">match_parent</item>
</style>
Where the side_menu_button inherit from all the attribute of buttonStyleSmall
As a workaround that can work in some situations, you can wrap your target view with LinearLayout and assign one style to the layout another to the view:
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="#style/padding">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Bold text with padding"
style="#style/text_bold" />
</LinearLayout>
This is a hack that I got to work:
<style name="TextAppearance.Title.App" parent="TextAppearance.AppCompat.Subhead">
<item name="android:textColor">#color/primary_text_default_material_light</item>
</style>
<style name="Custom.TV" parent="TextView.App">
<item name="android:textAppearance">#style/TextAppearance.Other.App</item>
</style>
For the particular case of a Button and other Views which support the textAttribute attribute, you can divide the two styles into a Button specific style which would be assigned to attribute:style and a Text specific style which would be assigned to attribute:textAppearance. Note though, that attributes defined in attribute:style will override values defined in attribute:textAppearance.
I know I'm 10 years late but I came across this problem myself and found a solution for it albeit it's quite a workaround.
To get started you need to declare styleable attributes to assign to your view later on
<declare-styleable name="TextView">
<attr name="style1" format="reference" />
<attr name="style2" format="reference" />
<attr name="style3" format="reference" />
<attr name="style4" format="reference" />
<attr name="style5" format="reference" />
</declare-styleable>
You can just add these style attributes to your view within the layout like
<TextView
android:id="#+id/button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/nice_cta"
app:style1="#style/Background_Blue"
app:style2="#style/CallToAction.Primary"
app:style3="#style/Button_Layout" />
To make it work you need to implement a custom ViewInflater that you assign to your application's theme under viewInflaterClass. Inside this ViewInflater you collect the styleable attributes and merge them into a theme as follows:
class MultiStyleViewInflater : MaterialComponentsViewInflater() {
// override the creators of any view you want to have multiple styles
override fun createTextView(context: Context, attrs: AttributeSet?): AppCompatTextView {
// create context if needed and set the attributes as usual
return super.createTextView(createContextIfMultiStyle(context, attrs), attrs)
}
// override fun anyOtherView as needed ...
private fun createContextIfMultiStyle(context: Context, attrs: AttributeSet?): Context {
// get our handy custom attributes
val styleAttributes = context.obtainStyledAttributes(attrs, R.styleable.TextView)
// collect the styles added to the view
val styles = extractStyles(styleAttributes)
// create the custom ContextThemeWrapper only if the view has a custom multi style attribute
val createdContext = if (styles.any { it != 0 }) {
// create a theme, add styles and create the wrapper using the theme
val theme = context.resources.newTheme()
theme.applyValidStyles(styles)
ContextThemeWrapper(context, theme)
} else {
// or just return the original context
context
}
// don't forget to call this!
styleAttributes.recycle()
return createdContext
}
private fun extractStyles(styleAttributes: TypedArray) = listOf(
// the zero values help us determine if we have a custom style added at all
styleAttributes.getResourceId(R.styleable.TextView_style1, 0),
styleAttributes.getResourceId(R.styleable.TextView_style2, 0),
styleAttributes.getResourceId(R.styleable.TextView_style3, 0),
styleAttributes.getResourceId(R.styleable.TextView_style4, 0),
styleAttributes.getResourceId(R.styleable.TextView_style5, 0)
)
private fun Resources.Theme.applyValidStyles(styles: List<Int>) {
// adding styles that actually exist. note we force update duplicate attributes
styles.filterNot { it == 0 }.forEach { this.applyStyle(it, true) }
}
}
To make this your app theme's ViewInflater add this line to it:
<item name="viewInflaterClass">com.agostonr.multistyleapp.utils.MultiStyleViewInflater</item>
After this if you build your application the styles should show up in the editor as well as on the running app on your device.
For a more detailed explanation see the article I wrote about it on Medium.

Custom attrs parameter used in styles.xml

I have a set of custom Android layout parameters defined in attrs.xml. Now I would like to use some tags in my styles.xml file.
At the moment I get this error:
error: Error: No resource found that matches the given name: attr 'custom:tag'
I have tried declaring custom XML namespace as follows:
<resources
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.my.project"
>
hoping, that the same logic used in every layout declaration can be applied here, but with no success.
The XML namespace mechanism is used to namespace tags and attributes. When you define a style like this:
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.my.project">
<style name="my_style"> <item name="custom:tag">some_value</item> </style>
</resources>
you are trying to apply XML namespacing to an attribute value, which won't work. In this case, you should specify the package name directly, like this:
<style name="my_style"> <item name="com.my.project:tag">some_value</item> </style>
Now Android will be able to resolve where the attribute is defined.
The accepted solution did not work for me, but it shed some light upon the situation.
The custom attributes are resolved and can be referenced in a global project's package name, like "com.ltst.project". Even if you have multiple modules (with the same base package name) the resources would be resolved in a project's package name.
So for me it was enough to just omit any prefixes for custom attributes in a style.
Custom attribute:
<declare-styleable name="SampleView">
<attr name="sample_color" format="reference" />
</declare-styleable>
Style:
<style name="SampleStyle">
<item name="sample_color">#color/sample_color</item>
</style>
You can use the link
xmlns: app = "http://schemas.android.com/apk/res-auto
and define the prefix for each tag as app

Categories

Resources