Android ViewGroup Remove Views? - java

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
}
});

Related

Dynamically Adding Compounds to Fragments

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.

How does a Dialog View integrate with its Activity's view Hierarchy

I am quite new to Android, and while trying to understand how dialogs work, I had this below question.
For an Activity, once the View Hierarchy is set through setContentView(View), the View hierarchy cannot be changed unless the activity is recreated. This is the reason Activities are recreated on Rotating the devices, so that new View hierarchy layouts can be utilized.
However assuming the above statements are true, then how does a Dialog fit in the Activities view Hierarchy, when they are created? They just appear floating above the Activity window, with having no apparent space in the Activities view hierarchy? Although certainly they are somehow linked with the activity, as getActivity() methods returns a valid Activity instance.
Any pointers or clarification will really be appreciated.
A dialog isn't actually a part of the activity's view hierarchy. Dialogs are added via the WindowManager.
Check the source code for Dialog. When the Dialog is instantiated, it obtains a reference to the WindowManager from the context, and initializes a new Window.
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
Then later, when you show() the Dialog:
WindowManager.LayoutParams l = mWindow.getAttributes();
try {
mWindowManager.addView(mDecor, l);
mShowing = true;
When you call setContentView(), the view you supply is attached to the activity's default window. When you call Dialog.show(), the dialog's view is attached to a different window for the same display. That's why they are both displayed without actually being part of the same view hierarchy.

Is there a generic way to display progress bar over Views in Android?

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.

Understanding fragment activities

I have app where it's only a timer with a whole bunch of special methods in the main activity. This works great with no problems. Now, that I have it working, I'm trying to include it in a much more complicated application where I'm going to be inserting the timer in numerous places via fragments.
Should I include all of my special methods in my fragment activity? IE:
public class Timer_fragment extends android.support.v4.app.Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.timer_frag, container, false);
}
//SHOULD I insert my special methods here?
}
or should I include them in my main?
I'm asking because I've tried both ways, and the fragment activity way gave me errors in java, but putting them in my main gave me errors during runtime. Basically I have a timer that runs great as a standalone application, but I'm trying to convert it into a fragment, so that I can plug it into multiple places in my app.
Examples of "special methods":
I take a view in the layout and update it according to the new numbers inputted on the timer. These methods require the view to be inflated before they can be used.
Note: The errors at runtime I am experiencing are nullPointerExceptions.
The idea behind a Fragment is that it is a resuable section (ie fragment) of an Activity that can be used in other Activities, or even in the same Activity but with the addition or absence of other Fragments.
In the Gmail app for example, the list of all emails in an inbox is one Fragment, and the actual contents of an email is another Fragment. On a tablet in landscape mode, both are shown. A phone in portrait mode, however, will only show one of the two Fragments at once.
Thus each Fragment should encapsulate all functionality and UI components necessary to use that Fragment. This means that all of your "special" methods such as inflating the Fragment's view XML should happen within the Fragment.
The Activity (or Activities) that utilize the Fragment should only need to use a FragmentManager (or similar method) to add your Fragment to the Activity's layout. The parent Activity of a Fragment should also handle communication between Fragments and other components such as other Activities, Fragments, or Threads.

Getting Currently Set Content View

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.

Categories

Resources