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".
Related
This always confused me. Let's say I have these two scenarios :
First scenario :
my_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout ..>
<CardView id="#+id/myCardView" ../> // or any other view
</RelativeLayout>
In code :
.. onCreate() {
View view = inflater.inflate(R.layout.my_layout, container, false);
myCardView = (CardView) view.findViewById(R.id.myCardView);
}
Second scenario :
my_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<CardView ../>
In code :
.. onCreate() {
myCardView = (CardView) inflater.inflate(R.layout.my_layout, container, false);
}
To inflate and get a CardView object, when should one use first scenario and when the second ?
Note that in first scenario, I never do anything with the RelativeLayout object. It's merely used to get the CardView object.
The reason I ask is I see the first scenario in so many tutorials but it is never explained why the extra encapsulating Layout is actually there.
Is there any scenario where the first example above does make more sense than the second ?
Post Edit: Could you please evaluate my question with RecyclerView.ViewHolder pattern in mind ?
Post Edit 2:
I use the first scenario in ViewHolder pattern.
public class EntryViewHolder extends RecyclerView.ViewHolder {
public CardView cv;
public EntryViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.entries_card_view);
}
}
Still, even in this scenario I never make use of the encapsulating RelativeLayout. I only use it to call findViewById(..) to get the CardView object.
The first scenario would be used whenever you have an XML layout with multiple child views inside of them. A couple TextViews, EditTexts, Spinners, etc, the things you would need to put together a presentable page for the user. The <RelativeLayout and the other layout tags help organize the child views inside of them, in this case "Relative" to each other. I would stick to this practice instead of your second scenario.
I'm not sure I have ever seen anything like your second scenario before. Inflating the layout and then casting the entire layout to a CardView seems odd. The closest thing I can relate it to is when you make an XML for a custom adapter view, where you might be making a list of card objects.
Here is a helpful link on Layouts.
I have two layout xml A and B
A linearlayout in A xml with id 'layout'
Now I want to add B in layout using layout.addView()
How can i do this by using databinding
I don't think this is the best practice, but here's how I dynamically added views with databinding.
In layout A, I have a FrameLayout like below:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
bind:createView="#{viewModel.dynamicViews}">
In my viewModel class, I have a static method with BindingAdapter annotation,
#BindingAdapter("bind:createView")
public static void createImproveView(FrameLayout layout, LinearLayout replacement) {
layout.removeAllViews();
layout.addView(replacement);
}
and I have my replacement layout here:
public LinearLayout getDynamicViews() {
LinearLayout layout = new LinearLayout(mContext);
// dynamically add views here. This could be your layout B.
return layout;
}
I couldn't find any other solutions, and this was working fine for me. Please give me any comments, I'm open to learn better solutions!
addView(databinding.getRoot())
you can see the getRoot() return a View instance, so you can addView by this method.
This databinding is the databinding instance of the view you want to add.
If you want to do the same using the layout xml . Use the include control like this:
<include layout="#layout/header_logo_lyt" //Name of xml you want to include/>
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 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 am trying to create a simple game. I would like to populate a LinearLayout which I have defined in an XML with several RelativeLayouts. Each time the user presses a button I would like a child View to be added to the LinearLayout. Each RelativeLayout would change slightly based on the users input (which buttons were pressed, etc).
I essentially want to create a new RelativeLayout based on an XML layout file. I then want to manipulate some attributes of the RelativeLayout's child Views (specifically the src of some ImageViews) and add it to a LinearLayout. This in itself is not particularly difficult. I can get at each of the RelativeLayout's children using findViewById but I start having problems when I want to create n number of RelativeLayouts based on the same XML. I'm pretty sure duplicate IDs are causing crashes. Can a RelativeLayout work without using IDs? Should I try and find a different way of constructing the interface using different ViewGroups?
I'm not sure if what I am asking is even possible but I know that creating these items on-the-fly using the code alternative to the XML layouts is a horrible thought.
Thank you in advance.
EDIT: I lost my train of thought halfway through, the previous example had some bugginess. I've updated.
pedr0 has the right idea, but to clarify, you could try something to this effect (assuming you have a RelativeLayout defined in relative.xml). This isn't tested, but the general idea should be valid. You don't even have to do a separate method, you can do it inline in a click handler, or whatever, but I just did the addChildView method for the sake of example:
LayoutInflater inflater;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//get the LinearLayout that you plan to add rows to
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.linear_layout);
//get the LayoutInflater from the system
inflater = getSystemService(LAYOUT_INFLATER_SERVICE);
//call getNewChildView with whatever drawable id you want to place
//as the source. If you want to pass in other types, just change
//the parameters (e.g. Drawable, Bitmap)
linearLayout.addView(getNewChildView(R.drawable.my_image));
}
public RelativeLayout getNewChildView(int resId) {
//inflates a copy of your RelativeLayout template
RelativeLayout rl = (RelativeLayout)inflater.inflate(R.layout.relative, null);
//this assumes an ImageView in your RelativeLayout with an id of image
ImageView img = (ImageView)rl.findViewById(R.id.image);
img.setBackgroundResource(resId);
return rl;
}
EDIT: I couldn't even work out how to use the code tags properly sigh
Thank you both for your quick responses. I had something very similar in mind and in fact was on the same track and using the LayoutInflator. I simplified your example a little as I do not need to pass the id of a drawable around.
LayoutInflater inflater;
private void drawGuess() {
// The top level LinearLayout to add to
LinearLayout topLevel = (LinearLayout) findViewById(R.id.guessList);
// get the inflater`
inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
// add the view (based on stored data elsewhere)
topLevel.addView(getLatestGuess());
}
private RelativeLayout getLatestGuess() {
//inflates a copy of your RelativeLayout template
RelativeLayout rl = (RelativeLayout)inflater.inflate(R.layout.guess_layout, null);
//this assumes an ImageView in your RelativeLayout with an id of image
ImageView guessOne = (ImageView)rl.findViewById(R.id.guess1);
guessOne.setImageResource(R.drawable.red);
}
Running this code works, the catch is it only works the first time you call it. The second time you call getLatestGuess() it crashes. Removing the IDs in the XML (e.g. guess1) from all the child views of the RelativeLayout results in no more crashing. Unfortunately now I have a fairly useless RelativeLayout that I can no longer get at the child views of as they do not have IDs. Presumably, IDs are required to be unique.
I think you have to create an root element, and then add the new child.
Like this:
RelatoveLayout root = ....;
RelativeLayout toadd = inflate(your XML);
apply by code change depending of users input, for example:
toadd.setBackgroundResource(....);
and finally add the result to the root :
root.addView(toadd).
I really hope to help you!
Bye!