At a point in my app I'm cycling through the objects on screen and checking if they're the last object in that line. To do that, I'm checking the Align Parent End property (which is checked for the last widget in each line). Here's part of my activity xml:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/relativeLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Average Temperature:"
android:id="#+id/AverageTemperatureText"
android:layout_below="#+id/TemperaturesText"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="#+id/AvgTempNum"
android:layout_alignBottom="#+id/AverageTemperatureText"
android:layout_toRightOf="#+id/AverageTemperatureText"
android:layout_below="#+id/TempMiddle"
android:gravity="center|bottom"
android:editable="false"
android:text="0.0"
android:maxLength="7"
android:nextFocusForward="#+id/TotalObservedVolumer"
android:layout_alignRight="#+id/FreeWaterVolume"
android:layout_alignEnd="#+id/FreeWaterVolume"
android:layout_alignParentEnd="true" />
^^^^ Right here. It's true.
You get the idea. Then, in my code, I'm cycling through the relative layout children, then for each view on it, checking it for that particular property. If it's true, I'm supposed to do something. But with the code I have, it's always false. So what am I doing wrong? Here it is:
RelativeLayout relLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
for(int i = 0; i < relLayout.getChildCount(); i++) {
View view = relLayout.getChildAt(i);
// Assess if it's the last field in that line
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
int[] rules = params.getRules();
if (rules[RelativeLayout.ALIGN_PARENT_END] == RelativeLayout.TRUE) {
// my code
}
}
Thanks!!
Fixed, but in a curious way.
In my case, all the last fields on screen had Align Parent End flagged. So I found online this would be the way to do it:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
int[] rules = params.getRules();
if ((rules[RelativeLayout.ALIGN_PARENT_END] == RelativeLayout.TRUE)) {
// Do something about it
}
Where RelativeLayout.ALIGN_PARENT_END stands for 21, according to http://developer.android.com/reference/android/widget/RelativeLayout.html
However, my code was returning false for that check. So I started displaying the value for the property, and it really was false. I tried this for a while, then I saw one of my edittexts returned true for it. But the activity xml showed this
android:layout_alignParentRight="true"
in addition to alignParentEnd. Which didn't make sense, since alignParentRight is supposed to be rule 11, according to the same link above.
So I devised this code to expose which rules where true, and put it in my code:
for(int x = 0; x < 22; x++) {
if (rules[x] == RelativeLayout.TRUE) Log.i("Property true:", String.valueOf(x));
}
Which gave me 11 for all fields, and 11 and 21 for the field that responded TRUE before. Remember, all fields had android:layout_alignParentEnd="true" in the activity xml.
So to fix my code, I'm now checking rules[RelativeLayout.ALIGN_PARENT_RIGHT] and it works, but I strongly believe those two properties are switched somehow. Can anybody confirm? Or please englighten me on where I screwed up, which is more likely :)
Related
In my Android Application I want to create a bunch of ConstraintLayouts where each has for example an ImageView and a TextView in them.
Everything is created from code exept for one TableRow with which I will start out. My Problem ist that despite being able to set the margins via LayoutParams for the ConstraintLayout, the ImageView and the TextView do not change at all when I try to set the margins.
public fun generateAsset(tableRow: TableRow) {
// <android.support.constraint.ConstraintLayout
// android:layout_margin="4dp"
// android:background="#drawable/x32_tile_dark">
val clayout = ConstraintLayout(context)
tableRow.addView(clayout)
val cparams = clayout.layoutParams as TableRow.LayoutParams
cparams.setMargins(4, 4, 4, 4) // <--- Works correctly!
clayout.layoutParams = cparams
clayout.setBackgroundResource(R.drawable.x32_tile_dark)
// <ImageView
// android:layout_width="0dp"
// android:layout_height="60dp"
// android:layout_marginTop="8dp"
// app:srcCompat="#drawable/x256_spaceship_door_gray_blue" />
val iView = ImageView(context)
clayout.addView(iView)
val iparams = iView.layoutParams as ConstraintLayout.LayoutParams
iparams.width = ConstraintLayout.LayoutParams.MATCH_PARENT
iparams.height = 200
iparams.setMargins(8, 8, 8, 8) // <--- Does not do anything!
iView.layoutParams = iparams
iView.setImageResource(R.drawable.x256_spaceship_door_gray_blue)
// <TextView
// android:text="Massive Spaceship Door"
// android:layout_marginLeft="8dp"
// android:layout_marginRight="8dp"
// android:layout_marginBottom="4dp"
// android:textColor="#android:color/white" />
val ntext = TextView(context)
clayout.addView(ntext)
val nparams = ntext.layoutParams as ConstraintLayout.LayoutParams
nparams.setMargins(8, 8, 8, 8) // <--- Does not do anything!
ntext.layoutParams = nparams
ntext.setTextColor(Color.WHITE)
ntext.text = assetName
}
The reason why this doesn't work is probably because I haven't set the constraints of the ImageView and the TextView but how do I do that in code?
The ImageView should be right in the center of the ConstraintLayout while the TextView is supposed to be in the center but at the bottom.
How can I set the constraints to do what I've described above?
You are right to suspect that not setting the constraints may be the issue. To set constraints programmatically take a look at ConstraintSet.
This class allows you to define programmatically a set of constraints to be used with ConstraintLayout. It lets you create and save constraints, and apply them to an existing ConstraintLayout.
As an added bonus, ConstraintSet permits the setting of margins while setting constraints.
There are some good tutorials online about how to use ConstraintSet.
Not 100% sure here, but i have a suggestion and believe you have to call
iView .updateMargins<ConstraintLayout.LayoutParama>{//do some stuff here}.
You probably ask "Why?".
Well, i think its because you already created val clayout = ConstraintLayout(context) but didnt specify which attributesSet it has, thus taking the default one. And since your View already has defaultParams, they need to be updated now.
But please do some independet reasearch or ask a real expert with real knowledge. Because i am not really sure and this is just some suggestion.
Looks like ConstraintSet is finding hard to cope up with Start/End constrains.
This example is taken from Google samples.
Github: android-ConstraintLayoutExamples
When you replace Left & Right constrains with Start & End, ConstraintSet - not working properly, It's working with Left/Right constrains only.
For example replace
layout_constraintStart_toStartOf with layout_constraintLeft_toLeftOf & replace
layout_constraintEnd_toEndOf with layout_constraintRight_toRightOf
in following files:
constraintset_example_main.xml
constraintset_example_big.xml
Behaviour:
onClick of image:
private ConstraintSet mConstraintSetNormal = new ConstraintSet();
private ConstraintSet mConstraintSetBig = new ConstraintSet();
public void toggleMode(View v) {
TransitionManager.beginDelayedTransition(mRootLayout);
mShowBigImage = !mShowBigImage;
applyConfig();
}
private void applyConfig() {
if (mShowBigImage) {
mConstraintSetBig.applyTo(mRootLayout);
} else {
mConstraintSetNormal.applyTo(mRootLayout);
}
}
By default Android studio uses start/ end constrains hence it's I want to know root cause and possible fix.
Or Is this a bug with ConstrainSet itself?
This does look like a problem with ConstraintSet, but let's see. The following analysis is based upon the sample project with the link that you supplied.
In the sample project, I have updated ConstraintLayout to the most recent version:
compile 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
I did this in case we are trying to track down an issue that has already been addressed. I also updated the layout constraintset_example_big and replaced all left/right constraints with start/end constraints as follows:
constraintset_example_big.xml
<android.support.constraint.ConstraintLayout
android:id="#+id/activity_constraintset_example"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:onClick="toggleMode"
android:scaleType="centerCrop"
android:src="#drawable/lake"
app:layout_constraintDimensionRatio="h,16:9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="#string/lake_tahoe_image" />
<TextView
android:id="#+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/lake_tahoe_title"
android:textSize="30sp"
app:layout_constraintStart_toStartOf="#+id/imageView"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/imageView" />
<TextView
android:id="#+id/textView11"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="#string/lake_discription"
app:layout_constraintStart_toStartOf="#+id/textView9"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/textView9"
app:layout_constraintEnd_toEndOf="#+id/imageView"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintVertical_bias="0.0" />
</android.support.constraint.ConstraintLayout>
With these changes in place, this is what we see.
This is clearly not right. It is supposed to look like this after the transition.
After some debugging, I tracked the issue down to this line in ConstraintSetExampleActivity.java:
mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintSet#load() seems to be straightforward, but if we replace the code above with an explicit inflation of the layout followed by a clone of the ConstraintSet on the inflated layout as follows:
// mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintLayout cl = (ConstraintLayout) getLayoutInflater().inflate(R.layout.constraintset_example_big,null);
mConstraintSetBig.clone(cl);
We see this behavior in the app which is much better.
So my takeaway is that ConstraintSet#load() has a problem with start/end constraints. The workaround is to inflate the ConstraintLayout then do a clone.
ConstraintSetExampleActivity#onCreate()
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraintset_example_main);
mRootLayout = (ConstraintLayout) findViewById(R.id.activity_constraintset_example);
// Note that this can also be achieved by calling
// `mConstraintSetNormal.load(this, R.layout.constraintset_example_main);`
// Since we already have an inflated ConstraintLayout in `mRootLayout`, clone() is
// faster and considered the best practice.
mConstraintSetNormal.clone(mRootLayout);
// Load the constraints from the layout where ImageView is enlarged.
// Toggle the comment status on the following three lines to fix/break.
// mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintLayout cl = (ConstraintLayout) getLayoutInflater().inflate(R.layout.constraintset_example_big,null);
mConstraintSetBig.clone(cl);
if (savedInstanceState != null) {
boolean previous = savedInstanceState.getBoolean(SHOW_BIG_IMAGE);
if (previous != mShowBigImage) {
mShowBigImage = previous;
applyConfig();
}
}
}
This issue is known and will be fixed in the 1.1 beta 6 release
https://issuetracker.google.com/issues/74253269
For those who faces issues like constraint set clone not working properly, my layout was not updating to new constraints when i called clone and applyTo methods after an api call, turns out it was due to a loading dialog i showed before the change that caused the error.
I have an int that depends on users input. How do I display that int on the screen?
Here is what I have been attempting:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#int/HW" // What goes here, This is where i am confused
tools:context=".DisplayMessageActivity" />
Also here are my defined variables, you may have seen my post not too long ago about this program:
// Receive messages from options page
Intent intent = getIntent();
int HW = intent.getIntExtra("MESSAGE_HW", 0);
int OTW = intent.getIntExtra("MESSAGE_OTW", 0);
int HPD = intent.getIntExtra("MESSAGE_HPD", 0);
I am trying to display the int HW on the screen, any help is greatly appreciated! Thank you!
android:text="#int/HW" // What goes here, This is where i am confused
A default value can go there. Something that would tell you that the TextView has not been populated yet.
How do i display that int on the screen?
To display the int on the screen inside the TextView you have shown us. You need to get the TextView in your Activity with something similar to the following:
TextView textView = (TextView) this.findViewById(R.id.textViewId);
Then you can set the text on that textView with the following:
textView.setText(String.valueOf(HW));
Here is an example of adding the id to your xml:
<TextView
android:id="#+id/textViewId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#int/HW" // What goes here, This is where i am confused
tools:context=".DisplayMessageActivity" />
When you get the users input, do this:
TextView hwTextView = (TextView)findViewById("R.id.hwTextView")
hwTextView.setText(String.valueOf(yourIntVariable));
Note this means you'll have to add an id attribute to your TextView with the value of "hwTextView"
Building on prolink007's answer, you could also omit that line then call setText, java side.
<TextView
android:id="#+id/hw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
tools:context=".DisplayMessageActivity" />
TextView HWgetText = (TextView)findViewById("R.id.hw");
HWgetText.setText(String.valueOf(HW));
Found a Solution!
I now use a ViewPager instead of a ViewFlipper.
The Views are now generated within my run() method (which is already there because I fetch data from the web) and saveed in a Map.
In my Handler I only call pagerAdapter.notifyDataSetChanged() the pagerAdapter uses the Map of views and it works smooth and fast.
So I'm now looking for a away to have the ViewPager scroll endless, but thats another problem not connected to this one ;)
Thank all of you for your answers and keep up the good support.
I'm quite new to Android development and facing a problem while inflating a (huge) layout.
I getting some Data from a Webservice which works fine then i'm using a handler within my Activity to bring this data to the frontend. Here is my handleMessage:
public void handleMessage(Message msg) {
LayoutInflater inflater = getLayoutInflater();
List<Integer> gamedays = new ArrayList<Integer>(games.keySet());
Collections.sort(gamedays);
for (Integer gameday : gamedays) {
View gamedaytable = inflater.inflate(R.layout.gamedaytable, null);
TableLayout table = (TableLayout) gamedaytable.findViewById(R.id.gameDayTable);
table.removeAllViews();
List<Game> gamelist = games.get(gameday);
int rowcount = 2;
for (Game game : gamelist) {
View tableRow = inflater.inflate(R.layout.gamedayrow, null);
TextView homeTeam = (TextView) tableRow.findViewById(R.id.gameDayHome);
TextView awayTeam = (TextView) tableRow.findViewById(R.id.gameDayAway);
TextView gameResult = (TextView) tableRow.findViewById(R.id.gameDayResult);
gameResult.setBackgroundResource(R.drawable.resultbackground);
homeTeam.setText(game.getHomeTeam().getName());
awayTeam.setText(game.getAwayTeam().getName());
if (game.getHomegoals() < 0 || game.getAwaygoals() < 0) {
gameResult.setText("-:-");
} else {
gameResult.setText(game.getHomegoals() + ":" + game.getAwaygoals());
}
if (rowcount % 2 == 0) {
tableRow.setBackgroundColor(0xffdee0dd);
} else {
// setting alternative background
tableRow.setBackgroundColor(0xfff1f3f0);
}
rowcount++;
table.addView(tableRow);
}
flipper.addView(gamedaytable);
}
flipper.setDisplayedChild(thisgameday - 1);
pd.dismiss();
}
My Problem is that this code runs quite slow and d the processdialog freezes for about 1 second before it disappears and the layout is shown.
games consists of 34 entries which contains 9 entries by itself.
So I'm adding 34 Views consisting of a relativeLayout () which holds the table
I think the problem is, that android starts to draw and calculte the layout and this takes too long.
If I'm correct i can not use AsynTask because i can not do UI stuff there and im doing UI stuff only.
I looking for a way to have the process dialog not freezing while doing this.
Or maybe I'm doing some completly wrong
R.layout.gamedaytable:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff1f3f0"
android:focusableInTouchMode="false" >
<TableLayout
android:id="#+id/gameDayTable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:focusableInTouchMode="false" >
</TableLayout>
</RelativeLayout>
R.layout.gamedayrow:
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tableRow1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:focusableInTouchMode="false"
android:paddingBottom="5dp"
android:paddingTop="5dp" >
<TextView
android:id="#+id/gameDayHome"
style="#style/textsizeSmallScreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Mannschaft 1" />
<TextView
android:id="#+id/textView2"
style="#style/textsizeSmallScreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text=":" />
<TextView
android:id="#+id/gameDayAway"
style="#style/textsizeSmallScreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mannschaft 2" />
<TextView
android:id="#+id/gameDayResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:background="#drawable/resultbackground"
android:paddingLeft="10dip"
android:text="0:5"
android:textColor="#FFFFFF"
android:textSize="11dp"
android:textStyle="bold"
android:typeface="monospace" />
</TableRow>
Additional Info:
This is how the Table should look like.
So i'm not sure if this should really be a ListView because for me its tabledata ;)
table
You seem to be building a list, you should probably look at using a ListView, which'll have the advantages of only needing to build the UI for the number of rows currently being shown, and to also do view re-use, so that you don't need to inflate as many rows.
Found a Solution!
I now use a ViewPager instead of a ViewFlipper. The Views are now generated within my run() method (which is already there because I fetch data from the web) and saveed in a Map. In my Handler I only call pagerAdapter.notifyDataSetChanged() the pagerAdapter uses the Map of views and it works smooth and fast. So I'm now looking for a away to have the ViewPager scroll endless, but thats another problem not connected to this one ;)
Thank all of you for your answers and keep up the good support.
It is better to go for Listview. Even we can add more than one design of rows in the listview in an optimized manner which will improves the performance better.
You definitely can do this on an AsyncTask. While you cannot update the UI on the doInBackground method of an AsyncTask, you can from the onProgressUpdate.
I would break up the code so you are iterating through items while in doInBackground, call publishProgress for each item, and then do the UI updates for the item when you get a callback in onProgressUpdate.
I have a bug with my activity.
I have three view stubs in my linear layout like so -
<ViewStub
android:id="#+id/index_1"
android:layout="#layout/index_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ViewStub
android:id="#+id/index_2"
android:layout="#layout/index_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ViewStub
android:id="#+id/index_3"
android:layout="#layout/index_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
my onCreate conditionally checks what to inflate:
for (int i = 0; i < 3; i++) {
int id = convertIndexToId(i); //will turn i into R.id.index_1
ViewStub stub = findViewById(id);
if (bShouldBeSpinner) {
stub.setLayoutResource(R.layout.index_spinner);
View root = stub.inflate();
Spinner spin = (Spinner)root.findViewById(R.id.my_spinner);
spinner.setAdapter(adapter);
spinner.setSelection(0);
}
else {
stub.setLayoutResource(R.layout.index_edittext);
View root = stub.inflate();
EditText et = (EditText)root.findViewById(R.id.my_edittext);
//et.phoneHome(); just kidding
et.setText(String.valueOf(System.currentTimeMillis()));
}
}
I force bShouldBeSpinner to false. The output of the edittext's is as follows:
1300373517172
1300373517192
1300373517221
However, when I rotate the screen and onCreate is called a second time the output is this:
1300373517221
1300373517221
1300373517221
Initially that made me think you should only inflate the view once, and the heirarchy is kept inbetween onCreate's... however when i only run it the first time the second time no views are shown for the stubs.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner style="#style/SearchInput" android:id="#+id/my_spinner" />
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<EditText style="#style/SearchInput" android:id="#+id/my_edittext" />
</LinearLayout>
I feel the documentation is assuming something that I did not notice or am missing. Does anyone see what I am doing wrong?
EDIT
I added to the view stubs android:inflatedId="index_1_root"... etc
it is the strangest thing, when I add these lines after the for loop:
EditText v = indexRoot1.findViewById(R.id.index_edit_text);
Log.d(TAG, "EditTExt: " + v);
EditText v2 = indexRoot2.findViewById(R.id.index_edit_text);
Log.d(TAG, "EditTExt: " + v2);
the output says (I believe) they are references to different EditTexts.
EditTExt: android.widget.EditText#47210fe8
EditTExt: android.widget.EditText#47212ba8
So they are getting inflated again, but the text is set to what the last edittext was set to on the first pass.
There may be some issues when recreating views of different types with the same id.
ViewStub is replaced by inflated view.
I suggest using
setInflatedId(int inflatedId)
to distinguish inflated views.
Hope that help.
Instead of using ViewStubs, I added an id to the root of those stubs (android:id="index_roots") and used
view.addView( (isSpinner) ?
new Spinner(this) : new EditText(this) );
to fix this problem, I will however not accept this answer right away, I'll allow others to answer using the method I was going for.