I create layout (ContentLayout) with wrap_content.
And I want to show loading layout (LoadingLayout) same size as ContentLayout above it.
Something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout.
android:id="#+id/ContentLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
...
</LinearLayout>
<RelativeLayout
android:id="#+id/LoadingLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
...
</RelativeLayout>
</RelativeLayout>
But when I want set LoadingLayout size in onMeasure, it will be set to right size only in second chance.
Works only something like this:
Activity
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) loadingLayout.getLayoutParams();
params.height = getMeasuredHeight();
loadingLayout.setLayoutParams(params);
super.onMeasure(widthMeasureSpec, heightMeasureSpec); // android realignment hack
}
I think this is a hack. Do you have a better solution?
If I understood correctly, you need LoadingLayout to keep width of ContentLayout
<RelativeLayout
android:id="#+id/LoadingLayout"
android:layout_alignLeft="#id/ContentLayout"
android:layout_alignRight="#id/ContentLayout"
....>
....
</RelativeLayout>
Edit: Below is one way for the height
You can add a one time listener when global refresh happens. Then all sizes will be available.
// in your activity
#Override
protected void onCreate(Bundle state) {
final View loading = findViewById(R.id.LoadingLayout);
final View content = findViewById(R.id.ContentLayout);
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
loading.getLayoutParams().height = content.getHeight();
loading.onRequestLayout();
loading.invalidate();
}
}
}
Related
With the following Java and XML code, I was wondering how to retrieve the dimensions in pixels of an inner/child layout (LinearLayout, whereas the parent is RelativeLayout with paddings of 20dp) properly:
Java code:
public class TestActivity extends Activity {
private LinearLayout linLay;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
linLay = (LinearLayout)findViewById(R.id.linLay);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
linLay.setLayoutParams(lp);
getDimensions();
}
private void getDimensions() {
System.out.println(linLay.getHeight());
System.out.println(linLay.getWidth());
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp" >
<LinearLayout
android:id="#+id/linLay"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true">
</LinearLayout>
</RelativeLayout>
... Since I get outputs of 0 for the dimensions, I am aware that the LinearLayout needs to be set first on the screen before I could retrieve the dimensions... But what am I doing wrong here?
It didn't get its dimensions at the time you try to output them.You can just add a delay or just use method below.
private void getDimensions() {
ViewTreeObserver vto = linLay.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
#Override
public voidonGlobalLayout() {
linLay.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height =linLay.getMeasuredHeight();
int width =linLay.getMeasuredWidth();
System.out.println(height);
System.out.println(width);
}
});
}
I have a fixed size custom view as follow.
RateAppBanner.java
public class RateAppBanner extends LinearLayout {
public RateAppBanner(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.rate_app_banner, this, true);
this.setBackgroundColor(Color.RED);
View view = this.findViewById(R.id.textView1);
if (view == null) {
Log.i("CHEOK", "WTF!");
} else {
// Reach here!
Log.i("CHEOK", "---> " + ((TextView)view).getText());
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = 320;
int h = 100;
setMeasuredDimension(w, h);
}
}
rate_app_banner.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffff0000"
android:text="Hello World" />
</merge>
However, I realize the textView1 is not visible on screen, even thought I can find it through findViewById.
I use the dump View Hierarchy through Eclipse. Here's what I get.
This puzzles me a lot. I can't see textView1 in the view hierarchy, even I can discover it through findViewById. Do you have any idea why textView1 is not visible on screen?
This is how I add RateAppBanner into my main activity.
MainActivity.java
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainView = (LinearLayout)this.findViewById(R.id.screen_main);
mainView.addView(new RateAppBanner(this));
}
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/screen_main">
</LinearLayout>
</FrameLayout>
Complete source code
The complete workable souce code, which compiled using Eclipse can be downloaded from abc.zip
p/s The code example shown in rate_app_banner.xml is a stripped down version of complex UI layout. To narrow down the problem scope, I make the layout file contain only single TextView.
Another approach
Instead of using merge, I had tried
rate_app_banner.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffff0000"
android:text="Hello World" />
</LinearLayout>
It doesn't work still...
I tested using your source abc.zip. Please change the red color of text view to other. then add
onMeasure(w, h);
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = 320;
int h = 200;
super.onMeasure(w, h);
setMeasuredDimension(w, h);
}
I have been created a button and use addView() to draw some chart in onClickListener(). But it will not extend the height of view automatically if the chart out of bound.So I want to add the height of view when all chart have been drawn. I try to use getLayoutParams().height to modify the height but it doesn't work.
public class MainActivity extends Activity {
private TableLayout layout;
private Button btnDraw;
public void onCreate(Bundle savedInstanceState){
layout=findViewById((TableLayout) findViewById(R.id.tableLayout));
btnDraw=findViewById((Button) findViewById(R.id.btnDraw));
btnDraw.setOnClickListener(new OnClickListener(){
public void onClick(View arg0) {
layout.addView(new Chart(MainActivity.this)
}
});
}
class Chart extends View {
public void onDraw(Canvas c) {
int addHeight=0;
for(...){ //draw several chart
...
addHeight+=500;
}
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) layout.getLayoutParams();
params.height += addHeight;
layout.setLayoutParams(params);
}
}
}
here is xml code
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<TableLayout
android:id="#+id/tableLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<Button
android:id="#+id/btnDraw"
android:layout_span="2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Draw" />
</TableRow>
...
</TableLayout>
</ScrollView>
I also try to call onMeasure(getWidth(),addHeight) outside the for loop but it still not work.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
}
Your Chart class should implement onMeasure() if you want to do it the eight way. If it is just a hack you can use AbsoluteLayout. You can also play with setting the layout params, but you should call updateViewLayout() and possibly even requestLayout().
What I want is to have a layout with 2 views arranged vertically. Let's call the top view A and the bottom one B. I want the amount of height given to B to be its normal height (i.e. wrap content) except that I don't want it to be given more than half of the available space. A gets what is left.
Another way to word it is that A should always get at least 50% of the available height and B should get at most 50%.
I can't seem to find an easy way to achieve that. I can set both layout heights to 0 and give them equal weights which makes them both 50% always, but if B is smaller than 50% it should be given only what it needs.
The only way I can see to do it is use a custom class for A or B and override onMeasure to constrain the height to 50% of the parent, but it seems there should be an easier way.
Ok, I got it now. If I understood correctly you want to have it like this:
if A > B -> do nothing
if B > A & B > parent layout -> 50% to both of them
if B > A & B < parent layout -> A = parent layout - B
I had to do it all in onWindowFocusChanged because otherwise in onCreate the height of the Views would return 0. I did it with 2 LinearLayouts as child layouts, but you can take what ever you want.
My XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/parent_lay"
android:orientation="vertical"
android:layout_height="match_parent" >
//Layout A:
<LinearLayout
android:id="#+id/lay_1"
android:layout_width="match_parent"
android:background="#android:color/background_dark"
android:layout_height="10dp" >
</LinearLayout>
//Layout B:
<LinearLayout
android:id="#+id/lay_2"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#123456" >
</LinearLayout>
</LinearLayout>
MainActivity:
public class MainActivity extends Activity {
LinearLayout parent_lay;
LinearLayout lay_1;
LinearLayout lay_2;
int parent_height;
int lay_1_height;
int lay_2_heigth;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
parent_lay = (LinearLayout) findViewById(R.id.parent_lay);
lay_1 = (LinearLayout) findViewById(R.id.lay_1);
lay_2 = (LinearLayout) findViewById(R.id.lay_2);
lay_1_height = lay_1.getHeight();
lay_2_heigth = lay_2.getHeight();
parent_height = parent_lay.getHeight();
if (lay_2.getHeight() > lay_1.getHeight()
&& lay_2.getHeight() > (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
lay_2.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
} else if (lay_2.getHeight() < (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, (parent_height - lay_2_heigth)));
}
}
}
Example:
If A is 60dp and B is 40dp:
If A is 60dp and B is 400dp:
You must write your own component to achieve this.
For example, if you use LinearLayout here, you can extends a LinearLayout with overdid onMeasure method. You can implement onMeasure like this:
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
setMeasuredDimension(width, height / 2);
}
This code is not elegant enough. If you really want to do it well, copy the original onMeasure method from Android source code (http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/LinearLayout.java#LinearLayout.onMeasure%28int%2Cint%29), and in measureVertical(int widthMeasureSpec, int heightMeasureSpec), set mTotalLength = mTotalLength / 2.
For detailed information of onMeasure, visit http://developer.android.com/guide/topics/ui/custom-components.html and http://developer.android.com/reference/android/view/View.html#onMeasure(int, int).
Now the desired effect can be achieved with the ConstraintLayout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/containerFrameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<FrameLayout
android:id="#+id/containerFrameLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guideline"
app:layout_constraintVertical_bias="1">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
Create a linear layout with two inner frames, each with .5 weight. Inside those frames, place your views, setting them to wrap_content or match_parent as appropriate.
I see some questions about "ImageView"s, but I need a FrameLayout.
I have a FrameLayout, which I need its width:height = 2:1, and the width is fill_parent. There are many image views inside the frame layout, I can just set them width=fill_parent and height=fill_parent.
I don't find a way to do this, please help .
My xml code is:
<FrameLayout
android:id="#+id/cardView"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_centerInParent="true">
<ImageView
android:id="#+id/frameView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitXY"
android:src="#drawable/card_style1"
/>
</FrameLayout>
See if this is what you want:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
final FrameLayout target = (FrameLayout) findViewById(R.id.frame_id);
target.post(new Runnable() {
#Override
public void run() {
int width = target.getWidth();
int height = width / 2;
LayoutParams lp = target.getLayoutParams();
lp.height = height;
target.setLayoutParams(lp);
}
});
}