Android的ListView控件 - 调用次数太多或变更订购。如何prevent两者兼而有之? [英] Android ListView - Called too many times OR changes order.. How to prevent both?

查看:199
本文介绍了Android的ListView控件 - 调用次数太多或变更订购。如何prevent两者兼而有之?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我们对列表视图的自定义ArrayAdapter以下报价Android开发页面:

From the Android development page we have the following quote for Custom ArrayAdapter of ListViews:

还有的顺序完全不保证其中getView()将会被调用,也没有多少次了。

"there is absolutely no guarantee on the order in which getView() will be called nor how many times."

当我有以下的和getView叫了几声,这是没有问题,因为我只是回收意见,而不是再次夸大他们:

When I have the following and getView is called a few times, it's no problem, since I'm just recycling the Views instead of inflating them again:

@Override
public void getView(int position, View convertView, ViewGroup parent){
    Log.i(TAG, "getView of position " + String.valueOf(position) + " is called");

    View view = convertView;
    if(view == null){
        view = inflater.inflate(layoutResourceId, parent, false);
        TextView myTextView1 = (TextView) view.findViewById(R.id.my_tv1);
        Button myButton1 = (Button) view.findViewById(R.id.my_btn1);
        ... // More views
        view.setTag(new MyViewHolder(myTextView1, myButton1, ...));
    }
    MyViewHolder h = (MyViewHolder) view.getTag();

    h.myTextView1.setText("Some text");
    h.myButton1.setText("Some button text");
    h.myButton1.setEnabled(buttonEnabled);
}

与使用如果(查看== NULL)的问题 - >充气查看和创建ViewHolder;其他 - >使用标签的ViewHolder是当你在列表中真正快速上下滚动,在列表中改变项目的顺序。可能是因为引用的第一部分:

The problem with using the if(view == null) -> inflate view and create ViewHolder; else -> use ViewHolder of Tag is when you scroll up and down real fast in the list, the order of the items in the list changes. Could be because of the first part of the quote:

没有在getView()将被调用的顺序上绝对没有保证......

"there is absolutely no guarantee on the order in which getView() will be called..."

虽然我认为它只是用列表视图本身,而不是适配器的错误。 (不知道,但不管是什么原因,它是一个错误。)

Though I think it's just a bug with ListViews itself instead of the Adapter. (Not sure, but whatever the cause is, it IS a BUG.)

我是能够解决这个问题,到目前为止一直膨胀的观点的唯一方法:

The only way I was able to fix that so far is always inflating the view:

@Override
public void getView(int position, View convertView, ViewGroup parent){
    Log.i(TAG, "getView of position " + String.valueOf(position) + " is called");

    // NOTE: In Android it's recommended to use the convertView as the view, instead of
    // retrieving it every time from the inflater. But since there is a bug in Android ListViews
    // which changes the order of the Items when you scroll up- and down real fast,
    // I use the inflater every time and ignore the convertView parameter
    view = inflater.inflate(layoutResourceId, parent, false);
    TextView myTextView1 = (TextView) view.findViewById(R.id.my_tv1);
    Button myButton1 = (Button) view.findViewById(R.id.my_btn1);
    ... // More views
    view.setTag(new MyViewHolder(myTextView1, myButton1, ...));

    MyViewHolder h = (MyViewHolder) view.getTag();

    h.myTextView1.setText("Some text");
    h.myButton1.setText("Some button text");
    h.myButton1.setEnabled(buttonEnabled);
}

一切工作正常,滚动向上和向下快时,项目的顺序依然存在。但现在的表现会迅速降下来。对于我只是忽略了这个性能问题相当长的时间,但之后,我做了一些改变,以我的项目的布局,现在我面临的一个新问题。从来没有过这个问题,但现在我面对的(不知什么原因)报价的第二部分:

All works fine, the order of the items remain when scrolling up- and down fast. But now the performance is going down quickly.. For quite a long time I just ignored this performance issues, but right now I'm facing a new problem after I made some changes to the layout of my item. Never had this issue before, but now I face (for some unknown reason) the second part of the quote:

是绝对没有保证多少次getView()将会被调用......

"there is absolutely no guarantee how many times getView() will be called..."

现在包含在创建14可见的元素一个ListView。当我去到包含ListView控件与此适配器的ChecklistActivity,我得到以下的logcat的消息:

Right now with a ListView containing 14 visible elements at creation. When I go to the ChecklistActivity which contains the ListView with this Adapter, I get the following Logcat-messages:

07-23 03:17:14.516: V/MyAdapter(1395): getView(0)
07-23 03:17:16.556: V/MyAdapter(1395): getView(1)
07-23 03:17:16.946: V/MyAdapter(1395): getView(2)
07-23 03:17:17.316: V/MyAdapter(1395): getView(3)
07-23 03:17:17.776: V/MyAdapter(1395): getView(4)
07-23 03:17:18.136: V/MyAdapter(1395): getView(5)
07-23 03:17:18.286: V/MyAdapter(1395): getView(6)
07-23 03:17:18.466: V/MyAdapter(1395): getView(7)
07-23 03:17:18.576: V/MyAdapter(1395): getView(8)
07-23 03:17:18.806: V/MyAdapter(1395): getView(9)
07-23 03:17:19.016: V/MyAdapter(1395): getView(10)
07-23 03:17:19.246: V/MyAdapter(1395): getView(11)
07-23 03:17:19.346: D/dalvikvm(1395): GC_FOR_ALLOC freed 220K, 10% free 5545K/6108K, paused 82ms, total 85ms
07-23 03:17:19.526: V/MyAdapter(1395): getView(12)
07-23 03:17:19.666: V/MyAdapter(1395): getView(13)
07-23 03:17:19.786: V/MyAdapter(1395): getView(14)
07-23 03:17:20.046: I/Choreographer(1395): Skipped 1491 frames!  The application may be doing too much work on its main thread.
07-23 03:17:20.066: V/ChecklistActivity(1395): onCreateOptionsMenu
07-23 03:17:20.406: I/Choreographer(1395): Skipped 64 frames!  The application may be doing too much work on its main thread.
07-23 03:17:20.416: V/MyAdapter(1395): getView(2)
07-23 03:17:20.576: V/MyAdapter(1395): getView(3)
07-23 03:17:20.866: V/MyAdapter(1395): getView(4)
07-23 03:17:21.036: V/MyAdapter(1395): getView(5)
07-23 03:17:21.206: V/MyAdapter(1395): getView(6)
07-23 03:17:21.396: V/MyAdapter(1395): getView(7)
07-23 03:17:21.606: V/MyAdapter(1395): getView(8)
07-23 03:17:21.866: V/MyAdapter(1395): getView(9)
07-23 03:17:22.116: V/MyAdapter(1395): getView(10)
07-23 03:17:22.336: V/MyAdapter(1395): getView(11)
07-23 03:17:22.496: V/MyAdapter(1395): getView(12)
07-23 03:17:22.646: V/MyAdapter(1395): getView(13)
07-23 03:17:22.806: V/MyAdapter(1395): getView(14)
07-23 03:17:23.616: V/MainActivity(1395): onStop
07-23 03:17:23.616: I/Choreographer(1395): Skipped 160 frames!  The application may be doing too much work on its main thread.
07-23 03:17:24.246: I/Choreographer(1395): Skipped 112 frames!  The application may be doing too much work on its main thread.
07-23 03:17:24.286: V/MyAdapter(1395): getView(4)
07-23 03:17:24.396: D/dalvikvm(1395): GC_FOR_ALLOC freed 397K, 8% free 6099K/6568K, paused 100ms, total 107ms
07-23 03:17:24.646: V/MyAdapter(1395): getView(5)
07-23 03:17:24.756: V/MyAdapter(1395): getView(6)
07-23 03:17:24.886: V/MyAdapter(1395): getView(7)
07-23 03:17:25.046: V/MyAdapter(1395): getView(8)
07-23 03:17:25.196: V/MyAdapter(1395): getView(9)
07-23 03:17:25.356: V/MyAdapter(1395): getView(10)
07-23 03:17:25.486: V/MyAdapter(1395): getView(11)
07-23 03:17:25.596: V/MyAdapter(1395): getView(12)
07-23 03:17:25.726: V/MyAdapter(1395): getView(13)
07-23 03:17:25.846: V/MyAdapter(1395): getView(14)
07-23 03:17:26.146: I/Choreographer(1395): Skipped 35 frames!  The application may be doing too much work on its main thread.
07-23 03:17:26.406: V/MyAdapter(1395): getView(6)
07-23 03:17:26.586: V/MyAdapter(1395): getView(7)
07-23 03:17:26.786: V/MyAdapter(1395): getView(8)
07-23 03:17:26.896: V/MyAdapter(1395): getView(9)
07-23 03:17:27.156: V/MyAdapter(1395): getView(10)
07-23 03:17:27.296: V/MyAdapter(1395): getView(11)
07-23 03:17:27.436: V/MyAdapter(1395): getView(12)
07-23 03:17:27.646: V/MyAdapter(1395): getView(13)
07-23 03:17:27.766: V/MyAdapter(1395): getView(14)
07-23 03:17:28.186: I/Choreographer(1395): Skipped 56 frames!  The application may be doing too much work on its main thread.
07-23 03:17:28.426: D/dalvikvm(1395): GC_FOR_ALLOC freed 523K, 9% free 6713K/7308K, paused 108ms, total 112ms
07-23 03:17:28.506: I/Choreographer(1395): Skipped 60 frames!  The application may be doing too much work on its main thread.
07-23 03:17:28.606: V/MyAdapter(1395): getView(8)
07-23 03:17:28.806: V/MyAdapter(1395): getView(9)
07-23 03:17:28.926: V/MyAdapter(1395): getView(10)
07-23 03:17:29.136: V/MyAdapter(1395): getView(11)
07-23 03:17:29.256: V/MyAdapter(1395): getView(12)
07-23 03:17:29.406: V/MyAdapter(1395): getView(13)
07-23 03:17:29.516: V/MyAdapter(1395): getView(14)
07-23 03:17:29.816: I/Choreographer(1395): Skipped 35 frames!  The application may be doing too much work on its main thread.
07-23 03:17:30.096: V/MyAdapter(1395): getView(9)
07-23 03:17:30.216: V/MyAdapter(1395): getView(10)
07-23 03:17:30.366: V/MyAdapter(1395): getView(11)
07-23 03:17:30.496: V/MyAdapter(1395): getView(12)
07-23 03:17:30.646: V/MyAdapter(1395): getView(13)
07-23 03:17:30.796: V/MyAdapter(1395): getView(14)
07-23 03:17:31.166: I/Choreographer(1395): Skipped 53 frames!  The application may be doing too much work on its main thread.
07-23 03:17:31.376: I/Choreographer(1395): Skipped 32 frames!  The application may be doing too much work on its main thread.
07-23 03:17:31.426: V/MyAdapter(1395): getView(10)
07-23 03:17:31.546: V/MyAdapter(1395): getView(11)
07-23 03:17:31.736: V/MyAdapter(1395): getView(12)
07-23 03:17:31.906: V/MyAdapter(1395): getView(13)
07-23 03:17:32.046: V/MyAdapter(1395): getView(14)
07-23 03:17:32.306: I/Choreographer(1395): Skipped 31 frames!  The application may be doing too much work on its main thread.
07-23 03:17:32.536: I/Choreographer(1395): Skipped 35 frames!  The application may be doing too much work on its main thread.
07-23 03:17:32.606: V/MyAdapter(1395): getView(11)
07-23 03:17:32.816: V/MyAdapter(1395): getView(12)
07-23 03:17:32.986: D/dalvikvm(1395): GC_FOR_ALLOC freed 729K, 10% free 7323K/8124K, paused 121ms, total 128ms
07-23 03:17:33.166: V/MyAdapter(1395): getView(13)
07-23 03:17:33.496: V/MyAdapter(1395): getView(14)
07-23 03:17:34.056: V/MyAdapter(1395): getView(12)
07-23 03:17:34.186: V/MyAdapter(1395): getView(13)
07-23 03:17:34.316: V/MyAdapter(1395): getView(14)
07-23 03:17:34.596: I/Choreographer(1395): Skipped 34 frames!  The application may be doing too much work on its main thread.
07-23 03:17:34.846: I/Choreographer(1395): Skipped 36 frames!  The application may be doing too much work on its main thread.
07-23 03:17:34.906: V/MyAdapter(1395): getView(13)
07-23 03:17:35.016: V/MyAdapter(1395): getView(14)
07-23 03:17:35.546: V/MyAdapter(1395): getView(14)
07-23 03:17:36.066: I/Choreographer(1395): Skipped 31 frames!  The application may be doing too much work on its main thread.

我绝对不知道为什么它被称为很多次,但我想它只能​​使用一次每个调用。如果有人知道这个快速滚动的错误,如果没有充气查看每一次在getView(改变顺序)的解决方案,也将是一个巨大的帮助。

I have absolutely no idea why it's called so many times, but I'd like it to only be called once each. And if someone knows a solution for this fast-scrolling bugs that changes the order without inflating the View every single time in the getView() it would also be a huge help.

PS:忽略第一个 3月7日至23日:17:20.046:I /编舞(1395):跳过1491帧!该应用程序可能会做太多的工作在其主线程。我目前工作的..充气视图就是为什么性能缺乏的原因之一,但也有一些其他问题我试图修复。

PS: Ignore the first 07-23 03:17:20.046: I/Choreographer(1395): Skipped 1491 frames! The application may be doing too much work on its main thread. I'm currently working on that.. Inflating the View is one of the reasons why the performance lacks, but there are also some other issues that I'm trying to fix.

不过,为什么不能仅仅的Andr​​oid拥有稳健ArrayAdapter。其中当有人滚动向上和向下快,不改变项目的顺序。或选项,以便您可以选择记住可见区域外的项目..如果你还记得许多项目从理论上还可以创建性能问题,但它会解决很多问题,包括那些与EditText上输入被再次复位后他们出可见光区的。然后,我们不应该用TextWatchers,标签与持有人及这样..

Still, why can't Android just have a robust ArrayAdapter. One that doesn't change the order of items when someone is scrolling up- and down fast. Or an options so you can choose to remember the items outside of the visible region.. This could theoretically also create performance issues if you remember to many items, but it will solve a lot of problems, including those with EditText-input being reset again after they are out of the visible region. Then we shouldn't have to use TextWatchers, Tags with Holders and such..

好吧,我现在侧钻..这只是事情是这样的,我不能改变这种状况。因此,让我们只专注于固定上面解释的问题(快速滚动的bug不充气每次和getView没有得到叫了这么多次在创建)。

Ok, I'm sidetracking now.. It's just the way it is and I can't change that. So let's just focus on fixing the problems explained above (fast-scrolling bug without inflating every time AND getView not getting called so many times on creation).

编辑1:

我的code的其他部位一些更多的信息:

Some more info about other parts of my code:

checklist_activity.xml:

checklist_activity.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:descendantFocusability="afterDescendants" />

</RelativeLayout>

list_item.xml外布局:

list_item.xml outer layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
<!-- height is wrap content since I have views that are Visible / Gone based on the Item's state -->

    <!-- All my View elements -->

</RelativeLayout>

有关我的项目,请参阅this矿山不同的计算器问题。

For my item, see this different stackoverflow question of mine.

编辑2:

下面下面我目前MyAdapter.java类的副本(PS:删除评论,进口和日志更容易阅读)。当我向上滚动,并在模拟器下降速度快,图像保持不变,但(产品,名称,价格和这样的)文本被混淆。我有按类别和产品名称排序列表,我想列出保留在ListView此顺序。

Here below a copy of my current MyAdapter.java class (PS: Removed comments, imports and Logs for easier readability). When I scroll up- and down fast in the Emulator, the Images stay the same, but the Texts (of the Product-Names, Prices and such) are being mixed up. I've got a list ordered by Category and Product-Name and I want to list to retain this order in the ListView.

public class MyAdapter extends ArrayAdapter<OrderedProductItem>
{
    private static final String TAG = "MyAdapter";
    public static MyAdapter mAdapter;

    private Context context;
    private int layoutResourceId;
    private LayoutInflater inflater;

    public MyAdapter(Context c, int layoutId, ArrayList<OrderedProductItem> objects){
        super(c, layoutId, objects);

        layoutResourceId = layoutId;
        context = c;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mAdapter = this;
    }

    private OnTouchListener itemOnTouchListener = new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(v instanceof EditText){
                EditText et = (EditText)v;
                et.setFocusable(true);
                et.setFocusableInTouchMode(true);
            }
            else{
                ListItemTagHolder h = (ListItemTagHolder) v.getTag();
                h.etAmount.setFocusable(false);
                h.etAmount.setFocusableInTouchMode(false);
                h.actvName.setFocusable(false);
                h.actvName.setFocusableInTouchMode(false);
            }
            return false;
        }
    };

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        View view = convertView;
        ListItemTagHolder h;
        if(view == null){
            view = inflater.inflate(layoutResourceId, parent, false);
            RelativeLayout rl = (RelativeLayout) view.findViewById(R.id.item_layout);
            h = new ListItemTagHolder(rl);
            view.setTag(h);
        }
        else
            h = (ListItemTagHolder) view.getTag();

        /*
        View view = inflater.inflate(layoutResourceId, parent, false);
        RelativeLayout rl = (RelativeLayout) view.findViewById(R.id.item_layout);
        view.setTag(new ListItemTagHolder(rl));
        final ListItemTagHolder h = (ListItemTagHolder) view.getTag();
        */

        if(h != null){
            h.orderedProductItem = Controller.getInstance().getOrderedProductItems().get(position);

            MainActivity.getImageLoader().imageForImageView(h.orderedProductItem.getCheckState().rID(), h.imageView);

            String productName = Controller.getInstance().getProductById(h.orderedProductItem.getProductId()).getName();
            // (I use a Validation-class to compare Strings or Check if they aren't null/empty)
            if(!V.compare(h.tvName.getText().toString(), productName, true, true))
                h.tvName.setText(productName);

            String priceString = C.doubleToPrice(Controller.getInstance().getProductById(h.orderedProductItem.getProductId()).getPrice(), "€", true);
            if(!V.compare(h.tvPrice.getText().toString(), priceString, true, true))
                h.tvPrice.setText(priceString);

            String resultAmount = String.valueOf(h.orderedProductItem.getResultAmount());
            if(!V.compare(h.etAmount.getText().toString(), resultAmount, true, true))
                h.etAmount.setText(resultAmount);

            ChecklistActivity.cActivity.updateAutoCompleteTextViewWithFilter(h, h.orderedProductItem.getSelectedFilter());

            ChecklistActivity.cActivity.updateTagsSpinner(h, null);

            h.etAmount.addTextChangedListener(new MyTextWatcher(h.etAmount));
            h.actvName.addTextChangedListener(new MyTextWatcher(h.actvName));

            h.etAmount.setTag(h.orderedProductItem);
            h.actvName.setTag(h.orderedProductItem);

            ChecklistActivity.cActivity.updateChangeEditTexts(view, h);

            view.setOnTouchListener(itemOnTouchListener);
            h.etAmount.setOnTouchListener(itemOnTouchListener);
            h.actvName.setOnTouchListener(itemOnTouchListener);
        }

        return view;
    }
}

通过以​​下座级:

public class ListItemTagHolder
{
    public final RelativeLayout relativeLayout;

    public final LinearLayout leftLL, rightLL;
    public final ImageView imageView;
    public final TextView tvName, tvPrice, tvTags;
    public final EditText etAmount;
    public final AutoCompleteTextView actvName;
    public final Spinner spTags;
    public final ImageButton btnTags;
    public final Space spaceImage, spacePrice;
    public OrderedProductItem orderedProductItem;

    public ListItemTagHolder(RelativeLayout layout){
        relativeLayout = layout;

        leftLL = (LinearLayout) relativeLayout.findViewById(R.id.left_ll);
        rightLL = (LinearLayout) relativeLayout.findViewById(R.id.right_ll);
        imageView = (ImageView) relativeLayout.findViewById(R.id.image);
        tvName = (TextView) relativeLayout.findViewById(R.id.tv_product_name);
        tvPrice = (TextView) relativeLayout.findViewById(R.id.tv_price);
        tvTags = (TextView) relativeLayout.findViewById(R.id.tv_tags);
        etAmount = (EditText) relativeLayout.findViewById(R.id.et_result_amount);
        actvName = (AutoCompleteTextView) relativeLayout.findViewById(R.id.actv_result_name);
        spTags = (Spinner) relativeLayout.findViewById(R.id.sp_tags);
        btnTags = (ImageButton) relativeLayout.findViewById(R.id.btn_tags);
        spaceImage = (Space) relativeLayout.findViewById(R.id.filler_space_image);
        spacePrice = (Space) relativeLayout.findViewById(R.id.filler_space_price);
    }
}

PS:我知道大多数人放在持有人的所有View元素接一个,和我曾经有过这一点。但让他们都从布局还是一个接一个不使我面临的问题有什么差别。

PS: I know most people put all View-elements in the Holder one by one, and I used to have this. But getting them all from the layout or one by one doesn't make any difference in the problem I'm facing.

PSS:从微调/ EditTexts / AutoCompleteTextView的OrderedProductItem领域这三个更新的方法重新申请新的数据。我已经把他们在ChecklistActivity,因为我也用同样的方法在那里。这也并不使我面临的问题有什么区别,我是否把它们直接在getView方法或拨打他们像我一样在这里。

PSS: Those three update-methods re-apply the new data from the OrderedProductItem fields in the Spinner / EditTexts / AutoCompleteTextView. I've placed them in the ChecklistActivity since I also use the same method there. This also doesn't make any difference in the problem I'm facing, whether I put them directly in the getView method or call them like I do here.

推荐答案

愿我建议你改变你的code一点点。我的ListView的getView()总是看起来是这样的,我有没有问题吧。

May I suggest you change your code a little. My ListView's getView() always looks like this and I have no problems with it.

@Override
public void getView(int position, View convertView, ViewGroup parent) {

    View view = convertView;
    MyViewHolder holder = new MyViewHolder();

    if (view == null) {

        view = inflater.inflate(layoutResourceId, null);

        holder.textView1 = (TextView) view.findViewById(R.id.my_tv1);
        holder.button1 = (Button) view.findViewById(R.id.my_btn1);
        // More views

        view.setTag(holder);
    } else {
        holder = (MyViewHolder) view.getTag();
    }

    holder.textView1.setText("Some text");
    holder.button1.setText("Some button text");
    holder.button1.setEnabled(buttonEnabled);
}

public static class MyViewHolder {
    public TextView textView1;
    public Button button1;
    // ... and so on
}

这篇关于Android的ListView控件 - 调用次数太多或变更订购。如何prevent两者兼而有之?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆