[feedback]优化软键盘弹出时,遮挡输入框

This commit is contained in:
renwj
2022-03-18 18:26:55 +08:00
parent 631b4cdb37
commit eb978fbfb5
10 changed files with 219 additions and 49 deletions

View File

@@ -14,17 +14,15 @@ import androidx.lifecycle.lifecycleScope
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.kotlin.PX
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.kotlin.lifecycleOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.kotlin.toast
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.util.Utils
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCasePresenter
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCaseView
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import com.zhjt.mogo_core_function_devatools.ext.toast
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import record_cache.RecordPanelOuterClass

View File

@@ -62,9 +62,9 @@ internal object BadCaseStore {
val data = mutableListOf<Cause>()
reasons.forEach { itx ->
data += Cause.newBuilder().let {
it.id = itx.id
it.reason = itx.reason
it.channel = itx.channel
it.id = itx.id ?: ""
it.reason = itx.reason ?: ""
it.channel = itx.channel ?: ""
it.build()
}
}

View File

@@ -0,0 +1,117 @@
package com.zhjt.mogo_core_function_devatools.ext
import android.R.id
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Rect
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowManager.LayoutParams
import android.widget.PopupWindow
import android.widget.TextView
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.mogo.eagle.core.utilcode.kotlin.PX
import com.mogo.eagle.core.utilcode.kotlin.lifeCycleOwner
import com.mogo.eagle.core.utilcode.kotlin.lifeCycleScope
import com.mogo.eagle.core.utilcode.kotlin.shape
import com.mogo.eagle.core.utilcode.reminder.Reminder
import com.mogo.eagle.core.utilcode.reminder.api.impl.PopupWindowReminder
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.SECONDS
/**
* 只供BaseCase使用的Toast样式
*/
internal fun Context.toast(text: CharSequence, duration: Long = 2, unit: TimeUnit = SECONDS) {
val activity = (this as? FragmentActivity) ?: throw IllegalStateException("please use Activity to trigger toast show.")
activity.lifeCycleScope.launchWhenResumed {
val pop = PopupWindow(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).also {
it.isOutsideTouchable = false
it.isTouchable = false
it.isFocusable = false
it.setBackgroundDrawable(shape(solid = Color.parseColor("#99000000"), radius = 32.PX))
}
val tv = TextView(this@toast)
tv.setTextColor(Color.WHITE)
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, 56.0f)
tv.setPaddingRelative(114.PX, 61.PX, 114.PX, 61.PX)
tv.text = text
pop.contentView = tv
val reminder = object : PopupWindowReminder(pop) {
override fun show() {
pop.showAtLocation(activity.window.decorView, Gravity.CENTER, 0, 0)
lifecycleOwner().lifecycleScope.launch {
delay(unit.toMillis(duration))
hide()
}
}
override fun isOverride(): Boolean = true
}
Reminder.enqueue(activity.lifeCycleOwner, reminder)
}
}
internal fun FragmentActivity.softKeyboardHeightChanged(block: ((height: Int) -> Unit)): ()-> Unit {
val decor = window.decorView
var softKeyboardDisplayed = false
var preBottom = 0
val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val decorH = decor.height
val content = decor.findViewById<View>(id.content) ?: return
val out = Rect()
content.getWindowVisibleDisplayFrame(out)
val navigationBarHeight = if (haveNavigationBar(this@softKeyboardHeightChanged)) getNavigationBarHeight(this@softKeyboardHeightChanged) else 0
if (decorH - navigationBarHeight == out.bottom) { //收起软件盘
if (!softKeyboardDisplayed) {
return
}
softKeyboardDisplayed = false
block.invoke(0)
} else { //展开软件盘
if (softKeyboardDisplayed && preBottom == out.bottom) {
return
}
preBottom = out.bottom
softKeyboardDisplayed = true
if (out.bottom != 0) {
//计算软件盘高度
val softKeyboardH = decorH - out.bottom - navigationBarHeight
if (softKeyboardH > 0) {
block.invoke(softKeyboardH)
}
}
}
}
}
decor.viewTreeObserver.addOnGlobalLayoutListener(listener)
return {
decor.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
private fun haveNavigationBar(activity: FragmentActivity): Boolean {
val decorView = activity.window.decorView //获取根view高度
val decorViewHeight = decorView.height
val contentView = decorView.findViewById<View>(id.content) ?: return false
val rect = Rect()
contentView.getWindowVisibleDisplayFrame(rect)
return decorViewHeight - rect.bottom > 0
}
private fun getNavigationBarHeight(activity: FragmentActivity): Int {
var result = 0
val res: Resources = activity.resources
val resourceId: Int = res.getIdentifier("navigation_bar_height", "dimen", "android")
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId)
}
return result
}

View File

@@ -18,6 +18,7 @@ import com.zhjt.mogo_core_function_devatools.R
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.toRecord
import com.zhjt.mogo_core_function_devatools.ext.toast
import com.zhjt.mogo_core_function_devatools.feedback.biz.FeedBackView
import com.zhjt.mogo_core_function_devatools.feedback.biz.bean.Feedback.BadCase
import com.zhjt.mogo_core_function_devatools.feedback.biz.impl.FeedbackPresenter

View File

@@ -2,19 +2,24 @@ package com.zhjt.mogo_core_function_devatools.feedback.biz
import android.content.Context
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.util.Log
import android.view.*
import android.widget.EditText
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.mogo.eagle.core.utilcode.kotlin.*
import com.zhjt.mogo_core_function_devatools.R
import com.zhjt.mogo_core_function_devatools.ext.softKeyboardHeightChanged
import com.zhjt.mogo_core_function_devatools.feedback.FeedbackManager
import com.zhjt.mogo_core_function_devatools.feedback.biz.adapter.FeedbackAdapter
import com.zhjt.mogo_core_function_devatools.feedback.biz.adapter.vh.BadCaseFBViewHolder
import com.zhjt.mogo_core_function_devatools.feedback.biz.bean.Feedback
import com.zhjt.mogo_core_function_devatools.feedback.biz.impl.FeedbackPresenter
import com.zhjt.mogo_core_function_devatools.feedback.callback.IFeedbackCallback
@@ -24,17 +29,32 @@ import kotlinx.android.synthetic.main.layout_fb.view.*
internal class FeedBackView : ConstraintLayout {
private var cb: IFeedbackCallback? = null
private var softKeyboardCb: (() -> Unit)? = null
private var editOutRect: Rect = Rect()
private val presenter by lazy {
FeedbackPresenter()
}
private var rvTransAnimator: ViewPropertyAnimator? = null
private val scope by lazy {
lifecycleOwner.lifecycleScope
}
internal val adapter by lazy {
FeedbackAdapter<Feedback>()
FeedbackAdapter<Feedback> { itx ->
if (itx is BadCaseFBViewHolder) {
itx.itemView.findViewById<EditText>(R.id.et).also {
val listener = object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
it.viewTreeObserver.removeOnPreDrawListener(this)
it.getGlobalVisibleRect(editOutRect)
return true
}
}
it.viewTreeObserver.addOnPreDrawListener(listener)
}
}
}
}
constructor(context: Context) : this(context, null)
@@ -44,6 +64,7 @@ internal class FeedBackView : ConstraintLayout {
observe(arrayOf(ON_DESTROY)) { itx ->
if (itx == ON_DESTROY) {
cb = null
softKeyboardCb?.invoke()
}
}
}
@@ -66,6 +87,30 @@ internal class FeedBackView : ConstraintLayout {
it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
loadFeedbackAndRefresh()
}
val activity = context as? FragmentActivity
activity?.also { itx ->
itx.softKeyboardHeightChanged {
Log.d(FeedbackManager.TAG, "-- onHeightChanged ---:H -> $it")
if (it == 0) {
transitionTo(0)
} else {
editOutRect.run {
transitionTo(-( it - top + height() + 240.PX))
}
}
}.also {
softKeyboardCb = it
}
}
}
private fun transitionTo(targetY: Int) {
rv?.also { itx ->
rvTransAnimator?.cancel()
itx.animate().translationY(targetY.toFloat()).setDuration(200).also {
rvTransAnimator = it
}.start()
}
}
private fun loadFeedbackAndRefresh() {
@@ -90,4 +135,5 @@ internal class FeedBackView : ConstraintLayout {
this.cb = cb
this.adapter.setCallback(cb)
}
}

View File

@@ -12,7 +12,7 @@ import com.zhjt.mogo_core_function_devatools.feedback.biz.bean.Feedback.BadCase
import com.zhjt.mogo_core_function_devatools.feedback.biz.diff.FeedbackDiffCallback
import com.zhjt.mogo_core_function_devatools.feedback.callback.IFeedbackCallback
internal class FeedbackAdapter<T: Feedback>: RecyclerView.Adapter<FeedbackViewHolder<T>>() {
internal class FeedbackAdapter<T: Feedback>(val onHolderAttached: (holder: FeedbackViewHolder<*>) -> Unit): RecyclerView.Adapter<FeedbackViewHolder<T>>() {
companion object {
const val ITEM_TYPE_BAD_CASE = 0x0101
@@ -60,4 +60,9 @@ internal class FeedbackAdapter<T: Feedback>: RecyclerView.Adapter<FeedbackViewHo
fun setCallback(cb: IFeedbackCallback) {
this.cb = cb
}
override fun onViewAttachedToWindow(holder: FeedbackViewHolder<T>) {
super.onViewAttachedToWindow(holder)
onHolderAttached.invoke(holder)
}
}

View File

@@ -28,6 +28,7 @@ import com.zhjt.mogo_core_function_devatools.feedback.biz.adapter.vh.base.Feedba
import com.zhjt.mogo_core_function_devatools.feedback.biz.bean.Feedback.BadCase
import com.zhjt.mogo_core_function_devatools.feedback.callback.IFeedbackCallback
@SuppressLint("SetTextI18n")
internal class BadCaseFBViewHolder(cb: IFeedbackCallback?, parent: ViewGroup): FeedbackViewHolder<BadCase>(cb,
LayoutInflater
@@ -42,6 +43,10 @@ internal class BadCaseFBViewHolder(cb: IFeedbackCallback?, parent: ViewGroup): F
itemView.findViewById<EditText>(R.id.et)
}
private val editParent by lazy {
itemView.findViewById<View>(R.id.et_root)
}
init {
itemView.findViewById<TextView>(R.id.record).also {
it.background = gradient(radius = 16.PX, orientation = LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(35, 146, 252), endColor = Color.rgb(28, 75, 252))
@@ -55,14 +60,15 @@ internal class BadCaseFBViewHolder(cb: IFeedbackCallback?, parent: ViewGroup): F
}
}
itemView.findViewById<View>(R.id.et_root).also {
editParent.also {
it.background = shape(solid = Color.parseColor("#263869"), radius = 20.PX)
}
val words = itemView.findViewById<TextView>(R.id.words_count)
et.onClick {
et.requestFocus()
et.isFocusable = true
et.isFocusableInTouchMode = true
it.requestFocus()
it.isFocusable = true
it.isFocusableInTouchMode = true
editParent.background = shape(solid = Color.parseColor("#263869"), radius = 20.PX, stroke = Color.parseColor("#5EBFFF"), strokeWidth = 2.PX)
}
et.watch(
200,
@@ -74,9 +80,6 @@ internal class BadCaseFBViewHolder(cb: IFeedbackCallback?, parent: ViewGroup): F
},
onReachMaxCountAction = {
ToastUtils.showShort("已超过最大字符数")
},
onGetFocus = {
it.background = shape(solid = Color.parseColor("#263869"), radius = 20.PX, stroke = Color.parseColor("#5EBFFF"))
}
)
}
@@ -88,6 +91,9 @@ internal class BadCaseFBViewHolder(cb: IFeedbackCallback?, parent: ViewGroup): F
if (!TextUtils.isEmpty(text)) {
et.setText(text)
Selection.setSelection(et.text, et.text.length)
editParent.background = shape(solid = Color.parseColor("#263869"), radius = 20.PX, stroke = Color.parseColor("#5EBFFF"), strokeWidth = 2.PX)
} else {
editParent.background = shape(solid = Color.parseColor("#263869"), radius = 20.PX)
}
}

View File

@@ -281,32 +281,3 @@ private class GestureConflictFixer(private val ids: List<Int>) : RecyclerView.On
}
}
fun Context.toast(text: CharSequence, duration: Long = 2, unit: TimeUnit = SECONDS) {
val activity = (this as? FragmentActivity) ?: throw IllegalStateException("please use Activity to trigger toast show.")
activity.lifeCycleScope.launchWhenResumed {
val pop = PopupWindow(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT).also {
it.isOutsideTouchable = false
it.isTouchable = false
it.isFocusable = false
it.setBackgroundDrawable(shape(solid = Color.parseColor("#99000000"), radius = 32.PX))
}
val tv = TextView(this@toast)
tv.setTextColor(Color.WHITE)
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, 56.0f)
tv.setPaddingRelative(114.PX, 61.PX, 114.PX, 61.PX)
tv.text = text
pop.contentView = tv
val reminder = object : PopupWindowReminder(pop) {
override fun show() {
pop.showAtLocation(activity.window.decorView, Gravity.CENTER, 0, 0)
lifecycleOwner().lifecycleScope.launch {
delay(unit.toMillis(duration))
hide()
}
}
override fun isOverride(): Boolean = true
}
Reminder.enqueue(activity.lifeCycleOwner, reminder)
}
}

View File

@@ -159,6 +159,7 @@ public class AMapViewWrapper implements IMogoMapView,
mMapView.setOnCameraChangeListener(this);
mMapView.setOnMapStyleListener(this);
mMapView.setOnMapViewVisualAngleChangeListener(this);
mMapView.
CallerLogger.INSTANCE.d(TAG, "styleop - initListeners - setOnMapStyleListener - view " + mMapView);
}

View File

@@ -13,6 +13,7 @@ import android.graphics.Rect;
import android.location.Location;
import android.os.Bundle;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -26,6 +27,7 @@ import com.mogo.eagle.core.function.call.map.CallerMapDataCollectorManager;
import com.mogo.eagle.core.function.call.map.CallerMapLocationListenerManager;
import com.mogo.eagle.core.function.call.map.CallerMapStyleListenerManager;
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager;
import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr;
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast;
@@ -51,6 +53,7 @@ import com.zhidaoauto.map.sdk.open.abs.OnMapLoadedListener;
import com.zhidaoauto.map.sdk.open.abs.OnMapStyleListener;
import com.zhidaoauto.map.sdk.open.abs.OnMapTouchListener;
import com.zhidaoauto.map.sdk.open.abs.OnMapViewVisualAngleChangeListener;
import com.zhidaoauto.map.sdk.open.abs.OnRoadInfoListener;
import com.zhidaoauto.map.sdk.open.camera.CameraPosition;
import com.zhidaoauto.map.sdk.open.camera.CameraUpdateFactory;
import com.zhidaoauto.map.sdk.open.camera.LatLngBounds;
@@ -63,6 +66,7 @@ import com.zhidaoauto.map.sdk.open.marker.BitmapDescriptorFactory;
import com.zhidaoauto.map.sdk.open.marker.Marker;
import com.zhidaoauto.map.sdk.open.marker.OnMarkClickListener;
import com.zhidaoauto.map.sdk.open.query.LonLatPoint;
import com.zhidaoauto.map.sdk.open.road.StopLine;
import com.zhidaoauto.map.sdk.open.tools.MapTools;
import com.zhidaoauto.map.sdk.open.view.MapAutoView;
import com.zhidaoauto.map.sdk.open.view.MapAutoViewHelper;
@@ -87,7 +91,9 @@ public class AMapViewWrapper implements IMogoMapView,
OnMapTouchListener,
OnMarkClickListener,
OnMapStyleListener,
MapStyleController.IMapStyleAutoChangedListener, OnMapViewVisualAngleChangeListener {
MapStyleController.IMapStyleAutoChangedListener,
OnMapViewVisualAngleChangeListener,
OnRoadInfoListener {
private static final String TAG = "AMapViewWrapper";
private final MapAutoView mMapView;
@@ -112,6 +118,9 @@ public class AMapViewWrapper implements IMogoMapView,
private boolean mIsFirstLocated = true;
private boolean mIsDelayed = false;
private volatile String mRoadId = null;
public AMapViewWrapper(MapAutoView mMapView) {
startTime = System.currentTimeMillis();
CallerLogger.INSTANCE.i(TAG, "autoop--AMapViewWrapper: init");
@@ -157,9 +166,25 @@ public class AMapViewWrapper implements IMogoMapView,
mMapView.setOnCameraChangeListener(this);
mMapView.setOnMapStyleListener(this);
mMapView.setOnMapViewVisualAngleChangeListener(this);
mMapView.setOnRoadInfoListener(this, 1);
CallerLogger.INSTANCE.d(TAG, "styleop - initListeners - setOnMapStyleListener - view " + mMapView);
}
@Override
public void onRoadIdInfo(@androidx.annotation.Nullable String s) {
Log.d(TAG, "-- onRoadIdInfo --RoadId:" + s);
mRoadId = s;
}
@Override
public void onStopLineInfo(@androidx.annotation.Nullable StopLine stopLine) {
Log.d(TAG, "-- onStopLineInfo:" + (stopLine != null ? stopLine.toString() : null) + "-> road_id:" + mRoadId);
if (stopLine != null && mRoadId != null && TextUtils.equals(mRoadId, stopLine.road_id)) {
MogoLocation location = CallerMapLocationListenerManager.INSTANCE.getCurrentLocation();
//MapAutoApi.INSTANCE.
}
}
private Context getContext() {
return mMapView.getContext();
}