I have an android ProgressBar which is indeterminate so it is simply a revolving circle animation.
It starts off displaying fine, but after I set the visibility of its parent (overlayLayout) to either gone or invisible and then set it back to visible later on, the progress bar is unseen?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/overlayLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:gravity="center" >
<TextView
android:text=""
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:textAlignment="center"
android:layout_margin="10dp"
android:layout_height="wrap_content"
android:id="#+id/overlayLabelText" />
<ProgressBar
android:id="#+id/overlayProgressBar"
android:layout_below="#id/overlayLabelText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foregroundGravity="center"
android:indeterminate="true" />
</RelativeLayout>
EDIT:
I'm unsure if the view is included but the progress bar is just not rendered or whether the ProgressBar view itself is completely excluded as I can't access the UI view hierarchy.
So far I have tried:
ProgressBar.Enabled = true;
ProgressBar.ForceLayout();
ProgressBar.Invalidate();
ProgressBar.SetProgress(0, true);
ProgressBar.Visibility = ViewStates.Visible;
But have had no breakthroughs yet.
EDIT 2:
Thankyou everyone for your help so far. I have switched to creating the layout programatically - this is my full code:
overlay = new RelativeLayout(mainActivity)
{
LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent)
};
overlay.SetBackgroundColor(Color.WhiteSmoke);
overlay.SetGravity(GravityFlags.Center);
description = new TextView(mainActivity)
{
LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent),
Gravity = GravityFlags.Center,
TextSize = 18,
Id = 1523112
};
description.Text = "Waiting for GPS";
description.SetBackgroundColor(Color.Aqua);
progressBar = new ProgressBar(mainActivity)
{
Indeterminate = true,
};
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
lp.AddRule(LayoutRules.Below, description.Id);
progressBar.LayoutParameters = lp;
progressBar.SetBackgroundColor(Color.Red);
container.AddView(overlay);
overlay.AddView(description);
overlay.AddView(progressBar);
With the two hiding and showing methods:
private void OnGpsUpdate()
{
overlay.Visibility = ViewStates.Gone;
}
private void NoGPS()
{
description.Text = "Waiting for GPS";
overlay.Visibility = ViewStates.Visible;
}
When the layout is first rendered, before its hidden for the first time:
(I screenshotted at a bad time, but blue drawing shows where the circle is moving around its loading animation)
After its been hidden and shown again, the progressBar loading view is there but there's no loading circle anymore:
I am starting to think it may just be a problem with my android emulator? Nope, same problem when testing on my physical phone. Text view still shows fine, its just the progress bar doesnt show?
SOLUTION
I don't fully understand it, seeing as everything else seemed to work except the progressBar, but a solution came from wrapping my visibility calls in a RunOnUIThread() call.
Few points
Based on how the problem was described, there is a need for you to show the code snippet you used for hiding/showing the overlayLayout
Recommendations
If you're only concerned with how the progress bar should behave in terms of hiding/showing, there's no need for this snippet:
ProgressBar.Enabled = true;
ProgressBar.ForceLayout();
ProgressBar.Invalidate();
ProgressBar.SetProgress(0, true);
ProgressBar.Visibility = ViewStates.Visible;
You just need to control the root layout which has the id of overlayLayout
private RelativeLayout overlayLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
// Instantiate the layout
overlayLayout = findViewById(R.id.overlayLayout);
// Do the logic how you inflate/show the layout
...
// Hide the overlay layout
overlayLayout.setVisibility(View.GONE);
// Show the overlay layout
overlayLayout.setVisibility(View.VISIBLE);
}
Decide on the visibility value, for this scenario, I'd recommend View.GONE rather than View.INVISIBLE
Read more on:
https://developer.android.com/reference/android/view/View.html#GONE
https://developer.android.com/reference/android/view/View.html#INVISIBLE
http://tips.androidgig.com/invisible-vs-gone-view-in-android/
The solution was to add a RunOnUIThread like so:
private void OnNewGPS()
{
mainActivity.RunOnUiThread(() =>
{
overlay.Visibility = ViewStates.Gone; });
}
private void NoGPS()
{
mainActivity.RunOnUiThread(() =>
{
overlay.Visibility = ViewStates.Visible;
});
}
Where mainActivity is a reference to the Activity the views are running in.
I wrote a demo based on your code. And to test it, I added a button to control the show and hide of the layout.
You may try this:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
// Instantiate the layout
overlayLayout = FindViewById<LinearLayout>(Resource.Id.overlayLayout);
progressBar = FindViewById<ProgressBar>(Resource.Id.overlayProgressBar);
description = FindViewById<TextView>(Resource.Id.textView1);
description.Text = "Waiting for GPS";
description.SetBackgroundColor(Color.Aqua);
progressBar.SetBackgroundColor(Color.Red);
switchBtn = FindViewById<Button>(Resource.Id.switchButton);
switchBtn.Click += SwitchBtn_Click;
overlayLayout.Visibility = Android.Views.ViewStates.Visible;
isShown = true;
}
private void SwitchBtn_Click(object sender, System.EventArgs e)
{
if (isShown)
{
overlayLayout.Visibility = Android.Views.ViewStates.Gone;
isShown = false;
switchBtn.Text = "Show";
}
else {
overlayLayout.Visibility = Android.Views.ViewStates.Visible;
isShown = true;
switchBtn.Text = "Hide";
}
You can download the demo from this
to hide layout:
findViewById(R.id.overlayLayout).setVisibility(View.GONE);
to display it again :
findViewById(R.id.overlayLayout).setVisibility(View.VISIBLE);
Related
Looking to create a popup where the user must type 'CONFIRM' to continue. I know how to develop a popup with 'CONTINUE' or 'CANCEL' on it but unsure of how to implement one that checks/validates the users input.
Using native Xamarin on Android with C#.
This is what I have so far. I just need some way of comparing what the user has input to the word CONFIRM
EditText et = new EditText(this);
AlertDialog.Builder ad = new AlertDialog.Builder (this);
ad.setTitle ("Type text");
ad.setView(et); // <----
ad.show();
Create a layout with EditText named CustomDialog.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="#+id/editText_Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Code in MainActivity.cs OnCreate method.
var editText = LayoutInflater.Inflate(Resource.Layout.CustomDialog, null);
var ad = (new AlertDialog.Builder(this)).Create();
ad.SetTitle("Type text");
ad.SetView(editText); // <----
ad.SetButton("Confirm", ConfirmButton);
ad.Show();
The code of ConfirmButton.
void ConfirmButton(object sender, DialogClickEventArgs e)
{
var dialog = (AlertDialog)sender;
var username = (EditText)dialog.FindViewById(Resource.Id.editText_Name);
var name = username.Text;
if (name=="hello")
{
}
}
You could get the text of EditText now.
Updated:
In Xamarin.forms, when you want to display a prompt, you could use DisplayPromptAsync.
protected override void OnAppearing()
{
base.OnAppearing();
PopUp();
}
public async void PopUp()
{
string s = await DisplayPromptAsync("Pop up Window", "Type text:", "Confirm", keyboard: Keyboard.Text);
if (s == "hello")
{
//do something here...
}
}
Display Pop-ups: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/pop-ups
I have inherited some code that requires a change in how it works. The original way didn't have the flexibility now required.
The application is a form generator, and hence has to create the UI on demand. This is Xamarin native, not Xamarin forms.
A FrameLayout for each form question is being created programmatically, added to the view, then a fragment is being added to this FrameLayout. All this is happening AFTER OnCreateView once the UI has been loaded to show a progress circle.
After working through a bunch of exceptions, I have become stuck with the exception
Java.Lang.IllegalArgumentException: No view found for id 0x50 (unknown) for fragment UploadFragment{a31e878 #7 id=0x50 upload_80}
My guess is that the FrameLayout doesn't exist when the fragment is trying to be displayed.
The exception occurs after the OnCreate() method runs after OnCreateView() completes.
I have not been able to find any code precedent for adding FrameLayouts programmatically with Fragments.
CODE Snippet
frame = new FrameLayout(this.Context);
frame.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
upload = new Widgets.UploadFragment(control, binding, Inflater, a, xFormInstance);
MainFormLayout.AddView(frame);
frame.Id = control.id;
fragmentTx.Add(frame.Id, upload, $"upload_{control.id}");
fragmentTx.Commit();
Any advice would be greatly appreciated. Thanks.
Extended Explanation
It may be a bit much to put in everything it does, but will try and put in as much as I can.
The Hierarchy of the page is
Activity -> FormFragment -> UploadFragment
So the parent of the UploadFragment is also a fragment, not the Activity.
Upload Fragment
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout>
<LinearLayout>
<TextView/>
<ImageButton/>
</LinearLayout>
<ImageView/>
</RelativeLayout>
</LinearLayout>
CODE
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
_inflater = inflater;
v = _inflater.Inflate(Resource.Layout.BindImageInput, container, false);
SetUpload();
return v;
//return base.OnCreateView(inflater, container, savedInstanceState);
}
SetUpload() Sets the values of the label, the events for the buttons, and the image (if exists) to the imageview. It also deals with a few extra events to do with form event handling. Stopping SetUpload() from running still has the exception occur.
FormFragment
<RelativeLayout>
<TextView />
<View />
<ScrollView>
<LinearLayout />
</ScrollView>
</RelativeLayout>
CODE
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ShowLoading();
View v = inflater.Inflate(Resource.Layout.Form2, container, false);
MainFormLayout = v.FindViewById<LinearLayout>(Resource.Id.mainFormView);
MainScrollView = v.FindViewById<ScrollView>(Resource.Id.mainScrollView);
formBuilderWorker = new BackgroundWorker();
return v;
}
OnResume() Calls the method where formBuilderWorker.DoWork() exists
formBuilderWorker.DoWork += delegate
{
Form.LoadForm(null, this, FormInstance);
}
LoadForm() uses a Interface to tell the FormFragment to display a control. One of which is the UploadFragment.
public void AddControl(Controls control, int? sectionID)
{
///CODE REMOVED FOR OTHER CONTROL TYPES (they still use old codebase)
Bindings binding = XForm.GetBindingForControl(control, FormInstance);
try
{
// Create a new fragment and a transaction.
FragmentTransaction fragmentTx = this.FragmentManager.BeginTransaction();
FrameLayout frame = null;
Widgets.UploadFragment upload = null;
frame = new FrameLayout(this.Context);
frame.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
frame.Id = control.id;
upload = new Widgets.UploadFragment(control, binding, Inflater, a, xFormInstance);
MainFormLayout.AddView(frame);
ControlViews.Add(frame);
fragmentTx.Replace(frame.Id, upload, $"upload_{control.id}");
//fragmentTx.Show(upload);
fragmentTx.Commit();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
This is cleaned code to remove as much irrelevant code as possible. The code shown is the path the code in question moves through.
I found the issue. Part of what I took out of the code above, was the Activity.RunOnUiThread() calls that add the frame to the main view. The issue was caused by Thread Timing. The UI thread was taking so long to add the frame to the view, that when the FragmentTransaction was trying to commit the changes, the frame still did not exist.
So I'm a bit of a newb in Android. I've read a few tutorials but found it extensively complicated to understand. I was hoping someone can help me understand on how to implement Animations to views on the current context I'm working on (noob friendly).
Let's say this is my .axml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/top_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical" />
Now this is my activity (Not exactly but enough information to know what I want to do:
private LinearLayout _topContainer;
private IDictionary<string, FrameLayout> _framelayoutViewsDictionary = new Dictionary<string, FrameLayout>();
private LinearLayout.LayoutParams layoutParams;
protected override void OnCreate(Bundle bundle)
{
//SetContentView and other stuff...
layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent, 1);
_topContainer = FindViewById<LinearLayout>(Resource.Id.top_container);
}
public void CreateFirstVideoPlayer
{
_framelayoutViewsDictionary.Add(Constants.Views.TOP_FRAMELAYOUT, new FrameLayout(this)
{
Id = 1,
LayoutParameters = layoutParams
});
var fragmentManager = FragmentManager.BeginTransaction();
fragmentManager.Add(_framelayoutViewsDictionary[Constants.Views.TOP_FRAMELAYOUT].Id, /*Create New MediaPlayer Here*/, Constants.FragmentTag.TOP_FRAGMENT);
fragmentManager.Commit();
_topContainer.AddView(_framelayoutViewsDictionary[Constants.Views.TOP_FRAMELAYOUT]);
}
public void CreateSecondVideoPlayer
{
_framelayoutViewsDictionary.Add(Constants.Views.BOTTOM_FRAMELAYOUT, new FrameLayout(this)
{
Id = 2,
LayoutParameters = layoutParams
});
var fragmentManager = FragmentManager.BeginTransaction();
fragmentManager.Add(_framelayoutViewsDictionary[Constants.Views.BOTTOM_FRAMELAYOUT].Id, /*Create New MediaPlayer Here*/, Constants.FragmentTag.BOTTOM_FRAGMENT);
fragmentManager.Commit();
_topContainer.AddView(_framelayoutViewsDictionary[Constants.Views.BOTTOM_FRAMELAYOUT]);
}
As you can see. I'm programmatically creating a new FrameLayout, adding a fragment on top. And then putting that FrameLayout on top view which is a LinearLayout. Now when I add the second FrameLayout. The first FrameLayout is halved and the second one is added. So now each FrameLayout is taking 50:50 space. So what I want to do is. When the second FrameLayout is added, I want the first FrameLayout to animate so it slowly moves upwards to the top half of the screen instead of instantly appearing on the top. And when I remove the second FrameLayout the first FrameLayout will slowly animate back to the centre of the screen.
Hope this all makes sense. If you need more info please comment!
Using the Support Libraries to perform a fragment slide in/out is as easy as setting the Fragment's custom animations to the built-in resource abc_slide_in_bottom and abc_slide_out_bottom animations.
Example:
SupportFragmentManager
.BeginTransaction()
.SetCustomAnimations(
Resource.Animation.abc_slide_in_bottom,
Resource.Animation.abc_slide_out_bottom,
Resource.Animation.abc_slide_in_bottom,
Resource.Animation.abc_slide_out_bottom)
.AddToBackStack(count.ToString())
.Add(Resource.Id.linearLayout1, new AFragmentSubClass(), count.ToString())
.Commit();
I want to add elements into my layout automatically, how do I do that? For example a textview appears when a user clicks a button
To add a TextView or any other view in Android you must add the view you want to a view that support addition, one example is the LinearLayout.
If you have this layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lnrRootView"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
And then in you Activity add a TextView programaticlly to your LinearLayout.
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
var linearLayout = FindViewById<LinearLayout>(Resource.Id.lnrRootView);
var textView = new TextView(this);
textView.Text = "Added programaticlly";
linearLayout.AddView(textView);
}
}
You will get something like this:
Much depends on what you're trying to accomplish. You could simulate adding to layout by turning visibility of elements after certain event, eg. button click.
If you want to add elements dynamically during runtime, consider using
ObservableCollection<T>
Link: https://developer.xamarin.com/api/type/System.Collections.ObjectModel.ObservableCollection%601/
I don't know Xamarin.Android very well but I think the right way is to add controls to layout with "IsVisible = false", then set IsVisible = true.
Otherwise you can take a look to
LinearLayout principalview = FindViewById(Resource.Id.mainlayout);
LinearLayout.LayoutParams parametros = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MatchParent, LinearLayout.LayoutParams.MatchParent);
var valueB = new Button(this);
valueB.Text = "Teste";
valueB.SetBackgroundColor(Color.Aqua);
principalview.AddView(valueB, parametros);
OR
var layout = new LinearLayout (this);
layout.Orientation = Orientation.Vertical;
var aLabel = new TextView (this);
aLabel.Text = "Hello, World!!!";
var aButton = new Button (this);
aButton.Text = "Say Hello!";
aButton.Click +=(sender, e) =>
{aLabel.Text="Hello Android!";};
layout.AddView (aLabel);
layout.AddView (aButton);
SetContentView (layout);
When my activity starts and I initially load my ListView with items, I want to be able to set some of those items to checked however that doesn't seem to be working.
I'm using the simple_list_item_multiple_choice layout so the items are CheckedTextView.
Here's the adapter. Text displays properly however the Checked property doesn't seem to be doing anything visually despite it being set to true/false.
public class VehicleSubListAdapter : BaseAdapter<string>
{
// ...
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView ?? _context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItemMultipleChoice, parent, false);
var checkedTextView = view.FindViewById<CheckedTextView>(Android.Resource.Id.Text1);
checkedTextView.Text = _items[position].Name;
// This doesn't seem to do anything
checkedTextView.Checked = _items[position].Checked;
// I've even tried this with no change visually
//checkedTextView.Checked = true;
return view;
}
}
Here's the activity which can be closed and re-opened but needs to resume from where it left off (some items checked, some items not). That is why things are stored in a global static variable.
If the global list is empty, manually update the lists. Else, load from the global list. It successfully loads from the global list and successfully stores the Checked property of that list as modified in the item click event. However, the checked items aren't shown as checked when the activity loads.
Also I've tried putting the call to FillList() in both OnCreate and OnResume and neither have worked.
public class VehicleConditionSubActivity : ListActivity
{
private List<VehicleConditionItemDTO> _items;
private VehicleSubListAdapter _vehicleAdapter;
//...
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetTheme(Resource.Style.MyAppTheme);
SetContentView(Resource.Layout.VehicleConditionItem);
// Set up action bar
ActionBar.SetDisplayHomeAsUpEnabled(true);
// Set up list view
ListView.ChoiceMode = ChoiceMode.Multiple;
_items = new List<VehicleConditionItemDTO>();
_vehicleAdapter = new VehicleSubListAdapter(this, _items);
ListAdapter = _vehicleAdapter;
ListView.ItemClick += ListView_ItemClick;
//...
// Fill list view
FillList();
}
private void FillList()
{
AddFluidItems();
_vehicleAdapter.NotifyDataSetChanged();
}
private void AddFluidItems()
{
// I've checked that this is being called correctly
if (Global.Conditions.FluidLevels.Count > 0)
{
foreach (var item in Global.Conditions.FluidLevels)
{
_items.Add(item);
}
}
else
{
_items.Add(new VehicleConditionItemDTO("1", "Engine oil"));
// ...
foreach (var item in _items)
{
Global.Conditions.FluidLevels.Add(item);
}
}
}
// ...
private void ListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
// Items already check/uncheck properly when tapped
// So I just update the global list items checked property
Global.Conditions.FluidLevels[e.Position].Checked = !Global.Conditions.FluidLevels[e.Position].Checked;
}
}
The layout for the activity is
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px"
android:background="#color/background">
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#android:id/list" />
</LinearLayout>
Selecting Items Programmatically
Manually setting which items are ‘selected’ is done with the
SetItemChecked method (it can be called multiple times for multiple
selection)
Customizing A List View's Appearance
Note that SetItemChecked is on the ListView.
You can remove the code in GetView that sets Checked.
Then, after (or during the call to FillList) for each item that should be initially checked, call:
ListView.SetItemChecked (position, true);