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.
1) I create a number of controls dynamically. I have a medium-sized RelativeLayout with an EditText that fills most of its parent visible space. The rest is covered by another view and the layout is supposed to slide up on click. The problem is that the layout does not receive onClick event when user clicks on the EditText. I can't use TextView because I will need user to enter some text into it later. I tried this code, without success
private void addTopView() {
RelativeLayout midView = new RelativeLayout(this);
ImageView imageView = new ImageView(this);
imageView.setImageResource(R.drawable.wallet_top);
imageView.setBackgroundColor(0xffff0000);
RelativeLayout.LayoutParams lp = new LayoutParams(scale(320), scale(heightTop));
midView.addView(imageView, lp);
RelativeLayout topCard = populateTPasses ? addTpass(0) : addCard(0);
topCard.setOnClickListener(topCardClick);
midView.addView(topCard);
lp = new LayoutParams(scale(320), scale(cardTopOffsetY + cardHeight));
lp.topMargin = scale(upperOffset);
walletScrollView.addView(midView, lp);
}
private RelativeLayout addCard(int indexVal) {
String cardName = "Add Credit Card";
String cardNum = "Max 16 digits";
String cardCVC = "Max 6 digits";
boolean switchStatus = NO;
String cardExpiry = dateFormatter.format(new Date());
if(indexVal < creditcards.size())
{
cardName = creditcards.get(indexVal).name;
cardNum = String.format("........%s", creditcards.get(indexVal).display);
cardCVC = "NOT STORED";
cardExpiry = creditcards.get(indexVal).expiry;
switchStatus = creditcards.get(indexVal).isDefault;
}
LayoutParams lp = new LayoutParams(scale(cardWidth), scale(cardHeight));
lp.leftMargin = scale(cardTopOffsetX);
lp.topMargin = scale(cardTopOffsetY);
RelativeLayout layout = new RelativeLayout(this);
layout.setLayoutParams(lp);
layout.setTag(indexVal);
addCardElements(layout, cardName, cardNum, cardExpiry, cardCVC, switchStatus);
return layout;
}
private ImageView addCardElements(RelativeLayout whichCard, String cardName, String cardNumber, String cardExpiry, String cardCVC, boolean defaultStatus) {
whichCard.setBackgroundResource(R.drawable.credit_card_tpay);
//Add card name
EditText cardNameField = addCardName(cardName);
whichCard.addView(cardNameField);
return null;
}
private EditText addCardName(String cName) {
EditText cardName = new EditText(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(scale(cardWidth - 90), LayoutParams.WRAP_CONTENT);
params.leftMargin = scale(32 + 50);
params.topMargin = scale(15);
cardName.setBackgroundColor(Color.TRANSPARENT);
cardName.setLayoutParams(params );
cardName.setTextColor(Color.WHITE);
if(isLast == NO || (_selectedIndex >= creditcards.size() && creditcards.size() != 0))
cardName.setText(cName);
else
{
cardName.setHint(cName);
}
isLast = NO;
cardName.setTag(kTag_CardName);
if(_selectedIndex >= creditcards.size())
cardName.setEnabled(YES);
else {
cardName.setEnabled(NO);
cardName.setFocusable(NO);
cardName.setFocusableInTouchMode(NO);
cardName.setClickable(NO);
cardName.setBackgroundColor(Color.YELLOW);
}
cardName.setGravity(5);
return cardName;
}
2) This one is solved. When my layout slides up with the help of TranslateAnimation I have to remove it from its parent and to put onto another ViewGroup or else all the user input gets broken. Then it slides back with similar change of parent. In the process I set the layout's OnClickListener to null and back to my listener. The problem is that after that the listener never fires and the layout can only slide once. I wonder what could be causing this.
My experience is that a click on EditText does not propagate to it's parent, unlike a click on a TextView. You have to listen to the children themselves.
You have 2 problems:
First you need to add your "clickable" content before adding the EditText (to the RelativeLayout). You can do this either by adding them first or by specifying index = 0 when using addView.
The second problem is after a translate animation, you will need to change the layout params to the target location so that they can get the clicks. You can use an animation listener and upon the end of the animation update the layout params of the clickable content to the new location.
I can not really tell because you did not include the relevant code.
After your comment:
So you do not have the first problem because it seems your clickable view is showing above correctly after the animation. However, the second problem is what you have because even if they appear above it, their real position is still unchanged and that is why you do not get any touch event. You need to update their position (LayoutParams) after the animation ends.
So i am creating my views programatically like so:
for (int i = 0; i < num_devices; i++) {
ImageView imageView = new ImageView(this);
LinearLayout.LayoutParams vp = new LinearLayout.
LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(vp);
try {
Class res = R.drawable.class;
Field field = res.getField(device_types.get(i));
int resId = field.getInt(null);
imageView.setImageResource(resId);
}
catch (Exception e) {
Log.e("MyTag", "Failure to get drawable id.", e);
}
LinearLayout link_devices = (LinearLayout) findViewById(R.id.link_devices);
link_devices.addView(imageView);
}
And i am trying to get the coordinates of the imageview:
I have tried the following methods, but am getting no where
imageView.getDrawable().getBounds().centerX() /centreY
imageView.getX() / getY
imageView.getLocationInWindow(); / onScreen();
Could someone please explain to me how to get the cooridinates of the image which is a drawable so i can draw a box around it or draw lines between two images (that is the planned functionality)
int top = imageView.getTop();
int left = imageView.getLeft();
From there you have the top-left point coordinates.
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
}
}
}
I want to add buttons programmatically on the screen and I am getting the value by parsing an API and now I want to display the buttons according to the length of an array. I am doing this but I am only getting the last button displayed, but inside the for loop I'm getting all values correct but displaying only the last button. This is my code:
RelativeLayout relate;
//...
relate = (RelativeLayout)findViewById(R.id.relative);
protected void onPostExecute(Void result) {
if(dialog.isShowing() == true) {
dialog.dismiss();
}
//int width = 100, height =50, x = 10, y = 20;
for (int i =0;i<adapt_obj.city_name_array.length;i++){
b1 = new Button(myref);
b1.setText(adapt_obj.city_name_array[i]);
relate.addView(b1);
//relate.addView(b1, i, new RelativeLayout.LayoutParams(width,height));
//height = height+80;
}
listlocation.setAdapter(adapt_obj);
adapt_obj.notifyDataSetChanged();
}
A RelativeLayout will stack the views you add to it at the top-let corner if you don't specify some placement rules. Your buttons are added to the layout but they are placed one on top of each other and so the only visible is the last one you add. Here are some modification of your for loop:
RelativeLayout relate; relate = (RelativeLayout)findViewById(R.id.relative);
for (int i = 0; i < adapt_obj.city_name_array.length; i++){
Button b1 = new Button(myref);
b1.setId(100 + i);
b1.setText(adapt_obj.city_name_array[i]);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (i > 0) {
lp.addRule(RelativeLayout.BELOW, b1.getId() - 1);
}
b1.setLayoutParams(lp);
relate.addView(b1);
}
You mustn't give x and y values in Android.you can add buttom top left right of an item. Also layout parameters you should use wrap_content or fill_parent.
Button button = new Button(this);
button.setText(#"text");
button.setLayoutParams(new LayoutParams(WRAP_CONTENT,WRAP_CONTENT));
layout.addView(button);
I think the problem is with the relative layout. Your buttons might be getting stack on top of each other. Try making the parent a linear layout.