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'
Related
I am having custom view which will take attribute set(xml value) as constructor value
public CustomView(Context context) // No Attributes in this one.
{
super(context);
this(context, null, 0);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
this(context, attrs, 0)
}
public CustomView(Context context, AttributeSet attrs, int default_style) {
super(context, attrs, default_style);
readAttrs(context, attrs, defStyle);
init();
}
In Fragment class i am setting the view as
CustomView customView = (CustomView) view.findViewById(R.id.customView);
where custom view contains various value such as height,width,padding etc.
i want to modify those values based on required condition and set it back to custom view.
I placed setting width height code in onDraw method and called invalidte view.
But above method will set the every time if i called invalidate method in CustomView class.
how to overcome this so that i can pass modified attribute set value in constructor only.?
Edit: I need to modify the view values(initialize with new values) which is set during attribute constructor so that i will get a refreshed view with a new values.
Override #OnDraw or 'Invalidate' is not a good function for me where inside invalidate i have written the methods which will execute in each second interval.
I see that your CustomView can have multiple attributes and you want to modify some of these attributes based on some condition and pass this in the constructor.
Few best practices while designing a custom view:
If you have custom attributes, make sure that you expose them via setters and getters. In your setter method, call invalidate();
Don't try modifying any attributes inside onDraw() or onMeasure() methods.
Try your best to avoid writing Custom constructors for your Custom View.
So the ideal way to solve your problem is to instantiate your CustomView and then modify the attributes, either externally (in your Activity or Fragment), or have a method inside the CustomView.java and then invoke it externally. Doing this will still give you the same result you are looking for.
So lets say you declared your custom attributes like this for view named StarsView
<declare-styleable name="StarsView">
<attr name="stars" format="integer" />
<attr name="score" format="float" />
</declare-styleable>
And you want to read attributes from something like this
<my.package..StarsView
app:stars="5"
app:score="4.6"
You do just this in constructor
public StarsView(Context context, AttributeSet attrs) {
super(context, attrs);
if(attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StarsView, defStyleAttr, 0);
stars = Tools.MathEx.clamp(1, 10, a.getInt(R.styleable.StarsView_stars, 5));
score = (int)Math.floor(a.getFloat(R.styleable.StarsView_score, stars) * 2f);
a.recycle(); // its important to call recycle after we are done
}
}
It's probably not the solution you were hoping for, but put a FrameLayout in your xml instead of the CustomView, and then create your CustomView programmatically with the FrameLayout as it's parent
I created a custom view:
public class SomeView extends View
The custom view constructors:
public SomeView (Context context)
{
super(context);
}
// Called when view is inflated from xml
public SomeView (Context context, AttributeSet attrs)
{
super(context, attrs);
}
// Perform inflation from XML and apply a class-specific base style from a theme attribute.
public SomeView (Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
I also tried the 4th constructor from api 21 with no luck:
public VeediView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs,defStyleAttr, defStyleRes);
}
In the xml layout i am defining this view and things work fine.
Testing on Galaxy S2 works fine and the view constructor are called but when running the app on Nexus-7 android 5.0.2 the constructors do not get called at all.
Any idea why?
Could it be related to rooted devices?
The related xml view:
<com.package.name
android:id="#+id/scene"
android:onClick="startx"
style="#style/txt_money_style"
android:layout_width="72dp"
android:layout_height="72dp"
android:background="#drawable/wtbtn"
android:layout_gravity="right"
android:gravity="center_vertical|right"
/>
I think you should use this constructor for bestway:
public SomeView (Context context)
{
this(context , null);
}
// Called when view is inflated from xml
public SomeView (Context context, AttributeSet attrs)
{
this(context, attrs , 0);
}
// Perform inflation from XML and apply a class-specific base style from a theme attribute.
public SomeView (Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
// Initialize customize constructor here
}
In API 21 theres now a 4th constructor it could be that your XML is calling this.
From the docs:
public View (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
Added in API level 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.
When determining the final value of a particular attribute, there are four inputs that come into play:
Any attribute values in the given AttributeSet.
The style resource specified in the AttributeSet (named "style").
The default style specified by defStyleAttr.
The default style specified by defStyleRes.
The base values in this theme.
Each of these inputs is considered in-order, with the first listed taking precedence over the following ones. In other words, if in the AttributeSet you have supplied , then the button's text will always be black, regardless of what is specified in any of the styles.
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.
defStyleAttr 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 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.
Here is source code of referred View.java class. If you check it out you will see, that public View(Context context) is always called. If you think it's not called but you see the view, then the issue is rather in the part detecting whether it gets called, than in Android code. You should look in there. It could be logging code or some wrong filters in AS, or similar.
From the source code you can also see, that this is the new constructor, used in Android 5.0 an higher, which has the most implementation.
public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
The thing is i got this code and didnt develop it myself and after trying everything it turns out that the app have multiple layout files:
layout-large, layout-small etc...
I only defined the custom view on the layout folder so switching to other screen sizes invoked the regular view.
I guess others can learn from my mistake , i wish Android Studio or Eclipse can support some kind of setContentView(R.layout.activity_scene) and the related file debug option
So the answer is to make sure all layouts have the custom view defined
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.
I am writing a custom Preference. I can easily read attribute values in my private namespace:
public MyPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MyPreference);
mDefaultValue = styledAttrs.getInt(R.styleable.MyPreference_defaultValue, 0);
}
But I do not know, how to read android attributes, e.g. android:defaultValue. In all examples on the web attributes contain values, but I use resources like #integer/my_number so simply reading attrs does not work - attribute contains resource reference but not the value. android.R.styleable is not accessible, so I do not understand how to do it.
I have a custom view and I simply wish to access the xml layout value of layout_height.
I am presently getting that information and storing it during onMeasure, but that only happens when the view is first painted. My view is an XY plot and it needs to know its height as early as possible so it can start performing calculations.
The view is on the fourth page of a viewFlipper layout, so the user may not flip to it for a while, but when they do flip to it, I would like the view to already contain data, which requires that I have the height to make the calculations.
Thanks!!!
that work :)... you need to change "android" for "http://schemas.android.com/apk/res/android"
public CustomView(final Context context, AttributeSet attrs) {
super(context, attrs);
String height = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height");
//further logic of your choice..
}
You can use this:
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int[] systemAttrs = {android.R.attr.layout_height};
TypedArray a = context.obtainStyledAttributes(attrs, systemAttrs);
int height = a.getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT);
a.recycle();
}
From public View(Context context, AttributeSet attrs) constructor docs:
Constructor that is called when
inflating a view from XML. This is
called when a view is being
constructed from an XML file,
supplying attributes that were
specified in the XML file.
So to achieve what you need, provide a constructor to your custom view that takes Attributes as a parameter, i.e.:
public CustomView(final Context context, AttributeSet attrs) {
super(context, attrs);
String height = attrs.getAttributeValue("android", "layout_height");
//further logic of your choice..
}
• Kotlin Version
The answers which are written to this question do not completely cover the issue. Actually, they are completing each other. To sum up the answer, first we should check the getAttributeValue returning value, then if the layout_height defined as dimension values, retrieve it using getDimensionPixelSize:
val layoutHeight = attrs?.getAttributeValue("http://schemas.android.com/apk/res/android", "layout_height")
var height = 0
when {
layoutHeight.equals(ViewGroup.LayoutParams.MATCH_PARENT.toString()) ->
height = ViewGroup.LayoutParams.MATCH_PARENT
layoutHeight.equals(ViewGroup.LayoutParams.WRAP_CONTENT.toString()) ->
height = ViewGroup.LayoutParams.WRAP_CONTENT
else -> context.obtainStyledAttributes(attrs, intArrayOf(android.R.attr.layout_height)).apply {
height = getDimensionPixelSize(0, ViewGroup.LayoutParams.WRAP_CONTENT)
recycle()
}
}
// Further to do something with `height`:
when (height) {
ViewGroup.LayoutParams.MATCH_PARENT -> {
// defined as `MATCH_PARENT`
}
ViewGroup.LayoutParams.WRAP_CONTENT -> {
// defined as `WRAP_CONTENT`
}
in 0 until Int.MAX_VALUE -> {
// defined as dimension values (here in pixels)
}
}