Spinner无法在ViewGroup中工作

时间:2013-09-23 08:05:49

标签: spinner viewgroup

从最近几天开始,我试图让Spinner在ViewGroup中工作,但没有任何成功。 : - {。 在自定义ViewGroup里面我放置了片段容器。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:boxMenu="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TestActivity"
android:id="@+id/frame" >

<com.imper.boxmenulibrary.BoxMenuLayout
    android:id="@+id/box_menu" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    boxMenu:boxSize="40dp"
    boxMenu:directionY="bottomToTop"
    boxMenu:directionX="rightToLeft"
    boxMenu:showStart="false"
    android:padding="5dp"
    android:background="#ffffff00">

    <LinearLayout 
        android:id="@+id/fragment_container"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center"
        android:orientation="vertical">

    </LinearLayout>

</com.imper.boxmenulibrary.BoxMenuLayout>

在那个片段容器中我替换像这样的片段:

FragmentManager fm = ((Activity) context).getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_container, new TestFragment());
ft.commit();

我的TestFragment xml布局如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <Spinner 
        android:id="@+id/test_spinner"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

TestFragment实现如下:

public class TestFragment extends BoxLayout implements OnItemSelectedListener {

Spinner testSpinner;

public TestFragment() {}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
    View view = inflater.inflate(R.layout.test_view, container, false);

    testSpinner = (Spinner) view.findViewById(R.id.test_spinner);
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(container.getContext(), R.array.day, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    testSpinner.setAdapter(adapter);
    testSpinner.setOnItemSelectedListener(this);

    return view;
}

@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
    Toast.makeText(getActivity(), "Bingo", Toast.LENGTH_SHORT).show();
    Log.d("Bingo", "onItemSelected");
}

@Override
public void onNothingSelected(AdapterView<?> arg0) {
    Toast.makeText(getActivity(), "Bingo Nothing", Toast.LENGTH_SHORT).show();
    Log.d("Bingo", "onNothingSelected");
}

}

现在,问题是当我试图从Spinner下拉选择某些内容时,没有选择任何内容,OnItemSelected事件根本不会被触发。

当FragmentContainer从我的自定义ViewGroup移出时,相同的代码工作正常。 为什么会这样?如何让Spinner在自定义视图组中正常工作?

3 个答案:

答案 0 :(得分:2)

第二次编辑--------------

我遇到了与你非常相似的问题。 (有关更多详细信息,请参阅下面的原始帖子。)如果不了解有关自定义ViewGroup实现的更多信息,我不知道我的解决方案是否适合您。

我的问题结果是在我的“layoutChildren”实现中我引入了一个“优化”,如果我重用一个子视图,我知道它的内容没有改变(并且布局窗口也没有改变)我没有费心去重新测量孩子的观点。

事实证明,如果你使用Spinners,你就无法逃避这一点。原因与某些View.mPrivateFlags(PFLAGS_FORCE_LAYOUT和PFLAGS_LAYOUT_REQUIRED)相对于跟踪重新处理子视图布局的需要实现了错综复杂的方式IN COMBINATION与Spinner决定触发onItemSelected侦听器的方式。

如下所述,Spinner在要求执行布局时触发onItemSelected侦听器,并在其中识别所选项目已更改。如果它不执行布局,它(内部)知道所选项已更改,但不会调用onItemSelected侦听器。

从Spinner的角度来看,这没关系,因为当Spinner看到新项目被选中时,它特意请求了布局传递(requestLayout)。

布局传递开始并向下传播到我的自定义ViewGroup。当它到达第一个子节点时,子节点的PFLAGS_FORCE_LAYOUT位置位......但处理逻辑忽略该标志。相反,它只查找PFLAGS_LAYOUT_REQUIRED位,当它没有看到该标志时,它决定没有必要在视图层次结构中向下传播布局,因此Spinner永远不会被要求执行布局而永远不会触发onItemSelected侦听器。

修复方法是删除优化以不测量尺寸已知的子视图。 PFLAGS_LAYOUT_REQUIRED(包私有......你无法从你写的任何内容中获取)仅在要求子视图自我测量时才设置,即使测量本身在我的情况下不是绝对必要的,它也是将mPrivateFlags操纵到正确状态的唯一方法。

检查以确定您正在衡量您的子视图(特别是从之前的布局过程中重复使用的视图),看看这是否解决了您的问题。

首先编辑---------------

我在这方面取得了一些进展,但仍未解决。 Spinner在调用onLayout()方法时调用onItemSelected侦听器,并且它识别出所选项已更改。

当Spinner由ListView托管时,ListView.onLayout(...)在Spinner中的选择被更改后被调用(虽然我无法弄清楚如何),以及ListView onLayout方法传播调用Spinner,导致onItemSelected()调用。

当我的自定义AdapterView托管Spinner时,在Spinner更改后永远不会调用onLayout方法,因此不会进行onItemSelected()调用。

我还没有弄清楚在一种情况下触发布局请求的原因,地点或方式,而不是另一种情况。因为布局请求被发布到消息队列并从那里执行,所以很难确定它的来源。

请告诉我这是否对您有所帮助,或者给您任何想法。

Original Post ------------

我遇到了同样的问题。我创建了一个自定义ViewGroup(扩展的AdapterView),现在包含微调器的子视图不会触发onItemSelected侦听器。如果我将相同的子视图附加到ListView(也从AdapterView扩展),它工作正常。我在自定义ViewGroup中做错了什么,但我不知道是什么。

您找到了问题的解决方案吗?如果是这样,你可以发布吗?

答案 1 :(得分:0)

这篇文章非常陈旧,但它给了我很大的帮助 - 虽然我仍然遇到了问题(尽管我每次都从下拉列表中选择一些东西 - 而不是每次都这样)。

如果有帮助 - 我的问题的解决方法是不在显示问题的微调器对象上调用bringToFront。

答案 2 :(得分:0)

正如史蒂夫已经指出的那样,问题是需要操纵某些标志来实际强制布局,而这样做的唯一(公共)方式是调用measure()......

作为一种解决方法,将以下方法添加到我的自定义ViewGroup可以解决我的问题,但您可能需要确保不要进行太多额外/冗余测量,具体取决于实际用例......

public void requestLayout() {
  super.requestLayout();
  measure(MeasureSpec.EXACTLY | getMeasuredWidth(),
          MeasureSpec.EXACTLY | getMeasuredHeight());
}

重现问题的最小代码段:

package org.kobjects.spinnertestapp;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import java.util.ArrayList;

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ArrayList<String> options = new ArrayList<>();
    options.add("Hello");
    options.add("Wutan");
    options.add("Lorem Ipsum");

    ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,android.R.layout.simple_spinner_dropdown_item, options);
    Spinner spinner = new Spinner(this);
    spinner.setAdapter(arrayAdapter);

    MyLayout layout = new MyLayout(this);
    layout.addView(spinner);
    setContentView(layout);
  }

  static class MyLayout extends ViewGroup {
    public MyLayout(Context context) {
        super(context);
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        for (int i = 0; i < getChildCount(); i ++) {
            getChildAt(i).layout(left, top, right, bottom);
        }
    }
  }
}