0

I recently started learning Firebase for Android. I've been completing the Udacity Firebase Weekend course where I created a chat using the database and storage. But I wanted to learn more and try to make it more interactive and at this point I'm struggling with something technically simple and I'd love tips and feedback on how to go on.

I have a class CityList where I display all the cities added to the database using ListView. I also have a floating button that upon clicking opens an EditCity activity where I can submit another city and its description (plus photos but that's not taken care of yet). It works fine, and the new cities show up in the CityList immediately. However what I want to do is click on one city name and have it open another activity, with that specific item, that would display the name + description + photo. To do that, I created another activity SingleCase.

I've been reading all about it and I know there are many answers to this question on here but I feel like I tried all of them and a) it's not working, usually because the code is the older version and no longer supported, b) I can't really understand it.

CityList.java

public class CityList extends AppCompatActivity {

    FirebaseDatabase mFirebaseDatabase;
    DatabaseReference mDatabaseReference;
    private ListView mListView;
    private CityAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cities);
        mListView= (ListView) findViewById(R.id.messageListView);

        List<City> cities = new ArrayList<>();
        mAdapter = new CityAdapter(this, R.layout.item, cities);
        mListView.setAdapter(mAdapter);
        getData();

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(CityList.this, EditCity.class));
            }
        });

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String value = (String) parent.getItemAtPosition(position);
                startActivity(new Intent(CityList.this, SingleCase.class) .putExtra("value", value));
            }
        });
    }

    public void getData() {
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        mDatabaseReference = mFirebaseDatabase.getReference().child("cities");
        mDatabaseReference.addChildEventListener(new ChildEventListener(){
            @Override
            public void onChildAdded (@NonNull DataSnapshot dataSnapshot, @Nullable String s){
                City city = dataSnapshot.getValue(City.class);
                key = dataSnapshot.getKey();
                mAdapter.add(city);
            }

            @Override
            public void onChildChanged (@NonNull DataSnapshot dataSnapshot, @Nullable String s){
            }

            @Override
            public void onChildRemoved (@NonNull DataSnapshot dataSnapshot){
            }

            @Override
            public void onChildMoved (@NonNull DataSnapshot dataSnapshot, @Nullable String s){
            }

            @Override
            public void onCancelled (@NonNull DatabaseError databaseError){
            }
        });
    }
}

City.java

public class City {
    private String title;
    private String desc;

    public City() {
    }

    public City(String title, String desc) {
        this.title = title;
        this.desc = desc;
    }

    public String getText() {
        return title;
    }

    public void setText(String title) {
        this.title = title;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

CityAdapter.java

public class CityAdapter extends ArrayAdapter<City> {
    public CityAdapter(Context context, int resource, List<City> objects) {
        super(context, resource, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.item, parent, false);
        }
        TextView messageTextView = (TextView) convertView.findViewById(R.id.titleTextView);
        TextView descTextView = (TextView) convertView.findViewById(R.id.descTextView);

        City city = getItem(position);

            messageTextView.setVisibility(View.VISIBLE);
            messageTextView.setText(city.getText());
            descTextView.setText(city.getDesc());

        return convertView;
    }
}

SingleCase.java

public class SingleCase extends AppCompatActivity{

    FirebaseDatabase mFirebaseDatabase;
    DatabaseReference mDatabaseReference;
    private ListView mListView;
    private CityAdapter mAdapter;
    private String position;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.single_case);
        position = getIntent().getStringExtra("value");
        mListView= (ListView) findViewById(R.id.messageListView);

        List<City> cities = new ArrayList<>();
        mAdapter = new CityAdapter(this, R.layout.item, cities);
        mListView.setAdapter(mAdapter);
        getData();
    }

    public void getData() {
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        mDatabaseReference = mFirebaseDatabase.getReference().child("cities");
        mDatabaseReference.addChildEventListener(new ChildEventListener(){
            @Override
            public void onChildAdded (@NonNull DataSnapshot dataSnapshot, @Nullable String s){

                City city = dataSnapshot.getValue(City.class);
                mAdapter.add(city);
            }

            @Override
            public void onChildChanged (@NonNull DataSnapshot dataSnapshot, @Nullable String s){
            }

            @Override
            public void onChildRemoved (@NonNull DataSnapshot dataSnapshot){
            }

            @Override
            public void onChildMoved (@NonNull DataSnapshot dataSnapshot, @Nullable String s){
            }

            @Override
            public void onCancelled (@NonNull DatabaseError databaseError){
            }
        });
    }
}

I've been through countless tutorials/videos/forums and I've read all the similar questions here on stack too, but I'm lost. I think if I understand well, I should use position, and I think I'm getting it (and key) right in CityList, but I have no idea what to do next in SingleCase to actually display it. I'm passing the position value and can pass key as well but I don't know where to use it next. I've read I can also use keys, but same thing, I tried getting a reference to .child("cities").child(key) in SingleCase, but it wasn't working (the app would crash). I'm sorry if it's a simple or too often asked question, but I'm new at this and I absolutely can't grasp the position/key concept and how to use it in this case. And yes, I've read the docs too. Any help would be great!

If you need me to post anything else from the code, just let me know, thank you.

EDIT: Posting the most crucial parts of code for clarity:

In CityList.java I'm getting the position variable (called value):

mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        String value = (String) parent.getItemAtPosition(position);
        startActivity(new Intent(CityList.this, SingleCase.class) .putExtra("value", value));
    }
});

Here I also putExtra the position (value) and "send" it to SingleCase. I also get the key in CityList from here:

    public void onChildAdded (@NonNull DataSnapshot dataSnapshot, @Nullable String s){
        City city = dataSnapshot.getValue(City.class);
        key = dataSnapshot.getKey();
        mAdapter.add(city);
    }

When in SingleCase, I have no idea what to do with those two parameters. I suppose they're properly parsed and transferred (key isn't transferred yet but I can do it, I just wasn't sure if position wouldn't be enough to make it work).

The problem is SingleCase. I feel like I should use key/position here to tell the program that I want only one specific City item, but I have no idea how.

    public void onChildAdded (@NonNull DataSnapshot dataSnapshot, @Nullable String s){

        City city = dataSnapshot.getValue(City.class);
        mAdapter.add(city);
    }
  • There's quite a bit of code here, and Stack Overflow is quite inefficient as an interactive debugger. You seem to have a pretty good grasp of what you're trying to accomplish and how you expect to accomplish it. At which specific step do things not work the way you expect them to? For example, after `String value = (String) parent.getItemAtPosition(position)` in the `onClick`: does `value` have the value you expect? What is that value? – Frank van Puffelen Nov 29 '18 at 00:26
  • My initial feeling is that you need to get the key of the item, not the value. And that would point to `adapter.getRef(position)`. See https://stackoverflow.com/questions/32997219/how-to-get-obj-key-from-firebaselistadapter-on-item-click-firebaseui. Except that you're not using FirebaseUI, so I'm not certain how to accomplish the same without it. – Frank van Puffelen Nov 29 '18 at 00:27
  • Sorry for the number of updates: as said, there's a lot (really too much) code here, and I'm trying to understand it. It seems that you're not adding the `key` to the adapter in `onChildAdded`, so there's no way to then get the key of the item when the user clicks on one. – Frank van Puffelen Nov 29 '18 at 00:30
  • @FrankvanPuffelen I'm grateful for any help, thank you! I edited the code and added the most crucial parts on the bottom, sorry, I know there's a lot of it. My main problem is that I don't get how to access the specific item. Say, I want to get third city from my db and its description etc., and display it as another activity once I click on its name on the CityList. Can that be done with position? I tried using getRef before but I don't think I was doing it right, it has to point to the adapter but shouldn't I be accessing a specific dataSnapshot? Does position mean 'n-th record' in db? – Tom J. Hanniger Nov 29 '18 at 01:48
  • To be able to look up the selected item in the other activity, you need to know its key. So when the user clicks on an item, you need to be able to find the **key** for the item they clicked on (from the position they clicked on). This requires that you maintain a list that has both the key and the value of each item in the list. If you'd use FirebaseUI, it comes with adapters that simply keep the list of `DataSnapshot` objects in the correct order, and then have a `getRef(position)` to look up the `DatabaseReference` by its index (and thus `getRef(position).getKey()` to get the key). – Frank van Puffelen Nov 29 '18 at 02:22
  • @FrankvanPuffelen sorry to get back to you in such a long time, I had to put work aside for some time. However thank you for your tips, I decided to use Firebase UI in the end and got it all working in the end. – Tom J. Hanniger Jan 04 '19 at 03:27

0 Answers0