After a lot search I found a great article that describes how to make compound objects. The recipe works great, Eclipse shows it in the custom made widgets. Fantastic.
http://mobile.cs.fsu.edu/creating-a-simple-compound-component-in-android/
Just one problem, I need a few of them. When I added a few into a layout, the resource id for the enclosing layer/object bumps as expected; that is, the new object receives new ids auomagically. The issue is the components inside the main layout do not; so if I need to use finViewById to get the view for internal components, they all have the same id. Does anyone know how to automagically assign different id for the internal components of the complex widget? I would try to assign them manually, but Eclipse does not show the internal structure of a compound object; so no help there.
-- in response to Xaver answer
Yes, at least I think I did. If I follow the example in the link I have something alike; now I see what you are saying, I got the views as just by adding findviewbyId to custom class, as this
public class HorizontalNumberPicker extends LinearLayout {
public HorizontalNumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater layoutInflater = (LayoutInflater)context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.horizontal_number_picker, this);
View v = findViewbyID(R.id.btn_minus); //
}
}
What I cannot understand and I most likely have wrong is how to hook my code to two objects, that is following the example in the link
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<example.example.HorizontalNumberPicker
android:id ="#+id/horizontal_number_picker"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content" />
<example.example.HorizontalNumberPicker2
android:id ="#+id/horizontal_number_picker"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content" />
</FrameLayout>
How does HorizontalNumberPicker construtor know is linked to object horizontal_number_picker or horizontal_number_picker2?
if in in my activity I add
HorizontalNumberPicker obj1 = HorizontalNumberPicker (this,null);
what links to horizontal_number_picker or horizontal_number_picker2?
By the way how do I pass the Attributeset?
Related
I'm new to Android and have recently started adopting the pattern to create an auto-layout-loading custom view based on a layout file. In the layout you use the 'merge' tag as the root, then in the constructor of your view, you inflate that layout into yourself so you are essentially the root of the merged-in controls. Right after you inflate, still in the constructor, you can find the child controls from the layout and assign them to private fields in the custom class so they will always be available, even when using recycler views.
Contrast that with including a layout. In that case, the layout has to define a root element (which can be your custom control, but in that case, you would not inflate the layout into yourself and would have to move the control lookup to onFinishInflate) but otherwise it ends up with the same view hierarchy.
In short, instead of this...
<include layout="#layout/layout_myCustomControl" />
You can now do this...
<com.mydomain.MyCustomControl />
However I find the auto-loading custom control version to be much more flexible and maintainable. The advantages of the second one are not only that you can use custom attributes in the referencing XML layouts (which you can't with the 'include' version) but it also gives you a central place to manage the code as well as layout management/control lookup.
So now I can do this...
<com.mydomain.MyCustomControl
app:myCustomAttribute="Foo" />
And if the control has to have code backing up its behavior, it's nicely encapsulated in the custom view.
Here's an example layout for the auto-loading version...
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is static text in the header" />
<TextView android:id="#+id/dateTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
And here's the class that uses it...
public class MainHeader extends LinearLayout
{
TextView dateTextView;
public MainHeader(Context context, #Nullable AttributeSet attrs)
{
super(context, attrs);
setOrientation(VERTICAL);
LayoutInflater.from(context).inflate(R.layout.header_main, this);
dateTextView = (TextView)findViewById(R.id.dateTextView);
dateTextView.setText(<<Code for setting date goes here>>);
}
}
But what I'm wondering is if the layout is purely static in nature--say holding a static image and fixed text--is it still considered good practice to use a custom view to represent it? I'd say yes for consistency, plus should I want to extend it in the future, you're already prepared for it and have to do so in just one place regardless of how many places it's used. In contrast, if you included the layout directly in say 20 places, you may have to update all 20 (depending on what's actually changed/needed.)
Pros for the Custom View approach:
Central location for managing layout loading and control lookup
Can support backing code implicitly/internally for updating the view.
Can use attributes when being referenced in other layout files
Better encapsulation (you can hide the layout itself from the outside world exposing behaviors via direct methods)
Can simply be 'new'd' up in code and the layout will automatically load. No inflating or casting needed.
Cons for Custom View approach
When used in Layout files, you now have to also specify width and height making usage a little more verbose.
May add extra classes to your project (not always, but sometimes.)
Again, it seems to me like a no-brainer, but I'm wondering if there's a preferred 'Androidy' way, or is it just a preference up to each developer?
Such approach will add additional level in your view trees. So you only made the UI tree more complex for no good reason. From other side, you can follow next steps to get rid of that side-effect:
<merge /> can only be used as the root tag of an XML layout
when inflating a layout starting with a <merge />, you must specify a parent
ViewGroup and you must set attachToRoot to true (see the
documentation of the inflate() method)
That's it.
Well apparently what I came up with wasn't that unique after all! Here's an article explaining in detail this exact scenario, along with the pros and cons of each. Looks like they too came to the same conclusion.
TrickyAndroid: Protip. Inflating layout for your custom view
I have a simple xml which I want to inflate as a java view object.
I know how to inflate a view:
view = LayoutInflater.from(context).inflate(R.layout.alarm_handling, this);
But then I have a view which is a child of the parent (this). And then the messy problem starts with setting layoutparameters and having an extra layout which I do not need. These are much easier to do in xml.
With an Activity one can just call: setContentView() but with a View that is not possible.
In the end I would like to have a Java class (extends ViewSomething) which I can refer to in an other xml. I have looked at ViewStub, which almost is the answer, except that it is final :(
public class AlarmView extends ViewStub{ //could work if ViewStub wasn't final
public AlarmView (Context context, AttributeSet attrs) {
super(context);
//using methods from ViewStub:
setLayoutResource(R.layout.alarm_handling);
inflate();
}
}
So how to to this? What to extend to be able to just call setContentView() or setLayoutResource()?
I looked at many SO answers but none of them fit my question.
as far as I understood the trick that you want to apply it's a bit different than what you trying to.
No ViewStub are not the solution as ViewStub have a very different way of handling everything.
Let's say for the sake of the example your XML layout is something like this (incomplete, just to show the idea):
<FrameLayout match_parent, match_parent>
<ImageView src="Myimage", wrap_content, Gravity.LEFT/>
<TextView text="hello world", wrap_content, Gravity.CENTER_HORIZONTAL/>
</FrameLayout>
then you don't want to extend FrameLayout and inflate this XML inside it, because then you'll have two FrameLayouts (one inside the other) and that's just a stupid waste of memory and processing time. I agree, it is.
But then the trick is to use the merge on your XML.
<merge match_parent, match_parent>
<ImageView src="Myimage", wrap_content, Gravity.LEFT/>
<TextView text="hello world", wrap_content, Gravity.CENTER_HORIZONTAL/>
</merge>
and inflate as normal on your widget that extends FrameLayout
public class MyWidget extends FrameLayout
// and then on your initialisation / construction
LayoutInflater.from(context).inflate(R.layout.alarm_handling, this);
and your final layout on screen will only have 1 FrameLayout.
happy coding!
I've got the crouton library ( https://github.com/keyboardsurfer/Crouton ) working with the default layout for notifications. I would like to use a custom layout xml file for the notifications so I could set a different typeface to the TextView to match the rest of my application. I have extended a TextView to get the custom typeface working.
Browsing the source for the library, I found a couple of methods that will probably help me:
public static Crouton make(Activity activity, View customView, ViewGroup viewGroup) {
return new Crouton(activity, customView, viewGroup);
}
public static Crouton make(Activity activity, View customView) {
return new Crouton(activity, customView);
}
But I'm struggling to find good example on how to use custom layouts for crouton messages and how I would set the text/message style for them (I have defined some custom styles using Style.Builder()).
The custom layout I want to use is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/uk.co.volume.pinkmothballs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<com.myapp.ui.views.TypefacedTextView
android:id="#+id/crouton_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:gravity="center"
/>
</RelativeLayout>
Can someone point me in the right direction?
You can a custom Style that uses the resourceId of your text appearance via Style.Builder.setTextAppearance(...).
This takes a reference from your styles.xml and uses it within the internal TextView of the Crouton.
Then you can call Crouton.makeText or Crouton.showText with your custom Style.
I've created a custom view which extends RelativeLayout, and I want to preview how it looks in the designer.
The java is something like this:
// The view for a snap in the search/browse fragment.
public class MyView extends RelativeLayout
{
public MyView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.the_layout, this);
}
public void setData(String text)
{
mText.setText(text);
}
// *** Views ***
private TextView mText;
#Override
protected void onFinishInflate()
{
super.onFinishInflate();
mText = (TextView)findViewById(R.id.text);
}
}
And the XML is like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- (And lots more views) -->
</RelativeLayout>
There are a few problems with this however:
This actually creates a RelativeLayout within a RelativeLayout which is pointless. It can be solved by changing the XML <RelativeLayout> to a <merge> but then I can't preview the layout at all!
I want to use the isInEditor() function (or whatever it is called) in the java to add some sample data for previewing purposes, but I can't find a way to tell the XML editor that it should display a preview of my class instead of the actual XML.
One unsatisfying solution I can think of is to create an empty preview.xml file with only <com.foo.bar.MyView/> in it... But that seems kind of silly. Is there a better way? (I don't really care about editing as much as previewing, since - let's face it - Eclipse/ADT are way too slow and flaky to make graphical layout editing usable.)
If I understand your problem correctly I would say that the solution is to just replace the "RelativeLayout" tags (or any other tag for that matter) in your xml layout with "your.packagename.MyView" as in:
<your.packagename.MyView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- (And lots more views) -->
</your.packagename.MyView>
If you'll get any exceptions regarding MyView class while running your app, add all the missing super/parent constructors.
I do this for almost all of my custom xml layouts. Extending your class with RelativeLayout, LinearLayout or any other GUI class also gives you great controll over how your GUI should behave (because you can also override parent methods etc.).
I'm trying to make some Android view classes (which are just wrappers around layouts defined in an XML file). Is this correct:
public class MyViewWrapper extends LinearLayout {
private TextView mTextView;
public MyViewWrapper(Context context) {
super(context);
}
public constructUI() {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.myview, this);
mTextView = (TextView)findViewById(R.id.myview_textview);
}
}
so the idea is just that I can construct my views like that, and they have logic inside for modifying their child views etc. The layout looks like:
<LinearLayout>
<TextView />
</LinearLayout>
It just looks like I'm going to get an extra unnecessary LinearLayout. The wrapper class is itself a LinearLayout, and then it will attach the inner LinearLayout from the xml file.
Is that ok?
Thanks
You can try replacing the <LinearLayout> in your layout file with <merge>. I have not tried that recently, and I think I ran into problems when I last tried it, but in theory it should serve the purpose. <merge> basically means "take all my children and put them directly into whatever container I'm being inflated into".