J'ai ce problème étrange qui se passe sur 1.6, 2.2, et une diapositive 3G MyTouch (qui est API # 7, et répertorié comme "2.1-Update1" dans le sélecteur de périphérique Android). Si quelqu'un peut expliquer ce que je fais mal & comment y remédier (ou peut-être confirmer qu'il s'agit d'un bug Android), je l'apprécierais grandement!Pourquoi TextView.setText provoque-t-il le défilement de ScrollView?
L'idée de base pour mon application est de faire un chronomètre-sorte de chose, en ce que l'utilisateur peut appuyer sur un bouton pour démarrer une minuterie, puis appuyez à nouveau pour arrêter (pause) la minuterie; d'autres tapotements alternent entre la reprise de la minuterie et la pause de la minuterie.
J'ai un ScrollView de haut niveau qui contient un RelativelLayout, qui contient un tas de widgets. Le premier widget est un bouton ÉNORME (de sorte qu'il est facile d'appuyer), ce qui pousse tous mes autres widgets au bas de l'écran. C'est intentionnel, car je veux me fier au ScrollView (et à un rappel à l'écran pour l'utilisateur) pour rendre le reste des options de saisie disponibles.
J'ai une configuration de type machine-état simple, où mState est le mode courant (STATE_TIMER_NOT_STARTED avant que l'utilisateur appuie sur des boutons, ... FONCTIONNANT après le premier appui, puis ... PAUSED après le second, retour à ... COURANT après le troisième, etc, etc). Tout cela fonctionne très bien, sauf que lorsque la minuterie est en marche et que l'utilisateur appuie de nouveau sur le bouton marche/arrêt/reprise, le ScrollView défile vers le bas. Je n'émets PAS cette commande (je n'ai même pas de référence à l'objet ScrollView), et je ne sais pas pourquoi c'est ce qu'il fait.
REPRO: Compilez + exécutez les exemples ci-dessous. Lorsque l'application démarre, appuyez sur le bouton 'Start Timing'. Utilisez votre pouce (ou la souris) pour faire glisser l'écran vers le haut (de sorte que vous pouvez voir la RatingBar), puis faites-le glisser vers le bas (de sorte que le bouton est à nouveau complètement à l'écran). Appuyez à nouveau sur le bouton (qui affiche maintenant «PauseTiming»), et il va sauter un peu. Il ne devrait PAS sauter/défiler vers le bas, car il n'y a pas de déclaration (que je peux voir) qui lui dit de faire défiler vers le bas. Aussi près que je peux dire, c'est le setText qui provoque le défilement (quand je commente ces lignes, aucun défilement ne se produit). CE QUE JE DEMANDE POUR: Si je fais quelque chose de stupide & vous pourriez préciser ce que c'est, je l'apprécierais vraiment! :) *** Je me demande si le 'mode tactile' pourrait avoir quelque chose à voir avec cela, car il ne semble pas arriver (dans l'émulateur) quand j'utilise la molette de la souris pour déplacer le panneau vers le haut (ie, le doigt-traînant simulé). Je ne peux pas trouver beaucoup sur le mode tactile, et rien de spécifique sur le focus/sélection en mode tactile dans un ScrollView
Si vous pouvez confirmer que cette erreur se produit pour vous aussi, ce serait bien aussi (depuis la misère aime la compagnie AHEM Je veux dire, car cela pourrait aider à confirmer que ce n'est pas seulement moi :)).
MyTestApp.java
package bug.android.scrollview;
import android.app.Activity;
import android.os.Bundle;
import android.text.format.Time;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
public class MyTestApp extends Activity {
public final static int STATE_TIMER_NOT_STARTED = 1;
public final static int STATE_TIMER_RUNNING = 2;
public final static int STATE_TIMER_PAUSED = 3;
private int mState;
Time t = new Time();
private Time data = new Time();
private Button btnStartStopResume;
private TextView lblSpacer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new_time_entry);
btnStartStopResume = (Button) findViewById(R.id.btnStartStopResume);
// Set the button's size so that the other info will also be visible
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE))
.getDefaultDisplay();
// This is such a hack, but the windowScroller doesn't appear to
// have a height at this point in the lifecycle (nor in 'onResume' :()
btnStartStopResume.setHeight(display.getHeight() - 200);
lblSpacer = (TextView) findViewById(R.id.lblSpacer);
reset();
}
public void doStartStopResume(View v) {
if (mState == MyTestApp.STATE_TIMER_NOT_STARTED) {
mState = MyTestApp.STATE_TIMER_RUNNING;
data.setToNow();
} else if (mState == MyTestApp.STATE_TIMER_RUNNING) {
mState = MyTestApp.STATE_TIMER_PAUSED;
String s = getString(R.string.add_scroll_down_to_add);
lblSpacer.setText(s);
} else if (mState == MyTestApp.STATE_TIMER_PAUSED) {
mState = MyTestApp.STATE_TIMER_RUNNING;
}
}
public void doReset(View v) {
}
public void doNewRunClick(View v) {
}
public void doAddTiming(View v) {
}
public void reset() {
mState = STATE_TIMER_NOT_STARTED;
}
}
new_time_entry.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/windowScroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:id="@+id/btnStartStopResume"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dip"
android:text="Start Timing"
android:textSize="40dp"
android:height="290dp"
android:onClick="doStartStopResume" />
<TextView
android:id="@+id/lblSpacer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btnStartStopResume"
android:layout_centerHorizontal="true"
android:text="@string/add_scroll_down_for_more" />
<TextView
android:id="@+id/lblTimeStartLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/lblSpacer"
android:layout_alignParentLeft="true"
android:clickable="true"
android:onClick="adjustStartTime"
android:text="Start of this run:"
android:textSize="8dp" />
<TextView
android:id="@+id/lblTimeStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lblTimeStartLabel"
android:layout_alignParentLeft="true"
android:clickable="true"
android:onClick="adjustStartTime"
android:text="--:--:-- --"
android:textColor="#FFFFFF"
android:textSize="26dp" />
<TextView
android:id="@+id/lblElapsedLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lblSpacer"
android:layout_alignRight="@id/lblSpacer"
android:layout_marginRight="5dp"
android:text="Elapsed Time:"
android:textSize="8dp" />
<TextView
android:id="@+id/lblTimeElapsed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lblElapsedLabel"
android:layout_alignRight="@id/lblSpacer"
android:layout_marginRight="5dp"
android:textColor="#99ff66"
android:text="-- m -- sec"
android:textSize="26dp"
android:layout_marginBottom="10dip"/>
<CheckBox
android:id="@+id/chkNewRun"
android:onClick="doNewRunClick"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/lblTimeElapsed"
android:text="This is a new run of timings"
android:layout_marginBottom="10dip" />
<TextView
android:id="@+id/lblIntensity"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Intensity (1 = none 5 = max)"
android:layout_below="@id/chkNewRun" />
<RatingBar
android:id="@+id/rbIntensity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lblIntensity"
android:numStars="5"
android:rating="2"
android:layout_marginBottom="5dip" />
<TextView
android:id="@+id/lblNotes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Notes:"
android:layout_below="@id/rbIntensity" />
<EditText
android:id="@+id/txtNotes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_below="@id/lblNotes"
android:layout_marginBottom="10dip" />
<Button
android:id="@+id/btnReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtNotes"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:text="Reset"
android:onClick="doReset" />
<Button
android:id="@+id/btnOk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtNotes"
android:layout_toRightOf="@id/btnReset"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:text="Add Timing To List"
android:onClick="doAddTiming" />
</RelativeLayout>
</ScrollView>
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Timer</string>
<string name="dlg_edit_timing_title">Edit A Timing</string>
<string name="add_scroll_down_for_more">< Scroll down for more options! ></string>
<string name="add_scroll_down_to_add">< Scroll down to save this timing! ></string>
<string name="start_timing">Start Timing\n\n</string>
<string name="stop_timing">Pause Timing\n\n</string>
<string name="resume_timing">Resume Timing\n\n</string>
</resources>
An droidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="bug.android.scrollview"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MyTestApp"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="5" />
</manifest>
MISE À JOUR 1: Ajout
if( btnStartStopResume.isInTouchMode())
Toast.makeText(this, "TOUCH MODE", 2000);
else
Toast.makeText(this, "NOT touch mode", 2000);
ensuite le réglage des points d'arrêt dans le débogueur confirme que le bouton est toujours en mode tactile (indépendamment du fait que le doigt glisser I le panneau vers le haut/bas ou rouler la souris vers le haut/bas). C'est donc une combinaison d'être en mode tactile ET de faire glisser le doigt après le 2ème appui sur le bouton (c'est-à-dire lorsque l'application est en mode 'arrêt/pause') qui provoque le temps supplémentaire impair dans les pauses suivantes.
MISE À JOUR 2: Je viens de remarquer qu'il fait défiler jusqu'à EditText, et pas plus loin. Il semble que lorsque vous déplacez le panneau vers le bas, EditText obtient la sélection, et après l'événement click, ScrollView revient à la chose qui a la sélection. Semble expliquer pourquoi l'approche de la molette de la souris n'a pas ce problème (il déplace la sélection/focus vers le bouton).
http://stackoverflow.com/questions/5375838/scrollview-disable-focus-move/7114179 # 7114179 – yanchenko