In my \drawable-mdpi folder, I have an image named: test.jpg
In my main.xml file, in my LinearLayout section, I have:
<ImageView
android:id="#+id/test_image"
android:src="#drawable/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
In my src folder, I have only 1 file, HelloAndroidActivity.java with only the following method:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView image = new ImageView(this);
image = (ImageView) findViewById(R.id.test_image);
setContentView(image);
}
This seems to be correct, yet whenever I try to run it, I get The application HelloAndroid (process xxxxx) has stopped unexpectedly. Please try again.
The strange part is it previously did display the image, but now it won't and I don't know why.
Also, when I comment out the ImageDisplay code, and replace it with TextDisplay code. i.e.
TextView tv = new TextView(this);
tv.setText("Does this work?");
setContentView(tv);
It does display the text.
Edit: was asked to post logcat. Link to pastebin.
This is going to sound harsh, but this is when I take the knife out of your hand and say you should do a little more reading or whatever:
setContentView(R.layout.main);
ImageView image = new ImageView(this);
image = (ImageView) findViewById(R.id.test_image);
setContentView(image);
You've set the content view to an xml layout. That's cool. Then you create a new ImageView instance in code, which is OK but probably a little over your skill level just now.
You then overwrite the ImageView you just created with one you pulled from the layout that Android has inflated for you from your original setContentView call. This makes no sense. Just do this.
setContentView(R.layout.main);
ImageView image = (ImageView) findViewById(R.id.test_image);
Finally, you overwrite the content view for the whole Activity with the image reference you pulled from within the content view for the current Activity. Do not do this. It makes no sense. My guess is that your exception is either that you're resetting the content view, or that you're trying to set the content view with a view that already exists in another view.
Are you trying to dynamically set the image that shows up? If not, if you're just trying to "show" the image, do this.
setContentView(R.layout.main);
That's it.
setContentView and findViewById don't do what you think they do.
setContentView sets the given view as the top-level, root view of your activity. Usually you set it to a layout that defines a hierarchy of many nested views. If you give it a layout resource, that layout will be inflated into new views. If you give it an existing view, that existing view instance will be used.
Activity#findViewById performs a tree search across the current view hierarchy for your activity's window, starting at the root. If it can find a view somewhere in the hierarchy with the given id, that view is returned. If it can't be found the function returns null.
So with those things in mind, let's step through your code:
setContentView(R.layout.main); - Inflates your main layout and sets it as your activity content. Looks good so far.
ImageView image = new ImageView(this); - Creates a new ImageView. Nothing technically wrong with this but probably not what you want because...
image = (ImageView) findViewById(R.id.test_image); - Searches through the current view hierarchy (the one where you inflated R.layout.main above) for a View with the id test_image. This overwrites your image variable, throwing out the new ImageView you created on the line above to be garbage collected.
setContentView(image); - Attempts to set the view with id test_image as the root of your activity's view hierarchy, overwriting the hierarchy you inflated in your first setContentView call. I'm guessing that if you check the stack trace from the exception, your app is crashing because at this point image already has a parent view and you're trying to give it a new one without removing it from its current parent first.
Once a view has been attached to the hierarchy you do not need to re-add it when content changes. Simply change the content in-place if that's what you want to do. Calling setContentView more than once is a very unusual thing to do. In the vast majority of cases it's unnecessary.
Since your main layout already specifies the drawable you want in your ImageView your onCreate can be shortened to just the first two lines to simply show the image:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.main);
}
Related
Found the solution, see answer below
tl;dr - My View from the Fragment's onCreateView() is fine, but the result shown in the app is not. WTF?
I am new to Android. I am developing a single-activity Android app with some tabs managed by a TabLayout. The contents are Fragments displayed in a ViewPager. My activity_main.xml looks as follows:
<!-- SIMPLIFIED VERSION -->
<LinearLayout>
<android.support.design.widget.TabLayout/>
<android.support.v4.view.ViewPager/>
</LinearLayout>
I'm using a FragmentPagerAdapter which keeps the Fragments alive all the time (contrary to FragmentStatePagerAdapter), but destroys the View when the Fragment cannot be reached by the user from the current position. This means, that if the user is on the outer left tab, all tabs past the second tab from the left exist as a Fragment, but have no View. This seems counter-intuitive, as the user could just click on the tab he wants and reach ANY Fragment at all time, but I understand that the FragmentPagerAdapter only sees the ViewPager (which is navigated through swiping), not the TabLayout.
As the fragments Views get created, onCreateView() is called. When the View is detroyed, onDestroyView() is called. My Fragments are rather simple:
<!-- SIMPLIFIED VERSION -->
<LinearLayout>
<ScrollView>
<LinearLayout>
<!-- I want to dynamically add Views here -->
</LinearLayout>
<ScrollView>
<LinearLayout>
Now, I have a bunch ob objects in the background representing some data. When the Fragment gets a View (onCreateView()), I want to add custom Views for each data item into the Fragment. This should end up looking like a vertical list. I already instantiated the Views to be added in onCreate(). This seems logical to me, as onCreate() is only called once in my whole app.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// The fragment UI
_me = (LinearLayout) inflater.inflate(R.layout.fragment_log, container, false);
// LinearLayout inside the ScrollView
LinearLayout l = (LinearLayout) _me.findViewById(R.id.scrollableLinearLayout);
// Already instantiated CompoundViews
for(LogEntryCompound compound: _logEntryCompounds) {
l.addView(compound);
// My method to populate all TextViews, etc. from compound._data
compound.refreshUI();
}
return _me;
}
Since the view of the fragments gets destroyed, but not the fragment itself, this is done every time the user swipes too far from this fragment. I don't get this part completely (see my list of questions below), but it seems that when onCreateView() is called again later, the Compounds I already added are still there. Therefore I remove all Views in the Fragments' onDetroyView():
#Override
public void onDestroyView() {
super.onDestroyView();
LinearLayout l = (LinearLayout) _me.findViewById(R.id.scrollableLinearLayout);
l.removeAllViews();
}
The Compounds I display are sort of like Tiles, have some TextViews, ProgressBars, and are clickable. They directly represent a data object. All things they display come from this data directly (each Compound has a reference to his _data). I set all relevant UI elements within these Compounds by calling refreshUI():
public void refreshUI() {
_txtName.setText(_data.getName());
_txtDescr.setText(_data.getDescription());
_txtTopRight.setText(_data.getTimeString());
...
}
Now I have a really weird problem: All _data objects behave as they should, all the data is correct, even the Compounds' UI Elements have the correct .text set. The view I return in onCreateView is perfectly fine. But the View then shown in the app is not. Three out of four TextViews are fine, but one shows the same value for all 'Tiles' im displaying. The value displayed would be the correct value only for the LAST Tile.
Additionally, I have some more general questions (and I assume the more experienced readers will already be on their toes to tell me) about how things SHOULD be done:
1. Can I get the FragmentPagerAdapter to behave in a way that the views of the fragments are never destroyed? It's only three tabs and not a horrendeous amount of data. Recreating the View every time is surely inefficient?
2. How exactly does the FragmentPagerAdapter/ViewPager work? Why is it, that even though the View is apparently destroyed, it still contains the children I added before it got destroyed?
3. Should I add my Compounds anywhere else in code? Does the onCreateView() automagiacally restores its previous state? Or is onCreate() for instantiation and onCreateView() for adding to the UI correct?
After some late night debugging and code review I had a look at my XML file for the 'Tile'-View in question. For the TextView that behaved weirdly I found this:
android:textIsSelectable="true"
Removed it - everything works like a charm now.
Can't really explain this. Can only imagine that maybe the fact that the view is selectable, android messes up something and sees the TextViews from all Tiles as one and thus showing the same text across all.
Instead of moving views in a current layout, I was wondering if i could instead load a different layout whilst the program is running.
For example in the on create i would use:
setContentView(R.layout.layout1);
and then in an on click listener i would use:
setContentView(R.layout.layout2);
I say this since I am using a custom dialogue which prevents me from producing a dialog to overwrite it. I have attempted it but so far have only received errors. I would really like to know if this is possible.
This is possible, but risky and not recommended for a final product. The problem is, you cannot access already defined views once you have switched views. You need to assign them all again for the new layout. So once you switch the content view, re-initialize all of your views and anything that references the old layout. Calling a view from the old layout will cause a crash or error message.
Like CodeMagic said, it is best to use fragments and the FragmentManager for this. And really, this is not a stable way to produce good code. I recommend using separate fragments and using backstacks and such so that not only will your game work, but you can easily navigate back to the original fragment, rather than use makeshift code that may barely work.
After setContentView(R.id.yourLayout) is called, you need to re-instantiate all of your other views. So like say you used an ImageView view to show the color changes, well you need to instantiate that ImageView after you setContentView(R.id.yourLayout) so that it pulls from the new layout, and does not reference to the original layout.
Example:
ImageView imageView;
public void onCreate(Bundle b){
super.savedInstanceState(b);
setContentView(originalLayout);
//Instatiate all of your original Views.
imageView = (ImageView) R.id.yourImageView;
}
public void onButtonClick(View){
setContentView(newLayout);
imageView = (ImageView) R.id.yourNewImageView;
//All other views
}
If you need an example of the fragment manager solution go here: https://developer.android.com/training/basics/fragments/creating.html
and look through some of their examples on how to properly do what you are trying to do.
I have seen numerous posts on how to display progress bar while the data loads in the background. All suggestions requires we manually place a ProgressBar in the layout xml and then use an AsyncTask to show and hide the ProgressBar and the View in question.
However, I would like to make a generic class which creates the ProgressBar programmatically at runtime and place it exactly over the view in question and maybe also slightly shade or blur the view while the ProgressBar is displayed. Had this been a Swing application I would have painted my progress bar on the "glass pane" of the view after slightly shading it with gray. In that case since the progress bar is the child of the same pseudo parent hence I could easily position that as centred.
In Android UI toolkit I am not aware of any such "glass panes". How do I achieve this effect?
Make a BaseActivity that you derive all your Activities from (same goes for Fragments).
Give it something like
protected void showLoading(){
ViewGroup content = findViewById(...);
content.setVisibility(Visibility.GONE);
ViewGroup root = findViewById(...);
root.addView(new ProgressBar());
}
Gotta make sure all your layouts have a ViewGroup for root and one for content, which otherwise might not be necessary and bloat layouts, but thats how I do it and it works fine.
Above is pseudocode of course, but you get the idea.
There's also this libary: http://www.androidviews.net/2013/04/progressfragment/, but I don't think it's necessary to import a library for that task.
Unfortunately you have to create this functionality. I always do this by creating a class from a framelayout and then place my imageview inside with my progressbar ontop. I then create an interface I use as a callback so that when said process is complete and the data is finished being processed I get my callback and I hide the progressbar. I use a framelayout because its the easiest view to use to "stack" views ontop of one another by simply placing them inside the FrameLayout. You may also need to place views inside the frame inside of a relativelayout with the width and height set to match parent so you can set the layout_centerInParent to true on your progressbar so it sits nicely inside your compound view.
Well, I'm not sure I get the question right, because it seems easier to me than it might be. But anyway:
To instantiate programmatically a progress bar, you need to do the following in your activity:
ProgressBar pb = new ProgressBar(this);
((ViewGroup) this.findViewById(R.id.view_that_will_contain_progressbar)).addView(pb);
This will add the view to the ViewGroup view_that_will_contain_progressbar. This ViewGroup should be a FrameLayout if you want to overlay over other information.
Tip: if you want to customize your ProgressBar, you can declare it in a layout file, and do the following to instantiate and attach the PB (still in your activity/fragment) :
this.getLayoutInflater().inflate(R.layout.progressbar,parent);
with parent refering to the parent you want to attach it to.
We are creating an app with two main views: sView and sViewSettings. If the Android Back button is pressed we want an if statment to check if the current view is set to sView settings, if it is then call the sView.
Already have a listener setup for the back button just need it to call the if statement to check the current view.
Have already tried
if (this.findViewById(android.R.id.content) == sViewSettings)
Any ideas on this?
Thank you for Reading,
Travis
The view with id android.R.id.content is a FrameLayout holding your content view. Try this:
ViewGroup contentFrame = (ViewGroup) findViewById(android.R.id.content);
if (contentFrame.getChild(0) == sViewSettings) { ... }
However, I suggest a slightly different approach: use a ViewSwitcher (or any kind of ViewAnimator) to flip between the two main views and keep track in your code of which one is on display.
EDIT: If you want to keep your layouts loaded separately, you can assign an id (the same one) to the root view of each layout and then retrieve the content view directly using findViewById.
In my app I have 3 radiobuttons on top of the view. When i choose one the 'body' underneath that changes to the XML-view i defined.
(If you want more information + pics, I asked a question about this earlier: Dynamically change view inside view in Android)
Now i want to call and change the text of the buttons and edittext's from the different views.
When I do btnAutopech = (Button) findViewById(R.id.btnAutopech); it's gives an NullPointerException.
How can I do this?
Try this.......
LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout ll= new LinearLayout(context);
ll=(LinearLayout)layoutInflater.inflate(R.layout.gegevens_verzekeringen, ll);
btnAutopech = (Button) ll.findViewById(R.id.btnAutopech);
Thanks.........
Depends on how you solved the last question you linked to. If your inflating new views into the area you want to change then you won't be able to retreive a reference to them using finViewById. I.e. they don't actually exist.
When you re-display your view, within which you want to display text based, on your other views outcome, you would have to re-assign the text when you re-attach/display your view. You could then assign text from a member variable of the class, or perhaps from sharedPreferences or a contentProvider.
EIther way, this sort of all depends on how you've solved the issue of your original question and when you attach/remove your views.
To summerise:
It looks like your removing your other views when you visit other views, so store your data in a member variable that persists.
attach view A
got to view B
Click button or something in view B and update member variable used by view A
Go to view A (Removing view B and attaching view A)
set text on view A from member variable (updated as a result of clicking button in view B for example)