First off, I've been reading this site for years now and it's helped me out of a bind several times, so thank you to the community here who contribute, and hopefully you can help me with a problem of my own.
I'm just starting out with Android development at my company and I'm attempting to port an existing application from Windows Mobile C# to Android Java. Most of it is going smoothly, but one area I'm having some difficulty is the UI.
The Windows Mobile application reads in a survey specification from a file when the WinForm is created. In the case of a closed-ended question (such as multiple choice), I need to populate the screen with either a CheckBox or RadioButton control for each applicable answer in the spec. Creating the layout and controls required is no problem, but we also have a requirement that the screen does not scroll. Because of this our software needs to be able to calculate the best possible fit within the available screen space without overflow (ie. 1-4 columns used for display) before it's displayed
I have written my UI (at least the layout) as both an XML resource or Java code, but because methods like GetWidth() and GetHeight() return 0 in onCreate(), I haven't yet been able to add this required pre-processing.
All of this needs to happen prior to the screen showing. If anyone can point me in the right direction, I would be immensely grateful.
When Android builds the UI from a layout, the root of the layout requests all of it's children to report their desired size by calling onMeasure(). It does this is a recursive fashion bottom up. if necessary, the parent view will then set the size of the children so that they fit. As you have found, this measuring pass is not finished during onCreate().
Try a global layout listener.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null);
// set a global layout listener which will be called when the layout pass is completed and the view is drawn
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
// measure your views here
}
}
);
setContentView(mainLayout);
Related
I have a fragment in my app that has a scroll view for the signup and login pages. Right now there isn't enough content in the scroll view to actually make it scroll, however when the keyboard appears, it does cover up most of the content in the view. This causes a lot of issues especially on devices with smaller screens, it blocks a lot, and the view is NOT scrollable, so I have to close the keyboard to get to the rest of the inputs.
I need the bottom of the fragments frame layout to be pushed up to JUST above the top of the keyboard, so the keyboard won't actually hide any content, and still allow the scroll view to actually scroll to the rest of the content.
I have seen the usual fix to an issue similar to this, which would to change the AndroidManifest.xml to the following:
android:windowSoftInputMode="adjustResize"
but this will push up the entire page, which includes the footer view I have under and outside of the login and signup fragment layouts. It makes my scrollview smaller and allows for it to scroll, but I need the footer to stay hidden under the keyboard still.
I think a work around to this would be to have override onConfigurationChanged(); in MyActivity that will detect if the keyboard has appeared, and if it has, push the bottom of the framelayout to be JUST above the keyboard, thus making the scroll view smaller, and allowing us to actually scroll. I am not quite sure HOW to do this though.
Here is what it looks like with the keyboard up, blocking the content. This would be okay IF the scroll view was scrollable, allowing me to see the rest of the content, however it will not scroll and the only way to access the content under it is to close the keyboard first.
EDIT
I was able to use the answer below, editing the Android manifest for
android:windowSoftInputMode="adjustResize"
and the first method using the code below
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});
I had it adjust my views so the footer would be pushed way down below, then resize the layout holding the fragment to extend down allowing it to be scrollable still.
Okay, here's how I solved it.
The basic idea is that you have to:
Detect whether or not a soft-keyboard is showing,
React. Based on the detected information (is-soft-keyboard-showing), resize your layout accordingly.
There are two ways of achieving this:
to give your activity's root view a known ID, say '#+id/activityRoot', hook a GlobalLayoutListener into the ViewTreeObserver, and from there calculate the size diff between your activity's view root and the window size:
Customize your top-level layout class into one which overrides onMeasure()
And I would like to credit the above answer to this SO Post: how-to-check-visibility-of-software-keyboard-in-android, which I have found earlier on this particular problem.
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.
For testing purposes, I need to get the coordinates of all visible views on the screen. However, when checking the output, it seems the UI Thread is not done drawing/positioning/applying settings to all the views yet. Some views are 0x0 pixels while they should be (and they are on both the emulator and a physical device) visible. Some bottom aligned buttons are positioned like stairs, et cetera.
Q: how can i wait for the UI Thread to complete drawing (or at least wait for like a second, that should be more than enough), so the coordinates of all visible views are accurate?
I suspects it's something with Threads, but I couldn't find any definitive answers. As of yet, I do not have any self-declared threads.
Edit: I use onBackPressed to make a bunch of views visible, then capture that in xml, make the previous views invisible and other views visible, capture that in xml, etc. I iterate trough a few different combinations of views and "xml-screenshot" each combination.
LinearLayout layout = (LinearLayout)findViewById(R.id.YOUD VIEW ID);
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
this.layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = layout.getMeasuredWidth();
int height = layout.getMeasuredHeight();
}
});
You'll need to adjust this to work with your layout, and add an ID to the basic parent layout.
By using a ViewTreeObserver, you can get to know when your layout has finished drawing, and run your required code then
I am working on a remote controlling app for a home automation system. The mobile and the home automation device communicate via WebSockets.
To display the functions of the automation device, I am currently using ListViews and Adapters with a varying cell layout. One adapter is displaying all of the device's functions.
The biggest problem I encountered: Adapters keep calling getView() very often, which triggers my functions to register themselves with my state handle and action dispatcher over and over again.
Please note:
There are about 20 different types of functions, all of them requiring a different cell layout. There are sliders, buttons, state text, ...
The functions have asynchronously updating states.
I need to find a way to display those functions in a ListView or a ListView-like layout.
Can you please help me?
Make sure to implement BaseAdapter.getViewTypeCount() which should return the count for the number of different view types your listView is expected to have for its enteries.
And also implement ListAdapter.getItemViewType(int position) to return an int that identifies a specific view and which view type it will be.
Following the above two recommendations will ensure that your ListView will be efficient and it will guarantee that your getView(...) method is called with the appropriate view type, if there is one already infalted.
That said, if you have a few fixed number of view in your list and they are all different, then consider using LinearLayout in a ScrollView.