oscar rodriguez
oscar rodriguez

Reputation: 71

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 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

Answers (1)

cipley
cipley

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:

  • You called the 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.
  • 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 the AsyncTask 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!

Upvotes: 1

Related Questions