Spinner Doesn't Show The Selected Item
Hi I have made a simple app in android that connects to a server and gets a list of posts, then I have a spinner that gets filled with the title of each post and you can select one
Solution 1:
Ok here are my assessments:
TL;DR:
You tried to update the UI on a Background Thread.
Explanation:
This is your piece of code on onCreate
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new GetDBConnection().execute(0);
Spinner spinner=(Spinner) findViewById(R.id.spinner);
spinnerArrayAdapter=new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, titulos);
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerArrayAdapter);
spinner.setSelection(1);
System.out.println(archivo);
}
A few pointers based on this:
- You called the
GetDBConnection
before your spinner is even ready. YourGetDBConnection
will run Asynchronously in the background, so while you were fetching your data and settingonItemSelectedListener
for your spinner (I'll explain later), you have created a race condition with you setting up your spinner. - It seems like your spinner is populated, but it was not a good practice nonetheless.
- Then, you set the selection, whether the Asynchronous call has been completed or not using
spinner.setSelection(1);
. I forgot whether this should throw an exception or not.
Now on to your onPostExecute
piece of code:
@Override
protected void onPostExecute(String result) {
Spinner spinner=(Spinner) findViewById(R.id.spinner);
spinner.setVisibility(View.VISIBLE);
for (Post i:archivo) {
titulos.add(i.getTitle());
}
TextView title=(TextView) findViewById(R.id.title);
TextView body=(TextView) findViewById(R.id.body);
title.setVisibility(View.VISIBLE);
body.setVisibility(View.VISIBLE);
TextView connection=(TextView) findViewById(R.id.connection);
connection.setVisibility(View.INVISIBLE);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
spinnerArrayAdapter.notifyDataSetChanged();
Post resultado=archivo.get(position);
title.setText(resultado.getTitle());
body.setText(resultado.getBody());
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
spinnerArrayAdapter.notifyDataSetChanged();
Post resultado=archivo.get(0);
title.setText(resultado.getTitle());
body.setText(resultado.getBody());
}
});
}
- If you want to update your spinner's elements on the fly, you should also include methods to update (add, delete, or update) the List elements in your Adapter.
- Like I mentioned earlier, putting a listener in an
AsyncTask
is, while perhaps applicable, not a good practice. Not to mention you try to change the UI, too (setting the TextView values). This will produce error because your call is not on the UI Thread. That's why I said you should focus theAsyncTask
on fetching the data only
Assuming you have your own Custom Adapter, here is my custom adapter that is capable to handle update of data:
public abstract class CustomListAdapter<T> extends BaseAdapter {
protected List<T> mData = new ArrayList<>();
protected LayoutInflater mInflater;
protected ViewHolder holder;
/*truncated*/
public void addItem(T item){
mData.add(item);
notifyDataSetChanged();
}
public void clear(){
mData = new ArrayList<>();
notifyDataSetChanged();
}
public void deleteItem(int position){
mData.remove(position);
notifyDataSetChanged();
}
public void fill(Collection<T> items){
mData.addAll(items);
notifyDataSetChanged();
}
}
Of course, this is just an example.
Summary:
Try to update your code as follows:
//Declare your elements here first;
TextView title;
TextView body;
TextView connection;
Spinner spinner;
private class GetDBConnection extends AsyncTask<Integer, Void, String>{
@Override
protected String doInBackground(Integer... params) {
try{
Connection conn= DBConnection.getInstance().getConnection();
Statement st= conn.createStatement();
String sql=("SELECT * FROM posts");
ResultSet rs=st.executeQuery(sql);
while(rs.next()) {
int id = rs.getInt("Id");
String title = rs.getString("Title");
String body = rs.getString("Body");
String date = rs.getString("Date");
Post post = new Post(id, title, body, date);
archivo.add(post);
System.out.println(archivo);
}
Log.d(TAG,"Terminado");
}catch(SQLException e){
e.printStackTrace();
}
return "Valido";
}
@Override
protected void onPostExecute(String result) {
/*
all code that changes your UI should resides in the UI Thread. This is still in Background Thread.
*/
//Spinner spinner=(Spinner) findViewById(R.id.spinner); --> this is also unneccessary
//spinner.setVisibility(View.VISIBLE);
/*
something like this is possible as long as it doesn't change your UI.
*/
for (Post i:archivo) {
titulos.add(i.getTitle());
/*
- update your Adapter List elements here.
*/
}
// - also call your adapter notifyDataSetChanged here, if neccessary.
//TextView title=(TextView) findViewById(R.id.title); --> unneccessary!
//TextView body=(TextView) findViewById(R.id.body); --> unneccessary!
//title.setVisibility(View.VISIBLE);
//body.setVisibility(View.VISIBLE);
//TextView connection=(TextView) findViewById(R.id.connection); --> unneccessary!
//connection.setVisibility(View.INVISIBLE);
/*
instead, you can call another method that resides in the UI Thread
*/
// updateUI();
/*
or, execute them under runOnUIThread()
*/
runOnUIThread(new Runnable(){
@Override
public void run(){
spinner.setVisibility(View.VISIBLE);
title.setVisibility(View.VISIBLE);
body.setVisibility(View.VISIBLE);
connection.setVisibility(View.INVISIBLE);
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
you should initiate ALL your UI elements on the onCreate()
*/
spinner=(Spinner) findViewById(R.id.spinner);
title=(TextView) findViewById(R.id.title);
body=(TextView) findViewById(R.id.body);
connection=(TextView) findViewById(R.id.connection);
spinnerArrayAdapter=new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, titulos);
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
/*
then, set your listeners
*/
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//spinnerArrayAdapter.notifyDataSetChanged(); --> this is unneccessary
Post resultado=archivo.get(position);
title.setText(resultado.getTitle());
body.setText(resultado.getBody());
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
//spinnerArrayAdapter.notifyDataSetChanged(); --> this is unneccessary
Post resultado=archivo.get(0);
title.setText(resultado.getTitle());
body.setText(resultado.getBody());
}
});
spinner.setAdapter(spinnerArrayAdapter);
System.out.println(archivo);
new GetDBConnection().execute(0); // --> After all has been set, then execute your AsyncTask.
}
private void updateUI(){
spinner.setVisibility(View.VISIBLE);
title.setVisibility(View.VISIBLE);
body.setVisibility(View.VISIBLE);
connection.setVisibility(View.INVISIBLE);
}
}
Hope this helps!
Post a Comment for "Spinner Doesn't Show The Selected Item"