ScrollView does not resize directly after child changes - android

I've run into a very annoying problem regarding ScrollView resizing, and I'm running out of possible solutions.
I have a FragmentPager containing several different Fragments, one of which has a ScrollView.
The Fragment with the ScrollView is made up of a Spinner and the ScrollView containing a LinearLayout with several rows of other Views (such as SeekBars, Buttons, Edittexts) in it.
Depending on which option is select in the Spinner, the ScrollView shows different views. To do so, some Views have their visibility turned to Gone, while others are turned to Visible.
This works great, except for the fact that the ScrollView does not seem to resize itself properly upon choosing a different option using the Spinner.
When the ScrollView is full of Views, and therefore scrollable, if the user selects an option which shows less Viewsthan required to fill the ViewPort the ScrollView still scrolls.
When the user then chooses the old option again, the ScrollView is now unable to scroll since it took on the size required for the previous option.
When the user then chooses the SAME option again, the ScrollView suddenly is scrollable, since it is now resized to the actual size required.
What is going on here? And better yet, how can I fix this annoying problem?
My layout:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/control_scroll"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#99000000"
android:fillViewport="true" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#DD000000"
android:gravity="bottom"
android:orientation="vertical" >
<TextView
android:id="#+id/lamp_choose_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:paddingLeft="5dp"
android:text="#string/choose_lamp_text"
android:textColor="#FFFFFF"
android:textSize="14sp" />
</LinearLayout>
<Spinner
android:id="#+id/lamp_select_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textColor="#FFFFFF" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#DD000000"
android:gravity="bottom"
android:orientation="vertical" >
<TextView
android:id="#+id/lamp_settings_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:background="#44000000"
android:paddingLeft="5dp"
android:text="#string/lamp_settings_text"
android:textColor="#FFFFFF"
android:textSize="14sp" />
</LinearLayout>
<!-- Lamp name -->
<LinearLayout
android:id="#+id/naam_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:paddingBottom="3dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="3dp" >
<TextView
android:id="#+id/bridge_naam"
style="#style/ConfigText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/config_lightname"
android:textColor="#FFFFFF"
android:textSize="16sp" />
<EditText
android:id="#+id/lamp_naam_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:background="#drawable/tasstextfield"
android:inputType="textNoSuggestions"
android:textColor="#FFFFFF" >
</EditText>
</LinearLayout>
<View
android:id="#+id/separator"
android:layout_width="fill_parent"
android:layout_height="0.5dp"
android:background="#android:color/black"
android:visibility="visible" />
<!-- Rest of the views -->
Things I already tried:
ForceLayout/requestLayout on the parent ScrollView
Invalidate the ScrollView
ForceLayout/requestLayout on the containing LinearLayout
Invalidating the LinearLayout
Invalidating all children of the ScrollView
ForceLayout/requestLayout on all children of the ScrollView

This might not be the most enjoyable solution, but it might work.
Use a handler to post a delayed message to refresh the view a second time, as if the user would choose the same option twice after having click on the refresh button.
// The code when the user wants to update the views
myButton.setOnClickListener(new OnClickListener{
#Override
public void onClick(View view) {
updateView();
Message m = Message.obtain();
m.what = UPDATE_TAG;
mHandler.sendMessageDelayed(msg, 200);
}
})
...
final int UPDATE_TAG = 1;
public void updateView() {
// Code where View.GONE and View.VISIBLE + invalidate is used.
}
Handler mHandler = new Handler {
#Override
public void handleMessage(Message input_msg) {
if(msg.what == UPDATE_TAG) {
updateView();
}
}
}

Try with this changes
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/control_scroll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#99000000"
android:fillViewport="true" >
Quit the android:layout_weight="1"

I suppose that LinearLayout uses some kind of caching mechanism to avoid measuring childs every time. Or maybe I'm wrong.
However, you can try to use RelativeLayout instead of bunch of LinearLayout. This will solve your problem and also this will be much more efficient, since nested ViewGroups isn't good for performance.

You can go ahead and set the ScrollView height to "wrap_content" so that even if the child views change the ScrollView can adjust to the new setting

Related

Horizontal android:animateLayoutChanges="true" Animation not smooth

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.

button is not clickable in versions < Lollipop

i have a basic layout which contains some views. When there is a network connection a RecyclerView is shown. When the user swipes to refresh and there is no connection a button and a textview, which are inside the basic layout invisible at first place, become visible. The problem is that the button is not clickable in devices with Android version < Lollilop.
basic_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listSurveysRlt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.rey.material.widget.TextView
android:id="#+id/codeDescTtv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="#dimen/code_dest_txv_padding_bottom"
android:visibility="gone"
android:gravity="center"
android:layout_above="#+id/sRetryBtn"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:textSize="#dimen/code_dest_txv_text_size"
android:textColor="#android:color/black"
android:textStyle="bold" />
<com.rey.material.widget.Button
android:id="#+id/sRetryBtn"
android:layout_width="#dimen/action_btn_width"
android:layout_height="#dimen/action_btn_height"
android:text="#string/retry"
android:textColor="#android:color/white"
android:background="#drawable/ripple_drawable"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:visibility="gone" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshLlt"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/completedSurveysRcV"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/surveys_rcv_margin_top" />
</android.support.v4.widget.SwipeRefreshLayout>
<ImageView
android:id="#+id/noSurveysImv"
android:layout_width="#dimen/no_surveys_imv_width"
android:layout_height="#dimen/no_surveys_imv_height"
android:src="#drawable/no_surveys"
android:layout_centerHorizontal="true"
android:layout_marginTop="#dimen/no_surveys_imv_margin_top"
android:visibility="gone"
android:contentDescription="#string/ongoing" />
<com.rey.material.widget.TextView
android:id="#+id/noSurveysTxv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/noSurveysImv"
android:text="#string/no_completed"
android:textSize="#dimen/no_surveys_txv_text_size"
android:textColor="#color/no_surveys_txv_color"
android:visibility="gone"
android:layout_centerHorizontal="true" />
</RelativeLayout>
the method which transforms the corrensponding views from visible to invisible and vice versa:
private void onErrorBackgroundView(int code) {
listSurveysRlt.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.view_color));
completedSurveysRcV.setVisibility(View.GONE); //RecyclerView
codeDescTtv.setVisibility(View.VISIBLE); //TextView
retryBtn.setVisibility(View.VISIBLE); //not clickable button
codeDescTtv.setText(getResources().getString(inputValidationCodes.get(code)));
}
After some digging i found out the reason behind this strange behavior of the framework. The culprit was the SwipeRefreshLayout that blocked ui clicks ,so by setting the visibility of this layout to gone every time the user swipes the screen and restore the visibility when connection is available solved the issue. If anyone knows why this was happening in versions < Lollipop, it would be much helpful.

Scrollview does not shrink when soft keyboard is shown

I have a simple chat activity, with a message entry box at the top and then a scrollview with the message list. When the soft keyboard is opened, I want the scrollview to shrink so that the last message is not covered by the keyboard. This is my xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp" >
<TextView
android:id="#+id/chat_with"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Chat with Gordon" />
<RelativeLayout
android:id="#+id/msg_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/chat_with"
android:layout_marginTop="10dp">
<EditText
android:id="#+id/chat_message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Type your message" />
<ImageButton
android:id="#+id/chat_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="#00000000"
android:src="#+android:drawable/ic_menu_send" />
</RelativeLayout>
<ScrollView
android:id="#+id/chat_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/msg_container"
android:layout_marginTop="15dp"
android:padding="10dp"
android:isScrollContainer="true"
>
<RelativeLayout
android:id="#+id/chat_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="#+id/msg1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hi, how are you?"
android:layout_alignParentRight="true"/>
<TextView
android:id="#+id/msg2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hey! All right"
android:layout_alignParentLeft="true"
android:layout_below="#+id/msg1"/>
</RelativeLayout>
</ScrollView>
</RelativeLayout>
I just manually inserted two messages but there will be many of them in the real app. I already tried with the adjustPan|adjustResize solution, but it does not work, when I fire the keyboard the layout is unchanged and the last messages are covered by the keyboard. How can I avoid this?
Thanks for your help!
I solved this problem by using the manifest. In the activity I used:
android:windowSoftInputMode="stateHidden|adjustResize"
This way the keyboard starts out hidden and then when the user clicks in the EditText the keyboard will show up and the rest of the screen gets resized.
Look into the windowSoftInputMode for other settings for keyboard behavior (e.g. just covering up screen instead of resizing).
Edit:
To scroll the scroll view to the bottom after the keyboard shows up I needed to use a slight delay because sometimes the scroll would happen first and then the keyboard showed up and the scroll view was no longer at the bottom. I ended up not keeping this for other reasons, but to delay the scroll a bit one can do this:
// now scroll to the bottom
ScrollView sv = (ScrollView) findViewById(R.id.myScrollView);
sv.post(new Runnable() {
#Override
public void run() {
ScrollView sv = (ScrollView) findViewById(R.id.myScrollView);
sv.scrollTo(0, sv.getBottom());
}
});

getLayoutParams for LinearLayout under ScrollView not working

I have a LinearLayout and I controlled the height of this layout using getLayoutParams previously and it was working beautifully. Now I want the layout to be scrollable so I placed it under a ScrollView. Now for some reason the getLayoutParams stopped working. I seem to have come to a dead end trying to resolve this. Please help.
Here is my code
if(subHistories.getLayoutParams().height==0)
(subHistories.getLayoutParams()).height=ViewGroup.LayoutParams.WRAP_CONTENT;
else
(subHistories.getLayoutParams()).height=0;
Here subHistories is the layout object I want to control the height for. I want it to switch it's height from zero to wrap_content on every onClick event
And here is the relevant xml code:
...<ScrollView
android:id="#+id/left"
android:layout_width="200dp"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_below="#id/head"
android:animateLayoutChanges="true">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/historyBut"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:background="#5261a5"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:text="Historical Places"
android:textAllCaps="false"
android:textSize="20sp"
android:typeface="normal" />
<!-- historical places category -->
<LinearLayout
android:id="#+id/histories"
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:animateLayoutChanges="true" >...
After touching layout params, call requestLayout() for the changes to take effect.
For hiding/showing a layout, consider using setVisibility() with GONE and VISIBLE instead.

Which layout to use for a 2x2 image-based menu?

I am trying to create a screen (in portrait mode) that shows 4 images (same size, intended to scale down to fit screen), taking up the entire screen, breaking up the screen into quadrants (a tall, 2x2 grid). This will act as a main menu type of activity and each image should be clickable, in order to take the user to a different activity.
I have tried using a GridView inside a LinerLayout (using a lot from Google's GridView tutorial) but cannot get the images to all scale properly to fill the entire screen. I get extra margins around the images and/or scrolling of the entire screen.
I have also tried using a TableLayout, placing 2 images in each of the 2 rows. Visually, that worked perfectly. Unfortunately when using that, I cannot seem to reference the ImageView items in the TableLayout in my activity code (findViewById always returns null).
I feel like a TableLayout is really not the "right thing to do" but I would like to hear what others have to say. Either way, what should be done to accomplish my desired functionality?
Thanks.
Edit 1.1:
The relative layout works much better for getting things lined up. Now I'm just left with the issue where findViewById always returns null. Here is my code so far:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/homescreen_bgcolor"
>
<ImageView id="#+id/one"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:src="#drawable/item1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView id="#+id/two"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:src="#drawable/item2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView id="#+id/three"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:src="#drawable/item3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView id="#+id/four"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:src="#drawable/item4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
public class HomeScreenActivity2 extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.homescreen2);
ImageView imageView = (ImageView) findViewById(R.id.one);
imageView.setClickable(true);
imageView.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Log.i("Test", "test");
}
});
}
}
Here is a sample layout showing how you can achieve a 2 X 2 grid that covers the entire screen using just a RelativeLayout.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="#+id/centerVerticalShim"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_centerVertical="true"
android:visibility="invisible" />
<View
android:id="#+id/centerHorizontalShim"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:visibility="invisible" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/centerVerticalShim"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="#+id/centerHorizontalShim"
android:background="#42A5F5"
android:gravity="center"
android:text="#string/one"
android:textColor="#FFFFFF" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/centerVerticalShim"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/centerHorizontalShim"
android:background="#EF5350"
android:gravity="center"
android:text="#string/two"
android:textColor="#FFFFFF" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_below="#+id/centerVerticalShim"
android:layout_toLeftOf="#+id/centerHorizontalShim"
android:background="#66BB6A"
android:gravity="center"
android:text="#string/three"
android:textColor="#FFFFFF" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_below="#+id/centerVerticalShim"
android:layout_toRightOf="#+id/centerHorizontalShim"
android:background="#5C6BC0"
android:gravity="center"
android:text="#string/four"
android:textColor="#FFFFFF" >
</TextView></RelativeLayout>
The above layout results in this:
I think a TableLayout could work for you, but I'd recommend trying out RelativeLayout as well. You can basically pin your images to the four quadrants by using combinations of
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"`
on your images.
I'm doing something similar in my app where I have multiple buttons on a homepage that can launch corresponding activities. RelativeLayout works fine, and it avoids nested Layout objects, which can hamper performance during render and layout procedures (if it gets out of hand).

Categories

Resources