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

标签 java android android-adapter grid-layout view-hierarchy

我想获得以下能力:

  • 每次长按 GridLayout 只选择一个子 View
  • 单击 GridLayout 或视觉层次结构中的任何祖先父级将取消选择已选择的子 View(如果已选择的话)。

问题是当向子 View 注册 View.OnLongClickListener 回调时,父 GridLayout 或任何祖先注册回调(View.OnClickListenerView.onTouchEvent) 在点击它们时调用。

如何在类似于 AdapterView.OnItemSelectedListenerAdapterView.OnItemLongClickListenerGridLayout 中获取选定的子项并解决上述问题?

最佳答案

如何将“选定” View 存储为全局变量,并在其焦点改变时将其删除?通过使用 focusablefocusableInTouchModeonClick 监听器,您可以获得正确的结果。我不确定这是最好的解决方案,但它确实有效。

你需要什么:

  • A global View variable: the GridLayout's child long clicked, as selected.
  • (optional) A custom parent container as any ViewGroup: it will set the focusable listeners on all its children [*]. In my tests, I used a LinearLayout and a RelativeLayout.

[*] 如果您不使用可选的父自定义类,则必须设置 android:focusable="true"android:focusableInTouchMode="true" 在父 ViewGroup 的所有子项上。您必须设置 OnClickListener 才能在单击父 ViewGroup 时调用 removeViewSelected()

  • Adding Click listeners for GridLayout children: which updates the selected view.
  • Implementing a Focus listener: which removes the selected view if it's losing focus.

它将处理父子层次结构上的所有焦点更改状态,查看输出:

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

让我们在 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();
}

在每个 GridLayout 子级上,添加 ClickLongClick 监听器以更新或删除所选 View 。我的是动态添加的 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;
    }
});

在父容器上设置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:它是可选的,因为您可以通过 XML 和 clickable 监听器动态设置 focusable 状态,但它对我来说似乎更容易。我使用以下自定义 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>

希望这会有用。

关于java - 如何在类似于 GridView 的 GridLayout 中获取选定的子项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41303999/

相关文章:

java - 在 Android 设备上运行 scala 代码

java - 使用构造函数参数模拟嵌套类并测试方法

android - 如何从 Android 应用程序上传数据库文件到谷歌驱动器?

java - Android AlertManager 通过重启、更新等

android - 为什么编译器要求我在适配器类上使用方法 "final"时声明变量 "OnClickListener"?

java - Recyclerview 适配器内的 Viewpager

java - Maven 编译错误

java - Java中的文件路径

android - 仅使用 XML 更新 Android EditText 对焦点更改的提示

Android 强制关闭使用滑动来关闭 Listview