简体   繁体   English

如何在类似于GridView的GridLayout中获取选定的子项

[英]How to get selected child in GridLayout similar to GridView

I want to achieve the following abilities: 我要实现以下能力:

  • Select only one child View inside a GridLayout each time by long clicking it. 每次通过长时间单击只能在GridLayout选择一个子View
  • A click on the GridLayout or any ancestor parent in the visual hierarchy will deselected selected child View if one already selected. 在A点击GridLayout还是在视觉层次有父父将取消选择的子View如果已经选定。

The problem is when when registering a View.OnLongClickListener callback to child View , neither parent GridLayout nor any ancestor registered callbacks (either View.OnClickListener or View.onTouchEvent ) called when clicking on them. 问题是,当将View.OnLongClickListener回调注册到子View时,单击它们时,父GridLayout或任何祖先注册的回调( View.OnClickListenerView.onTouchEvent )都View.OnClickListener被调用。

How can I get a selected child inside a GridLayout similar to either AdapterView.OnItemSelectedListener or AdapterView.OnItemLongClickListener and solve the above mentioned problem? 如何在与AdapterView.OnItemSelectedListenerAdapterView.OnItemLongClickListener相似的GridLayout获取选定的子AdapterView.OnItemLongClickListener并解决上述问题?

What about storing a "selected" view as a global variable, and removing it when its focus changes? 将“选定”视图存储为全局变量,并在其焦点更改时将其删除怎么办? By playing with focusable , focusableInTouchMode and onClick listeners, you could have the right results. 通过使用focusablefocusableInTouchModeonClick侦听器,您可以获得正确的结果。 I'm not sure that's the best solution, but it works. 我不确定这是否是最好的解决方案,但它是否有效。

What you will need: 您将需要什么:

  • A global View variable : the GridLayout 's child long clicked, as selected. 全局View变量 :长时间选中GridLayout的子级。
  • (optional) A custom parent container as any ViewGroup : it will set the focusable listeners on all its children [*] . (可选)与任何ViewGroup一样的自定义父容器 :它将在其所有子级[*]上设置可聚焦的侦听器。 In my tests, I used a LinearLayout and a RelativeLayout . 在测试中,我使用了LinearLayoutRelativeLayout

[*] If you don't use the optional parent custom Class, you have to set android:focusable="true" and android:focusableInTouchMode="true" on all children of the parent ViewGroup. [*]如果您不使用可选的父级自定义类,则必须在父级ViewGroup的所有子级上设置android:focusable="true"android:focusableInTouchMode="true" And you'll have to set OnClickListener in order to call removeViewSelected() when the parent ViewGroup is clicked. 而且,您必须设置OnClickListener才能在单击父ViewGroup时调用removeViewSelected()

  • Adding Click listeners for GridLayout children: which updates the selected view. GridLayout子级添加Click侦听器:更新所选视图。
  • Implementing a Focus listener : which removes the selected view if it's losing focus. 实现Focus侦听器 :如果所选视图失去焦点,则将其删除。

It will handle all focus change state on parent and child hierarchy, see the output: 它将处理父级和子级层次结构上的所有焦点更改状态,请参见输出:

GridLayout选定的视图并在单击侦听器上禁用视图

I used the following pattern: 我使用以下模式:

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

Let's preparing the selected View and its update methods in the Activity : 让我们在Activity准备选定的View及其更新方法:

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

On each GridLayout child, add the Click and LongClick listeners to update or remove the selected view. 在每个GridLayout子级上,添加ClickLongClick侦听器以更新或删除所选视图。 Mine were TextView s added dynamically, but you could easily create a for-loop to retrieve the children: 我的是动态添加的TextView ,但是您可以轻松创建一个for循环来检索子级:

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

Set the FocusChange listener on the parent container: 在父容器上设置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();
        }
    }
});

Then, the optional custom ViewGroup : it's optional because you could set the focusable state by XML and the clickable listener dynamically, but it seems easier to me. 然后,可选的自定义ViewGroup :这是可选的,因为您可以通过XML和clickable侦听器动态设置focusable状态,但对我来说似乎更容易。 I used this following custom Class as parent container: 我使用以下自定义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
    }
}

For example, this is the layout I used: 例如,这是我使用的布局:

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

Hope this will be useful. 希望这会有用。

Use the following code : 使用以下代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM