加速大量以编程方式创建的按钮显示
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);
【问题讨论】:
您反对使用ListView
或RecycleView
吗?通过使用自定义布局,它们可以非常灵活。
非常感谢 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