Recyclerview scrolls down when soft keyboard appears or disappears - java

The issue is that the recyclerview scrolls down almost a complete row when the soft keyboard appears or disappears.
Here is a visual example:
The layout for the activity uses a MessageInput and MessageList object from the ChatKit UI library and is as follows:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context="apps.cal.calchat.ChatActivity.ChatView">
<TextView
android:id="#+id/chatIsTypingTextView"
android:layout_width="match_parent"
android:layout_marginLeft="8dp"
android:layout_height="wrap_content"
android:layout_above="#+id/chatMessageInput"/>
<com.stfalcon.chatkit.messages.MessageInput
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:id="#+id/chatMessageInput"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<com.stfalcon.chatkit.messages.MessagesList
android:id="#+id/chatMessagesList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_above="#+id/chatIsTypingTextView"
/>
</RelativeLayout>
The activity also has adjustPan set in the manifest:
<activity
android:name=".ChatActivity.ChatView"
android:windowSoftInputMode="adjustPan|stateHidden"/>
The Recyclerviews linear layout manager is set as vertical, with a reverse layout and with the default item animator:
public <MESSAGE extends IMessage>
void setAdapter(MessagesListAdapter<MESSAGE> adapter, boolean reverseLayout) {
SimpleItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setSupportsChangeAnimations(false);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(),
LinearLayoutManager.VERTICAL, reverseLayout);
setItemAnimator(itemAnimator);
setLayoutManager(layoutManager);
adapter.setLayoutManager(layoutManager);
adapter.setStyle(messagesListStyle);
addOnScrollListener(new RecyclerScrollMoreListener(layoutManager, adapter));
super.setAdapter(adapter);
}
I ideally do not want to set up a keyboard listener and programatically scroll to the bottom every time the keyboard is shown/hidden. It seems that I must be doing something wrong somewhere. Any ideas?

When implementing the android ui chat, I spent a long time looking for a solution to the problem with scrolling messages when the keyboard opens. Basically, the decisions were based on scrolling through the recycler to the height of the keyboard and it looked like a crutch.
As it turned out, you need only few things
1. Do not use layoutManager.stackFromEnd = true
2. Do not use ConstraintLayout with high 0dp as a recycler parent
2. Do not use RelativeLayout with wrap_content height as a recycler parent
Setting example:
val adapter = MessagesAdapter ()
        val layoutManager = LinearLayoutManager (this, RecyclerView.VERTICAL, true)
        rvMessages.layoutManager = layoutManager
        rvMessages.adapter = adapter
For layout you can use LinearLayout, RelativeLayout or ConstraintLayout with match_parent height and padding from bottom to editText height.
<LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android"
    android: layout_width = "match_parent"
    android: layout_height = "match_parent"
    android: orientation = "vertical">
    <androidx.recyclerview.widget.RecyclerView
        android: id = "# + id / rvMessages"
        android: layout_width = "match_parent"
        android: layout_height = "0dp"
        android: layout_weight = "1" />
    <EditText
        android: id = "# + id / etMessage"
        android: layout_width = "match_parent"
        android: layout_height = "wrap_content" />
</LinearLayout>

Related

How to center content of GridLayoutManager?

Just trying to center my RecyclerView horizontal.
Here is the xml:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
And here the java code:
mRecyclerView = findViewById(R.id.recycler_view);
itemLayoutManager = new GridLayoutManager(this, 6);
mRecyclerView.setLayoutManager(itemLayoutManager);
...
mItemAdapter = new ItemAdapter(MainActivity.this, mItemList);
mRecyclerView.setAdapter(mItemAdapter);
When I run the application the recyclerview aligns left.
What exactly do I have to do to center the content so that the margin is the same left and right like in the example picture on the right side?
Create a class like to set paddings to item:
class SpacingItemDecorator (private val padding: Int) : RecyclerView.ItemDecoration()
{
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
)
{
super.getItemOffsets(outRect, view, parent, state)
outRect.top = padding
outRect.bottom = padding
outRect.left = padding
outRect.right = padding
}
}
Now in Your RecyclerView set paddingStart and paddingEnd, e.g 4dp :
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="4dp"
android:paddingEnd="4dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="6"/>
When You create adapter just apply this item decorator:
val x = (resources.displayMetrics.density * 4).toInt() //converting dp to pixels
recyclerView.addItemDecoration(SpacingItemDecorator(x)) //setting space between items in RecyclerView
RecyclerView item:
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="6dp"
app:cardElevation="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txtContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{Integer.toString(dataView.id)}"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Result:
I faced the same issue recently and looked into the source code. There's no straight forward way to do this using GridLayoutManager. The other was is to use a GridView which is more customisable.
This is an alternate solution for you or any one else looking for a workaround.
I didn't want to change to a GridView so this is what I did. I set the RecyclerView's layout width to wrap_content and centered it horizontally within the parent (which in your case is a RelativeLayout so it will be something like android:centerHorizontally="true")
I knew the item width so I calculated the spanCount by dividing the screen width by the item width (plus any horizontal margins). Now I set the span count to the grid layout manager and it gets centered horizontally just how you want it.
Adding margins to you item's layout root play an important role here.

Set width and height based on dimentions of parent's parent Android Studio

I'm dynamicly adding ImageViews in a LinearLayout witch is inside a HorizontalScrollView.
Now I want to set the width and height of these ImageViews based on the parent LinearLayout of this HorizontalScrollView.
This is my xml (information_imagescrollview_item.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<HorizontalScrollView
android:id="#+id/horizontal_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="#+id/linear"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
and this is my code:
View contentView = inflater.inflate(R.layout.information_imagescrollview_item, container, false);
LinearLayout scrollViewLayout = (LinearLayout) contentView.findViewById(R.id.linear);
for(Object intObj : page.content) {
ImageView imageView = new ImageView(this.getContext());
Drawable myDrawable = getResources().getDrawable((int) intObj);
imageView.setImageDrawable(myDrawable);
scrollViewLayout.addView(imageView);
}
page.addView(contentView);
How can I set each imageView to have the same width and height as the parentlayout in the xml?
It is possible to change a dynamic view's dimensions by code. So I would bet you should change the ImageView's layout parameters in the for loop before adding it to your LinearLayout like this:
for(Object intObj : page.content) {
ImageView imageView = new ImageView(this.getContext());
Drawable myDrawable = getResources().getDrawable((int) intObj);
imageView.setImageDrawable(myDrawable);
//Changing height...
imageView.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
//...and width
imageView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
scrollViewLayout.addView(imageView);
}
This will set the ImageView's width and height to the containing (parent) LinearLayout's width and height.

Cannot center view in RelativeLayout

I am trying to center a view within a parent RelativeLayout:
private void addLoadingAnimation() {
RelativeLayout mainView = (RelativeLayout) findViewById(R.id.medical_supply_tile_activity);
mainView.removeAllViews();
GifView gifView = new GifView(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
gifView.setId(R.id.loading_gif);
mainView.setBackgroundColor(getResources().getColor(android.R.color.background_light));
mainView.addView(gifView,layoutParams);
}
I have also tried adding layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0); before the CENTER_IN_PARENT line. Nothing seems to be working. It is always aligned to the bottom right.
Here is my parent view:
<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"
android:background="#android:color/background_light"
tools:context=".MedecineTileActivity"
android:id="#id/medical_supply_tile_activity"
android:layout_gravity="left">
</RelativeLayout>
The gravity=left is because when the loading animation is removed, I insert fragments and want them to go to the left.
I have achieved this with TextViews and ImageViews by aligning the child View's right to the parent's right and also aligning the child View's left to the parent's left.
<TextView
style="#style/NewTextM2"
android:id="#+id/soft_dialog_info_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/<Parent>"
android:layout_alignLeft="#+id/<Parent>"
android:layout_alignRight="#+id/<Parent>"
android:gravity="center"
android:tag="item_text"
android:text="#string/text"
android:maxLines="1" />
This will align the view to the bottom center of the parent.

Crash when setting parameter to layout in Android

Expected Result
Clicking on the toggle button will show up the menu and slide out the content view rightward. After animation is finished, the layout parameters of the content view gets updated to the final position.
Problem
When updating the final position of the content view, the statement mViewContent.setLayoutParams(params); causes the crash. The error message is java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams
Source Code
Main.java > public class MainActivity extends Activity {}
public void onToggleButtonMenuClicked(View view) {
// Is the toggle on?
boolean toggleTurnedOn = ((ToggleButton) view).isChecked();
if (toggleTurnedOn) { // If the toggle is turned on
// Show menu
LinearLayout mViewMenu = (LinearLayout) findViewById(R.id.linear_layout_menu);
Animation animMenuOn = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_menu_on);
mViewMenu.startAnimation(animMenuOn);
LinearLayout mViewContent = (LinearLayout) findViewById(R.id.linear_layout_content);
Animation animContentOff = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_content_off);
mViewContent.startAnimation(animContentOff);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(480, 800);
params.leftMargin = 384; // Shift 384 pixels from left screen border
params.rightMargin = -96; // Exceed 96 pixels from right screen border
mViewContent.setLayoutParams(params); // This statement causes crash!
} else {
// Hide menu...
} // End of toggle events handling
} // End of onToggleButtonMenuClicked()
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="329dp"
android:layout_height="wrap_content" >
<!-- The Menu View -->
<LinearLayout
android:id="#+id/linear_layout_menu"
android:layout_width="263dp"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/table_row_1_search_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:weightSum="10"
android:orientation="horizontal" >
<EditText
android:id="#+id/edit_text_search_id"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="7"
android:hint="#string/edit_text_search_id"
android:textSize="14sp" />
<Button
android:id="#+id/button_search_id"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="3"
android:text="#string/button_search_id" />
</LinearLayout>
<!-- Other rows in the menu are omitted -->
</LinearLayout> <!-- End of Menu -->
<!-- The Content View -->
<LinearLayout
android:id="#+id/linear_layout_content"
android:layout_width="329dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<ToggleButton
android:id="#+id/toggle_button_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onToggleButtonMenuClicked" />
<TextView
android:id="#+id/text_content"
android:layout_width="480dp"
android:layout_height="wrap_content"
android:text="#string/text_content" />
</LinearLayout> <!-- End of Content -->
</FrameLayout> <!-- End of the root linear layout -->
mViewContent params should be added with resp to your Parent view, suppose you have parent view as LinearLayout then, LinearLayout.LayoutParams must be used.
Explanation (Adapted from here):-
Take for example, LinearLayout.LayoutParams and RelativeLayout.LayoutParams, they are different independent classes. They store different additional information about child views... say..
LinearLayout.LayoutParams can associate weight value with each
view, while RelativeLayout.LayoutParams can't.
RelativeLayout.LayoutParams can have values like alightWithParent,above, below
with each view while LinearLayout.LayoutParams can't.
Although the code will not give compile time error because all LayoutParams have same parent class i.e. ViewGroup.LayoutParams. So, it is always essential, to assign Layout params with respect to parent layout..
make sure you imported the correct layout params
import android.widget.LinearLayout.LayoutParams;

Android. Add controls programmatically

I have layout with controls:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contact_phones_layout_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/contact_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:inputType="phone" />
<Spinner
android:id="#+id/contact_phone_type"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
And I want to inflate it into another layout on the fragment. Quantity of these controls depends on values in a array. Array contains controls objects. So every time onCreateView rises I filled the layout from the array:
private void addLine(PhoneLine line) {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = line.getParent();
((ViewGroup) view.getParent()).removeView(view);
layout.addView(view, layout.getChildCount() - 1);
setButtonVisible(false);
}
if line is null controls are created this way:
private void addLine() {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = inflater.inflate(R.layout.contact_phone_line, layout, false);
EditText phoneEditText = (EditText) view.findViewById(R.id.contact_phone);
Spinner phoneType = (Spinner) view.findViewById(R.id.contact_phone_type);
phoneLines.add(new PhoneLine(phoneEditText, phoneType, view));
layout.addView(view, layout.getChildCount() - 1);
}
But after that I get same values in all EditText/Spinner controls, values equal to last element in the array. What can be wrong? May be there is more pure way to add controls dynamically?
I fixed it by adding controls when onResume rises, not onCreateView. But I still don't understand why onCreateView changes all values.

Categories

Resources