加速大量以编程方式创建的按钮显示

Posted

技术标签:

【中文标题】加速大量以编程方式创建的按钮显示【英文标题】:Speeding up big set of programmatically created buttons display 【发布时间】:2017-11-03 16:28:10 【问题描述】:

我有一个活动,它从一个 List 中生成一个以编程方式创建的按钮的可滚动列表(比如说一列),这是读取 sqlite 表的结果,我的问题是随着 List 的增长(因此数量按钮)屏幕的初始绘制变得缓慢(目前绘制 50 个按钮需要 3 秒)所以我正在寻找解决方案。

起初我想使用一个线程(可运行,处理程序或任何最好的),假设在 For 内部创建一个新线程,它迭代列表但它不起作用(或者至少我无法让它工作)所以我的问题是下一个:

从列表开始这是创建大量可滚动按钮的最合适的方式,因此用户在访问屏幕时不会有这样的延迟。

分页可能是一种选择,但我想先了解其他可能性并将其作为最后一个资源。

谢谢,下面是我的代码。

public static void createButtons(LinearLayout llContainer,
                                     List<TestType> TestTypes, List<Test> Tests,
                                     int buttonFontSize) 

        Context oContext = llContainer.getContext();
        String strTheme = TMAppearance.getThemeFromPreferences(oContext);
        testMe = ((ApplicationConfiguration)oContext.getApplicationContext());
        int callerActivity = TestTypes!=null ? 2 : 1;

        if (TestTypes!=null || Tests!=null) 
            int lCols = strTheme.equals(testMe.theme_vivid) ? 1 : 2;

            //int sourceElementIndex = 0;
            int originListSize = calculateOriginalListSize(callerActivity, TestTypes, Tests);
            int lRows = (int) Math.ceil((double)originListSize/lCols);

            List<String> aStartColors = TMUtils_ThemeVivid.generateStartColorArray(lRows, oContext);
            List<String> aEndColors = TMUtils_ThemeVivid.generateEndColorArray(lRows, oContext);

            for (i = 0; i < lRows; i++) 

                LinearLayout outerButtonLayout = generateOuterButtonLayout(oContext);

                for (j = 0; j < lCols; j++) 

                    final Thread r = new Thread() 
                        public void run() 
                            LinearLayout innerButtonLayout = generateInnerButtonLayout(oContext);
                            outerButtonLayout.addView(innerButtonLayout, j);

                            if (sourceElementIndex<originListSize)
                                final TMButton oButton = new TMButton(oContext);

                                if (callerActivity==1)  //testMenu
                                    setTestMenuButtonSettings(oButton, sourceElementIndex, Tests);
                                 else 
                                    if (callerActivity==2)  //testTypeMenu
                                        setTestTypeMenuButtonSettings(oButton, sourceElementIndex, TestTypes);
                                    
                                

                                if (strTheme.equals(testMe.theme_vivid))
                                    oButton.fontSize = buttonFontSize;
                                    oButton.gradientStartColor = aStartColors.get(i);
                                    oButton.gradientEndColor = aEndColors.get(i);
                                else
                                    if (strTheme.equals(testMe.theme_purple))
                                        oButton.gradientStartColor = testMe.btnStartColor_purple;
                                        oButton.gradientEndColor = testMe.btnEndColor_purple;
                                    
                                

                                configureButton(oButton, callerActivity);

                                oButton.setOnTouchListener(new OnTouchListener() 
                                    @Override
                                    public boolean onTouch(View v, MotionEvent event) 

                                        Context oContext = v.getContext();
                                        TMButton oButton = (TMButton) v;

                                        int callerActivity = Integer.valueOf(v.getTag().toString().split("@")[0]);
                                        String sourceId = String.valueOf(v.getTag().toString().split("@")[1]);

                                        if(event.getAction() == MotionEvent.ACTION_DOWN)  //pressed
                                            setButtonPressed(oButton);
                                            TMSound.playButtonSound(oContext);
                                         else if (event.getAction() == MotionEvent.ACTION_UP)  //released
                                            setButtonReleased(oButton);
                                            startTargetActivity(callerActivity, sourceId, oContext);
                                        

                                        return true;
                                    
                                );
                                TMAppearance.doButtonAnimation(oContext, oButton, i);
                                innerButtonLayout.addView(oButton);
                                sourceElementIndex++;
                            
                        
                    ;
                    r.run();
                
                llContainer.addView(outerButtonLayout);
            
        
    

【问题讨论】:

您反对使用ListViewRecycleView 吗?通过使用自定义布局,它们可以非常灵活。 非常感谢 Barns52 的回复,不,我当然不反对 ListView,事实上我正在研究如何实现 ListView,因为我的按钮是以编程方式生成的。您认为我可以毫无问题地使用带有动态生成按钮的 ListView 吗? 我喜欢 ListView(怀旧? ;-) )但 RecyclerView 被介绍为与 ListView 相比具有更好的性能,例如滚动时。 再次感谢您的回复@0X0nosugar,但我问了同样的问题:是否可以将 RecycleView 与动态生成的按钮列表一起使用? 是的。不可以。可以将 RecyclerView 与包含信息的数据列表一起使用,以便 RecyclerView 适配器可以决定将哪种视图类型用于一个数据列表条目。各种视图类型可能是您不同的按钮类型。所以总而言之,是的:) 【参考方案1】:

0X0nosugar 是正确的。 RecycleView 将提供更好的性能,但许多初学者很难实现它,并且只有 50 个按钮的性能应该不是问题。尽管我通常喜欢遵守“使用最佳可用解决方案”的规则,但我认为学习如何实施ListView 仍然是合适的。所以……

您需要创建一个自定义适配器:

public class MyListDataAdapter extends ArrayAdapter<MyListData> 

    private static final String TAG = "MyListDataAdapter";


    public MyListDataAdapter(Context context, ArrayList<MyListData> data) 
        super(context, 0, data);

    

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

        final MyListData data = getItem(position);

        if(convertView == null)
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.edit_list_item, parent, false);
        

        // Add a TextView if you need one
        final TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
        Button btnEditTicketHolder = (Button) convertView.findViewById(R.id.btnEdit);
        btnEditTicketHolder.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                long userId = data.getUserId();
                String fName = data.getFirstName();

                Intent intent = new Intent(getContext(), EditActivity.class);
                intent.putExtra("userId", userId);
                intent.putExtra("fName", fName);
                getContext().startActivity(intent);
            
        );


        String name = data.getFirstName();
        tvName.setText(name);
        return convertView;
    


现在您需要 MyListData 类来保存数据:

public class MyListData 

    // Add more as you need
    private long userId;
    private String firstName;

    public MyListData()
    


    public void setFirstName(String name) this.firstName = name; 
    public void setUserId(long id) this.userId = id; 

    public String getFirstName() return this.firstName; 
    public long getUserId() return this.userId; 

您的自定义 ListView 布局可能类似于:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_
              android:layout_
    >

    <LinearLayout
        android:orientation="vertical"
        android:layout_
        android:layout_
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        >

        <TextView
            android:id="@+id/tvName"
            android:text="With Whom"
            android:layout_
            android:layout_
            />

    </LinearLayout>

    <LinearLayout
        android:orientation="vertical"
        android:layout_
        android:layout_
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        >

        <Button
            android:id="@+id/btnEdit"
            android:layout_
            android:layout_
            android:paddingRight="10dp"
            android:paddingLeft="10dp"
            android:text="Edit"
            android:backgroundTint="@color/colorAccent"
            android:focusable="false"
            />

    </LinearLayout>

</RelativeLayout>

在您的活动中(例如在onCreate() 方法中),您需要在ListView 的位置填充ListView 的数据。这应该在 UI 线程上完成

    ArrayList<MyListData> arrayListData = new ArrayList<MyListData>();
    MyListDataAdapter adapter = new MyListDataAdapter(this, arrayListData);

    for (MyListData g : result) 
        adapter.add(g);
    
    mLstMy.setAdapter(adapter);

如果需要,还可以在要维护ListView 的活动中设置一些onClick 事件处理程序: (我发现ListView 的一个小优点是onClick 事件比RecycleView 更容易实现)

mMyLst = (ListView) findViewById(lstMy);
mMyLst.setOnItemClickListener(new AdapterView.OnItemClickListener() 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long l) 
        MyListData data = (MyListData) parent.getItemAtPosition(position);
        selectedName = data.getName();

        Intent intent = new Intent(ShowListDataActivity.this, MainActivity.class);
        startActivity(intent);
    
);

mMyLst.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() 
    @Override
    public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) 

        MyListData data = (MyistData) adapterView.getItemAtPosition(i);
        selectedName = data.getFirstName();

        selectedTicketPosition = i;
        // removeValue is my own method for removing an entry from the 
        // list MyListData and then call adapter.notifyDataSetChanged();
        removeValue(i);

        return true;
    
);

【讨论】:

非常感谢@Barns52 对 ListView 的精彩解释。最后,我能够实现 RecyclerView,但结果相同,并发现延迟出现在 sqlite 查询中。我能够优化该查询,因此屏幕渲染现在要快得多。由于您在所有 ListView 教程中的努力,我会将您的答案设置为正确的。

以上是关于加速大量以编程方式创建的按钮显示的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式创建的后退按钮未显示在导航栏中?

以编程方式创建带有后退按钮的导航控制器

以编程方式创建的后退按钮没有箭头

点击 UIButton 以编程方式创建另一个 UIButton

以编程方式创建 UILabel、UITextField 等时的图形错误

如何更改以编程方式创建的一组 UIButton 的标题颜色?