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.
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.
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.
I have a poll of ID's (ids.xml), and I assign id's for views I create dynamically. Now my question is pretty simple - assume I create a new view and assign it an id with setId() in conjuction with R.id.uniqueId. Later on, can I access the view with findViewById(R.id.uniqueId)?
If so, what could be the reason it returns null?
Here is a toy example: UPDATED
LinearLayout l = new LinearLayout(this);
l.setId(R.id.mId);
setContentView(l); //i see on screen the views added to 'l'
l = (LinearLayout) findViewById(R.id.mId); //it returns null :(
How come it does not register\map the assgined ID to the view it was assigned?
Does your desired view have a parent view that you could use to call parent.findViewById on? That may help narrow your problem down.
One thing I noticed that's missing from your brief example: you need to make sure you're adding the new LinearLayout to the view hierarchy before you will be able to find it with findViewById:
findViewById(R.id.parent).addView(l)
You can also use the hierarchy viewer to take a look and see if everything's being set up properly.
encountered this many times, try cleaning your project by making a clean, Also check findViewById() returns null for custom component in layout XML, not for other components
From what you have given us it appears that the view is never added to the Activity's view. This is necessary as findViewById uses the activity's view as a parent to find a child view with that Id.
If you are creating the view you can always create it as a member variable and refer to it that way if you are going to be adding it to a view at a later time. Please note that the view must be added after the id has been set.
I have a tabhost. One of the tab's activity is a ViewGroup. This viewgroup manages two different activities. I do this so I can navigate between activities within a tab. I add the activities like so:
if (videoViewLive == null)
videoViewLive = getLocalActivityManager().startActivity("VideoPlayerLive", new Intent(this,VideoPlayerLive.class).
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
videoViewLive.setVisibility(View.VISIBLE);
this.addContentView(videoViewLive, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.FILL_PARENT));
Each of my content view activities receives asynchronous notifications. What I would like to do is somehow remove the activity/content view that is not being used. So in essence, I load content view A, B dies, becomes null, or whatever, and vice versa. I want to do this because the way I am managing these views seems problematic. (errors when loading a view, loading the other view, then loading the first again, etc.)
Have you tried ViewGroup.removeAllViews()?
This is kind of tangential to the issue here, but why are you doing this with separate Activities?
This is exactly the kind of thing Fragments were designed for. There is actually a class called ViewPager included in the android support library (see also FragmentStatePagerAdapter) which allows the same kind of behavior via tabs (potentially in the ActionBar) or swiping. The adapter automagically handles the lifecycles of the Fragments as you navigate between them, all within the context of a single Activity, such that you can use the top-level Activity for routing events and maintaining overarching state if necessary.
I would try following approach:
//Add OnGlobalLayout Listener using ViewTreeObserver
View rootView = (android.R.id.content);
//Assuming you are managing these two activities inside the ViewGroup
Activity activityA = <someRef Value>;
Activity activityB = <someRef Value>;
rootView.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
//try couple of things here
// 1. determine which activity has focus
// 2. you could also check position of View on screen to determine which one is active or on the top
if (activityA.hasWindowFocus())
{
//do some action --remove other content from ViewGroup
}
});
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)