It's my very first question here, so please go easy on me ;)
I've built my custom View class extending ImageView.
public class CustomImageView extends ImageView {
// ...
}
I have created a set of custom parameters for it in the shape of a <declare-styleable> item in the attrs.xml file.
<declare-styleable name="CustomImageView">
<attr name="angle" format="integer"/>
</declare-styleable>
I've figured out how to access (i.e. read from within the class and set from within the layout) these values.
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, 0, 0);
try {
a.getInt(R.styleable.CustomImageView_angle, 0);
} finally {
a.recycle();
}
So far, so easy. All of the above are directly taken from the guide.
However, I could not figure out how to access the inherited attributes of the ImageView class. Specifically, I want to read what was set as the src attribute of the ImageView. I'm assuming I have to use a different value for the second parameter of the obtainStyledAttributes(...) call, but I don't know what to use there and this obviously does not work:
a = context.getTheme().obtainStyledAttributes(attrs, ImageView, 0, 0);
So, how do I access the built-in attributes of my super class?
How do I get the int value (drawable res id) that was set for the android:src attribute?
Thanks for your help!
How do I get the int value (drawable res id) that was set for the
android:src attribute?
Use getAttributeResourceValue to get id of drawable :
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
String android_schemas = "http://schemas.android.com/apk/res/android";
int srcId = attrs.getAttributeResourceValue(android_schemas, "src", -1);
}
Related
I'm working on a project that relies on the user adding a custom attribute to their Android layout elements. Just like MvvmCross' app:MvxBind. There are no custom view classes as the idea is that the user can use the normal Android views.
The problem is that in order to get the value of this tag I need to get the IAttributeSet that is used during the view inflation process and I can't find a method of doing so that suits my needs.
I have a working example using LayoutInflater.IFactory however, this requires me to set my own LayoutInflater/factory which, if the user is using a library such as MvvmCross, causes problems as only one factory can be set at once.
I'm looking for a way that I can get the IAttributeSet object whenever a view is inflated to check for my attribute that doesn't interfere with the standard LayoutInflater or LayoutInflater's from other libraries. Or if there is any way to get my attribute after the view has been inflated.
Thanks in advance!
Edit
I want to be able to get the value of MyAttribute from a view without subclassing views or creating custom views. This is easily accomplished with LayoutInflater.IFactory but this method interferes with libraries such as MvvmCross.
<TextView
android:layout_width="wrap_content"
android:layout_width="wrap_content"
app:MyAttribute="My attribute value" />
I'm not sure if I understand what you mean. Please refer to the following:
you could create a custom view and define its attribute in attrs.xml and when it is created from an XML layout, all of the attributes in the XML tag are read from the resource bundle and passed into the view’s constructor as an IAttributeSet
for example,here is a custom view named IconView
public class IconView : View
{
}
define some attributes in attrs.xml(Resources/values/attrs.xml)
<declare-styleable name="IconView">
<attr name="bg_color" format="color" />
<attr name="src" format="integer" />
<attr name="showIconLabel" format="boolean" />
<attr name="iconLabelTextColor" format="color" />
<attr name="iconLabelText" format="string" />
</declare-styleable>
then we could process the attributes in the view constructor when it is created (here define an Initialize method):
public IconView(Context context) : base(context)
{
Initialize(context);
}
public IconView(Context context, IAttributeSet attrs) : base(context, attrs)
{
Initialize(context, attrs);
}
private void Initialize(Context context, IAttributeSet attrs = null)
{
if (attrs != null)
{
// Contains the values set for the styleable attributes you declared in your attrs.xml
var array = context.ObtainStyledAttributes(attrs, Resource.Styleable.IconView, 0, 0);
iconBackgroundColor = array.GetColor(Resource.Styleable.IconView_bg_color, Color.Gray);
iconLabelTextColor = array.GetColor(Resource.Styleable.IconView_iconLabelTextColor, Color.ParseColor("#D9000000"));
_labelText = array.GetString(Resource.Styleable.IconView_iconLabelText);
_showIconLabel = array.GetBoolean(Resource.Styleable.IconView_showIconLabel, false);
var iconResId = array.GetResourceId(Resource.Styleable.IconView_src, 0);
if (iconResId != 0) // If the user actually set a drawable
_icon = AppCompatDrawableManager.Get().GetDrawable(context, iconResId);
// If the users sets text for the icon without setting the showIconLabel attr to true
// set it to true for the user anyways
if (_labelText != null)
_showIconLabel = true;
// Very important to recycle the array after use
array.Recycle();
}
...
}
You can refer to it for more details make custom view
I finally managed to figure this out.
Non-MvvmCross Solution
I stumbled across an article called "Layout Inflater: Friend or Foe?". I think this is the link but at the time of posting this answer it isn't working.
The author did an amazing talk on LayoutInflater and how he changed the Android LayoutInflater process so that he could intercept it for his library Calligraphy. The resulting solution is called ViewPump and it's written in Kotlin.
I have written the ViewPump library in Xamarin for use with non-MvvmCross projects: https://github.com/lewisbennett/viewpump.
MvvmCross Solution
MvvmCross uses a solution based on InflationX' ViewPump to do its binding and we can access it by first creating the below classes:
public class BindingBuilder : MvxAndroidBindingBuilder
{
protected override IMvxAndroidViewBinderFactory CreateAndroidViewBinderFactory()
{
return new ViewBinderFactory();
}
}
public class ViewBinderFactory : IMvxAndroidViewBinderFactory
{
public IMvxAndroidViewBinder Create(object source)
{
return new ViewBinder(source);
}
}
public class ViewBinder : MvxAndroidViewBinder
{
public override void BindView(View view, Context context, IAttributeSet attrs)
{
base.BindView(view, context, attrs);
// Do your intercepting here.
}
public ViewBinder(object source)
: base(source)
{
}
}
Then in your MvxAndroidSetup or MvxAppCompatSetup class add the following:
protected override MvxBindingBuilder CreateBindingBuilder()
{
return new BindingBuilder();
}
Done! I hope this helps someone :)
I wrote a custom view that extends RelativeLayout. My view has text, so I want to use the standard android:text without the need to specify a <declare-styleable> and without using a custom namespace xmlns:xxx every time I use my custom view.
this is the xml where I use my custom view:
<my.app.StatusBar
android:id="#+id/statusBar"
android:text="this is the title"/>
How can I get the attribute value? I think I can get the android:text attribute with
TypedArray a = context.obtainStyledAttributes(attrs, ???);
but what is ??? in this case (without a styleable in attr.xml)?
use this:
public YourView(Context context, AttributeSet attrs) {
super(context, attrs);
int[] set = {
android.R.attr.background, // idx 0
android.R.attr.text // idx 1
};
TypedArray a = context.obtainStyledAttributes(attrs, set);
Drawable d = a.getDrawable(0);
CharSequence t = a.getText(1);
Log.d(TAG, "attrs " + d + " " + t);
a.recycle();
}
i hope you got an idea
EDIT
Another way to do it (with specifying a declare-styleable but not having to declare a custom namespace) is as follows:
attrs.xml:
<declare-styleable name="MyCustomView">
<attr name="android:text" />
</declare-styleable>
MyCustomView.java:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
CharSequence t = a.getText(R.styleable.MyCustomView_android_text);
a.recycle();
This seems to be the generic Android way of extracting standard attributes from custom views.
Within the Android API, they use an internal R.styleable class to extract the standard attributes and don't seem to offer other alternatives of using R.styleable to extract standard attributes.
Original Post
To ensure that you get all the attributes from the standard component, you should use the following:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextView);
CharSequence t = a.getText(R.styleable.TextView_text);
int color = a.getColor(R.styleable.TextView_textColor, context.getResources().getColor(android.R.color.darker_gray)); // or other default color
a.recycle();
If you want attributes from another standard component just create another TypedArray.
See http://developer.android.com/reference/android/R.styleable.html for details of available TypedArrays for standard components.
I extended the class ImageView and added some custom parameters. I succeed to get these custom parameters from my code, using the method Context.getTheme().obtainStyledAttributes().
What I need is to access the standard parameters of the ImageView object, such as android:src and android:background. I know it exist the class android.R.styleable.* which I could use to get those parameters, but that class has been deprecated (and is not visible anymore). What can I do to access those android parameters?
While I’m not sure how to extract parent values from a TypedArray, you’re able to access them with appropriate getters, e.g.:
public class MyImageView extends ImageView {
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.whatever);
try {
// get custom attributes here
} finally {
array.recycle();
}
// parent attributes
final Drawable background = getBackground();
final Drawable src = getDrawable();
// etc.
}
}
It's not exactly what you're looking for, but it might help.
There are several constructors available for defining an ImageView.
For Example
1) public ImageView (Context context)
2) public ImageView (Context context, AttributeSet attrs)
3) public ImageView (Context context, AttributeSet attrs, int defStyle)**
I am confused in using 2nd and 3rd type of constructor.
basically i don't know what to pass in place of AttributeSet.
Kindly provide a coding example.
These constructors are defined in the View documentation. Here is a description of the parameters from View(Context, AttributeSet, int):
Parameters
context The Context the view is running in, through which it can access the current theme, resources, etc.
attrs The attributes of the XML tag that is inflating the view.
defStyle The default style to apply to this view. If 0, no style will be applied (beyond what is included in the theme). This may
either be an attribute resource, whose value will be retrieved from
the current theme, or an explicit style resource.
It's worth noting that you can pass null in place of an AttributeSet if you have no attributes to pass.
In terms of coding the AttributeSet, here's a bit of code I use for a custom TextView class I have:
public EKTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// ...
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LocalTextView);
determineAttrs(context, a);
}
// ...
}
private void determineAttrs(Context c, TypedArray a) {
String font = a.getString(R.styleable.fontName);
if (font != null)
mTypeface = Typeface.createFromAsset(c.getAssets(), "fonts/" + font);
mCaps = a.getBoolean(R.styleable.allCaps, false);
}
As you can see, once you get a TypedArray from the attributes, you can just use its various methods to collect each of the attributes. Other code you may want to review is that of View(Context, AttributeSet, int) or Resources.obtainStyledAttributes(AttributeSet, int[], int, int).
Ways of creating imageView, ImageView with Context
ImageView image= new ImageView(context);
Here when you want set the values like height, width gravity etc you need to set
image.set****();
based on the number of attributes you need to use no of setXXX() methods,.
2.Using Attribute set
you can define set of attributes like height, width etc in your res/values folder in separate xml file, pass the xml file to getXml()
XmlPullParser parser = resources.getXml(yourxmlfilewithattribues);
AttributeSet attributes = Xml.asAttributeSet(parser);
ImageView image=new ImageView(context,attributes);
Here you can also define your custom attributes in your xml . and you can access the by using the methods provided by AttributeSet class example
getAttributeFloatValue(int index, float defaultValue)
//Return the float value of attribute at 'index'
I am building a custom View that contains two standard Views. I have a default style for each contained View, and a custom attribute that lets the user specify a custom style for each contained View. I can get the default vs. custom styles just fine, and pass the right style id as the third parameter of each contained View's constructor. What I am having a hard time doing is generating a ViewGroup.LayoutParams for these contained Views, based on the android:layout_height and android:layout_width in the appropriate style.
It seems like I need to use the ViewGroup.LayoutParams(Context, AttributeSet) constructor, and the AttributeSet docs say that I should get an AttributeSet via
XmlPullParser parser = resources.getXml(myResouce);
AttributeSet attributes = Xml.asAttributeSet(parser);
... but that throws a Resources$NotFoundException with a warning from frameworks/base/libs/utils/ResourceTypes.cpp that Requesting resource %p failed because it is complex.
Hence, my questions, in decreasing order of specificity:
Is there a way to get an XmlPullParser that works with "complex" elements?
Is there some other way to get an AttributeSet that corresponds to a <style> element?
Is there some other way to construct a LayoutParameters that will pay attention to the layout_height and layout_width values in a given style?
static ViewGroup.LayoutParams layoutFromStyle(Context context,
int style) {
TypedArray t = context.getTheme().obtainStyledAttributes(
null,
new int[] { android.R.attr.layout_width,
android.R.attr.layout_height }, style, style);
try {
int w = t
.getLayoutDimension(0, ViewGroup.LayoutParams.WRAP_CONTENT);
int h = t
.getLayoutDimension(1, ViewGroup.LayoutParams.WRAP_CONTENT);
return new ViewGroup.LayoutParams(w, h);
} finally {
t.recycle();
}
}