This sound simple and probably answer is trivial.
I have SomeCustomView class which extending RelativeView. This SomeCustomView have simple addShapeInCustomView():
public class SomeCustomView extends RelativeLayout {
//constructor etc.
public void addShapeInCustomView(){
View testView = new View(ctx);
testView.setLayoutParams(new LayoutParams(50, viewHeight));
ViewHelper.setTranslationX(testView, 20);
testView.setBackgroundColor(Color.GREEN);
addView(testView);
invalidate();
}
}
Of course I added this in my xml layout:
<com.example.test.app.lib.SomeCustomView
android:id="#+id/someCustomView"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="5dp"
android:background="#color/darkblue">
Then in Fragment I calling addShapeInCustomView():
public View onCreateView(...){
//inflate etc.
someCustomView = (SomeCustomView) view.findViewById(R.id.someCustomView);
someCustomView.addShapeInCustomView();
}
SomeCustomView is creating correctly. No errors. But testView from addShapeInCustomView() isn't visible.
What am I doing wrong? What if I want add some elements after initialization (and after onDraw())? I should call addShapeInCustomView() in other moment of Fragment lifecycle (tested and nothing happens)?
If I call addShapeInCustomView() with 200ms of delay it's works perfectly. someView shows up. But obviously it's strange work around.
You should use requestLayout() in place of invalidate(). Invalidating just redraws the current layout, whereas requestLayout() will re-measure and layout the child views, then draw them.
Related
If I create two classes, a Parent class which extends FrameLayout, and a Child class that extends View, and then use XML to initialise them. Is it possible to get child elements from the Parent class constructor? If i use getChildAt() , I always get null, since the activity is still not created.
<com.example.Parent
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_centerInParent="true">
<com.example.Child
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.example.Child
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.example.Parent>
Parent and Child classes
public class Parent extends FrameLayout {
public Parent(#NonNull Context context) {
super(context);
this.getChildAt(0); // return null
}
...
}
public class Child extends View {
...
}
Since you are calling getChildAt at the construction of your FrameLayout you are getting null values (your children are still not fully attached to the view). One solution would be to overwrite onLayout() or onMeasure() to get your childs.
Before inflating you can not get child view form Parent.
But you can do it dynamically but is also work in same way like , you have to add views manually to Parent view.
And then you can get child views form Parent View.
There is a Fragment named FragmentA that has a RelativeLayout with an ImageView behind it. (Say 4)Textviews are dynamically added to the rlParentView This layout resides inside a Fragment layout.
The Textviews are draggable inside the parent layout.
Another Fragment is loaded in the same activity and when FragmentA is reloaded, now the dynamically added textviews are lost so how can I retain the dynamically added TextViews with their text and other bounds.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.85"
android:layout_margin="5dp"
android:id="#+id/rlParentView">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/background_image" />
</RelativeLayout>
Dragable TextView are added dynamically like this:
View inflateLayout=mInflater.inflate(R.layout.text_drag_layout,mParentContainer,false);
TextView draggableView= (TextView) inflateLayout.findViewById(R.id.draggableView);
rlParentView.addView(inflateLayout);
draggableView.setText(Some_Text_here);
OnDragTouchListener listener=new OnDragTouchListener(draggableView, rlParentView,
new OnDragTouchListener.OnDragActionListener() {
#Override
public void onDragStart(View view) {
}
#Override
public void onDragEnd(View view) {
}
}
);
draggableView.setOnTouchListener(listener);
I haven't tried it, but here's an idea:
In onSaveInstanceState() of Fragment A, save the positions (and whetever else) of the TextViews inside their parent. Something like:
class ViewPositionInfo implements Serializable {
String text;
int xPositionInParent;
int yPositionInParent;
// ... other variables you need to store
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
List<ViewPositionInfo> positionInfos = new ArrayList<>();
// iterate over the views and add them inside the list
outState.putExtra("positions", positionInfos);
}
Then, when the fragment is re-created, read this information in onCreateView(), create TextViews with those attributes and add them to the parent layout.
The solution above has a drawback. When rotating the screen, the width / height will be different than before, so saving x and y positions of the the text views inside their parent might not be a good idea. You might need to save a relative position, i.e. a percentage. But first things first - make the simple case work (as explained above) and then think about this one.
after I asked if I should use XML or a View class for my project you told me, that I should do everything possible in XML and use a class for the rest. You told me, that animating Sprites isn't possible with XML so I wanted to make a View Class. I got the tip to google "LayoutInflater" for this and I did.
There aren't many Informations about inflaters so I visited android's developers database and tried to find out how this works.
As far as I know now, you have to put something into the onCreate method of your main game activity (the setContentView has to be the mainXML).
So now I created a LinearLayout in my mainXML and called it "container" and made this being a ViewGroup called "parent".
Now I have created a global variable "private View view" and wrote this line:
view = LayoutInflater.from(getBaseContext()).inflate(new ViewClass(this),
null);
Thw Problem now is that u can't inflate a class like this and I think I'm doing this whole inflating thing wrong.
Do you have any tips and tricks for me for making it work to have a LinearLayout in my mainXML and being able to make the content from my View Class appear in it?
EDIT:
Got it to work without errors, but nothing happens if I start my game now.
Here is the code pls answer if u have any solutions:
super.onCreate(savedInstanceState);
// inflate mainXML->
View mainView = getLayoutInflater().inflate(R.layout.activity_game, null);
// find container->
LinearLayout container = (LinearLayout) mainView.findViewById(R.id.container);
// initialize your custom view->
view = new GameLayout(this);
// add your custom view to container->
container.addView(view);
setContentView(R.layout.activity_game);
And my GameLayout:
public GameLayout(Context context)
{
super(context);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color.BLACK);
}
There are two ways of going about this. I'll show you one of them. Do the following in your onCreate(Bundle) before calling setContentView(...):
// inflate mainXML
View mainView = getLayoutInflater().inflate(R.layout.mainXML, null);
// find container
LinearLayout container = (LinearLayout) mainView.findViewById(R.id.container);
// initialize your custom view
view = new ViewClass(this);
// add your custom view to container
container.addView(view);
Finally:
setContentView(mainView);
Alternatively, you can place your custom view inside mainXML:
<your.package.name.ViewClass
android:id="#+id/myCustomView"
android:layout_width="match_parent"
android:layout_height="match_parent"
.... />
I have an activity, called CollectionActivity, with a viewpager. The viewpager has it owns adapter, PagerAdapter: CollectionPagerAdapter extends PagerAdapter.
The important instantiateItem method:
#Override
public Object instantiateItem(View collection, int position) {
LinearLayout lay = null;
if(position==0){
lay = new Report(context);
lay.setOrientation(LinearLayout.VERTICAL);
((ViewPager) collection).addView(lay);
}
//else for the other positions...
return lay;
}
The constructor in the class Report (extends LinearLayout) looks like:
public Report(Context context) {
super(context);
this.context = context;
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.activity_report, null);
this.addView(view);
Button button = new Button(context);
button.setText("LinearLayoutExtended");
this.addView(button);
The problem; the view I´m getting from R.layout.activity_report shows up with a really tiny width on the screen. The Button below it however, get the width it needs.
activity_report.xml has the width and height of fill parent, and starts with a horizontal layout filled with textboxes, buttons and other stuff. Activity_report.xml looks great on the preview in eclipse, and has looked fine when I used it to an activity before.
Why cant I use this.addView(view) to add my layout activity_report.xml and getting it on the whole screen? (not with a tiny width as it is now)
When you use addView(View) you have to specify the LinearLayout.LayoutParams of the parent view.
For some reason, everything worked when I had no images lying by themselves in my view xml file. I simply put each image in their own linear layout - and I could now see the whole layout on the whole screen.
I have no idea why this was the issue. If you know, leave a comment!
Can anyone suggest a way to improve this API8 example? While they say the views could have been defined in XML, what they have in fact done is code them in java. I see why they wanted to. They have added some members to an extended LinearLayout, and the values are determined at runtime.
According to oh, everyone in the universe, the layout directives should move to XML. But for this app, it makes sense to keep setting the text as-is in the runtime logic. So we've got a hybrid approach. Inflate the views, then populate the dynamic text. I'm having trouble figuring out how to accomplish it. Here's the source and what I tried.
from API8 Examples, List4.java
private class SpeechView extends LinearLayout {
public SpeechView(Context context, String title, String words) {
super(context);
this.setOrientation(VERTICAL);
// Here we build the child views in code. They could also have
// been specified in an XML file.
mTitle = new TextView(context);
mTitle.setText(title);
...
I figured since the LinearLayout has an android:id="#+id/LinearLayout01", I should be able to do this in the OnCreate
SpeechView sv = (SpeechView) findViewById(R.id.LinearLayout01);
but it never hits the minimal constructor I added:
public class SpeechView extends LinearLayout {
public SpeechView(Context context) {
super(context);
System.out.println("Instantiated SpeechView(Context context)");
}
...
I just ran into this exact problem myself. What I think you(we) need is this, but I'm still working through some bugs so I can't yet say for sure:
public class SpeechView extends LinearLayout {
public SpeechView(Context context) {
super(context);
View.inflate(context, R.layout.main_row, this);
}
...
I'll be anxious to hear if you have any luck with it.
Edit: It's now working for me just like this.
It looks like you inflated your Layout, which resides in the file main_row.xml. Correct? My need is different. I want to inflate a TextView child of the Layout I have in main.xml.
Still, I used a similar solution. Since I had already inflated the LinearLayout from XML in the onCreate
setContentView(R.layout.main);
what remained is to inflate the TextView from XML in my View constructor. Here's how I did it.
LayoutInflater li = LayoutInflater.from(context);
LinearLayout ll = (LinearLayout) li.inflate(R.layout.main, this);
TextView mTitle = (TextView) ll.findViewById(R.id.roleHeading);
R.id.roleHeading is the id of the TextView I'm inflating.
<TextView android:id="#+id/roleHeading" ... />
To increase efficiency I was able to move the LayoutInflater to an Activity member, so that it just gets instantiated once.