I am trying to display @ user mentions like facebook or twitter. I am able to implement this functionality. But i have a ui problem.
If edittext is at the bottom of the screen i need to show suggestion in recyclerview at the top.(above edittext cursor)
If edittext is at the top of the screen i need to show suggestion in recycelrview at the bottom (below edittext cursor)
What have i tried?
I have used recyclerview that is constrained below edittext
Problem
When soft keyboard opens it covers the recyclerview. This happens when edittext cursor is at the bottom.
How can i fix this using existing recyclerview itself?.
When edittext cursor is at the top see suggestions are shown below properly.
Code
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/black"
android:fontFamily="@font/source_sans_pro"
android:id="@+id/titleHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/create_feed_post"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="#c8c8c8"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleHeader" />
<com.linkedin.android.spyglass.ui.MentionsEditText
android:padding="8dp"
android:id="@+id/mentionsEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="300dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@null"
android:gravity="start|top"
android:hint="Share team wins or recognize colleague for a job well done"
android:inputType="textMultiLine"
android:minHeight="50dp"
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/line">
</com.linkedin.android.spyglass.ui.MentionsEditText>
<ImageView
android:id="@+id/preview"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mentionsEditText" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="0dp"
android:layout_height="250dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:elevation="5dp"
android:orientation="vertical"
android:overScrollMode="never"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="none"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mentionsEditText" />
<View
android:id="@+id/line2"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="#c8c8c8"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/preview" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/optionsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line2">
<ImageView
android:id="@+id/visibility"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:src="@drawable/toggle_comment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/attach"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/attach"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_upload_image" />
<Button
android:id="@+id/done"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_marginStart="32dp"
android:text="@string/post"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/visibility"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@+id/titleHeader"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/titleHeader"
app:srcCompat="@drawable/ic_close"
app:tint="@color/black" />
<FrameLayout
android:visibility="gone"
android:elevation="5dp"
android:id="@+id/container"
app:layout_constraintBottom_toTopOf="@+id/preview"
app:layout_constraintEnd_toEndOf="@+id/preview"
app:layout_constraintTop_toTopOf="@+id/preview"
android:layout_width="20dp"
android:layout_height="20dp">
<com.mikhaellopez.circleview.CircleView
app:cv_border_width="1dp"
app:cv_border_color="#EAEAEA"
app:cv_border="true"
app:cv_color="@color/white"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:layout_gravity="center"
android:id="@+id/removePreview"
android:layout_width="15dp"
android:layout_height="15dp"
app:srcCompat="@drawable/ic_close"
app:tint="#999999" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
Update: i tried using a pop up window. I am able to show above or below a view depending on where the view is located. But there is still one problem with keyboard opening. The pop up comes over the keyboard when the edittext is at the bottom. The edittext moves up but popup window stays in same place
Update 2:
if(anchor instanceof EditText) {
EditText editText = (EditText) anchor;
int pos = editText.getSelectionStart();
Layout layout = editText.getLayout();
int line = layout.getLineForOffset(pos);
int baseline = layout.getLineBaseline(line);
int ascent = layout.getLineAscent(line);
float cursorx = layout.getPrimaryHorizontal(pos);
cursory = baseline + ascent- editText.getScrollY();
}
final View contentView = view;
final Rect windowRect = new Rect();
contentView.getWindowVisibleDisplayFrame(windowRect);
final int windowW = windowRect.width();
final int windowH = windowRect.height();
contentView.measure(
makeDropDownMeasureSpec(getWidth(), windowW),
makeDropDownMeasureSpec(getHeight(), windowH)
);
final int measuredW = contentView.getMeasuredWidth();
final int measuredH = contentView.getMeasuredHeight();
final int[] anchorLocation = new int[2];
anchor.getLocationInWindow(anchorLocation);
final int anchorBottom = anchorLocation[1] + anchor.getHeight();
if (y + anchorBottom < 0) {
y = -anchorBottom;
} else {
y = (int) (y + cursory );
}
And then
popupWindow.showAsDropDown(anchor, 0, y);
and the flag in manifest is
android:windowSoftInputMode="adjustResize"
Using pop up window show at location will fix this
private fun showPopupWindow(anchor: View) {
var popUpHeight = 0
PopupWindow(anchor.context).apply {
isOutsideTouchable = true
val inflater = from(anchor.context)
contentView = inflater.inflate(R.layout.popup_layout, null).apply {
measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
measuredWidth
measuredHeight
popUpHeight = measuredHeight
}
height = ViewGroup.LayoutParams.WRAP_CONTENT
width = ViewGroup.LayoutParams.MATCH_PARENT
}.also { popupWindow ->
val location = IntArray(2).apply {
anchor.getLocationInWindow(this)
}
val size = Size(
popupWindow.contentView.measuredWidth,
popupWindow.contentView.measuredHeight
)
val editText = anchor as EditText
val pos = editText.selectionStart
val layout: Layout = editText.layout
val line: Int = layout.getLineForOffset(pos)
editText.getLocationOnScreen(location)
val point = Point()
point.x = layout.getPrimaryHorizontal(pos).toInt()
point.y = layout.getLineBottom(line)// location[1] //- (baseline + ascent )
val y = point.y
val top = layout.getLineTop(line)
val editTextheight = editText.measuredHeight
val yOffset = if (y > (editTextheight / 2)) {
top - (popUpHeight + 50)
} else {
point.y + 50
}
popupWindow.showAsDropDown(
anchor, 0,
yOffset
)
}
}
Based on where the cursor is and also the height of edittext you can calculate the yoffset and show the pop up at a specified position.
In case edittext scrolls you can use editText.scrolly and re-calculate your offset.