如何判断多个getView结果中的哪一个可见?

时间:2014-11-05 08:54:35

标签: android listview

我需要为ListView中的每一行添加自定义按钮对象。这是一个简化的行布局:

<LinearLayout android:id="@+id/table_cell"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >

    <TextView android:id="@+id/label"
        android:textSize="19dp"
        android:textStyle="bold"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:lines="1"
    />

    <LinearLayout android:id="@+id/button_wrapper"
        android:layout_width="100dp"
        android:layout_height="match_parent"
    />

</LinearLayout>

在我的自定义ArrayAdapter中,我将按钮放入getView()的单元格中:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // recycle the cell if possible
    View cell = null;
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        cell = inflater.inflate(R.layout.table_cell, parent, false);
    } else {
        cell = convertView;
    }

    MyButton button = (MyButton) this.buttons.get(position);
    if (button != null) {
        // remove the button from the previous instance of this cell
        ViewGroup parent = (ViewGroup)button.getParent();
        if (parent != null) {
            parent.removeView(button);
        }

        // add the button to the new instance of this cell
        ViewGroup buttonWrapper = (ViewGroup)cell.findViewById(R.id.button_wrapper);
        buttonWrapper.addView(button);
    }
}

我知道每个表行都会多次调用getView(),因为我滚动表或单击按钮或执行其他操作,因此上面的代码会在将其添加到新视图之前从上一个视图中删除按钮以避免“视图已有父级”例外。

问题在于,假设从getView生成的最新视图是在屏幕上可见的视图,但通常情况并非如此。有时getView()会生成新视图,但屏幕上会显示较旧的视图。在那种情况下,我的按钮会消失,因为getView()会将其移动到不可见的新视图。我通过初始化名为repeatRowTest的int变量然后在getView()中添加此代码来发现该行为:

if (position == 0) {
    Log.d("getView", "repeat row count: " + repeatRowCountTest);
    TextView label = (TextView)cell.findViewById(R.id.label);
    label.setText(String.format("%d %s", repeatRowCountTest, label.getText()));
    repeatRowCountTest++;
}

这显示了生成给定行的次数,以及当前显示的实例。我可能会看到一行生成了10次,而只显示了第5行。但是,只有在显示该行的最新实例时,我的按钮才会显示。

所以问题是,如何判断getView()中生成的行是否实际显示,所以我知道是否将按钮移动到其中,或者将按钮保留在原来的位置?或者更一般地说,如何在行中添加一个按钮并确保它仍然可见,因为对于给定的位置重复了getView?

我已经检查了显示行的所有属性与未显示的额外行,但找不到任何差异。在我的按钮消失之后,我也尝试在数组适配器上调用notifyDataSetChanged,并使用包含按钮的所有最新视图刷新列表 - 但是不清楚哪些事件触发getView重复自身,所以我不知道什么时候我需要调用notifyDataSetChanged才能使事情再次正确。我想我可以克隆按钮并为该行的每个新实例添加一个新的按钮实例,但这似乎比必要的资源更密集,并且会产生其他问题,因为其他对象都引用了这些按钮。我没有找到任何代码示例显示最佳方法,但它似乎是一个常见的要求,所以希望我错过了一些简单的东西!

更新:是否有一个我可以覆盖的ArrayAdapter方法在调用getView()方法后调用?如果是这样,我可以检查所有最近创建的行的父项,看看它们是否实际显示在ListView中,如果不是,则刷新ListView。

2 个答案:

答案 0 :(得分:0)

您不需要通过代码创建自定义按钮,您可以将其插入行布局xml中,就像普通的android按钮一样。通过这种方式,您可以从getView中删除按钮包装器布局和添加/删除逻辑。

<LinearLayout android:id="@+id/table_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>

<TextView android:id="@+id/label"
    android:textSize="19dp"
    android:textStyle="bold"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:lines="1"
/>

<yourpackagename.MyButton 
    android:id="@+id/button"
    android:layout_width="100dp"
    android:layout_height="match_parent"
/>

</LinearLayout>

使用代码更容易理解,但也许你必须适应它。

XML:

<LinearLayout android:id="@+id/table_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>

<TextView android:id="@+id/label"
    android:textSize="19dp"
    android:textStyle="bold"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:lines="1"
/>

<yourpackagename.MyButton 
    android:id="@+id/button1"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button2"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button3"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button4"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button5"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button6"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button7"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
<yourpackagename.MyButton 
    android:id="@+id/button8"
    android:layout_width="12dp"
    android:layout_height="match_parent"
/>
</LinearLayout>

传递给适配器的模型类:

public class MyRowModel
{
    public boolean isButton1Visible;
    public boolean isButton2Visible;
    public boolean isButton3Visible;
    public boolean isButton4Visible;
    public boolean isButton5Visible;
    public boolean isButton6Visible;
    public boolean isButton7Visible;
    public boolean isButton8Visible;
}

ViewHolder:

private class ViewHolder {
    public MyButton b1;
    public MyButton b2;
    public MyButton b3;
    public MyButton b4;
    public MyButton b5;
    public MyButton b6;
    public MyButton b7;
    public MyButton b8;
}

getView方法:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder;
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.table_cell, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.b1 = (MyButton)convertView.findViewById(R.id.button1);
        viewHolder.b2 = (MyButton)convertView.findViewById(R.id.button2);
        viewHolder.b3 = (MyButton)convertView.findViewById(R.id.button3);
        viewHolder.b4 = (MyButton)convertView.findViewById(R.id.button4);
        viewHolder.b5 = (MyButton)convertView.findViewById(R.id.button5);
        viewHolder.b6 = (MyButton)convertView.findViewById(R.id.button6);
        viewHolder.b7 = (MyButton)convertView.findViewById(R.id.button7);
        viewHolder.b8 = (MyButton)convertView.findViewById(R.id.button8);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = convertView.getTag();
    }
    MyRowModel myRowModel = getItem(position);
    if(myRowModel.isButton1Visible)
    {
        viewHolder.b1.setVisibility(View.VISIBLE);
    }
    else
    {
        viewHolder.b1.setVisibility(View.INVISIBLE);
    }
    if(myRowModel.isButton2Visible)
    {
        viewHolder.b2.setVisibility(View.VISIBLE);
    }
    else
    {
        viewHolder.b2.setVisibility(View.INVISIBLE);
    }
    //and so on
    return convertView;
}

答案 1 :(得分:0)

我注意到如果我在问题发生后滚动ListView,所有行都会重新显示按钮,所以显然Android打算显示每行的最新视图,但并不总是刷新视图。

然后我试图弄清楚是什么导致getView重复运行当前可见的行(通常它只会在新行进入视图时运行)。不幸的是,此活动中其他地方发生的很多事情都会触发ListView重新生成其视图,例如在播放音频时移动的ProgressBar,缩短和延长ListView以显示其旁边的另一个视图的动画,以及内部按钮表行使用不同的图形更新,以显示应用正在跟踪的不同内容的状态。我能够消除其中的一些,例如通过在更新状态之前检查按钮是否已处于所需状态,但我无法消除所有状态。

由于触发getView的最常见操作是更新音频ProgressBar,每当我更新ProgressBar时,我都会在ListView上添加一行来调用invalidateViews()。这样可以使ListView保持刷新状态,以便最新的视图始终可见,因此我的视图始终可见。在调试器中运行时,会使应用程序速度降低很多,但在独立设备上运行时,性能变化不明显。

在这一点上提出一个更好的问题可能是为什么与ListView无关的ProgressBar会导致ListView不断重新生成其视图。如果我有时间或者遇到更多问题,我会将其作为一个单独的问题发布。