Bir görünümün tıklama hedefinin boyutunu artırmak için Android'de TouchDelegate'in nasıl kullanılacağına dair bir örnek var mı?


82

Anladığım kadarıyla, kolayca dokunulamayacak kadar küçük bir görünüme sahip olduğunuzda, o görünüm için tıklanabilir bölgeyi artırmak için bir TouchDelegate kullanmanız gerekir.

Bununla birlikte, Google'da kullanım örnekleri arandığında birden çok kişinin soruyu sorduğu, ancak birkaç yanıtın olduğu ortaya çıkar.

Tıklanabilir bölgesini her yönde 4 piksel artırmak için bir görünüm için bir dokunmatik delege ayarlamanın doğru yolunu bilen var mı?


3
TouchDelegate'in nasıl kullanılacağına dair ek bir güzel gönderi: groups.google.com/group/android-developers/browse_thread/thread/…
Mason Lee

Yanıtlar:


100

Google'dan bir arkadaşıma sordum ve TouchDelegate'i nasıl kullanacağımı anlamama yardım ettiler. İşte ortaya çıkardığımız şey:

final View parent = (View) delegate.getParent();
parent.post( new Runnable() {
    // Post in the parent's message queue to make sure the parent
    // lays out its children before we call getHitRect()
    public void run() {
        final Rect r = new Rect();
        delegate.getHitRect(r);
        r.top -= 4;
        r.bottom += 4;
        parent.setTouchDelegate( new TouchDelegate( r , delegate));
    }
});

16
Ya bir ekranda bunu yapmak istediğiniz iki düğme varsa? TouchDelegate'i tekrar ayarlarsanız, önceki dokunmatik temsilciyi siler.
cottonBallPaws

2
Bu benim için işe yaramadı ... Temsilcinin TouchDelegate'i eklemeye çalıştığım görünüm olduğunu varsayıyorum.
Kevin Parker

20
@AmokraneChentir Aynı sorunu yaşadım. TouchDelegates kullanmak yerine, düğmeyi genişleten bir sınıfım var. Bu sınıfta getHitRect'i geçersiz kılıyorum. Vuruş dikdörtgenini üst görünümün boyutuna genişletebilirsiniz. public void getHitRect (Rect outRect) {outRect.set (getLeft () - mPadding, getTop () - mPadding, getRight () + mPadding, getTop () + mPadding); }
Brendan Weinstein

5
@Amokrane: Görünüşe göre sunduğum çözüm ICS'de işe yaramıyor. İki farklı Touch Delege stratejisi üzerine bir blog yazısı yazdım brendanweinstein.me/2012/06/26/… ve her iki strateji için de örnek kodu github.com/brendanw/Touch-Delegates/blob/master/src/com/ brendan /…
Brendan Weinstein

8
@ZsoltSafrany için, bunu yapmanın daha güvenli bir yolu, bir OnGlobalLayoutListener aracılığıyla ve temsilciyi onGlobalLayout () atamaktır.
greg7gkb

20

Bunu, büyük ölçüde bu blog gönderisinden çizimle tek ekranda birden çok görünümle (onay kutuları) gerçekleştirebildim . Temel olarak, emmby'nin çözümünü alırsınız ve bunu her bir düğmeye ve onun ebeveynine ayrı ayrı uygularsınız.

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));
        }
   });
}

Benim durumumda, üst üste yerleştirilmiş onay kutuları olan görüntü görüntülemelerinin bir ızgara görünümüne sahiptim ve yöntemi şu şekilde adlandırdım:

CheckBox mCheckBox = (CheckBox) convertView.findViewById(R.id.checkBox1);
final ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView1);

// Increase checkbox clickable area
expandTouchArea(imageView, mCheckBox, 100);

Benim için harika çalışıyor.


11

Bu çözüm yorumlarda @BrendanWeinstein tarafından yayınlanmıştır.

Bir göndermek yerine kendi yönteminizi TouchDelegategeçersiz kılabilirsiniz (bir tane genişletmeniz durumunda).getHitRect(Rect)View

public class MyView extends View {  //NOTE: any other View can be used here

    /* a lot of required methods */

    @Override
    public void getHitRect(Rect r) {
        super.getHitRect(r); //get hit Rect of current View

        if(r == null) {
            return;
        }

        /* Manipulate with rect as you wish */
        r.top -= 10;
    }
}

İlginç fikir. Ama daha görünüm anlamına olmaz değil bir düzen oluşturulabilir? Hayır, bekleyin - eğer yeni bir görünüm sınıfı tasarlarsam ve bunu düzende kullanırsam. Işe yarayabilir. Denemeye değer.
Martin

10
@Martin bu doğru fikir. Ne yazık ki, getHitRect artık ICS tarafından dispatchTouchEvent grepcode.com/file/repository.grepcode.com/java/ext/… tarafından çağrılmamaktadır. GetHitRect'in geçersiz kılınması yalnızca zencefilli kurabiye ve altı için işe yarayacaktır.
Brendan Weinstein

6

emmby'nin yaklaşımı benim için işe yaramadı ama küçük değişikliklerden sonra işe yaradı:

private void initApplyButtonOnClick() {
    mApplyButton.setOnClickListener(onApplyClickListener);
    final View parent = (View)mApplyButton.getParent();
    parent.post(new Runnable() {

        @Override
        public void run() {
            final Rect hitRect = new Rect();
            parent.getHitRect(hitRect);
            hitRect.right = hitRect.right - hitRect.left;
            hitRect.bottom = hitRect.bottom - hitRect.top;
            hitRect.top = 0;
            hitRect.left = 0;
            parent.setTouchDelegate(new TouchDelegate(hitRect , mApplyButton));         
        }
    });
}

Belki birinin zamanını kurtarabilir


bu benim için çalıştı. aslında tüm üst öğenin alt öğeye tıklama olayları göndermesini sağlar. doğrudan ebeveyn olmak zorunda bile değildir (bir ebeveynin ebeveyni olabilir).
android geliştiricisi

Bu, devredilecek bir görüşünüz varsa muhtemelen işe yarar. Ya dokunma alanını geliştirmek için 50 görünümünüz varsa? - Bu Ekran görüntüsündeki gibi: plus.google.com/u/0/b/112127498414857833804/… - O halde bir TouchDelegate hala uygun bir çözüm mü?
Martin

2

@Mason Lee yorumuna göre, bu sorunumu çözdü. Projemin göreceli bir düzeni ve bir düğmesi vardı. Yani ebeveyn -> düzen ve çocuk -> bir düğmedir.

İşte bir google bağlantısı örneği google kodu

Çok değerli cevabının silinmesi durumunda cevabını buraya yazıyorum.

Yakın zamanda bir TouchDelegate'in nasıl kullanılacağı soruldu. Bu konuda biraz paslandım ve bununla ilgili iyi bir belge bulamadım. İşte küçük bir deneme yanılma sonrasında yazdığım kod. touch_delegate_view, touch_delegate_root kimliğine sahip basit bir RelativeLayout'tur. Düzenin tek bir alt öğesi olan delegated_button düğmesini tanımladım. Bu örnekte, düğmenin tıklanabilir alanını düğmemin üst kısmının 200 piksel yukarısına genişletiyorum.

public class TouchDelegateSample extends Activity {

  Button mButton;   
  @Override   
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.touch_delegate_view);
    mButton = (Button)findViewById(R.id.delegated_button);
    View parent = findViewById(R.id.touch_delegate_root);

    // post a runnable to the parent view's message queue so its run after
    // the view is drawn
    parent.post(new Runnable() {
      @Override
      public void run() {
        Rect delegateArea = new Rect();
        Button delegate = TouchDelegateSample.this.mButton;
        delegate.getHitRect(delegateArea);
        delegateArea.top -= 200;
        TouchDelegate expandedArea = new TouchDelegate(delegateArea, delegate);
        // give the delegate to an ancestor of the view we're delegating the
        // area to
        if (View.class.isInstance(delegate.getParent())) {
          ((View)delegate.getParent()).setTouchDelegate(expandedArea);
        }
      }
    });   
  } 
}

Sevgiler, Justin Android Ekibi @ Google

Benden birkaç kelime: Sol tarafı genişletmek istiyorsanız eksi ile değer verirsiniz ve nesnenin sağ tarafını genişletmek isterseniz artı ile değer verirsiniz. Bu, üstte ve altta aynı şekilde çalışır.


Buradaki sihirli ipucu tek bir düğme gibi görünüyor - Bir şekilde TouchDelegate'in birden çok düğme için kullanılamaz olduğu hissine kapılıyorum. Bunu onaylayabilir misin?
Martin

1

Bu belirli bileşene (Button) Padding vermek daha iyi bir fikir değil mi?


1
Bu daha büyük görmeyi sağlar. Bir düğmenin üzerinde tıklanabilir olmayan bir metin metniniz varsa ve bu metnin alanının düğme için tıklama doğruluğunu iyileştirmek için kullanılmasını istiyorsanız ne olur? - Bu ekran görüntüsündeki gibi: plus.google.com/u/0/b/112127498414857833804/…
Martin

1

Partiye biraz geç kaldım ama çok araştırdıktan sonra şimdi kullanıyorum:

/**
 * Expand the given child View's touchable area by the given padding, by
 * setting a TouchDelegate on the given ancestor View whenever its layout
 * changes.
 */*emphasized text*
public static void expandTouchArea(final View ancestorView,
    final View childView, final Rect padding) {

    ancestorView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                TouchDelegate delegate = null;

                if (childView.isShown()) {
                    // Get hitRect in parent's coordinates
                    Rect hitRect = new Rect();
                    childView.getHitRect(hitRect);

                    // Translate to ancestor's coordinates
                    int ancestorLoc[] = new int[2];
                    ancestorView.getLocationInWindow(ancestorLoc);

                    int parentLoc[] = new int[2];
                    ((View)childView.getParent()).getLocationInWindow(
                        parentLoc);

                    int xOffset = parentLoc[0] - ancestorLoc[0];
                    hitRect.left += xOffset;
                    hitRect.right += xOffset;

                    int yOffset = parentLoc[1] - ancestorLoc[1];
                    hitRect.top += yOffset;
                    hitRect.bottom += yOffset;

                    // Add padding
                    hitRect.top -= padding.top;
                    hitRect.bottom += padding.bottom;
                    hitRect.left -= padding.left;
                    hitRect.right += padding.right;

                    delegate = new TouchDelegate(hitRect, childView);
                }

                ancestorView.setTouchDelegate(delegate);
            }
        });
}

Bu, kabul edilen çözümden daha iyidir çünkü bir TouchDelegate'in yalnızca üst Görünümde değil, herhangi bir üst Görünümde de ayarlanmasına izin verir.

Kabul edilen çözümün aksine, aynı zamanda ata Görünümünün düzeninde bir değişiklik olduğunda TouchDelegate'i de günceller.


0

Bunu programlı olarak yapmak istemiyorsanız, görüntünün etrafında şeffaf bir alan oluşturun, Görüntüyü düğme (görünüm) için arka plan olarak kullanıyorsanız.

Gri alan, dokunma alanını artırmak için şeffaf olabilir.

görüntü açıklamasını buraya girin


1
Bu, başka hiçbir şey için kullanılamayacak bir görünüm etrafında boş alan yaratmaz mı? Bir düğmeyi kapatın ve metin alanını kullanarak düğme tıklama alanını geliştirmek için bilgi metnini görüntülemek isterseniz ne olur?
Martin

Bunun en iyi ve son yaklaşım olduğunu söylemiyorum. Ancak, küçük bir düğme görüntüsü göstermek istediğiniz ancak dokunma alanının genişletilmesini istediğiniz birçok senaryoya uyacaktır. Sizin durumunuzda, diğer görünümleri (metin görünümü) tıkladığınızda düğmeye öncelik vermek zordur, hepsi birlikte farklı bir zorluktur.
Vinayak Bevinakatti

0

Çoğu durumda, başka bir başsız görünümde (yapay şeffaf görünüm) daha büyük bir dokunma alanı gerektiren görünümü sarabilir ve sarıcı görünümüne dolgu / kenar boşluğu ekleyebilir ve tıklama / dokunma görünümünü, orijinal görünüm yerine sarıcı görünümüne ekleyebilirsiniz. daha geniş bir dokunma alanına sahip olması gerekir.


Bu, başka hiçbir şey için kullanılamayacak bir görünüm etrafında boş alan yaratmaz mı? Bir düğmeyi kapatın ve metin alanını kullanarak düğme tıklama alanını geliştirmek için bilgi metnini görüntülemek isterseniz ne olur?
Martin

@Martin, bunu nasıl yaptığına bağlı. Ve istediğiniz her şeyi yapmak kolay olmalıdır - tüm bu görünümlerin üzerine şeffaf bir görünümde istediğinizi (ancak istediğiniz kadar) sarın ve bu alana eşit bir tıklama atayın.
2013

0

Dokunma alanını genel olarak birkaç kısıtlamayla genişletmek için aşağıdaki kodu kullanın.

viewVerilen ancestorgörünümde verilen dokunma alanını verilen expansionpiksel cinsinden genişletmenize izin verir . Verilen görünüm atalar mizanpaj ağacında olduğu sürece herhangi bir atayı seçebilirsiniz.

    public static void expandTouchArea(final View view, final ViewGroup ancestor, final int expansion) {
    ancestor.post(new Runnable() {
        public void run() {
            Rect bounds = getRelativeBounds(view, ancestor);
            Rect expandedBounds = expand(bounds, expansion);
            // LOG.debug("Expanding touch area of {} within {} from {} by {}px to {}", view, ancestor, bounds, expansion, expandedBounds);
            ancestor.setTouchDelegate(new TouchDelegate(expandedBounds, view));
        }

        private Rect getRelativeBounds(View view, ViewGroup ancestor) {
            Point relativeLocation = getRelativeLocation(view, ancestor);
            return new Rect(relativeLocation.x, relativeLocation.y,
                            relativeLocation.x + view.getWidth(),
                            relativeLocation.y + view.getHeight());
        }

        private Point getRelativeLocation(View view, ViewGroup ancestor) {
            Point absoluteAncestorLocation = getAbsoluteLocation(ancestor);
            Point absoluteViewLocation = getAbsoluteLocation(view);
            return new Point(absoluteViewLocation.x - absoluteAncestorLocation.x,
                             absoluteViewLocation.y - absoluteAncestorLocation.y);
        }

        private Point getAbsoluteLocation(View view) {
            int[] absoluteLocation = new int[2];
            view.getLocationOnScreen(absoluteLocation);
            return new Point(absoluteLocation[0], absoluteLocation[1]);
        }

        private Rect expand(Rect rect, int by) {
            Rect expandedRect = new Rect(rect);
            expandedRect.left -= by;
            expandedRect.top -= by;
            expandedRect.right += by;
            expandedRect.bottom += by;
            return expandedRect;
        }
    });
}

Geçerli kısıtlamalar:

  • Dokunma alanı, atanın görünüme iletmek için dokunma olayını yakalayabilmesi gerektiğinden, görünümün atasının sınırlarını aşamaz.
  • Yalnızca biri TouchDelegatea olarak ayarlanabilir ViewGroup. Birden çok dokunmatik delege ile çalışmak istiyorsanız, farklı atalar seçin veya Çoklu TouchDelegate Nasıl Kullanılır ?

0

TouchDelegate'in dikdörtgeninin yeni boyutunu almak için düzen geçişini beklemekten hoşlanmadığım için farklı bir çözüme gittim:

public class TouchSizeIncreaser extends FrameLayout {
    public TouchSizeIncreaser(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final View child = getChildAt(0);
        if(child != null) {
            child.onTouchEvent(event);
        }
        return true;
    }
}

Ve sonra, bir düzende:

<ch.tutti.ui.util.TouchSizeIncreaser
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <Spinner
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
</ch.tutti.ui.util.TouchSizeIncreaser>

Buradaki fikir, TouchSizeIncreaser FrameLayout'un Spinner'ı (herhangi bir çocuk View olabilir) sarması ve içinde yakalanan tüm dokunma olaylarını alt View'e iletmesidir. Tıklamalar için çalışır, döndürücü sınırlarının dışına tıklansa bile açılır, diğer daha karmaşık durumlar için sonuçların ne olduğundan emin değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.