如何像在使用ContextMenu时一样在触摸位置附近显示自定义的Popup窗口? [英] How to show a customized Popup window near the touch location, like what we have when we use ContextMenu?

查看:109
本文介绍了如何像在使用ContextMenu时一样在触摸位置附近显示自定义的Popup窗口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景



看到正式使用上下文菜单的行自定义视图甚至图标是不可能的(



但是,我找不到如何做到这一点的方法



我尝试过的事情



显示PopupWindow可以通过长时间触摸来完成,因为例如:

  onCreateViewHolder(parent:ViewGroup,viewType:Int)的替代乐趣:ViewHolder {
val inflater = LayoutInflater。 from(context)
valholder = ViewHolder(inflater.inflate(R.layout.list_item_main,parent,false))
holder.itemView.setOnLongClickListener {
val contextMenuView = inflater.inflate(R .layout.context_menu,null)
val popupWindow = PopupWindow(contextMenuView,ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT,true)
popupWindow.showAsDropDown(holder.itemView,0,0);
真实的
}
返回持有者
}

而且,如果您想要一个好的背景而不是透明的,可以使用



问题




  1. 如何将弹出窗口设置为显示在屏幕上的触摸位置附近,而无需甚至处理onTouch事件,就像使用ContextMenu在示例上所做的一样?


  2. 上下文菜单(或类似菜单)是否具有我可以获取的一些属性,可以将其设置为显示的最大大小(简而言之:默认最大宽度)?如果可以,该如何使用?如何设置宽度和高度以考虑膨胀视图之一?



解决方案


如何设置弹出窗口显示在屏幕上的触摸位置附近?


为此,您需要在用户触摸视图的位置找到精确的协调,因此需要使用 setOnTouchListener()



尝试这种方式



您这个 PopupWindowHelper



< blockquote>

PopupWindowHelper




  import android.view.Gravity 
导入android.graphics.drawable.BitmapDrawable
导入android.content.Context
导入android.graphics.Rect
导入android.view.LayoutInflater
导入android.view.MotionEvent
导入android.view.View
导入android.widget.LinearLayout
导入android.widget.PopupWindow

class PopupWindowHelper(priv val ctx:Context ){
private val tipWindow:PopupWindow?
私有val内容查看器:查看
私有val充气器:LayoutInflater

内部val isTooltipShown:布尔值
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)
}

内部乐趣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)
锚点。 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)

}

内部乐趣dismissTooltip(){
if(tipWindow!= null& tipWindow.isShowing)
tipWindow.dismiss()
}


}




MainActivity




 导入androidx.appcompat.app.AppCompatActivity 
导入android.os.Bundle
导入androidx.recyclerview.widget.LinearLayoutManager
导入kotlinx.android.synthetic.main.activity_main。*

类MainActivity:AppCompatActivity(){

重写fun onCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main )
myRecyclerView.layoutManager = LinearLayoutManager(this)
myRecyclerView.setHasFixedSize(true)
myRecyclerView.adapter = DataAdapter(this)
}
}




DataAdapter




 导入android.content.Context 
导入android.util.Log
导入andro id.view.LayoutInflater
导入android.view.View
导入android.view.ViewGroup
导入androidx.recyclerview.widget.RecyclerView
导入kotlinx.android.synthetic.main。 row_layout.view。*
导入android.view.MotionEvent
导入android.view.View.OnTouchListener

类DataAdapter(context:Context):
RecyclerView.Adapter< ; DataAdapter.ViewHolder>(){
val mContext = context
private var lastTouchDown:Long = 0
private val CLICK_ACTION_THRESHHOLD = 200

覆盖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->如果(System.currentTimeMillis()-lastTouchDown< CLICK_ACTION_THRESHHOLD){
val popupWindowHelper = PopupWindowHelper(mContext)
myView?.let {
popupWindowHelper.showToolTip(
it
,事件

}
}
}

}
return ViewHolder(view)
}

重写fun getItemCount():Int {
return 30
}

重写fun onBindViewHolder(holder:ViewHolder,position:Int){

holder.tvDescription.text =行描述$ position
holder.tvTitle.text =行标题$ position

}

内部类ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
val tvTitle = itemView.tvTitle
val tvDescription = itemView.tv的描述
}
}

您可以在中找到完整的代码href = https://github.com/RathodNilesh14/PopupWindowIn_RecyclerViewItem rel = nofollow noreferrer>我的GitHub存储库


Background

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 own solution (of custom view that acts like it).

The problem

When using a context menu on a RecyclerView, the touch position matters, so if you long touch an item, the context menu will try to appear near the touch location (sample taken from here), and without me giving this information (meaning via OnClickListener or onLongClickListener ) :

However, I can't find how to do this in the more basic classes.

What I've tried

Showing a PopupWindow can be done via long touch, as such:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val inflater = LayoutInflater.from(context)
    val holder = ViewHolder(inflater.inflate(R.layout.list_item_main, parent, false))
    holder.itemView.setOnLongClickListener {
        val contextMenuView=inflater.inflate(R.layout.context_menu,null)
        val popupWindow = PopupWindow(contextMenuView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true)
        popupWindow.showAsDropDown(holder.itemView,0,0);
        true
    }
    return holder
}

And, if you want to have a nice background for it instead of being transparent, you could use a workaround, of ListPopupWindow, and if you don't want a list, you can just set its promptView , as such (code available here) :

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val inflater = LayoutInflater.from(context)
    val holder = ViewHolder(inflater.inflate(R.layout.list_item_main, parent, false))
    val maxAllowedPopupWidth = context.resources.displayMetrics.widthPixels * 90 / 100
    holder.itemView.setOnLongClickListener {
        val contextMenuView = inflater.inflate(R.layout.context_menu, null)
        val listPopupWindow = ListPopupWindow(context)
        contextMenuView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
        val width = min(maxAllowedPopupWidth, contextMenuView.measuredWidth)
        listPopupWindow.setPromptView(contextMenuView)
        listPopupWindow.setContentWidth(width)
        listPopupWindow.anchorView = it
        listPopupWindow.show()
        true
    }
    return holder
}

I'm not sure about the max width that I've calculated, because I can't find what's the maximum size that a popup can have. I know that the context menu has some maximum and then it just truncates the text for some reason. Maybe it's the same as of Dialog? Except that for dialog I could find a maximum width, yet I've found a minimal : windowMinWidthMajor and windowMinWidthMinor.

But back to the issue: I can't find any function here that's related to putting the popup near the touch location.

So this is what I get, for example:

The questions

  1. How to set the popup window to appear near the touch location on the screen, without even handling onTouch event, as done on the sample using ContextMenu ?

  2. Does the context menu (or similar) have some attribute that I can get, to set as the max size for what I show (in short: a default max width) ? If so, how do I use it? How can I set the width&height to consider the one of the inflated view?

解决方案

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

这篇关于如何像在使用ContextMenu时一样在触摸位置附近显示自定义的Popup窗口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆