Open PopupWindow at top of the view - android

I want to have a show more button on click of which a popup should be opened with 3 options by default, but as per some logic then options would change ie. hide one of the options based on some condition.
I tried to add a popup menu:
popup = new PopupMenu(this, _showMore, GravityFlags.Top);
popup.MenuInflater.Inflate(Resource.Menu.showMoreMenu, popup.Menu);
if (!string.IsNullOrEmpty(someValue))
{
popup.Menu.FindItem(Resource.Id.ABC1).SetVisible(false);
}
popup.MenuItemClick += Popup_MenuItemClick;
popup.Show();
But in this case, initially, when I have all 3 options the popup is displayed above the button as there is less space below the show more button. But if I hide one of the options then my popup menu opens up at the bottom of the button. I want my popup menu to always be opened above the anchor button.
Then I also tried
PopupWindow popupWindow = new PopupWindow(this); // inflet your layout or diynamic add view
LayoutInflater inflater = (LayoutInflater)GetSystemService(Context.LayoutInflaterService);
View view = inflater.Inflate(Resource.Layout.TestLayout, null, false);
TextView abc1 = (TextView)view.FindViewById(Resource.Id.abc1);
TextView abc2 = (TextView)view.FindViewById(Resource.Id.abc2);
TextView abc3 = (TextView)view.FindViewById(Resource.Id.abc3);
abc1.Click += abc1_Click;
abc2.Click += abc2_Click;
if (!string.IsNullOrEmpty(somecondition))
{
abc1.Visibility = ViewStates.Gone;
}
else
{
abc2.Visibility = ViewStates.Gone;
}
popupWindow.Focusable = (true);
popupWindow.Width = (WindowManagerLayoutParams.WrapContent);
popupWindow.Height = (WindowManagerLayoutParams.WrapContent);
popupWindow.ContentView = (view);
popupWindow.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
/*
//tried
var tttt= (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, Resource.Dimension._15sdp, Resources.DisplayMetrics);
//tried
int test=(int)(Resource.Dimension._15sdp * Resources.DisplayMetrics.Density);
//tried
float scale = Resources.DisplayMetrics.Density;
test= (int)(Resource.Dimension._15sdp * scale + 0.5f);
//tried
var dp = Resource.Dimension._15sdp;
int pixel = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, dp, Resources.DisplayMetrics);
*/
popupWindow.ShowAsDropDown(_showMore, -220, -570); <-- fixed offset which works for 3 options
The above code works but I have set fixed offset values which I assume will not render it correctly on different resolutions of the phone and also when I hide one of the options from the menu.
I also tried using ListPopupWindow but the same issue of setting the location. Can anybody please help me in setting offsets dynamically.

could it work like this :
int[] location = new int[2];
_showMore.getLocationOnScreen(location);
popupWindow.showAtLocation(_showMore,Gravity.NO_GRAVITY,location[0], location[1] - popupWindow.getHeight());

You can try below code:
fun showPopupAbove(anchor: View) {
viewAnchor = anchor
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
popupWindow.showAsDropDown(anchor)
} else {
popupWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, 0, 0)
}
measureSizeThenUpdatePopupWindowPosition()
}
private fun measureSizeThenUpdatePopupWindowPosition() {
val viewTreeObserver = rootView.viewTreeObserver
if (!viewTreeObserver.isAlive) {
return
}
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
val anchorRect = getRectFromView(viewAnchor)
// calculate x,y position for display popup window above/below..ect anchor view
var popupWinDownPositionX = (anchorRect.centerX() - rootView.width.toFloat() / 6).toInt()
var popupWinDownPositionY = anchorRect.top
// anchor view close to left
if (popupWinDownPositionX < 0) {
popupWinDownPositionX = 0
}
// anchor view close to top
if (popupWinDownPositionY < 0) {
popupWinDownPositionY = 0
}
// anchor view close to right
if (popupMayExceedsTheScreen(popupWinDownPositionX,
rootView.width)) {
popupWinDownPositionX = (Resources.getSystem().displayMetrics.widthPixels - rootView.width.toFloat()).toInt()
}
popupWindow.update(popupWinDownPositionX, popupWinDownPositionY,
rootView.width, rootView.height)
updateImageArrowMargin(popupWinDownPositionX)
}
})
}
You can modify the value of popupWinDownPositionX and popupWinDownPositionY to fit with your purpose

Thanks, #LeoZhu #LeoZhu's reply. I managed to get it working using the following code
PopupWindow popupWindow = new PopupWindow(this); // inflet your layout or diynamic add view
LayoutInflater inflater = (LayoutInflater)GetSystemService(Context.LayoutInflaterService);
View view = inflater.Inflate(Resource.Layout.TestLayout, null, false);
TextView abc1 = (TextView)view.FindViewById(Resource.Id.abc1);
TextView abc2 = (TextView)view.FindViewById(Resource.Id.abc2);
TextView abc3 = (TextView)view.FindViewById(Resource.Id.abc3);
abc1.Click += GenerateQR_Click;
abc2.Click += PrintQR_Click;
if (!string.IsNullOrEmpty(somecode))
{
abc1.Visibility = ViewStates.Gone;
}
else
{
abc2.Visibility = ViewStates.Gone;
}
int[] location = new int[2];
_showMore.GetLocationOnScreen(location);
popupWindow.Focusable = (true);
popupWindow.Width = (WindowManagerLayoutParams.WrapContent);
popupWindow.Height = (WindowManagerLayoutParams.WrapContent);
popupWindow.ContentView = (view);
popupWindow.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
view.Measure(View.MeasureSpec.MakeMeasureSpec(0, MeasureSpecMode.Unspecified),
View.MeasureSpec.MakeMeasureSpec(0, MeasureSpecMode.Unspecified));
int yOffset = location[1] - (int)(view.MeasuredHeight );
popupWindow.ShowAtLocation(_showMore, GravityFlags.NoGravity, location[0], yOffset);

Related

Horizontal Split Value bar - Android

I need to develop the horizontal split value bar to show the remaining or completed data. There would be 2 horizontal bar - 1 which will have grayed out color and one which will have a filled color by which i need to show some amount of data which user has already used. Please give your input if some of you already have done this.
thanks in advance
private void showBillCycleProgress(int billCycleProgressBarId, int total, int progress, int fillProgressDrawable, int blankProgressDrawable, View view2) {
LinearLayout layout = (LinearLayout) view2.findViewById(billCycleProgressBarId);
layout.removeAllViews();
for (int i = 0; i < total; i++) {
View view = new View(getActivity());
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(35, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(0, 0, 3, 0);
view.setLayoutParams(layoutParams);
view.setBackgroundResource(fillProgressDrawable);
if (i >= progress) {
mHandler.postDelayed(new AnonymousClass(this, view, blankProgressDrawable), (long) (((total - i) * 50) + 200));
}
layout.addView(view);
}
}

Why can't this label in ListView.Header show all text?

Android: 6.0
Xamarin.Forms: 2.1.0.6529
Create a new Xamarin.Forms project and paste this:
class Model
{
public string Text
{
get;
set;
}
}
public class App : Application
{
public App()
{
var label = new Label
{
BackgroundColor = Color.Yellow,
Text = genLongText(),
HeightRequest = 10000, // force height
};
var list = new List<Model>();
for (int i = 0; i < 10; i++)
{
list.Add(new Model { Text = i + " list item" });
}
var template = new DataTemplate(typeof(TextCell));
template.SetBinding(TextCell.TextProperty, nameof(Model.Text));
var listview = new ListView
{
Header = label,
ItemsSource = list,
ItemTemplate = template,
BackgroundColor = Color.Blue,
};
var content = new ContentPage
{
Title = "test",
Content = listview
};
MainPage = new NavigationPage(content);
}
private string genLongText()
{
var t = " Welcome to Xamarin Forms!\n";
var s = "";
for (int i = 0; i < 2000; i++)
{
s += i.ToString() + t;
}
return s;
}
}
The result is this:
As you can see, the text is cropped and doesn't show more than the first 100 lines. The Label itself (yellow background) is fine because I forced the height to be big, otherwise the height of the label would be also of this same length.
So, basically if you set a very long text on the label, it will only expand to show the first 100 lines and if you change its height manually through HeightRequest then the label will be fine but the text will still be clipped.
This must be related to ListView.Header implementation.
On Android, a Form's ListView is a android.widget.ListView and within that the ListView's header is being set to a Container that contains your choice of a VisualElement.
Since you are using a Forms' Label which is an Android TextView, the 100 lines of text is internally set within the Android widget class. Trying to set the height via the Forms' ListViewRenderer.cs Container OnMeasure method will not override that.
From ListViewRenderer.cs
int heightSpec = MeasureSpecFactory.MakeMeasureSpec((int)ctx.ToPixels(request.Request.Height), MeasureSpecMode.Exactly);
_child.ViewGroup.Measure(widthMeasureSpec, heightMeasureSpec);
If you really need to display that much text in a ListView Header without creating a custom ListViewRender and manually manipulating the SetLines, SetMinLines and SetMaxLines of the TextView within that Container , use an Forms' Editor (disabled) instead as your header's VisualElement:
var label = new Editor
{
BackgroundColor = Color.Yellow,
Text = genLongText(),
MinimumHeightRequest = 10000,
IsEnabled = false
};
You can also embed your header disabled Editor in a ScrollView and assign that as your ListView Header so you are scroll the text within Editor:
var scrollViewHeader = new ScrollView
{
HeightRequest = 200,
Content = label
};

Android LisPopUpWindow Stays On Right Side of view without Margin

I am trying to implement a ListPopUpWindow something like the twitter like Menu Popup. But then it's stuck to the right side of the screen. Any help would be appreciated thanks. My code's below
PopUpWindow Method
private void onListPopUp(View anchor) {
ArrayList<Person> persoonItem = new ArrayList<>();
persoonItem.add(new Person(R.drawable.ic_profile_logo, "Oladeji Abubakar", "abubakar.oladeji#deliveryscience.co", ""));
persoonItem.add(new Person(0, "", "", "Update Phone"));
persoonItem.add(new Person(0, "", "", "Your Cards"));
persoonItem.add(new Person(0, "", "", "Invite Friends"));
persoonItem.add(new Person(0, "", "", "Logout"));
ListPopupWindowAdapter mListPopUpWindowAdapter = new ListPopupWindowAdapter(getApplicationContext(), persoonItem);
final ListPopupWindow pop = new ListPopupWindow(this);
pop.setAdapter(mListPopUpWindowAdapter);
pop.setAnchorView(anchor);
pop.setModal(true);
pop.setContentWidth(toolbar.getWidth() / 2);
pop.setHeight(ListPopupWindow.WRAP_CONTENT);
pop.setVerticalOffset(-105);
//pop.setHorizontalOffset(100);
//pop.setPromptPosition(20);
//pop.setHorizontalOffset();
pop.setPromptPosition(0);
pop.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
pop.dismiss();
Toast.makeText(TestActivity.this, "Clicked" + ((Person) parent.getItemAtPosition(position)).getOthers(), Toast.LENGTH_LONG).show();
}
});
pop.show();
WindowManager.LayoutParams wlp = new WindowManager.LayoutParams(toolbar.getWidth()/2, LinearLayout.LayoutParams.WRAP_CONTENT);
wlp.gravity = Gravity.TOP | Gravity.END;
wlp.horizontalMargin = -20;
wlp.x = -20;
View parentView = (View) pop.getListView().getParent();
parentView.setLayoutParams(wlp);
}
And the image
This is what I'm trying to achieve
You can offset the ListPopupWindow by using ListPopupWindow#setHorizontalOffset and ListPopupWindow#setVerticalOffset.
What is confusing is that if your anchor is positioned on the right of the screen you need to add the width value of your ListPopupWindow to the horizontal offset while the vertical offset is always 0 based. Using values below the width for the horizontal offset when the ListPopupWindow is anchored to the right seems to not have any effect. Also positive in the horizontal axis is pointing to the right so we invert the horizontal value.
The following Code positions the ListPopupWindow 5dp down and 5dp to the left from an anchor positioned on the top right:
final ListPopupWindow popupWindow = new ListPopupWindow(context);
int widthPixels = convertDpToPixels(200, context);
int horizontalOffset = -(widthPixels + convertDpToPixels(5, context));
int verticaloffset = convertDpToPixels(5, context);
popupWindow.setAnchorView(anchor);
popupWindow.setWidth(widthPixels);
popupWindow.setHorizontalOffset(horizontalOffset);
popupWindow.setVerticalOffset(verticaloffset);

i want to redraw on a view Android programming

I am creating an app that displays the graphs of input given by a user. The graph is drawn on a linear layout using some library..i want the linear layout to redraw the new function requested by the user everytime the user clicks the draw button..I have tried using layout.invalidate() but this is not working in my app.please help ..below is code snipet :
bb.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
String text = ee.getText().toString(); // getting the user expression input
LinearLayout layout = (LinearLayout) findViewById(R.id.graph2);
layout.setVisibility(View.INVISIBLE);
Expression data = Expression.createExpression(text) ;
if(text == ""){
Toast.makeText(getApplicationContext(), "please enter a valid equation", Toast.LENGTH_LONG).show();
layout.setVisibility(View.INVISIBLE);
}
else
{
draw(data) ;
layout.setVisibility(View.VISIBLE);
layout.invalidate();
}
}
});
public void draw(Expression x)
{
final GraphView graphing = new LineGraphView(this, "sketch");
int num = 350;
GraphViewData[] array = new GraphViewData[num];
double w=0;
for (int i=0; i<num; i++) {
w += 0.2;
array[i] = new GraphViewData(i, x.evaluate(w,0,0)); }
// add data
graphing.addSeries(new GraphViewSeries(array));
// set view port, start=2, size=40
graphing.setViewPort(0, 120);
graphing.getGraphViewStyle().setNumHorizontalLabels(2);
graphing.setScrollable(true);
// optional - activate scaling / zooming
graphing.setScalable(true);
LinearLayout layout = (LinearLayout) findViewById(R.id.graph2);
layout.addView(graphing);
layout.invalidate();
}
First:
graphing.setViewPort(0, 120);
is it correct? To me it looks like you set view width to 0.
Next, you sure you don't want to discard old results by removing old views from layout?
Last, layout auto-invalidated when you add or remove child views.

How to move the Android Google Maps API Compass Position

Does anyone know if you can change the compass position within the map?
All I can find is how to enable or disable it. But I need to move it down a bit so my overlay is not obstructing it.
Use GoogleMap.setPadding() method:
https://developers.google.com/maps/documentation/android/map#map_padding
#Override
public void onMapReady(GoogleMap map) {
try {
final ViewGroup parent = (ViewGroup) mMapView.findViewWithTag("GoogleMapMyLocationButton").getParent();
parent.post(new Runnable() {
#Override
public void run() {
try {
Resources r = getResources();
//convert our dp margin into pixels
int marginPixels = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics());
// Get the map compass view
View mapCompass = parent.getChildAt(4);
// create layoutParams, giving it our wanted width and height(important, by default the width is "match parent")
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(mapCompass.getHeight(),mapCompass.getHeight());
// position on top right
rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0);
//give compass margin
rlp.setMargins(marginPixels, marginPixels, marginPixels, marginPixels);
mapCompass.setLayoutParams(rlp);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
I know it's been a long time that the question was asked , but I feld on this issue a few days ago I fixed the problem like this :
try {
assert mapFragment.getView() != null;
final ViewGroup parent = (ViewGroup) mapFragment.getView().findViewWithTag("GoogleMapMyLocationButton").getParent();
parent.post(new Runnable() {
#Override
public void run() {
try {
for (int i = 0, n = parent.getChildCount(); i < n; i++) {
View view = parent.getChildAt(i);
RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams) view.getLayoutParams();
// position on right bottom
rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP,0);
rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
rlp.rightMargin = rlp.leftMargin;
rlp.bottomMargin = 25;
view.requestLayout();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
in this exemple i put the compass in the corner right. You need to be sure that your mapFragment is created when you do this, i suggest you run your code in the method "onMapReady" of your MapFragment.
As I know you can't change compass position but you can disable it and build yours private one.
See example here
Padding-based solutions work for small offsets, but as far as I know there's no API exposed that allows us to robustly change the horizontal "gravity" of the compass from left to right like the stock Google Maps app does:
Note that if you applied a large amount of left padding to move the compass to the right in your own app, the Google logo in the bottom left would also be shifted over to the right.
On 2.0 map api.
// change compass position
if (mapView != null &&
mapView.findViewById(Integer.parseInt("1")) != null) {
// Get the view
View locationCompass = ((View) mapView.findViewById(Integer.parseInt("1")).getParent()).findViewById(Integer.parseInt("5"));
// and next place it, on bottom right (as Google Maps app)
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)
locationCompass.getLayoutParams();
// position on right bottom
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
layoutParams.setMargins(0, 160,30, 0); // 160 la truc y , 30 la truc x
}
Based on the answer of #Vignon (https://stackoverflow.com/a/38266867/2350644), here is a Kotlin code snippet to position the compass icon to the top right of the screen.
You can also add custom margins (in this example the compass image has a marginTop of 50dp).
mapFragment?.view?.let { mapView ->
mapView.findViewWithTag<View>("GoogleMapMyLocationButton").parent?.let { parent ->
val vg: ViewGroup = parent as ViewGroup
vg.post {
val mapCompass: View = parent.getChildAt(4)
val rlp = RelativeLayout.LayoutParams(mapCompass.height, mapCompass.height)
rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0)
rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP)
rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0)
val topMargin = (50 * Resources.getSystem().displayMetrics.density).toInt()
rlp.setMargins(0, topMargin, 0, 0)
mapCompass.layoutParams = rlp
}
}
}
Better use following snippet:
mapView.findViewWithTag<View>("GoogleMapCompass")?.let { compass ->
compass.post {
val topMargin = compass.marginTop
val rlp = RelativeLayout.LayoutParams(compass.height, compass.height)
rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0)
rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP)
rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0)
val endMargin = (4 * Resources.getSystem().displayMetrics.density).toInt()
rlp.setMargins(0, topMargin, endMargin, 0)
compass.layoutParams = rlp
}
}
This way you don't have to use "GoogleMapMyLocationButton" and go over the parent. Iterating to the parent doesn't bring any benefit and could break more easy in the future, when google provides an update of the mapView.
Within that code snipped you directly access the compass, that's the element you need here.
There is my solution.
First of all, I need this Kotlin extension to get all subview for any view
fun View.getAllChildren(): List<View> {
val result = ArrayList<View>()
if (this !is ViewGroup) {
result.add(this)
} else {
for (index in 0 until this.childCount) {
val child = this.getChildAt(index)
result.addAll(child.getAllChildren())
}
}
return result
}
After that, I just retrieve the compass by looking for it with his content description (Use Layout Inspector to retrieve it just in cas if content description change in the future).
When you have your view, change his position like this:
binding.mapView
.getAllChildren()
.firstOrNull { it.contentDescription == "Compass" }
?.let { it.y = it.y + 400 }
Following #Vignon herre's a suggestion for position the compass in lower left corner (in kotlin):
mapFragment.view?.let { mapView->
mapView.findViewWithTag<View>("GoogleMapMyLocationButton").parent?.let { parent->
val vg: ViewGroup = parent as ViewGroup
vg.post {
val mapCompass :View = parent.getChildAt(4)
val rlp = RelativeLayout.LayoutParams(mapCompass.height, mapCompass.height)
rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
rlp.addRule(RelativeLayout.ALIGN_PARENT_TOP,0)
rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,0)
rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
val bottomMargin = (40 * Resources.getSystem().displayMetrics.density).toInt()
val leftMargin = (5 * Resources.getSystem().displayMetrics.density).toInt()
rlp.setMargins(leftMargin,0,0,bottomMargin)
mapCompass.layoutParams = rlp
}
}
}

Categories

Resources