根据光标所在位置和屏幕可用高度,通过锚定在编辑文本下方或上方显示 RecyclerView/PopupWIndow
Show RecyclerView/PopupWIndow by anchoring below or above edittext depending on where the cursor is and depending on screen available height
我正在尝试显示 @ 用户提及,例如 facebook 或 twitter。我能够实现这个功能。但是我有一个 ui 问题。
如果 edittext 在屏幕底部,我需要在顶部的 recyclerview 中显示建议。(在 edittext 光标上方)
如果 edittext 在屏幕顶部,我需要在底部的 recycelrview 中显示建议(在 edittext 光标下方)
我尝试了什么?
我使用了限制在 edittext 下方的 recyclerview
问题
软键盘打开时覆盖recyclerview。当 edittext 光标位于底部时会发生这种情况。
我如何使用现有的 recyclerview 本身解决这个问题?
当 edittext 光标位于顶部时,请参阅下面正确显示的建议。
代码
<?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>
更新:我尝试使用弹出窗口 window。根据视图所在的位置,我可以在视图上方或下方显示。但是键盘打开还有一个问题。当编辑文本位于底部时,弹出窗口会出现在键盘上。 edittext 向上移动,但弹出 window 留在原处
更新 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 );
}
然后
popupWindow.showAsDropDown(anchor, 0, y);
清单中的标志是
android:windowSoftInputMode="adjustResize"
使用弹出 window 在位置显示将解决此问题
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
)
}
}
根据光标所在的位置以及编辑文本的高度,您可以计算 yoffset 并在指定位置显示弹出窗口。
如果 edittext 滚动,您可以使用 editText.scrolly 和 re-calculate 您的偏移量。
我正在尝试显示 @ 用户提及,例如 facebook 或 twitter。我能够实现这个功能。但是我有一个 ui 问题。
如果 edittext 在屏幕底部,我需要在顶部的 recyclerview 中显示建议。(在 edittext 光标上方)
如果 edittext 在屏幕顶部,我需要在底部的 recycelrview 中显示建议(在 edittext 光标下方)
我尝试了什么?
我使用了限制在 edittext 下方的 recyclerview
问题
软键盘打开时覆盖recyclerview。当 edittext 光标位于底部时会发生这种情况。
我如何使用现有的 recyclerview 本身解决这个问题?
当 edittext 光标位于顶部时,请参阅下面正确显示的建议。
代码
<?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>
更新:我尝试使用弹出窗口 window。根据视图所在的位置,我可以在视图上方或下方显示。但是键盘打开还有一个问题。当编辑文本位于底部时,弹出窗口会出现在键盘上。 edittext 向上移动,但弹出 window 留在原处
更新 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 );
}
然后
popupWindow.showAsDropDown(anchor, 0, y);
清单中的标志是
android:windowSoftInputMode="adjustResize"
使用弹出 window 在位置显示将解决此问题
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
)
}
}
根据光标所在的位置以及编辑文本的高度,您可以计算 yoffset 并在指定位置显示弹出窗口。
如果 edittext 滚动,您可以使用 editText.scrolly 和 re-calculate 您的偏移量。