如何使用多个TouchDelegate

我有两个ImageButtons,每个都在RelativeLayout中,这两个RelativeLayout在另一个RelativeLayout中,我想为每个ImageButton设置TouchDelegate。 如果通常我将TouchDelegate添加到每个ImageButton并且它的父RelativeLayout然后只有一个ImageButton正常工作,另一个不扩展它的点击区域。 因此,PLease帮助我了解如何在两个ImageButtons中使用TouchDelegate。 如果不可能,那么扩展视图点击区域的有效方法是什么? 提前致谢 ……..

这是我的xml代码:

          

我的Activity类:

 import android.app.Activity; import android.graphics.Rect; import android.os.Bundle; import android.view.TouchDelegate; import android.view.View; import android.widget.ImageButton; import android.widget.Toast; public class TestTouchDelegate extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); View mParent1 = findViewById(R.id.relativeLayout1); mParent1.post(new Runnable() { @Override public void run() { Rect bounds1 = new Rect(); ImageButton mTutorialButton1 = (ImageButton) findViewById(R.id.tutorial1); mTutorialButton1.setEnabled(true); mTutorialButton1.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { Toast.makeText(TestTouchDelegate.this, "Test TouchDelegate 1", Toast.LENGTH_SHORT).show(); } }); mTutorialButton1.getHitRect(bounds1); bounds1.right += 50; TouchDelegate touchDelegate1 = new TouchDelegate(bounds1, mTutorialButton1); if (View.class.isInstance(mTutorialButton1.getParent())) { ((View) mTutorialButton1.getParent()).setTouchDelegate(touchDelegate1); } } }); //View mParent = findViewById(R.id.FrameContainer); View mParent2 = findViewById(R.id.relativeLayout2); mParent2.post(new Runnable() { @Override public void run() { Rect bounds2 = new Rect(); ImageButton mTutorialButton2 = (ImageButton) findViewById(R.id.tutorial2); mTutorialButton2.setEnabled(true); mTutorialButton2.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { Toast.makeText(TestTouchDelegate.this, "Test TouchDelegate 2", Toast.LENGTH_SHORT).show(); } }); mTutorialButton2.getHitRect(bounds2); bounds2.left += 50; TouchDelegate touchDelegate2 = new TouchDelegate(bounds2, mTutorialButton2); if (View.class.isInstance(mTutorialButton2.getParent())) { ((View) mTutorialButton2.getParent()).setTouchDelegate(touchDelegate2); } } }); } 

}

您可以使用复合模式将多个TouchDelegate添加到View 。 脚步:

  1. 创建TouchDelegateComposite (无论您将作为parameter passing给哪个视图,它仅用于获取上下文)
  2. 创建必要的TouchDelegates并将它们添加到复合
  3. 添加复合到这里推荐的视图(通过view.post(new Runnable)

     public class TouchDelegateComposite extends TouchDelegate { private final List delegates = new ArrayList(); private static final Rect emptyRect = new Rect(); public TouchDelegateComposite(View view) { super(emptyRect, view); } public void addDelegate(TouchDelegate delegate) { if (delegate != null) { delegates.add(delegate); } } @Override public boolean onTouchEvent(MotionEvent event) { boolean res = false; float x = event.getX(); float y = event.getY(); for (TouchDelegate delegate : delegates) { event.setLocation(x, y); res = delegate.onTouchEvent(event) || res; } return res; } } 

每个视图只应该有一个触摸代理。 View类中getTouchDelegate()的文档如下:

“获取此视图的TouchDelegate。”

只有一个TouchDelegate。 要在每个视图中仅使用一个TouchDelegate,您可以在视图中包装每个可触摸视图,其尺寸反映了您想要触摸的内容。 广场上的Android开发人员举例说明了如何使用一种静态方法(http://www.youtube.com/watch?v=jF6Ad4GYjRU&t=37m4s)为多个视图执行此操作:

  public static void expandTouchArea(final View bigView, final View smallView, final int extraPadding) { bigView.post(new Runnable() { @Override public void run() { Rect rect = new Rect(); smallView.getHitRect(rect); rect.top -= extraPadding; rect.left -= extraPadding; rect.right += extraPadding; rect.bottom += extraPadding; bigView.setTouchDelegate(new TouchDelegate(rect, smallView)); } }); 

}

假设您不希望混淆视图层次结构。 我还能想到另外两个选择。 您可以在可触摸视图中定义可触摸内容的边界,并确保将所有touchevents从各个父视图传递到该子视图。 或者,您可以为可触摸视图覆盖getHitRect()。 前者会很快混乱你的代码并使其难以理解,因此后者是更好的前进方式。 你想要重写getHitRect。

其中mPadding是您希望在视图周围可以触摸的额外区域的数量,您可以使用以下内容:

  @Override public void getHitRect(Rect outRect) { outRect.set(getLeft() - mPadding, getTop() - mPadding, getRight() + mPadding, getTop() + mPadding); } 

如果您使用上述代码,则必须考虑附近的可触摸视图。 堆栈中最高的视图的可触摸区域可能在另一个视图的顶部重叠。

另一个类似的选择是仅更改可触摸视图的填充。 我不喜欢这个解决方案,因为很难跟踪视图的大小调整。

这似乎对我有用http://cyrilmottier.com/2012/02/16/listview-tips-tricks-5-enlarged-touchable-areas/

要使代码正常工作,您需要减少bounds2 ,而不是增加它。

 bounds2.left -= 50; 

在玩TouchDelegate ,我来到了下面的代码,它适用于任何Android版本。 诀窍是在调用布局后扩展区域保证。

 public class ViewUtils { public static final void extendTouchArea(final View view, final int padding) { view.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @Override @SuppressWarnings("deprecation") public void onGlobalLayout() { final Rect touchArea = new Rect(); view.getHitRect(touchArea); touchArea.top -= padding; touchArea.bottom += padding; touchArea.left -= padding; touchArea.right += padding; final TouchDelegate touchDelegate = new TouchDelegate(touchArea, view); final View parent = (View) view.getParent(); parent.setTouchDelegate(touchDelegate); view.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); } } 

好吧,我想没有人提供真正的答案来解决它并使其变得简单。最后我有同样的问题和阅读以上所有我不知道如何才能使它工作。但最后我做到了。首先要保持你的想法!让我们假装你有一个完整的布局,它拿着你的两个小按钮,哪个区域必须扩展,所以你必须在你的主布局中做另一个布局,并把另一个按钮放到你新创建的布局,所以在这种情况下用静态方法你可以同时为2个按钮提供触摸委托。现在更加深入并逐步进入代码! 首先你肯定只是find你的MAINLAYOUT和Button的视图。(这个布局将保留我们的第一个按钮)

 RelativeLayout mymainlayout = (RelativeLayout)findViewById(R.id.mymainlayout) Button mybutoninthislayout = (Button)findViewById(R.id.mybutton) 

好吧,我们find了主要布局及其按钮视图,它将保存所有内容,只是我们的onCreate()显示布局,但你必须find以后再使用它。接下来是什么?我们在主要布局中创建另一个RelativeLayout,宽度和高度符合您的口味(这个新创建的布局将保留我们的第二个按钮)

 RelativeLayout myinnerlayout = (RelativeLayout)findViewById(R.id.myinnerlayout) Button mybuttoninsideinnerlayout = (Button)findViewById(R.id.mysecondbutton) 

好的,我们已经完成了查看视图,所以我们现在可以复制并粘贴我们首先回答您问题的人的代码。只需在主要活动中复制该代码。

  public static void expandTouchArea(final View bigView, final View smallView, final int extraPadding) { bigView.post(new Runnable() { @Override public void run() { Rect rect = new Rect(); smallView.getHitRect(rect); rect.top -= extraPadding; rect.left -= extraPadding; rect.right += extraPadding; rect.bottom += extraPadding; bigView.setTouchDelegate(new TouchDelegate(rect, smallView)); } }); } 

现在如何使用此方法并使其工作?这是如何! 在onCreate()方法上粘贴下一个代码段

  expandTouchArea(mymainlayout,mybutoninthislayout,60); expandTouchArea(myinnerlayout, mybuttoninsideinnerlayout,60); 

解释我们在这个snipped中做了什么。我们创建了名为expandTouchArea()静态方法并给出了3个参数。 第1个参数 – 保持必须扩展区域的按钮的布局第2个参数 – 扩展区域的实际按钮第3个参数 – 想要按钮区域扩大多少个像素的区域! 请享用!

我遇到了同样的问题:尝试为不同的LinearLayouts添加多个TouchDelegates ,它们将触摸事件分别路由到单独的Switches ,在一个布局中。

有关详细信息,请参阅我提出的问题和我的答案 。

我发现的是:一旦我将LinearLayouts分别用另一个 LinearLayout包围,第二个(每隔一个连续结束) TouchDelegate开始按预期工作。

因此,这可能有助于OP为他的问题创建一个可行的解决方案。 不过,我对它为什么会这样做有一个令人满意的解释。

我复制了TouchDelegate的代码并做了一些修改。 现在它可以支持多个视图,无论这些视图是否有共同的父母

 class MyTouchDelegate: TouchDelegate { private var mDelegateViews = ArrayList() private var mBoundses = ArrayList() private var mSlopBoundses = ArrayList() private var mDelegateTargeted: Boolean = false val ABOVE = 1 val BELOW = 2 val TO_LEFT = 4 val TO_RIGHT = 8 private var mSlop: Int = 0 constructor(context: Context): super(Rect(), View(context)) { mSlop = ViewConfiguration.get(context).scaledTouchSlop } fun addTouchDelegate(delegateView: View, bounds: Rect) { val slopBounds = Rect(bounds) slopBounds.inset(-mSlop, -mSlop) mDelegateViews.add(delegateView) mSlopBoundses.add(slopBounds) mBoundses.add(Rect(bounds)) } var targetIndex = -1 override fun onTouchEvent(event: MotionEvent): Boolean { val x = event.x.toInt() val y = event.y.toInt() var sendToDelegate = false var hit = true var handled = false when (event.action) { MotionEvent.ACTION_DOWN -> { targetIndex = -1 for ((index, item) in mBoundses.withIndex()) { if (item.contains(x, y)) { mDelegateTargeted = true targetIndex = index sendToDelegate = true } } } MotionEvent.ACTION_UP, MotionEvent.ACTION_MOVE -> { sendToDelegate = mDelegateTargeted if (sendToDelegate) { val slopBounds = mSlopBoundses[targetIndex] if (!slopBounds.contains(x, y)) { hit = false } } } MotionEvent.ACTION_CANCEL -> { sendToDelegate = mDelegateTargeted mDelegateTargeted = false } } if (sendToDelegate) { val delegateView = mDelegateViews[targetIndex] if (hit) { // Offset event coordinates to be inside the target view event.setLocation((delegateView.width / 2).toFloat(), (delegateView.height / 2).toFloat()) } else { // Offset event coordinates to be outside the target view (in case it does // something like tracking pressed state) val slop = mSlop event.setLocation((-(slop * 2)).toFloat(), (-(slop * 2)).toFloat()) } handled = delegateView.dispatchTouchEvent(event) } return handled } 

}

像这样使用它

  fun expandTouchArea(viewList: List, touchSlop: Int) { val rect = Rect() viewList.forEach { it.getHitRect(rect) if (rect.left == rect.right && rect.top == rect.bottom) { postDelay(Runnable { expandTouchArea(viewList, touchSlop) }, 200) return } rect.top -= touchSlop rect.left -= touchSlop rect.right += touchSlop rect.bottom += touchSlop val parent = it.parent as? View if (parent != null) { val parentDelegate = parent.touchDelegate if (parentDelegate != null) { (parentDelegate as? MyTouchDelegate)?.addTouchDelegate(it, rect) } else { val touchDelegate = MyTouchDelegate(this) touchDelegate.addTouchDelegate(it, rect) parent.touchDelegate = touchDelegate } } } }