1

我有一个使用操作栏的选项卡功能托管多个片段的活动。其中一个片段包含一个 ListView。选择此选项卡后,我想选择某个项目。

要以编程方式执行此操作,我使用以下代码(其中调用是 ListView)

private void selectItem(int position)
{
    long itemId = calls.GetItemIdAtPosition(position);
    calls.PerformItemClick(calls, position, itemId);
}

如果这个 ListView 已经被渲染,并且我正在调用它,那么没问题。但是,如果我从 onResume 调用它,则代码会执行,但最后没有选择任何内容。我认为这是因为在我调用 selectItem 时,尚未呈现 ListView 的所有项目。但是,如果我启动一个后台线程,休眠几百毫秒,然后运行相同的代码(当然是在 ui 线程中),一切都很好,但这是一个丑陋的 hack。

现在您可能想知道,“他为什么不使用calls.setSelection”?问题是,我正在使用执行扩展的自定义布局 - 所以我需要实际单击我想要选择的项目(这反过来会触发所选项目的布局扩展)。但是,我可以直接调用在 PerformItemClick 上执行的代码,结果是一样的(不执行布局扩展)。

有没有什么办法让我及时捕捉到“Listview 已完成渲染所有可查看项目”的时间点,然后在那时执行我的 selectItem 调用?在 ASP.NET 中,我在每个 UI 项目上都有一个事件,告诉我它何时完成渲染,所以我在那时进行项目选择,但我没有找到任何东西。

问候斯蒂芬

这是我正在使用的适配器

public class ActiveCallsAdapter: ObservableAdapter<Call>
{

    public ActiveCallsAdapter(Activity activity, ObservableCollection<Call> calls)
        : base(activity, calls)
    {
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        var item = items[position];
        var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
        //View view = convertView;
        //if (view == null) // no view to re-use, create new
        //    view = context.LayoutInflater.Inflate(Resource.Layout.Call, null);

        SetTextView(view, Resource.Id.CallerName, item.CallerName);
        SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
        SetTextView(view, Resource.Id.CallStatus, item.State.ToString());
        SetTextView(view, Resource.Id.CallDuration, item.Duration);

        return view;
    }

    public void Update(LinearLayout view, Call item)
    {
        SetTextView(view, Resource.Id.CallerName, item.CallerName);
        SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);

        string identifier = "callState_" + item.State.ToString();
        int resourceId = Application.Context.Resources.GetIdentifier(identifier, "string", Application.Context.PackageName);
        string callStateString = item.State.ToString();
        if (resourceId != 0)
        {
            try
            {
                callStateString = Application.Context.Resources.GetString(resourceId);
            }
            catch (Exception e)
            {
                AndroidLogModel.Model.AddLogMessage("ActiveCallsAdapter", "Unable to find call state string with resource id " + resourceId + " state string: " + identifier, 3);
            }
        }
        SetTextView(view, Resource.Id.CallStatus, callStateString);
        //SetTextView(view, Resource.Id.CallDuration, item.Duration);
    }

    public void UpdateDuration(LinearLayout view, Call item)
    {
        SetTextView(view, Resource.Id.CallDuration, item.Duration);
    }

}

以及那个适配器的基类

    public class ObservableAdapter<T>: BaseAdapter<T>
{

    protected readonly Activity context;
    protected readonly ObservableCollection<T> items;

    public ObservableAdapter(Activity context, ObservableCollection<T> collection)
    {
        this.context = context;
        this.items = collection;
        //this.collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);
        this.items.CollectionChanged += (sender, e) => NotifyDataSetChanged();
    }

    void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        NotifyDataSetChanged();
    }

    public override T this[int position]
    {
        get { return items[position]; }
    }

    public override int Count
    {
        get { return items.Count; }
    }

    public override long GetItemId(int position)
    {
        return position;
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        var item = items[position];
        var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
        // configure view here
        return view;
    }

    protected void SetTextView(LinearLayout view, int id, string text)
    {
        var textView = view.FindViewById<TextView>(id);
        if (textView != null)
            textView.SetText(text, TextView.BufferType.Normal);
    }
}
4

2 回答 2

2

我的 Mono 技能有限,所以我不知道我是否完全理解您的适配器,无论如何我已经修改了一些旧代码并制作了一个适配器,在单击时会扩展单个项目,它也会将其移至ListView所需onResume位置:

private static class CustomAdapter extends BaseAdapter {

        // the data
        private ArrayList<String> mData;

        // an int pointing to a position that has an expanded layout,
        // for simplicity I assume that you expand only one item(otherwise use
        // an array or list)
        private int mExpandedPosition = -1; // -1 meaning no expanded item
        private LayoutInflater mInflater;

        public CustomAdapter(Context context, ArrayList<String> items) {
            mInflater = LayoutInflater.from(context);
            mData = items;
        }

        public void setExpandedPosition(int position) {
            // if the position equals mExpandedPosition then we have a click on
            // the same row so simply toggle the row to be gone again
            if (position == mExpandedPosition) {
                mExpandedPosition = -1;
            } else {
                // else change position of the row that was expanded
                mExpandedPosition = position;
            }
            // notify the adapter
            notifyDataSetChanged();
        }

        @Override
        public int getCount() {
            return mData.size();
        }

        @Override
        public String getItem(int position) {
            return mData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.ad_expandedelement,
                        parent, false);
            }
            ((TextView) convertView.findViewById(R.id.textView1))
                    .setText(getItem(position));
            // see if there is an expanded position and if we are at that
            // position
            if (mExpandedPosition != -1 && mExpandedPosition == position) {
                // if yes simply expand the layout
                convertView.findViewById(R.id.button1).setVisibility(
                        View.VISIBLE);
            } else {
                // this is required, we must revert any possible changes
                // otherwise the recycling mechanism will hurt us
                convertView.findViewById(R.id.button1).setVisibility(View.GONE);
            }
            return convertView;
        }

    }

onListItemClick只是:

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    // set the expanded(or collapsed if it's a click on the same row that
    // was previously expanded) row in the adapter
    ((CustomAdapter) getListView().getAdapter())
            .setExpandedPosition(position);
}

并且onResume将有:

@Override
protected void onResume() {
    super.onResume();
    // set the position to the desired element
    ((CustomAdapter) getListView().getAdapter()).setExpandedPosition(15);
    // set the selection to that element so we can actually see it
    // this isn't required but has the advantage that it will move the
    // ListView to the desired
    // position if not visible
    getListView().setSelection(15);
}

R.layout.ad_expandedelement是一个简单的垂直LinearLayout,带有一个TextView和一个最初隐藏的(可见性设置为消失)Button。为此,Button我更改了可见性以模拟在ListView. 您应该能够理解我的代码,如果您愿意,我可以在 github 上发布完整示例。

于 2012-10-02T19:46:34.047 回答
1

虽然我不确定 C#/Mono 中的确切等价物,但 Android 框架在Activity调用时提供了一个回调onWindowFocusChanged(),指示Window与给定关联的时间段Activity对用户可见。等到那个时候调用你的选择方法,你可能会有更好的运气,因为ListView应该在那个时候测量和布局。在 Java 中,它会是这样的:

@Override
public void onWindowFocusChanged (boolean hasFocus) {
    if (hasFocus) {
        selectItem(position);
    }
}

您可能需要在其中添加更多逻辑,此回调直接与窗口焦点相关联,并不是真正的生命周期方法。如果您正在显示对话框或执行其他类似操作,我可能会被多次调用。

于 2012-10-02T18:04:20.390 回答