I have an imageView that I want to move given a specific situation.
Initially I have a Relative layout with two textViews and an imageview. The textViews are oriented with one above the other. The imageView is set
android:layout_below="#id/text_view1"
android:layout_toEndOf="#+id/text_view2".
In the logic text_view2 is removed when a specific condition is met. I want to programmatically move the imageView to the end of text_view1 when this condition is met. Essentially when text_view2 is removed, I want to set the imageView to
android:layout_toEndOf="#+id/text_view1"
I don't believe setting X,Y,Z values is appropriate here because programmatically, I don't know where the imageView will show up given different screen sizes, and densities. I just need it to move to the end of the first textView.
Take a look at RelativeLayout.LayoutParams. You will need to manipulate the layout rules in the layout params as follows:
// Make textView2 invisible
tv2.visibility = View.INVISIBLE
// Get the LayoutParams of the ImageView
val ivParams = iv.layoutParams as RelativeLayout.LayoutParams
// Change the rule to be to the right of textView1
ivParams.addRule(RelativeLayout.RIGHT_OF, tv1.id)
// Since the placement of textView2 is changing, request a layout.
iv.requestLayout()
Consider using "END_OF" instead of "RIGHT_OF".
You can either place the Views in a nested LinearLayout or use a ConstraintLayout with a Barrier.
It is generally recommended to use ConstraintLayout because nested LinearLayouts are bad for performance but since ConstraintLayout takes some getting used to, I did not want to omit the other option.
To demonstrate the two approaches I've set up a small example with a LinearLayout and a ConstraintLayout in the same screen:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.TabTwoFragment">
<LinearLayout
android:layout_width="240dp"
android:layout_height="128dp"
android:background="#cccccc">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#44ff0000"
android:maxWidth="160dp"
android:text="Upper TextView\nin\n nested\n LinearLayout" />
<TextView
android:id="#+id/longTextViewInLinearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#4400ff00"
android:maxWidth="160dp"
android:text="Lower TextView in nested LinearLayout" />
</LinearLayout>
<ImageView
android:id="#+id/linearLayoutImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="#ffffff"
android:src="#drawable/ic_android_black_24dp"
android:tint="#color/colorPrimary" />
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="240dp"
android:layout_height="128dp"
android:layout_gravity="bottom|end"
android:background="#666666">
<TextView
android:id="#+id/shortTextViewInConstraintLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#44ff0000"
android:maxWidth="160dp"
android:text="Upper TextView\nin\n nested\nConstraintLayout"
android:textColor="#ffffff"
app:layout_constraintBottom_toTopOf="#+id/longTextViewInConstraintLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="#+id/longTextViewInConstraintLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#4400ff00"
android:maxWidth="160dp"
android:text="Lower TextView in ConstraintLayout"
android:textColor="#ffffff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shortTextViewInConstraintLayout" />
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="shortTextViewInConstraintLayout, longTextViewInConstraintLayout" />
<ImageView
android:id="#+id/constraintLayoutImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="#ffffff"
android:importantForAccessibility="no"
android:src="#drawable/ic_android_black_24dp"
android:tint="#color/colorAccent"
app:layout_constraintStart_toEndOf="#+id/barrier"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
If the ImageView is clicked, the longer TextView will disappear and the ImageView will move closer to the short TextView. The animations are provided by Android's transition framework, so basically all you have to do is trigger the transition by calling TransitionManager.beginDelayedTransition()
For demonstration purposes, I've placed all the code in one method. Please note that normally one would have the TransitionSet as field of the Fragment so that it does not have to be recreated every time you need it. (The code is in Java since Android Studio supports automatic translation to Kotlin if required but not the other way round ;-) )
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TransitionSet ts = new TransitionSet();
ts.addTransition( new ChangeBounds());
ts.addTransition(new Slide());
View imageViewInLinearLayout = view.findViewById(R.id.linearLayoutImageView);
imageViewInLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TransitionManager.beginDelayedTransition((ViewGroup)getView(), ts);
view.findViewById(R.id.longTextViewInLinearLayout).setVisibility(GONE);
}
});
View imageViewInConstraintLayout = view.findViewById(R.id.constraintLayoutImageView);
imageViewInConstraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TransitionManager.beginDelayedTransition((ViewGroup)getView(), ts);
view.findViewById(R.id.longTextViewInConstraintLayout).setVisibility(GONE);
}
});
}
Related
I have the following layout:
<android.support.constraint.ConstraintLayout
android:id="#+id/target"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="#FF0000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="Hello"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/left_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LEFT VIEW"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/right_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RIGHT VIEW"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<Button
android:id="#+id/left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="LEFT"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/right_btn"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/target" />
<Button
android:id="#+id/right_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="RIGHT"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/left_btn"
app:layout_constraintTop_toBottomOf="#+id/target" />
Which result in this:
This is the fragment:
public class IncreaseWidthLeftRightFragment extends Fragment {
private IncreaseWidthLeftRightViewModel mViewModel;
private TextView mRightView;
private Button mLeftBtn;
private Button mRightBtn;
private TextView mLeftView;
private ConstraintLayout mTarget;
public static IncreaseWidthLeftRightFragment newInstance() {
return new IncreaseWidthLeftRightFragment();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.increase_width_left_right_fragment, container, false);
mRightView = v.findViewById(R.id.right_view);
mLeftBtn = v.findViewById(R.id.left_btn);
mLeftBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showLeftText();
}
});
mRightBtn = v.findViewById(R.id.right_btn);
mRightBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showRightText();
}
});
mLeftView = v.findViewById(R.id.left_view);
mTarget = v.findViewById(R.id.target);
return v;
}
private void showLeftText() {
// increase the left side of the target container
// ...
mLeftView.setVisibility(View.VISIBLE);
}
private void showRightText() {
// increase the right side of the target container
// ...
mRightView.setVisibility(View.VISIBLE);
}
}
The left_view and right_view TextViews are initially set to visibility GONE. The left button must show the left_view while expanding the left side but the right side should be kept in the same place. Similar for the right side but in the opposite direction.
How could I achieve this? I tried to play with the LayoutParams but without success. I would like to do this with an animation, but that will be the next step.
UPDATE:
Just to be clear, for instance, if I click on the left button, this should be the end result:
As you can see, the right side of the red rectangle is in the same X coordinate, however, the width of the rectangle increase to the left.
If you need your target view be anchored with the text views, they should not be inside of it (supposing you want to use the ConstraintLayout). The text views themselves also should have some anchor on the layout, so they can expand related to it position.
1) Add guidelines
For this purpose you can use constraint guidelines. E.g. if you want the text views (and consequently target view) expand from 32% from left and right edges of the root screen, you can add guidelines as follow (they should be at the same level of hierarchy with your buttons/target view):
<android.support.constraint.Guideline
android:id="#+id/left_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.32" />
<android.support.constraint.Guideline
android:id="#+id/right_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.68" />
P.S. the layout_constraintGuide_percent always calculates from left side of a view the guide is inside of
2) Align your text views
As I said above, the text views should be at the same level with the target view, so grab them from inside the target view and put somewhere in the root constraint layout such that the right view is to right of the right guideline and left view is to left of the left one:
<TextView
android:id="#+id/left_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="LEFT VIEW"
app:layout_constraintEnd_toStartOf="#+id/left_guideline" />
<TextView
android:id="#+id/right_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RIGHT VIEW"
app:layout_constraintStart_toStartOf="#+id/right_guideline" />
We also need set vertical constraint for the views, and make a feeling that they are inside of the target view. In order to do that we need to align top side of both text views with the top side of the target view. In addition to that, the text views initial state should be "folded" (so they are hidden), unfortunately i don't know how to make them of width 0dp, since such value for layout_width or layout_height makes a constraint layout think that the view just comply constraints instead of using it's own size. As a quick workaround let's set width to 1px. To prevent the text views from extending vertically, i also would like to propose set singleLine property for them to true.
<TextView
android:id="#+id/left_view"
android:singleLine="true"
android:layout_width="1px"
android:layout_height="wrap_content"
android:text="LEFT VIEW"
app:layout_constraintEnd_toStartOf="#+id/left_guideline"
app:layout_constraintTop_toTopOf="#+id/target" />
<TextView
android:id="#+id/right_view"
android:singleLine="true"
android:layout_width="1px"
android:layout_height="wrap_content"
android:text="RIGHT VIEW"
app:layout_constraintStart_toStartOf="#+id/right_guideline"
app:layout_constraintTop_toTopOf="#+id/target" />
3) Align the target view
Now just align your target view left and right sides with left and right side of left and right text view respectively (so it's aligned with the outer boundaries of both text views) and set the layout_width attribute to 0dp, that will make it follow constraints instead of plain values.
<android.support.constraint.ConstraintLayout
android:id="#+id/target"
android:layout_width="0dp"
android:layout_height="200dp"
android:background="#FF0000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/right_view"
app:layout_constraintStart_toStartOf="#+id/left_view"
app:layout_constraintTop_toTopOf="parent">
4) Add root layout id
In order to get root layout for animation, add id for the root layout:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent">
After all steps your layout blueprint should look like this:
If you struggle at any step, feel free to use full layout gist from here.
5) Animate expanding
Eventually your show method should look something like that:
...
private void showLeftText() {
expandView(mLeftView);
}
private void showRightText() {
expandView(mRightView);
}
private void expandView(View view) {
TransitionManager.beginDelayedTransition(mRootView);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
view.setLayoutParams(layoutParams);
}
And here is a short demo:
I have a layout that animate as below
The txt_billion is shown dynamically, with android:animateLayoutChanges="true" (Layout code below).
Notice the Hundred is jumping (actually all are jumping, but the Hundred is just more obvious). How to prevent the text from jumping?
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:background="#9f9"
android:text="Hundreds" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:background="#f9f"
android:text="Thousands" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:background="#0ff"
android:text="Millions" />
<TextView
android:id="#+id/txt_billion"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="8dp"
android:visibility="gone"
android:background="#ff0"
android:text="Billions" />
</LinearLayout>
You could get the code from https://github.com/elye/issue_horizontal_layout_animate to test out
Try to use Support Transitions instead animateLayoutChanges
First, remove android:animateLayoutChanges="true" from your XML file
After, add compile 'com.android.support:transition:25.4.0' to your app dependencies.
Then, add this line before change visibility (TransitionManager from android.support.transition package)
TransitionManager.beginDelayedTransition(parentOfAnimatedView);
For your code
public void clickMe(View view) {
TransitionManager.beginDelayedTransition((ViewGroup) billionText.getParent());
if (billionText.getVisibility() == View.GONE) {
billionText.setVisibility(View.VISIBLE);
} else {
billionText.setVisibility(View.GONE);
}
}
The problem is that animateLayoutChanges only affects subclasses of ViewGroup. TextView can't react to layout change animations, so the text jumps. There are two ways to fix it:
1) Wrap each TextView in a FrameLayout and put the weight on the FrameLayout. You'll also have to add android:animateLayoutChanges="true" to each, as well as calling getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING) on each FrameLayout. This is kind of gross layout-wise, but it will allow you to keep using the transition animations.
2) Use a ValueAnimator and animate the weight of the (dis)appearing item. The animation may be a little choppier since it needs to lay out the LinearLayout on each frame, but it should still be passable. You'd also have to solve for text reflowing on the disappearing item, maybe by animating it fading out first and then animating the weight change.
I have android layout design in which I have an AppBar layout which will contain a Toolbar and one more LinearLayout design with circle Drawable as TextView. which is off same height. So when I try to get the Toolbar or AppBar height/width it's returning only 0.
activity_main.xml:
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#color/colorPrimary"
app:layout_scrollFlags="enterAlways" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_gravity="center_horizontal"
android:background="#color/colorPrimary"
android:orientation="horizontal"
app:layout_scrollFlags="enterAlways">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="2dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="#+id/marked_questions"
style="#style/textview_summary_omr_toolbar_count"
android:background="#drawable/bg_percentage_default"
android:text="18" />
<TextView
style="#style/textview_heading_summary_omr_toolbar"
android:text="Marked" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="2dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="#+id/correct"
style="#style/textview_summary_omr_toolbar_count"
android:background="#drawable/bg_percentage_6"
android:text="18"
/>
<TextView
style="#style/textview_heading_summary_omr_toolbar"
android:text="Correct" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="2dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="#+id/wrong"
style="#style/textview_summary_omr_toolbar_count"
android:background="#drawable/bg_percentage_wrong"
android:text="18" />
<TextView
style="#style/textview_heading_summary_omr_toolbar"
android:text="Wrong" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="2dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="#+id/conflicts"
style="#style/textview_summary_omr_toolbar_count"
android:background="#drawable/bg_percentage_3"
android:text="0" />
<TextView
style="#style/textview_heading_summary_omr_toolbar"
android:text="Conflicts" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="2dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
style="#style/textview_summary_omr_toolbar_count"
android:text="Score:"
android:textColor="#android:color/white" />
<TextView
android:id="#+id/marks_scored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:text="18/30"
android:textColor="#android:color/white"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
MainActivity.java:
Here I am initializing the Toolbar and AppBar and try to print the width and height of both in logs ,which is returning me zero.
public class MainActivity extends AppCompatActivity {
private Toolbar toolbar;
private AppBarLayout app_bar;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
app_bar = (AppBarLayout) findViewById(R.id.app_bar);
toolbar = (Toolbar) findViewById(R.id.toolbar);
logToolbarLayoutParams();
}
private void logToolbarLayoutParams() {
Log.d(TAG,"Toolbar width/Height"+this.toolbar.getWidth()+"/"+this.toolbar.getHeight());
Log.d(TAG,"App_bar width/height"+this.app_bar.getWidth()+"/"+this.app_bar.getHeight());
}
I have also referred to this question in the stackoverflow.
final AppBarLayout app_bar = (AppBarLayout) findViewById(R.id.app_bar);
ViewTreeObserver vto = app_bar.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
app_bar.getViewTreeObserver().removeGlobalOnLayoutListener(this);
Log.d(TAG, "Global layout");
logToolbarLayoutParams();
}
});
If I add the following code in my MainActivity it's getting me the proper height and width of Toolbar. From the stackoverflow question which I mentioned before, I came to know that, the views were not drawn on the screen, so I have to wait. But my doubt here is, the design which i have specified is not a complex one?
why its taking much time to draw on screen? am I missing something?
I have used Hierarchy Viewer to find out, what's making the child layout to load slowly. I found that I am using mulitple LinearLayout where single RelativeLayout can be used to obtain the same structure. I have changed my layout design as the following.
Still I am facing the same issue.
updated: activity_main.xml:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#color/colorPrimary"
app:layout_scrollFlags="enterAlways" />
<RelativeLayout
android:id="#+id/summary_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_gravity="center_horizontal"
android:background="#color/colorPrimary">
<TextView
android:id="#+id/marked_questions"
style="#style/textview_summary_omr_toolbar_count"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="#drawable/bg_percentage_default"
android:text="18" />
<TextView
android:id="#+id/marked_textview"
style="#style/textview_heading_summary_omr_toolbar"
android:layout_toEndOf="#+id/marked_questions"
android:layout_toRightOf="#+id/marked_questions"
android:text="Marked" />
<TextView
android:id="#+id/correct"
style="#style/textview_summary_omr_toolbar_count"
android:layout_toEndOf="#+id/marked_textview"
android:layout_toRightOf="#+id/marked_textview"
android:background="#drawable/bg_percentage_6"
android:text="18"
/>
<TextView
android:id="#+id/correct_textview"
style="#style/textview_heading_summary_omr_toolbar"
android:layout_toEndOf="#+id/correct"
android:layout_toRightOf="#+id/correct"
android:text="Correct" />
<TextView
android:id="#+id/wrong"
style="#style/textview_summary_omr_toolbar_count"
android:layout_toEndOf="#+id/correct_textview"
android:layout_toRightOf="#+id/correct_textview"
android:background="#drawable/bg_percentage_wrong"
android:text="18" />
<TextView
android:id="#+id/wrong_textview"
style="#style/textview_heading_summary_omr_toolbar"
android:layout_toEndOf="#+id/wrong"
android:layout_toRightOf="#+id/wrong"
android:text="Wrong" />
<TextView
android:id="#+id/conflicts"
style="#style/textview_summary_omr_toolbar_count"
android:layout_toEndOf="#+id/wrong_textview"
android:layout_toRightOf="#+id/wrong_textview"
android:background="#drawable/bg_percentage_3"
android:text="0" />
<TextView
android:id="#+id/conflicts_textview"
style="#style/textview_heading_summary_omr_toolbar"
android:layout_toEndOf="#+id/conflicts"
android:layout_toRightOf="#+id/conflicts"
android:text="Conflicts" />
<TextView
android:id="#+id/score_textview"
style="#style/textview_summary_omr_toolbar_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="#+id/conflicts_textview"
android:layout_toRightOf="#+id/conflicts_textview"
android:background="#null"
android:gravity="center"
android:text="Score:"
android:textColor="#android:color/white" />
<TextView
android:id="#+id/marks_scored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_toEndOf="#+id/score_textview"
android:layout_toRightOf="#+id/score_textview"
android:gravity="center"
android:text="18/30"
android:textColor="#android:color/white"
android:textSize="18sp" />
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
The following is the hierarchy viewer Image I am obtaining
You're thinking about this wrong. There is nothing wrong with your layout (as it relates to your question). To prove it, completely change your layout and try again
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
Log.d("SomeTag", "w: " + recyclerView.getWidth() + ", h: " + recyclerView.getHeight());
Log.d("SomeTag", "mw: " + recyclerView.getMeasuredWidth() + ", mh: " + recyclerView.getMeasuredHeight());
}
The output for a view with only 1 child is still
12-14 10:17:04.902 3004-3004/? D/SomeTag: w: 0, h: 0
12-14 10:17:04.902 3004-3004/? D/SomeTag: mw: 0, mh: 0
So there is nothing wrong with your layout (except you do have a misnomer. You call the method logToolbarLayoutParams, but you aren't accessing the layout params at all. You are trying to get properties on the view object before the layout pass has occurred. Maybe this is your confusion). So what you're missing is an understanding of Android's layout system, viewgroups, and views. Romain Guy had a really cool talk at one point on Devoxx about how to build a custom FlowLayout. Either the site is down or the video is pulled but here's the code in GitHub If you look at the method onLayout, you can see that a ViewGroup loops through all of it's children and calls child.layout(). Until this method is called, the view.getWidth and view.getHeight methods will return 0. And this method is called by the OS whenver it schedules it. So basically, you are trying to access the width and height immediately even before the OS scheduled a layout. Make a custom viewgroup yourself, add some break points, and give it a shot.
toolbar.getWidth() will tell you the width of the view after it has been measured and laid out.
After inflating the view its width and height will always be 0 because neither a measure() nor a layout() has occured.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
// check after inflation -> will be 0
logToolbarLayoutParams();
}
onGlobalLayout() reports the correct data, because—as the name suggests—both, onMeasure() and onLayout() have been called. The dimensions of the view are known. This does not mean the view has been drawn. onDraw() is the third and last step.
It also does not make any difference of how complex your layout is or which views you are using. Measuring and layout passes don't happen immediately after the inflation of the view.
To get and work with the correct dimensions of your view you have 3 options:
Use ViewTreeObserver.OnGlobalLayoutListener#onGlobalLayout() like you already suggested
Check the size to a later time, when you can be sure the view has been drawn, (e.g. upon user interaction, in an OnClickListener)
Go custom and create your own View. There you get the opportunity to handle all of onMeasure, onLayout, and onDraw yourself!
Drawing views take considerable time but we can reduce by removing nested layout and make view hierarchy flat either by using RelativeLayout or Constrain layout.
To find overdraw. Select "Show overdraw areas" in Debug GPU overdraw developer options from settings.
Study this and this important article from developer.android on optimizing layouts topic
I've just solved same problem in same case. All solutions with ViewTreeObserver.OnGlobalLayoutListener() look's dirty and not works for me. The easiest way to know width and height at current screen looks like this:
toolbar.post(new Runnable() {
#Override
public void run() {
int headerWidth = toolbar.getWidth();
int headerHeight = toolbar.getHeight();
// do whatever you want ...
}
});
view.post() will add your code in the end at view's message queue, when all sizes will already calculated. imho, that's obviously and more understandable, then OnGlobalLayoutListener().
Hope it will help you.
Anyway, this solution is just "delayed getting sizes", like a OnGlobalLayoutListener(). But this is normal behavior of system, it takes some time to draw views on the screen. Best way is not expecting calculated dimensions - instead you should coordinate all relationships between views in .xml files. Attributes like WRAP_CONTENT and MATCH_PARENT is easiest way to solve problems like this
you try to get width and height of a view before its attached to screen
you can create a custom view and wath you need in onAttach method
I have a three tab interface setup using a ViewPager. In the 3rd tab, I have a CardView which can expand/retract onclick to reveal more information. Inside the CardView is a RecyclerView which is inflated with a bunch of data.
The first time the app opens and the CardView is expanded, the expand animation is quite laggy but afterwards, it can be expanded/retracted smoothly. The RecyclerView data can also be updated with no problem.
Is there a way to somehow load/cache the data upon opening the app so the lagg does not occur.
Sorry if question is poor, just starting android development.
Thanks
Layout on third tab:
<android.support.v7.widget.CardView
android:id="#+id/SF_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp">
<LinearLayout
android:id="#+id/SF_wrapper"
android:layout_width="fill_parent"
android:layout_height="80dp"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/SF_list"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v7.widget.CardView>
The data which is used to inflate the recyclerView is a list of objects each with 2 float variables. Roughly length ~50.
The xml used to inflate each item:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4sp"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="36sp"
android:layout_weight="1">
<TextView
android:id="#+id/value_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="lorem"
android:textSize="8sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="36sp"
android:layout_weight="1">
<TextView
android:id="#+id/value_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="ipsom"
android:textSize="8sp" />
</RelativeLayout>
</LinearLayout>
The code to expand/retract the cardView simply animated changing the height of SF_frame to reveal more elements of the RecyclerView and it is this first reveal which is laggy.
Resolve your Layouting as first:
Use sp only with texts and dp for other dimensions (width, height, margins, paddings etc.). For texts it is recommended to use at least android:textsize="12sp". Look at the android:gravity attribute instead of android:layout_gravity so you don't have to use parent layout just to center it.
<android.support.v7.widget.CardView
android:id="#+id/SF_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp">
<!-- No need to use LinearLayout since only one child is used -->
<android.support.v7.widget.RecyclerView
android:id="#+id/SF_list"
android:layout_width="match_parent"
android:layout_height="80dp">
</android.support.v7.widget.RecyclerView>
</android.support.v7.widget.CardView>
In this layout, if you are expanding RecyclerView, it is possible that new views are being instantiated (RecyclerView.Adapter.onCreateViewHolder and RecyclerView.Adapter.onBindViewHolder) are called mulitple times.
In fact switching to that Tab is also a little bit delayed, but it is clearly visible on animation. (you are expandig from about 3 items to maybe 20 or so, I don't know how rest of your layout looks)
and second:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:orientation="horizontal">
<TextView
android:id="#+id/value_1"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:layout_gravity="center"
android:text="lorem"
android:textSize="8sp" />
<TextView
android:id="#+id/value_2"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:layout_gravity="center"
android:text="ipsom"
android:textSize="8sp" />
</LinearLayout>
If filling these 2 TextViews is so terribly slow, that you are experiencing hickups. Then you probably doing very very complex math operations (try to simplify them if possible) or you are somehow downloading data synchronously and waiting for results.
Please have a look at AsyncTask and use it while binding data... You can use it something like this (modify by yourself):
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
private TextView view;
MyAsyncTask(TextView view) {
this.view = view;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
// display progressbar or something
}
#Override
protected String doInBackground(Void... params) {
// do heavyweight operation here
String result = heavyWeightOperation(params);
return result;
}
#Override
protected void onPostExecute(String s) {
// hide progressbar or something
if (view != null) {
view.setText(s);
}
super.onPostExecute(s);
}
}
I'm having troubles getting my head around how to create add animation to a LinearLayout that is translucent. There are two views with opacity and a moving background (Google map if it is of importance). View1 is a LinearLayout and view2 is being added to view1. However when adding view2 it is seen through view1 (see left animation below).
Is there any way I can prevent this to achieve my animation correctly? (goal: see animation on the right) Keep in mind that the background is changing and is not a fixed picture.
Best regards
Rawa
I imagine my comment may not have been entirely clear, so I put together a simple example to better explain and demonstrate. As I'm uncertain as to what your Views will be, I've left them rather generic, and set some hard-coded properties you will want to change.
main.xml layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/stripes_diag" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000" >
<TextView android:id="#+id/view2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="#88ffffff"
android:textColor="#000000"
android:textSize="40sp"
android:text="Lorem Ipsum" />
</LinearLayout>
<View android:id="#+id/view1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#88ffffff" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Animate"
android:onClick="onClick" />
</LinearLayout>
</RelativeLayout>
MainActivity class:
public class MainActivity extends Activity
{
View view1, view2;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
view1 = findViewById(R.id.view1);
view2 = findViewById(R.id.view2);
}
public void onClick(View v)
{
view2.setY(view2.getHeight());
ObjectAnimator anim = ObjectAnimator.ofFloat(view2, "y", 0);
anim.setDuration(1500);
anim.start();
}
}
A look at the effect: