I have a custom view, and I want to use RoboGuice to get views references.
I used this example:https://github.com/roboguice/roboguice/wiki/Your-First-Injection-into-a-Custom-View-class
I tried both options (v3, v3.1), but it does not seem to work (in onFinishInflate, my members are null)
In case it matters, my custom view inherits from a base custom view with a generic type.
Does anyone know why can this happen?
Try adding a call to injectViewMembers, after the injectMembers, which takes care of non-view injections. Not sure why it is not documented and in the sample code.
public ContactView(Context context, AttributeSet attrs) {
super(context, attrs);
this.attrs = attrs;
inflate(context,R.layout.contact_view, this);
if (!isInEditMode()) {
RoboGuice.getInjector(getContext()).injectMembers(this);
RoboGuice.getInjector(getContext()).injectViewMembers(this);
}
}
Related
Im trying to create a sort of HUD overlay for Google Cardboard.
The HUD needs to be duplicated (one for each eye). A simplistic solution would be to manually copy all the XML elements into another view but giving them different names. This feels like a bad approach since it involves lots of code duplication.
So i came up with the following solution for a ViewGroup with is supposed to render everything two times:
public class StereoView extends FrameLayout {
private static final String TAG = StereoView.class.getSimpleName();
public StereoView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
testPaint.setColor(Color.RED);
}
private Paint testPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right/2, bottom);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.translate(getWidth() / 2, 0);
super.dispatchDraw(canvas);
canvas.restore();
super.dispatchDraw(canvas);
}
public StereoView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public StereoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public StereoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
}
The first problem is that neither dispatchDraw or onDraw is called except from one or two times. It is not called when child views are invalidated.
The second problem is that background on elements which has a with of MATCH_PARENT renders outside the ViewGroups inner bounds:
200DP width
MATCH_PARENT
Is this approach hoping for too much, or am i thinking wrong? Creating a completely custom view to handle complex layouts and images seems like lots of work while copying my layout seems like bad design.
You say:
A simplistic solution would be to manually copy all the XML elements
into another view but giving them different names. This feels like a
bad approach since it involves lots of code duplication.
Actually you can go ahead and use the <include> tag. All you need to do is create a layout that contains all the views that you are going to show to a single eye. Then in your main layout you have to <include> this layout twice, one for the left eye and the other for the right eye.
You might wonder, if this is the case then how can i use findViewById() on this main layout, since now there will be two views with the same id. Well, you can fix that by doing it as follows. Let's say you have created the eye.xml layout. Then your main_layout should look like below.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<include
android:id="#+id/leftEye"
layout="#layout/eye" />
<include
android:id="#+id/rightEye"
layout="#layout/eye" />
</LinearLayout>
When you do the findViewById() in your code, you could do that as follows:
RelativeLayout leftEye = (RelativeLayout)findViewById(R.id.leftEye);
ImageView iv = (ImageView)leftEye.findViewById(R.id.something);
You need write a simple method in your activity where you just pass the leftEye or rightEye as a parameter and perform all code in this method. This lets you perform UI changes in leftEye and rightEye simultaneously.
In the future, you could write a custom View in which you could just inflate the eye.xml. That would modularize your idea.
This is my thoughts to your problem.
A ViewGroup hosts Views; any xml layout are Views, so extend a ViewGroup of your choice, either LinearLayout ,Framelayout-(i prefer), and in your initialisation process, inflate your Layout twice and add them as Views later you can research on how to use onLayout() to position your Views in your preferred Location.
And what ever you call a View 1, View 2 needs to be onboard, you can bind the two, using any approach you want, interfaces or beans
Note
you create one layout and inflate it twice. which will give you two separate View objects, hence this won't be code duplication as its more of
Elltz _20yearElltz = new Elltz(20),_21yearElltz = new Elltz(21);
Hope it helps
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 have a code something like this:
public class CannonView extends SurfaceViewimplements SurfaceHolder.Callback{
Activity activity;
And its constructor:
public CannonView(Context context, AttributeSet attrs){
Super(context,attrs)
activity = (Activity) context;
but apparently the AttributeSet is doing nothing, I dont know why is there, so my questions are: 1.-what is AttributeSet? 2.-why do we need to provide AttributeSet attrs as a second argument? by the way the rest of code is for painting using canvas. Thanks.
http://developer.android.com/training/custom-views/create-view.html
- this is an explanation.
Shortly, attributeSet is needed for GUI editor. AttributeSet is a set of parameters like
layout_width, layour_height and so on.
It you need you new custom attributes, the you need to extend to expand AttributSet class
Views have 3 constructors:
SurfaceView(Context context)
SurfaceView(Context context, AttributeSet attrs)
SurfaceView(Context context, AttributeSet attrs, int defStyle)
NOTE: The third style was added in API Level 11. But if you want to create a custom view for newer versions of the API you should implement it.
When implementing a custom view, if you want it to be widely usable, then you should implement the three Constructors - as another use of your View in a different part of the code or another app could instantiate it using any of the constructors.
If you are constructing the view programatically, then you can decide which constructor you use.
But, the Android Framework instantiates your view when they are referenced from XML.
<com.me.Common.MyView
android:layout_width="wrap_contents"
...
/>
etc.
These XML declarations that instantiate your view can include many Attributes, some of them the standard android ones in the "android:" namespace. If you pass these to the SuperClass you are extending (if you are extending a View class - as you are) then it will parse them and use them and you don't need to do much.
But you can also define and use custom attributes in your own name name
<com.me.Common.MyView
android:layout_width="wrap_contents"
...
com.me:num_elements="10"
/>
and then you should parse the attribute set passed in the constructor and change the behaviour of your View object to respect the settings in the XML. The "android:" attributes will be parsed and used by the Superclass.
So, as you donĀ“t know exactly how your custom view will be instantiated by the Android Framework (it will depend on the XML tag declaring it), you should implement the three constructors.
NOTE: It's tempting to do the standard Java override style and have each constructor use the more complex one via super:
MyView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
But I have seen this lead to problems, as 0 is not always a valid style.
So, I recommend you implement a init() method that does your own customer code, and that you call the constructor of the Superclass that corresponds to the parameters of the constructor the Framework use for your customer view:
public AnimationController(Context context) {
super(context);
initUI(context, null, -1);
}
public AnimationController(Context context, AttributeSet attrs) {
super(context, attrs);
initUI(context, attrs, -1);
}
public AnimationController(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initUI(context, attrs, defStyle);
}
Especially as the super() with the three parameters may not exist on a device with API < 11.
Implementing custom views this way makes them much more configurable and reusable, as you can lay them out in different XML files with different attributes set, or use a style that defines a set of attributes - just as the Android views are used.
There are 3 constructors for SurfaceView:
SurfaceView(Context context)
SurfaceView(Context context, AttributeSet attrs)
SurfaceView(Context context, AttributeSet attrs, int defStyle)
I believe you should be able to override any of these so technically you don't have to provide an AttributeSet as a second parameter.
As for why there is an AttributeSet you can refer to the View documentation for that: http://developer.android.com/reference/android/view/View.html. The constructor SurfaceView(Context context, AttributeSet attrs) is "called when inflating a view from XML" according to that site.
As for what an AttributeSet is: it is "a collection of attributes, as found associated with a tag in an XML document" according to http://developer.android.com/reference/android/util/AttributeSet.html
How do I set up a constructor in a custom TextView to be able to pass text from a fragment?
In other words, I'm confused how to send text from my fragment (Fragment1) to the custom view (View1):
public class View1 extends TextView {
//constructors:
public View1(Context context, AttributeSet ats, int ds) {
super(context, ats, ds);
init();
}
public View1(Context context) {
super(context);
init();
}
public View1(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
...
canvas.drawText(myString, margin1, margin2, paint); //myString is from Fragment1
....
}
I asked a similar question here, but didn't really get much help. Example code would go a long way towards clearing up my confusion. Thanks in advance!
You are extending a TextView anyway. As A--C mentioned, you can use getText(), as well as setText() to get and set the text.
In your context, I am not sure if it is a good idea to use TextView to implement your custom view/widget. View might be a better starting point, as TextView carries all kind of stuff around for formatting, icon/drawable display, click/button logic etc.
You need to define the standard constructors if you want to be able to have the system instantiate/inflate your components from an XML layout. Then you can use standard getters/setters for your data, same way as all other controls do it.
If you instantiate your widget/view yourself (in your code), you are free to define whatever constructors you want to (I believe).