Чи є приклад того, як використовувати TouchDelegate в Android, щоб збільшити розмір цілі кліку представлення даних?


82

Я розумію, що коли у вас вигляд, який занадто малий, щоб його можна було легко торкнутися, ви повинні використовувати TouchDelegate для збільшення області, на яку можна натиснути.

Однак пошук прикладів використання в Google виявляє кілька людей, які задають питання, але мало відповідей.

Хтось знає, як правильно налаштувати делегата дотику для перегляду, скажімо, збільшення області, на яку можна натискати, на 4 пікселі в кожному напрямку?


3
Ось додатковий хороший допис про те, як користуватися TouchDelegate: groups.google.com/group/android-developers/browse_thread/thread/…
Мейсон Лі

Відповіді:


100

Я запитав друга в Google, і вони змогли допомогти мені зрозуміти, як користуватися TouchDelegate. Ось що ми придумали:

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
Що робити, якщо на одному екрані є дві кнопки, для яких ви хотіли б це зробити? Якщо ви встановитеTouchDelegate знову, він видалить попередній делегат дотику.
cottonBallPaws

2
Це мені не вдалося ... Я припускаю, що делегат - це подання, до якого я намагаюся додати TouchDelegate.
Кевін Паркер,

20
@AmokraneChentir У мене була та сама проблема. Замість того, щоб використовувати TouchDelegates, у мене є клас, який розширює кнопку. У цьому класі я перевизначаю getHitRect. Ви можете розширити прямокутник звернення до розміру батьківського подання. public void getHitRect (Rect outRect) {outRect.set (getLeft () - mPadding, getTop () - mPadding, getRight () + mPadding, getTop () + mPadding); }
Брендан Вайнштейн

5
@Amokrane: Виявляється, запропоноване мною рішення не працює в ICS. Я написав повідомлення в блозі про дві різні стратегії Touch Delegate brendanweinstein.me/2012/06/26/…, і ви можете отримати зразок коду для обох стратегій на github.com/brendanw/Touch-Delegates/blob/master/src/com/ brendan /…
Brendan Weinstein

8
Відповідно до @ZsoltSafrany, безпечніший спосіб зробити це - за допомогою OnGlobalLayoutListener і призначити делегата onGlobalLayout ().
greg7gkb

20

Мені вдалося досягти цього за допомогою декількох переглядів (прапорців) на одному екрані, в основному з цього повідомлення в блозі . В основному ви приймаєте рішення emmby і застосовуєте його до кожної кнопки та її батьківського індивідуально.

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

У моєму випадку я мав сітку подання зображень із прапорцями, накладеними зверху, і викликав метод наступним чином:

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

Чудово працює для мене.


11

Це рішення опублікував @BrendanWeinstein в коментарях.

Замість надсилання a TouchDelegateви можете замінити 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;
    }
}

Цікава ідея. Але чи не означає це, що вигляд не можна створити з макета? Ні, зачекайте - якщо я буду розробляти новий клас перегляду і використовувати це в макеті. Це може спрацювати. Варто спробувати.
Мартін

10
@Martin це правильна ідея. На жаль, getHitRect з ICS більше не викликається dispatchTouchEvent grepcode.com/file/repository.grepcode.com/java/ext/... Перевизначення getHitRect буде працювати лише для пряників і нижче.
Брендан Вайнштейн

6

Підтвердження ембі не працювало для мене, але після невеликих змін:

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

Можливо, це може врятувати чийсь час


це те, що мені вдалося. це фактично змушує всього батька надсилати дитині події кліків. він навіть не повинен бути безпосереднім батьком (може бути батьком батьків).
розробник android

Це, мабуть, працює, якщо у вас є одне представлення для делегування. що, якщо у вас є, скажімо, 50 переглядів для покращення зони дотику? - Як на цьому знімку екрана: plus.google.com/u/0/b/112127498414857833804/… - Чи є TouchDelegate тоді як і раніше відповідним рішенням?
Мартін

2

Згідно з коментарем @Mason Lee, це вирішило мою проблему. Мій проект мав відносне розташування та одну кнопку. Отже батьків - це -> макет, а дочірній -> кнопка.

Ось приклад посилання на Google, коду google

У разі видалення його дуже цінної відповіді я поміщаю тут його відповідь.

Нещодавно мене запитали про те, як користуватися TouchDelegate. Я трохи поспішав з цього приводу, і не зміг знайти хорошої документації. Ось код, який я написав після невеликих спроб і помилок. touch_delegate_view - це простий RelativeLayout з ідентифікатором touch_delegate_root. Я визначив за допомогою однієї, дочірньої частини макета кнопку delegated_button. У цьому прикладі я розширюю клікабельну область кнопки до 200 пікселів над верхньою частиною моєї кнопки.

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

Вітаємо, Джастін, команда Android @ Google

Кілька слів від мене: якщо ви хочете розгорнути ліву сторону, ви даєте значення з мінусом, а якщо ви хочете розширити праву сторону об’єкта, ви надаєте значення з плюсом. Це однаково працює з верхом і низом.


Чарівний натяк тут, здається, одна кнопка - Якось у мене складається відчуття, що TouchDelegate непридатний для використання кількох кнопок. Ви можете це підтвердити?
Мартін

1

Хіба це не краща ідея надати відступ для цього конкретного компонента (Кнопка).


1
Це зробило б вигляд більшим. Що робити, якщо над кнопкою у вас є текст тексту, що не натискається, і ви хочете, щоб область цього тексту використовувалася для підвищення точності натискання кнопки. - Як на цьому знімку екрана: plus.google.com/u/0/b/112127498414857833804/…
Мартін,

1

Трохи пізно на вечірку, але після довгих досліджень я зараз використовую:

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

Це краще, ніж прийняте рішення, оскільки воно також дозволяє встановлювати TouchDelegate у будь-якому поданні предків, а не лише в батьківському поданні.

На відміну від прийнятого рішення, він також оновлює TouchDelegate щоразу, коли змінюється макет подання предків.


0

Якщо ви не хочете робити це програмно, просто створіть прозору область навколо зображення, якщо ви використовуєте зображення як фон для кнопки (перегляду).

Сіра зона може бути прозорою для збільшення області дотику.

введіть тут опис зображення


1
Хіба це не створить порожній простір навколо тодішнього перегляду, який не можна використовувати ні для чого іншого? Що робити, якщо ви хочете відобразити інформаційний текст, закрийте кнопку і використовуйте текстову область для покращення області натискання кнопки?
Мартін

Я не кажу, що це найкращий і остаточний підхід. Але це підходить для багатьох сценаріїв, коли ви хочете показати крихітне зображення кнопки, але область дотику має бути розширена. У вашому випадку важко надати перевагу кнопці, коли ви натискаєте інші подання (текстовий перегляд), це все разом інша проблема.
Вінаяк Бевінакатті

0

У більшості випадків ви можете обернути вигляд, для якого потрібна більша область дотику, в інший безголовий вигляд (штучний прозорий вигляд) і додати відступ / поле до подання обгортки та прикріпити клацання / дотик навіть до вигляду обгортки замість початкового вигляду, який має мати більшу площу дотику.


Хіба це не створить порожній простір навколо тодішнього перегляду, який не можна використовувати ні для чого іншого? Що робити, якщо ви хочете відобразити інформаційний текст, закрийте кнопку і використовуйте текстову область для покращення області натискання кнопки?
Мартін

@Martin, це залежить від того, як ти це робиш. І легко робити все, що завгодно - просто оберніть все, що завгодно (скільки завгодно переглядів), у прозорий вигляд поверх усіх цих подань і призначте клацання навіть цій області.
inteist

0

Для загального розширення області дотику за допомогою незначних обмежень використовуйте наступний код.

Це дозволяє розширити область торкання даного viewв межах даного ancestorвиду на подані expansionв пікселях. Ви можете вибрати будь-якого предка, якщо вказаний вигляд знаходиться в дереві макета предків.

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

Обмеження, що застосовуються:

  • Область дотику не може перевищувати межі предка подання, оскільки предок повинен мати можливість вловити подію дотику, щоб переслати її у вигляд.
  • Тільки для одного TouchDelegateможна встановити значення a ViewGroup. Якщо ви хочете працювати з кількома делегатами дотику, виберіть інших предків або скористайтесь складовим делегатом дотику, як описано в розділі Як використовувати кілька дотиків дотику .

0

Оскільки мені не сподобалася ідея чекати пропуску макета лише для того, щоб отримати новий розмір прямокутника TouchDelegate, я пішов за іншим рішенням:

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

А потім у макеті:

<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>

Ідея полягає в тому, що TouchSizeIncreaser FrameLayout оберне Spinner (може бути будь-яким дочірнім видом) і перенаправить всі події дотику, зафіксовані в його прямому зверненні, до дочірнього View. Він працює для клацань, блешня відкривається, навіть якщо клацнути поза її межами, не впевнений, які наслідки мають інші складніші випадки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.