Reputation: 71
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 of them.
It all works until the point where I have to select a post, I open the spinner
, it shows all the title I select one and nothing happens, the post is not selected in the spinner
and the texts doesn't change, I read like 10 posts about spinners not showing the items but I couldn't make it work, please help me, this is my java code:
package com.example.lagarto.blog;
import android.graphics.Color;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import android.util.Log;
import android.widget.Spinner;
public class MainActivity extends AppCompatActivity {
ArrayList<Post> archivo=new ArrayList<Post>();
ArrayList<String> titulos=new ArrayList<String>();
public ArrayAdapter<String> spinnerArrayAdapter;
private static final String TAG= MainActivity.class.getSimpleName();
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) {
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());
}
});
}
}
@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);
}
}
And this is my xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.lagarto.blog.MainActivity">
<TextView
android:layout_width="400dp"
android:layout_height="50dp"
android:text="Hello World!"
android:textSize="30dp"
android:textAlignment="center"
android:layout_alignParentEnd="true"
android:id="@+id/title"
android:layout_marginTop="40dp"
android:visibility="invisible"
/>
<TextView
android:layout_width="400dp"
android:layout_height="500dp"
android:text="Hello World!"
android:textSize="16dp"
android:layout_marginTop="100dp"
android:layout_alignParentEnd="true"
android:id="@+id/body"
android:visibility="invisible"
/>
<Spinner
android:layout_width="400dp"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:id="@+id/spinner"
android:textSize="20dp"
android:visibility="invisible"
android:backgroundTint="@color/colorPrimaryDark"
android:textAlignment="center"
/>
<TextView
android:text="Waiting for connection please wait"
android:layout_width="400dp"
android:layout_height="100dp"
android:textSize="30dp"
android:textAlignment="center"
android:layout_marginTop="150dp"
android:id="@+id/connection" />
</RelativeLayout>
Upvotes: 0
Views: 1609
Reputation: 1112
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:
GetDBConnection
before your spinner is even ready. Your GetDBConnection
will run Asynchronously in the background, so while you were fetching your data and setting onItemSelectedListener
for your spinner (I'll explain later), you have created a race condition with you setting up your spinner.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());
}
});
}
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 the AsyncTask
on fetching the data onlyAssuming 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!
Upvotes: 1