微调器在 ViewGroup 中不起作用



从最近几天开始,我试图让Spinner在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");
}

}

现在,问题是,当我尝试从微调器下拉列表中选择某些内容时,没有选择任何内容,根本不会触发 OnItemSelected 事件。

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

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

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

我的问题是,在我的"layoutChildren"实现中,我引入了一个"优化",如果我重用一个子视图,我知道其内容没有改变(布局窗口也没有改变),我就懒得重新测量子视图。

事实证明,如果您使用的是微调器,您将无法逃脱。 原因与某些 View.mPrivateFlags(PFLAGS_FORCE_LAYOUT 和 PFLAGS_LAYOUT_REQUIRED)的实现方式有关,相对于跟踪重新处理子视图布局的需求以及微调器决定触发 onItemSelected 侦听器的方式。

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

从微调器

的角度来看,这是可以的,因为当微调器看到所选的新项时,它会专门请求布局传递 (requestLayout)。

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

解决方法是删除优化,以便不测量维度已知的子视图。 PFLAGS_LAYOUT_REQUIRED(这是包私有...你不能从你写的任何内容中得到它)只有在要求子视图测量自己时才会设置,即使测量本身在我的情况下并不是绝对必要的,这也是将 mPrivateFlag 操纵到正确状态的唯一方法。

检查以确保您正在测量子视图(尤其是从以前的布局通道中重复使用的视图),并查看这是否解决了您的问题。

第一次编辑---------------

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

当微调器由 ListView 托管时,在微调器中的选择更改后调用 ListView.onLayout(...)(尽管我无法弄清楚如何),并且 ListView onLayout 方法将该调用向下传播到 Spinner,从而导致 onItemSelected() 调用。

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

我只是还无法弄清楚为什么、在哪里或如何触发布局请求在一种情况下触发,而在另一种情况下没有。 由于布局请求发布到消息队列并从那里执行,因此很难确定它的来源。

让我知道这是否对您有所帮助,或者给您任何想法。

原始帖子------------

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

你找到解决问题的方法了吗? 如果是这样,你能发布它吗?

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

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

正如史蒂夫已经指出的那样,问题在于需要操纵某些标志才能实际强制布局,而唯一的(公共)方法是调用 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);
        }
    }
  }
}

最新更新