2016-12-23 3 views
1

Я хочу получить следующие возможности:Как получить выбранный ребенок в GridLayout подобно GridView

  • Выберите только один ребенок View внутри GridLayout каждый раз долго нажав на него.
  • Нажмите на GridLayout или любой родитель предка в визуальной иерархии отменит выбор выбранного дочернего View, если вы уже выбрали.

Проблема заключается в том, когда при регистрации View.OnLongClickListener обратного вызова для ребенка View, ни один из родителей GridLayout, ни каких-либо предков зарегистрированы обратные вызовы (либо View.OnClickListener или View.onTouchEvent) вызывается при нажатии на них.

Как я могу получить выбранного ребенка внутри GridLayout, аналогичный AdapterView.OnItemSelectedListener или AdapterView.OnItemLongClickListener и решить вышеупомянутую проблему?

ответ

4

насчет сохранения «выбранного» вида как глобальные переменные, и удалить его, когда его фокус меняется? Играя с focusable, focusableInTouchMode и onClick слушателей, вы сможете выбрать нужный результат. Я не уверен, что это лучшее решение, но оно работает.

Что вам понадобится:

  • глобальная переменная Посмотреть: ребенок GridLayout «s долго щелкнул, как выбрано.
  • (опционально)Пользовательский родительский контейнер как любой ViewGroup: он установит фокусируемые слушателей на всех своих детей [*]. В моих тестах я использовал LinearLayout и RelativeLayout.

[*] Если вы не используете дополнительный родительский пользовательский класс, вы должны установить android:focusable="true" и android:focusableInTouchMode="true" на всех детей материнской ViewGroup. И вам нужно будет установить OnClickListener, чтобы позвонить removeViewSelected() при щелчке по родительской группе ViewGroup.

  • Добавление Нажмите слушателей для GridLayout детей: обновляющий выбранный вид.
  • Реализация Фокус-слушатель: удаляет выбранный вид, если он теряет фокус.

Он будет обрабатывать все состояния изменения фокуса на родителя и иерархии ребенка, увидеть выход:

GridLayout selected view and disable view on click listeners

я использовал следующую схему:

CoordinatorLayout   --- simple root group 
    ParentLayout   --- aka "parentlayout" 
     Button   --- simple Button example 
     GridLayout  --- aka "gridlayout" 
    FloattingActionButton --- simple Button example 

Давайте его получения выбранный View и его методы обновления в Activity:

private View selectedView; 

... 
private void setViewSelected(View view) { 
    removeViewSelected(); 

    selectedView = view; 
    if (selectedView != null) { 
     // change to a selected background for example 
     selectedView.setBackgroundColor(
       ContextCompat.getColor(this, R.color.colorAccent)); 
    } 
} 

private View getViewSelected() { 
    if (selectedView != null) { 
     return selectedView; 
    } 
    return null; 
} 

private void removeViewSelected() { 
    if (selectedView != null) { 
     // reset the original background for example 
     selectedView.setBackgroundResource(R.drawable.white_with_borders); 
     selectedView = null; 
    } 
    // clear and reset the focus on the parent 
    parentlayout.clearFocus(); 
    parentlayout.requestFocus(); 
} 

На каждом GridLayout ребенка, добавьте Click и LongClick слушателей, чтобы обновить или удалить выбранный вид. Mine были TextView s добавлены динамически, но вы можете легко создать для цикла, чтобы получить детей:

TextView tv = new TextView(this); 
... 
gridlayout.addView(tv); 

tv.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View view) { 
     removeViewSelected(); 
    } 
}); 

tv.setOnLongClickListener(new View.OnLongClickListener() { 
    @Override 
    public boolean onLongClick(View view) { 
     setViewSelected(view); 
     return true; 
    } 
}); 

Установите FocusChange слушателем на родительском контейнере:

parentlayout.setOnFocusChangeListener(new View.OnFocusChangeListener() { 
    @Override 
    public void onFocusChange(View view, boolean hasFocus) { 
     View viewSelected = getViewSelected(); 
     // if the selected view exists and it lost focus 
     if (viewSelected != null && !viewSelected.hasFocus()) { 
      // remove it 
      removeViewSelected(); 
     } 
    } 
}); 

Затем дополнительный пользовательский ViewGroup : это необязательно, потому что вы можете установить состояние focusable по XML и прослушивателю clickable динамически, но мне кажется, что это проще. Я использовал этот следующий обычай Class как родительский контейнер:

public class ParentLayout extends RelativeLayout implements View.OnClickListener { 

    public ParentLayout(Context context) { 
     super(context); 
     init(); 
    } 

    public ParentLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(); 
    } 

    public ParentLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     init(); 
    } 

    // handle focus and click states 
    public void init() { 
     setFocusable(true); 
     setFocusableInTouchMode(true); 
     setOnClickListener(this); 
    } 

    // when positioning all children within this 
    // layout, add their focusable state 
    @Override 
    protected void onLayout(boolean c, int l, int t, int r, int b) { 
     super.onLayout(c, l, t, r, b); 

     final int count = getChildCount(); 
     for (int i = 0; i < count; i++) { 
      final View child = getChildAt(i); 
      child.setFocusable(true); 
      child.setFocusableInTouchMode(true); 
     } 
     // now, even the Button has a focusable state 
    } 

    // handle the click events 
    @Override 
    public void onClick(View view) { 
     // clear and set the focus on this viewgroup 
     this.clearFocus(); 
     this.requestFocus(); 
     // now, the focus listener in Activity will handle 
     // the focus change state when this layout is clicked 
    } 
} 

Например, это макет я использовал:

<?xml version="1.0" encoding="utf-8"?> 
<android.support.design.widget.CoordinatorLayout ...> 

    <com.app.ParentLayout 
     android:id="@+id/parent_layout" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:gravity="center_horizontal"> 

     <Button 
      android:id="@+id/sample_button" 
      android:layout_width="250dp" 
      android:layout_height="wrap_content" 
      android:layout_centerHorizontal="true" 
      android:layout_alignParentBottom="true" 
      android:text="A Simple Button" 
      android:layout_marginTop="20dp" 
      android:layout_marginBottom="20dp"/> 

     <android.support.v7.widget.GridLayout 
      android:id="@+id/grid_layout" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:layout_centerHorizontal="true" 
      android:layout_above="@id/sample_button" .../> 
    </com.app.ParentLayout> 

    <android.support.design.widget.FloatingActionButton .../> 
</android.support.design.widget.CoordinatorLayout> 

Надеется, что это будет полезно.

+0

Большое спасибо за ваш подробный ответ. После реализации вашего решения _without_ реализации ** ParentLayout ** (1. Использовал экземпляр 'ViewGroup' и раздул XML вместо' ParentLayout' 2. Объявил настройки в завышенном XML вместо внутренних 'onLayout' и' init' методов 3. Зарегистрировал и обработанный 'View.OnClickListener' с расширенным экземпляром' ViewGroup' вместо 'ParentLayout'), кажется, что для того, чтобы _избрать уже выбранный дочерний элемент с помощью щелчка на макете, вам необходимо ** двойное касание ** макет вместо * * одиночный touch ** it. Вы знаете, как его решить? – Eido95

+1

@ Eido95, я внес изменения, чтобы использовать непосредственно «LinearLayout», писать «focusable», «focusableInTouchMode» для детей в XML, а также пытаться использовать «RelativeLayout», создавать 'OnClickListener' на' LinearLayout' вызове 'removeViewSelected() '... все работает так, как ожидалось ** с одним касанием **. Не могли бы вы установить некоторые «Журналы», чтобы увидеть, вызвано ли все? Возможно, см. Этот [связанный вопрос] (http://stackoverflow.com/questions/16792238/onclicklistener-not-triggered-for-custom-view). Если вы не нашли, разместите свой код на Pastebin и свяжите его; Я мог бы проверить это. У меня нет проблем без использования пользовательского класса. – Fllo

+0

Я прочитал вашу связанную с этим проблему, но, к сожалению, она не решила мою проблему. Я загрузил [часть моего кода] (http://pastebin.com/YtdqL4Zp) (6 файлов), который имеет отношение к моему вопросу, поэтому вы можете легко создать его и воспроизвести проблему, о которой я упомянул в своем предыдущем комментарии. Также я выделил соответствующие строки кода, которые выводят журналы, чтобы узнать, какой метод вызван. – Eido95

0

Используйте следующий код:

int last_pos = -1; 
GridLayout gridLayout; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    gridLayout = (GridLayout) findViewById(R.id.gridLayout); 
    int child_count = gridLayout.getChildCount(); 
    for(int i =0;i<child_count;i++){ 
     gridLayout.getChildAt(i).setOnLongClickListener(new View.OnLongClickListener() { 
      @Override 
      public boolean onLongClick(View view) { 
       //Deselect previous 
       if(last_pos!=-1) gridLayout.getChildAt(last_pos).setSelected(false); 
       //Select the one you clicked 
       view.setSelected(true); 
       last_pos = gridLayout.indexOfChild(view); 
       return false; 
      } 
     }); 
    } 
    //Remove focus if the parent is clicked 
    gridLayout.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      gridLayout.getChildAt(last_pos).setSelected(false); 
     } 
    }); 
+0

Благодарим вас за ответ, но ваше решение, к сожалению, не работает. Это сработало для вас? – Eido95

+1

Вы должны объяснить, почему код, который вы предоставляете, работает и что он делает. Если вы предоставляете просто код, это не поможет OP и следующим пользователям, которые могут получить аналогичный вопрос. – AxelH