Android - 动态视图计时问题?
Android - Dynamic View Timing Issue?
我是一名 Android 新手开发人员,我正在开发的一个 Android 小应用程序有问题,我怀疑它与动态视图创建的时间有关。这是一个小记分员应用程序,它具有动态生成的播放器按钮和文本字段,在播放器 class 中定义。以下是我如何执行此操作的摘录:
public Player(String name, int score, int number, boolean enabled, RelativeLayout mainScreen, List<Button> presetButtonList, Editor editor, Context context) {
super();
this.name = name;
this.score = score;
this.number = number;
this.enabled = enabled;
this.editor = editor;
// Get the lowest child on the mainScreen
Integer count = mainScreen.getChildCount(), lowestChildId = null;
Float lowestChildBottom = null;
for (int i = 0; i < count; i++) {
View child = mainScreen.getChildAt(i);
if ((lowestChildId == null || child.getY() > lowestChildBottom) &&
!presetButtonList.contains(child)) {
lowestChildId = child.getId();
lowestChildBottom = child.getY();
}
}
playerNameText = (EditText) setTextViewDefaults(new EditText(context), name);
playerNameText.setSingleLine();
// playerNameText.setMaxWidth(mainScreen.getWidth());
// playerNameText.setEllipsize(TextUtils.TruncateAt.END); //TODO: Prevent names which are too long for screen
playerNameText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable changedText) {
setName(changedText.toString());
updateScreen();
}
public void beforeTextChanged(CharSequence changedText, int start, int count, int after) {}
public void onTextChanged(CharSequence changedText, int start, int before, int count) {}
});
RLParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (lowestChildId != null) {
RLParams.addRule(RelativeLayout.BELOW, lowestChildId);
} else {
RLParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
RLParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
}
RLParams.setMargins(0, 10, 0, 10);
mainScreen.addView(playerNameText, RLParams);
class 以类似的方式进一步定义按钮等,将顶部与名称文本视图对齐。当用户单击按钮添加播放器时,我调用它,它工作正常,在屏幕下方显示第一个播放器下方的每个播放器。当我一开始加载一堆保存的玩家时,问题就来了。这是我加载播放器的位置:
public void loadData() {
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
playerCount = savedData.getInt("playerCount", playerCount);
Player player;
String name;
playerList.clear();
for (int i=1; i<=playerCount; i++) {
name = savedData.getString("name" + i, null);
if (name != null) {
Log.v("name", name);
player = new Player(name, savedData.getInt("score" + i, 0), i, savedData.getBoolean("enabled" + i, false), mainScreen, presetButtonList, editor, this);
playerList.add(player);
player.updateScreen();
}
}
updateScreen();
}
最后,我在应用程序启动时调用 loadData() 方法,此处:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedData = this.getPreferences(Context.MODE_PRIVATE);
editor = savedData.edit();
presetButtonList.add((Button)findViewById(R.id.newGame));
presetButtonList.add((Button)findViewById(R.id.addPlayer));
presetButtonList.add((Button)findViewById(R.id.removePlayer));
loadData();
}
结果呢?当有两个以上的玩家要加载时,所有玩家都会加载到同一个位置,在玩家 2 之上。
我怀疑玩家是同时生成的,因此都认为最低玩家视图是玩家 1,而不是相互检查。我试过在 onCreate() 之后触发加载,但它仍然会发生。我还尝试在每个玩家加载后在 loadData() 的 for 循环中添加 3 秒睡眠,看看是否有帮助,但没有成功。
我是不是做出了错误的假设?我做错了什么,我该如何解决?
我怀疑这里发生的事情是您试图定位没有 ID 的视图(看起来您没有在代码中的任何位置设置它们)。动态创建的视图不会自动具有 ID,因此调用 getId()
将 return NO_ID
(See documentation). If you want to use an ID you will have to set it yourself using View.setId().
毕竟是时间问题。我需要等待主屏幕加载,然后才能向其添加视图。不幸的是,在 UI 线程上的所有 activity 完成之前,它永远不会加载。
因此,我需要线程。我最终将 loadData() 放在一个线程中,如下所示:
new Thread(new loadDataAsync(this)).start();
class loadDataAsync implements Runnable {
MainActivity mainActivity;
public loadDataAsync(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
@Override
public void run() {
try {
Thread.sleep(700);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
loadData(mainActivity);
}
}
然后我在初始睡眠后在 UI 线程上创建播放器,以便为 UI 加载留出时间。
runOnUiThread(new Runnable() {
Player player;
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
@Override
public void run() {
player = new Player(savedData.getString("name" + playerNum, null), savedData.getInt("score" + playerNum, 0), playerNum, savedData.getBoolean("enabled" + playerNum, false), mainScreen, presetButtonList, editor, mainActivity);
playerList.add(player);
player.updateScreen();
mainScreen.invalidate();
}
});
这不是最优雅的解决方案,但它现在有效。
我是一名 Android 新手开发人员,我正在开发的一个 Android 小应用程序有问题,我怀疑它与动态视图创建的时间有关。这是一个小记分员应用程序,它具有动态生成的播放器按钮和文本字段,在播放器 class 中定义。以下是我如何执行此操作的摘录:
public Player(String name, int score, int number, boolean enabled, RelativeLayout mainScreen, List<Button> presetButtonList, Editor editor, Context context) {
super();
this.name = name;
this.score = score;
this.number = number;
this.enabled = enabled;
this.editor = editor;
// Get the lowest child on the mainScreen
Integer count = mainScreen.getChildCount(), lowestChildId = null;
Float lowestChildBottom = null;
for (int i = 0; i < count; i++) {
View child = mainScreen.getChildAt(i);
if ((lowestChildId == null || child.getY() > lowestChildBottom) &&
!presetButtonList.contains(child)) {
lowestChildId = child.getId();
lowestChildBottom = child.getY();
}
}
playerNameText = (EditText) setTextViewDefaults(new EditText(context), name);
playerNameText.setSingleLine();
// playerNameText.setMaxWidth(mainScreen.getWidth());
// playerNameText.setEllipsize(TextUtils.TruncateAt.END); //TODO: Prevent names which are too long for screen
playerNameText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable changedText) {
setName(changedText.toString());
updateScreen();
}
public void beforeTextChanged(CharSequence changedText, int start, int count, int after) {}
public void onTextChanged(CharSequence changedText, int start, int before, int count) {}
});
RLParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (lowestChildId != null) {
RLParams.addRule(RelativeLayout.BELOW, lowestChildId);
} else {
RLParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
RLParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
}
RLParams.setMargins(0, 10, 0, 10);
mainScreen.addView(playerNameText, RLParams);
class 以类似的方式进一步定义按钮等,将顶部与名称文本视图对齐。当用户单击按钮添加播放器时,我调用它,它工作正常,在屏幕下方显示第一个播放器下方的每个播放器。当我一开始加载一堆保存的玩家时,问题就来了。这是我加载播放器的位置:
public void loadData() {
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
playerCount = savedData.getInt("playerCount", playerCount);
Player player;
String name;
playerList.clear();
for (int i=1; i<=playerCount; i++) {
name = savedData.getString("name" + i, null);
if (name != null) {
Log.v("name", name);
player = new Player(name, savedData.getInt("score" + i, 0), i, savedData.getBoolean("enabled" + i, false), mainScreen, presetButtonList, editor, this);
playerList.add(player);
player.updateScreen();
}
}
updateScreen();
}
最后,我在应用程序启动时调用 loadData() 方法,此处:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedData = this.getPreferences(Context.MODE_PRIVATE);
editor = savedData.edit();
presetButtonList.add((Button)findViewById(R.id.newGame));
presetButtonList.add((Button)findViewById(R.id.addPlayer));
presetButtonList.add((Button)findViewById(R.id.removePlayer));
loadData();
}
结果呢?当有两个以上的玩家要加载时,所有玩家都会加载到同一个位置,在玩家 2 之上。
我怀疑玩家是同时生成的,因此都认为最低玩家视图是玩家 1,而不是相互检查。我试过在 onCreate() 之后触发加载,但它仍然会发生。我还尝试在每个玩家加载后在 loadData() 的 for 循环中添加 3 秒睡眠,看看是否有帮助,但没有成功。
我是不是做出了错误的假设?我做错了什么,我该如何解决?
我怀疑这里发生的事情是您试图定位没有 ID 的视图(看起来您没有在代码中的任何位置设置它们)。动态创建的视图不会自动具有 ID,因此调用 getId()
将 return NO_ID
(See documentation). If you want to use an ID you will have to set it yourself using View.setId().
毕竟是时间问题。我需要等待主屏幕加载,然后才能向其添加视图。不幸的是,在 UI 线程上的所有 activity 完成之前,它永远不会加载。
因此,我需要线程。我最终将 loadData() 放在一个线程中,如下所示:
new Thread(new loadDataAsync(this)).start();
class loadDataAsync implements Runnable {
MainActivity mainActivity;
public loadDataAsync(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
@Override
public void run() {
try {
Thread.sleep(700);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
loadData(mainActivity);
}
}
然后我在初始睡眠后在 UI 线程上创建播放器,以便为 UI 加载留出时间。
runOnUiThread(new Runnable() {
Player player;
RelativeLayout mainScreen = (RelativeLayout)findViewById(R.id.relativeLayout);
@Override
public void run() {
player = new Player(savedData.getString("name" + playerNum, null), savedData.getInt("score" + playerNum, 0), playerNum, savedData.getBoolean("enabled" + playerNum, false), mainScreen, presetButtonList, editor, mainActivity);
playerList.add(player);
player.updateScreen();
mainScreen.invalidate();
}
});
这不是最优雅的解决方案,但它现在有效。