Seeing that it\'s not officially possible to have a context menu which has a customized view or even icons for its rows (here), I decided to create my o
It has been a while since I have done this, but I think we had the same problem. Let me see if I can answer.
Not being able to make custom context menus for the EditText was one of the main reasons that I finally decided to create a library with custom components for Mongolian. Although the vertical Mongolian parts won't be useful to you, the concepts should be the same for other custom popups.
Here are a couple screenshots of what I have:
This one is a custom EditText that used a custom popup menu. It takes the user touch location to place the popup location.
The next one is a more general demonstration of different ways to set the popup location.
Both of these demos are included in the mongol-library demo app.
My custom menu was a PopupWindow subclass. You can find the source code here.
The way I placed it at a particular location was to use the showAtLocation
method, which as I recall is just a normal method on PopupWindow:
private void showMongolContextMenu(MongolMenu menu, int xTouchLocation, int yTouchLocation) {
float paddingPx = CONTEXT_MENU_TOUCH_PADDING_DP * getResources().getDisplayMetrics().density;
Rect menuSize = menu.getDesiredSize();
int y = yTouchLocation - menuSize.height() - (int) paddingPx;
menu.showAtLocation(this, Gravity.NO_GRAVITY, xTouchLocation, y);
}
That code is from here.
Oh, yes, and I also used this in custom keyboards:
See these classes for more:
How to set the popup window to appear near the touch location on the screen?
For this purpose, you need to find exact coordination where the user has touch the view so you need to use setOnTouchListener()
Try this way
You this PopupWindowHelper
PopupWindowHelper
import android.view.Gravity
import android.graphics.drawable.BitmapDrawable
import android.content.Context
import android.graphics.Rect
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.widget.LinearLayout
import android.widget.PopupWindow
class PopupWindowHelper(private val ctx: Context) {
private val tipWindow: PopupWindow?
private val contentView: View
private val inflater: LayoutInflater
internal val isTooltipShown: Boolean
get() = tipWindow != null && tipWindow.isShowing
init {
tipWindow = PopupWindow(ctx)
inflater = ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
contentView = inflater.inflate(R.layout.popup_window, null)
}
internal fun showToolTip(anchor: View, event: MotionEvent) {
tipWindow!!.height = LinearLayout.LayoutParams.WRAP_CONTENT
tipWindow.width = LinearLayout.LayoutParams.WRAP_CONTENT
tipWindow.isOutsideTouchable = true
tipWindow.isTouchable = true
tipWindow.isFocusable = true
tipWindow.setBackgroundDrawable(BitmapDrawable())
tipWindow.contentView = contentView
val screenPos = IntArray(2)
anchor.getLocationOnScreen(screenPos)
val anchorRect =
Rect(screenPos[0], screenPos[1], screenPos[0] + anchor.width, screenPos[1] + anchor.height)
contentView.measure(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val contentViewHeight = contentView.measuredHeight
val contentViewWidth = contentView.measuredWidth
val positionX = anchorRect.centerX() - contentViewWidth / 2
val positionY = anchorRect.bottom - anchorRect.height() / 2
tipWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, event.x.toInt(), positionY)
}
internal fun dismissTooltip() {
if (tipWindow != null && tipWindow.isShowing)
tipWindow.dismiss()
}
}
MainActivity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myRecyclerView.layoutManager=LinearLayoutManager(this)
myRecyclerView.setHasFixedSize(true)
myRecyclerView.adapter=DataAdapter(this)
}
}
DataAdapter
import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.row_layout.view.*
import android.view.MotionEvent
import android.view.View.OnTouchListener
class DataAdapter(context: Context) :
RecyclerView.Adapter<DataAdapter.ViewHolder>() {
val mContext = context
private var lastTouchDown: Long = 0
private val CLICK_ACTION_THRESHHOLD = 200
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(mContext)
.inflate(R.layout.row_layout, parent, false)
view.setOnTouchListener { myView, event ->
when (event?.action) {
MotionEvent.ACTION_DOWN -> lastTouchDown = System.currentTimeMillis()
MotionEvent.ACTION_UP -> if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD) {
val popupWindowHelper = PopupWindowHelper(mContext)
myView?.let {
popupWindowHelper.showToolTip(
it
, event
)
}
}
}
true
}
return ViewHolder(view)
}
override fun getItemCount(): Int {
return 30
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvDescription.text = "Row Description $position"
holder.tvTitle.text = "Row Title $position"
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvTitle = itemView.tvTitle
val tvDescription = itemView.tvDescription
}
}
You can find complete code from my GitHub repo